Android Open Source - SanDisk-HQME-SDK Unten Cache Service






From Project

Back to project page SanDisk-HQME-SDK.

License

The source code is released under:

Apache License

If you think the Android project SanDisk-HQME-SDK listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/** 
* This reference code is an implementation of the IEEE P2200 standard.  It is not
* a contribution to the IEEE P2200 standard.
* /* w w  w  .ja  va  2 s.com*/
* Copyright (c) 2011 SanDisk Corporation.  All rights reserved.
* 
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License.  You may obtain a copy of the
* License at
* 
*        http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
* 
* See the License for the specific language governing permissions and limitations
* under the License.
*/

package com.hqme.cm.cache;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.StatFs;
import android.util.Log;

import com.hqme.cm.IVSD;
import com.hqme.cm.IStorageManager;
import com.hqme.cm.IContentObject;
import com.hqme.cm.HqmeError;
import com.hqme.cm.VSDEvent;
import com.hqme.cm.Property;
import com.hqme.cm.VSDProperties;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.OverlappingFileLockException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;

public class UntenCacheService extends Service {
    private static final String sTag = "UntenCacheService";
    private static final String[] EMPTY_STRINGS = new String[0];
    private static final long[] EMPTY_LONGS = new long[0];

    private static String sStorageName = "Default Cache";
    private static int sStorageId = -1;
    private static long[] sFunctionGroups = EMPTY_LONGS;

    static HashMap<String, HashMap<String, UntenCacheObject>> sObjects = new HashMap<String, HashMap<String, UntenCacheObject>>();
    private static HashMap<String, HashMap<String, Properties>> sObjectProperties = new HashMap<String, HashMap<String, Properties>>();
    private static long sObjectCount = 0;
    
    private static Properties sProperties = new Properties();
    private static File sMetaFile = null;

    protected static File sRoot = null;
    protected static IStorageManager sPluginManagerProxy = null;
    protected static boolean sLoaded = false;
   
    private static PackageManager sPackageManager = null;
    static Context sPluginContext = null;
    static boolean sIsDebugMode = false;

    private static final StreamingServer streamingServerHandler = new StreamingServer();
    private static Thread streamingServerWorker = null;

    /**********************************************************************************************
     * UntenCache is the default IVSD plug-in implementation. The IVSD interface links 
     * [any of several] underlying physical or virtual storage device with the IVSD 
     * public interface. The UntenCacheService serves as the default external storage
     * adaptor; it uses the public file system partition of any external storage
     * card as the backing store.
     */
    protected static final IVSD.Stub sUntenCache = new IVSD.Stub() {
        @Override
        public int storageId() throws RemoteException {
            return sStorageId;
        }

        @Override
        public String name() throws RemoteException {
            return sStorageName;
        }

        @Override
        public long[] functionGroups() throws RemoteException {
            return sFunctionGroups;
        }

        @Override
        public String[] allObjects(String filter) throws RemoteException {
            if (su()) {
                ArrayList<String> paths = new ArrayList<String>();
                for (String origin : sObjects.keySet()) {
                    for (String name : allObjects(filter, origin)) {
                        paths.add(origin + ":/" + name);
                    }
                }
                return paths.toArray(EMPTY_STRINGS);
            } else {
                return allObjects(filter, getCallingOrigin());
            }
        }
        
        private String[] allObjects(String filter, String origin) {
            synchronized (sObjects) {
                ArrayList<String> paths = new ArrayList<String>();
                try {
                    HashMap<String, UntenCacheObject> objects = getObjects(origin);
                    String _filter = normalize(filter);
                    for (String name : objects.keySet()) {
                        if (name.contains(_filter)) {
                            paths.add(objects.get(name).mName);
                        }
                    }
                } catch (NullPointerException e) {
                }
                return paths.toArray(EMPTY_STRINGS);
            }
        }
        
        @Override
        public IContentObject getObject(String name) throws RemoteException {
            String callingOrigin = getCallingOrigin();
            String targetOrigin = null;
            String elements[] = name.split(":/");
            if (elements != null && elements.length == 2) {
                targetOrigin = elements[0];
                name = elements[1];
            } else if (elements.length != 1) {
                return null;
            }
            String key = normalize(name);
            synchronized (sObjects) {
                try {
                    if (su()) {
                        if (targetOrigin != null) {
                            if (getObjects(targetOrigin).containsKey(key)) {
                                return new UntenCacheObject(name, targetOrigin);
                            }
                        } else {
                            if (getObjects(callingOrigin).containsKey(key)) {
                                return new UntenCacheObject(name, callingOrigin);
                            } else {
                                for (String otherOrigin : sObjects.keySet()) {
                                    if (getObjects(otherOrigin).containsKey(key)) {
                                        return new UntenCacheObject(name, otherOrigin);
                                    }
                                }
                            }
                        }
                    } else {
                        if (targetOrigin == null || targetOrigin.equals(callingOrigin)) {
                            if (getObjects(callingOrigin).containsKey(key)) {
                                return new UntenCacheObject(name, callingOrigin);
                            }
                        }
                    }
                } catch (NullPointerException e) {
                }
                return null;
            }
        }

        @Override
        public IContentObject createObject(String name) throws RemoteException {
            String callingOrigin = getCallingOrigin();
            String targetOrigin = null;
            String elements[] = name.split(":/");
            if (elements != null && elements.length == 2) {
                targetOrigin = elements[0];
                name = elements[1];
            } else if (elements.length != 1) {
                return null;
            }
            synchronized (sObjects) {
                try {
                    if (su()) {
                        if (targetOrigin != null) {
                            return createObject(name, targetOrigin);
                        } else {
                            return createObject(name, callingOrigin);
                        }
                    } else {
                        if (targetOrigin == null || targetOrigin.equals(callingOrigin)) {
                            return createObject(name, callingOrigin);
                        }
                    }
                } catch (NullPointerException e) {
                }
                return null;
            }
        }

        private IContentObject createObject(String name, String origin) {
            synchronized (sObjects) {
                if (name == null || origin == null) {
                    return null;
                }
                
                HashMap<String, UntenCacheObject> objects = getObjects(origin);
                
                UntenCacheObject obj = null;
                String key = normalize(name);
                if (objects.containsKey(key)) {
                    try {
                        int result = HqmeError.STATUS_SUCCESS.getCode();
                        obj = objects.get(key);
                        if (obj != null && obj.isValidObject()) {
                            result = obj.remove();
                            if (result != HqmeError.STATUS_SUCCESS.getCode()) {
                                Log.w(sTag, "createObject: cannot remove the overwriting object: " + name);
                                return null;
                            }
                        } else {
                            objects.remove(key);
                            sObjectProperties.get(origin).remove(key);
                        }
                    } catch (RemoteException e) {
                        e.printStackTrace();
                        Log.e(sTag, "createObject: RemoteException fault:" + e);
                    }
                }

                obj = new UntenCacheObject(name, origin);
                if (!obj.isValidObject()) {
                    Log.e(sTag, "createObject: cannot create object: " + name);
                    obj = null;
                } else {
                    objects.put(key, obj);
                    if (!sObjects.containsKey(origin)) {
                        sObjects.put(origin, objects);
                    }
                }
                
                return obj;
            }
        }

        @Override
        public int removeObject(String name) throws RemoteException {
            String callingOrigin = getCallingOrigin();
            String targetOrigin = null;
            String elements[] = name.split(":/");
            if (elements != null && elements.length == 2) {
                targetOrigin = elements[0];
                name = elements[1];
            } else if (elements.length != 1) {
                return HqmeError.ERR_GENERAL.getCode();
            }
            synchronized (sObjects) {
                try {
                    if (su()) {
                        if (targetOrigin != null) {
                            return removeObject(name, targetOrigin);
                        } else {
                            return removeObject(name, callingOrigin);
                        }
                    } else {
                        if (targetOrigin == null || targetOrigin.equals(callingOrigin)) {
                            return removeObject(name, callingOrigin);
                        }
                    }
                } catch (NullPointerException e) {
                }
                return HqmeError.ERR_GENERAL.getCode();
            }
        }
        
        private int removeObject(String name, String origin) {
            synchronized (sObjects) {
                if (name == null || origin == null) {
                    return HqmeError.ERR_NOT_FOUND.getCode();
                }
                
                if (!sObjects.containsKey(origin)) {
                    return HqmeError.ERR_NOT_FOUND.getCode();
                }

                HashMap<String, UntenCacheObject> objects = getObjects(origin);
                String key = normalize(name);
                if (!objects.containsKey(key)) {
                    return HqmeError.ERR_NOT_FOUND.getCode();
                }
                
                try {
                    int result = HqmeError.STATUS_SUCCESS.getCode();
                    UntenCacheObject obj = objects.get(key);
                    if (obj != null) {
                        result = obj.remove();
                        if (result == HqmeError.STATUS_SUCCESS.getCode()) {
                            obj = null;
                        }
                    } else {
                        objects.remove(key);
                        sObjectProperties.get(origin).remove(key);
                    }
                    return result;
                } catch (RemoteException e) {
                    e.printStackTrace();
                    Log.e(sTag, "removeObject: RemoteException fault:" + e);
                    return HqmeError.ERR_GENERAL.getCode();
                }
            }
        }

        @Override
        public String[] getPropertyKeys() throws RemoteException {
            ArrayList<String> arrayList = new ArrayList<String>();

            // add default properties
            arrayList.add("VS_ROOT_FOLDER");
            
            // add defined properties
            for (VSDProperties.VSProperty mandatoryKey : VSDProperties.VSProperty.values()) {
                arrayList.add(mandatoryKey.name());
            }

            // add user properties
            for (Enumeration e = sProperties.keys(); e.hasMoreElements();) {
                arrayList.add((String)e.nextElement());
            }

            return arrayList.toArray(EMPTY_STRINGS);
        }

        @Override
        public String getProperty(String key) throws RemoteException {
            try {
                if (key.equals("VS_ROOT_FOLDER")) {
                    return sRoot.getAbsolutePath();
                } else if (key.equals(VSDProperties.VSProperty.VS_FN_GROUPS.name())) {
                    return "";
                } else if (key.equals(VSDProperties.VSProperty.VS_TOTAL_CAPACITY.name())) {
                    File root = Environment.getExternalStorageDirectory();
                    // return root.getTotalSpace();
                    StatFs fs = new StatFs(root.getAbsolutePath());
                    if (fs != null) {
                        int totalBlocks = fs.getBlockCount();
                        int blockSize = fs.getBlockSize();
                        return Long.toString((long) totalBlocks * blockSize);
                    }
                } else if (key.equals(VSDProperties.VSProperty.VS_AVAILABLE_CAPACITY.name())) {
                    File root = Environment.getExternalStorageDirectory();
                    // return root.getFreeSpace();
                    StatFs fs = new StatFs(root.getAbsolutePath());
                    if (fs != null) {
                        int freeBlocks = fs.getAvailableBlocks();
                        int blockSize = fs.getBlockSize();
                        return Long.toString((long) freeBlocks * blockSize);
                    }
                } else if (key.equals(VSDProperties.VSProperty.VS_OBJECT_COUNT.name())) {
                    int count = 0;
                    if (su()) {
                        for (String origin : sObjects.keySet()) {
                            count += getObjects(origin).size();
                        }
                    } else {
                        count = getObjects(getCallingOrigin()).size(); 
                    }
                    return Integer.toString(count);
                }
                
                synchronized (sProperties) {
                    return sProperties.getProperty(key);
                }
            } catch (NullPointerException e) {
                return null;
            }
        }

        @Override
        public int setProperty(String key, String value) throws RemoteException {
            try {
                // Backing store properties (none of these properties are writable)
                if (key.equals("VS_ROOT_FOLDER")) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals("VS_STORAGE_ID")) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals(VSDProperties.VSProperty.VS_FN_GROUPS.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals(VSDProperties.VSProperty.VS_TOTAL_CAPACITY.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals(VSDProperties.VSProperty.VS_AVAILABLE_CAPACITY.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals(VSDProperties.VSProperty.VS_OBJECT_COUNT.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                }
    
                synchronized (sProperties) {
                    sProperties.put(key, value);
                    if (doSaveProperties(sMetaFile, sProperties)) {
                        sPluginManagerProxy.updateStatus(VSDEvent.VSD_MODIFIED.getCode(), sStorageId);
                        return HqmeError.STATUS_SUCCESS.getCode();
                    } else {
                        return HqmeError.ERR_GENERAL.getCode();
                    }
                }
            } catch (NullPointerException e) {
                return HqmeError.ERR_INVALID_ARGUMENT.getCode();
            }
        }

        @Override
        public int removeProperty(String key) throws RemoteException {
            try {
                if (key.equals("VS_ROOT_FOLDER")) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals("VS_STORAGE_ID")) {
                        return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals(VSDProperties.VSProperty.VS_FN_GROUPS.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals(VSDProperties.VSProperty.VS_TOTAL_CAPACITY.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals(VSDProperties.VSProperty.VS_AVAILABLE_CAPACITY.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals(VSDProperties.VSProperty.VS_OBJECT_COUNT.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                }

                synchronized (sProperties) {
                    if (!sProperties.containsKey(key)) {
                        return HqmeError.ERR_NOT_FOUND.getCode();
                    } else if (null == sProperties.remove(key)) {
                        return HqmeError.ERR_NOT_FOUND.getCode();
                    } else if (doSaveProperties(sMetaFile, sProperties)) {
                        sPluginManagerProxy.updateStatus(VSDEvent.VSD_MODIFIED.getCode(), sStorageId);
                        return HqmeError.STATUS_SUCCESS.getCode();
                    } else {
                        return HqmeError.ERR_GENERAL.getCode();
                    }
                }
            } catch (NullPointerException e) {
                return HqmeError.ERR_INVALID_ARGUMENT.getCode();
            }
        }
        
        @Override
        public int issueCommand(int commandId, Property[] arguments) throws RemoteException {
            return HqmeError.ERR_NOT_SUPPORTED.getCode();
        }

        @Override
        public Property[] getCommandStatus(int commandId) throws RemoteException {
            return null; // HqmeError.ERR_NOT_SUPPORTED.getCode();
        }
        
        private HashMap<String, UntenCacheObject> getObjects(String origin) {
            HashMap<String, UntenCacheObject> objects = sObjects.get(origin);
            return objects != null ? objects : new HashMap<String, UntenCacheObject>();
        }
        
        private String getCallingOrigin() {
            // Local application origin is the process name or package name for the client
            int uid = getCallingUid();
            return sPackageManager.getNameForUid(uid);
        }
        
        private boolean su() {
            int res = sPluginContext.checkCallingPermission("com.hqme.cm.core.SU");
            if (res == PackageManager.PERMISSION_GRANTED) {
                return true;
            } else {
                int uid = getCallingUid();
                String nameForUid = sPackageManager.getNameForUid(uid);
                return "com.hqme.cm.core".equals(nameForUid);
            }
        }
    };

    static class UntenCacheObject extends IContentObject.Stub {
        String mName = null;
        private File mDataFile = null;
        private File mMetaFile = null;
        private FileLock mLock = null;
        private RandomAccessFile mAccessor = null;
        private Properties mProperties = null;

        /**
         * @param name
         */
        private UntenCacheObject(String name, String origin) {
            sObjectCount++;
            
            mName = name.startsWith("/") ? name.substring(1) : name;

            mDataFile = new File(sRoot, origin + "/" + mName + ".data");
            if (!mDataFile.exists()) {
                File folder = mDataFile.getParentFile();
                if (!folder.exists()) {
                    if (!folder.mkdirs()) {
                        Log.e(sTag, "UntenCacheObject: cannot create parent folder " + folder.getAbsolutePath());
                        mDataFile = null;
            return;
                    }
                }
                try {
                    mDataFile.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(sTag, "UntenCacheObject: data file creation failed, fault:" + e);
                    mDataFile = null;
                    return;
                }
            }

            mMetaFile = new File(sRoot, origin + "/" + mName + ".meta");
            if (!mMetaFile.exists()) {
                try {
                    mMetaFile.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(sTag, "UntenCacheObject: metadata file creation failed, fault:" + e);
                    mMetaFile = null;
                    mDataFile.delete();
                    mDataFile = null;
                    return;
                }
            }
            
            String key = normalize(name);
            HashMap<String, Properties> objectProperties = sObjectProperties.get(origin);
            if (objectProperties == null) {
                objectProperties = new HashMap<String, Properties>();
                sObjectProperties.put(origin, objectProperties);
            }
            if ((mProperties = objectProperties.get(key)) == null) {
                mProperties = new Properties();
                if (!doLoadProperties(mMetaFile, mProperties)) {
                    Log.e(sTag, "UntenCacheObject: metadata file loading failed");
                    sObjectProperties.remove(key);
                    mProperties = null;
                    mMetaFile.delete();
                    mMetaFile = null;
                    mDataFile.delete();
                    mDataFile = null;
                    return;
                }
                mProperties.put(VSDProperties.SProperty.S_LOCKED.name(), "false");
                String _origin = mProperties.getProperty(VSDProperties.SProperty.S_ORIGIN.name());
                if (_origin == null) {
                    mProperties.put(VSDProperties.SProperty.S_ORIGIN.name(), origin);
                }
                doSaveProperties(mMetaFile, mProperties);
                objectProperties.put(key, mProperties);
            }
        }

        protected void finalize() throws Throwable {
            try {
                close();
            } finally {
                super.finalize();
                sObjectCount--;
            }
        }

        private boolean isValidObject() {
            return (mDataFile != null && mMetaFile != null && mProperties != null);
        }
        
        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#properties()
         */ 
        @Override
        public Property[] properties() throws RemoteException {
            try {
                synchronized (mProperties) {
                    Property[] propertyArray = new Property[mProperties.size()];
                    String[] keys = getPropertyKeys();
                    for (int i = 0; i < keys.length; i++) {
                        propertyArray[i] = new Property(keys[i], mProperties.getProperty(keys[i]));
                    }
                    return propertyArray;
                }
            } catch (NullPointerException e) {
                Log.e(sTag, "properties: invalid object");
                return null;
            }
        }
        
        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#getPropertyKeys()
         * 
         * The mandatory properties associated with all content objects:
         *      S_NAME, S_SIZE, S_SOURCEURI, S_ORIGIN, S_LOCKED, S_TYPE.
         *      
         * The optional properties are:
         *      S_REDOWNLOAD_URI, S_METADATA, S_POLICY, S_CONTENTPROFILE, S_RIGHTSCHECK, S_VALIDITYCHECK, etc.
         */
        @Override
        public String[] getPropertyKeys() throws RemoteException {
            try {
                synchronized (mProperties) {
                    return mProperties.keySet().toArray(EMPTY_STRINGS);
                }
            } catch (NullPointerException e) {
                Log.e(sTag, "propertyKeys: invalid object");
                return EMPTY_STRINGS;
            }
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#getProperty(java.lang.String)
         */
        @Override
        public String getProperty(String key) throws RemoteException {
            try {
                synchronized (mProperties) {
                    if (key.equals(VSDProperties.OptionalProperty.S_REDOWNLOAD_URI.name())) {
                        if (!mProperties.containsKey(VSDProperties.OptionalProperty.S_REDOWNLOAD_URI.name()))
                            return mProperties.getProperty(VSDProperties.SProperty.S_SOURCEURI.name());
                    }

                    return mProperties.getProperty(key);
                }
            } catch (NullPointerException e) {
                return null;
            }
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#setProperty(java.lang.String, java.lang.String)
         */
        @Override
        public int setProperty(String key, String value) throws RemoteException {
            try {
                if (!isGranted()) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (mMetaFile == null || mProperties.isEmpty()) {
                    return HqmeError.ERR_NOT_FOUND.getCode();
                } else if (key.equals(VSDProperties.SProperty.S_LOCKED.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals(VSDProperties.SProperty.S_ORIGIN.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } 
                
                synchronized (mProperties) {
                    mProperties.put(key, value);

                    if (doSaveProperties(mMetaFile, mProperties)) {
                        return HqmeError.STATUS_SUCCESS.getCode();
                    } else {
                        return HqmeError.ERR_GENERAL.getCode();
                    }
                }
            } catch (NullPointerException e) {
                return HqmeError.ERR_NOT_FOUND.getCode();
            }
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#removeProperty(java.lang.String)
         */
        @Override
        public int removeProperty(String key) throws RemoteException {
            try {
                if (!isGranted()) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (mMetaFile == null || mProperties.isEmpty()) {
                    return HqmeError.ERR_NOT_FOUND.getCode();
                } else if (key.equals(VSDProperties.SProperty.S_LOCKED.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (key.equals(VSDProperties.SProperty.S_ORIGIN.name())) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } 
                
                synchronized (mProperties) {
                    if (null == mProperties.remove(key)) {
                        return HqmeError.ERR_NOT_FOUND.getCode();
                    } else if (doSaveProperties(mMetaFile, mProperties)) {
                        return HqmeError.STATUS_SUCCESS.getCode();
                    } else {
                        return HqmeError.ERR_GENERAL.getCode();
                    }
                }
            } catch (NullPointerException e) {
                e.printStackTrace();
                Log.e(sTag, "removeProperty: invalid " + key == null ? "key" : "object");
                return HqmeError.ERR_NOT_FOUND.getCode();
            }
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#size()
         */
        @Override
        public long size() throws RemoteException {
            try {
                if (!isGranted()) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                }
                long size = mDataFile.length();
                if (size == 0) {
                    if (!mDataFile.exists()) {
                        return HqmeError.ERR_NOT_FOUND.getCode();
                    }
                }
                return size;
            } catch (NullPointerException e) {
                return HqmeError.ERR_NOT_FOUND.getCode();
            }
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#tell()
         */
        @Override
        public long tell() throws RemoteException {
            try {
                if (!isGranted()) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (mDataFile == null) {
                    return HqmeError.ERR_NOT_FOUND.getCode();
                } 
                
                return mAccessor.getFilePointer();
            } catch (NullPointerException e) {
                return HqmeError.ERR_IO.getCode();
            } catch (IOException e) {
                e.printStackTrace();
                return HqmeError.ERR_IO.getCode();
            }
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#setPosition(long offset, int origin) 
         *      SEEK_SET (0) --- Beginning of file 
         *      SEEK_CUR (1) --- Current position of the file pointer 
         *      SEEK_END (2) --- End of file
         */
        @Override
        public long seek(long offset, int origin) throws RemoteException {
            try {
                if (!isGranted()) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (mDataFile == null) {
                    return HqmeError.ERR_NOT_FOUND.getCode();
                } 
                
                long position = offset; // count from beginning of the file
                if (origin == VSDProperties.SEEK_ORIGIN.SEEK_CUR.ordinal()) {
                    position += mAccessor.getFilePointer(); // offset by current position
                } else if (origin == VSDProperties.SEEK_ORIGIN.SEEK_END.ordinal()) {
                    position += mDataFile.length(); // offset by file size
                }
                mAccessor.seek(position);
                return position;
            } catch (NullPointerException e) {
                return HqmeError.ERR_IO.getCode();
            } catch (IOException e) {
                e.printStackTrace();
                return HqmeError.ERR_IO.getCode();
            }
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#open(String mode, boolean lock)
         */
        @Override
        public int open(String mode, boolean lock) throws RemoteException {
            try {
                if (!isGranted()) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                }
                if (mAccessor == null) {
                    mAccessor = new RandomAccessFile(mDataFile, mode);
                    if (mode == null) {
                        return HqmeError.ERR_INVALID_ARGUMENT.getCode();
                    } else if (mProperties.get(VSDProperties.SProperty.S_LOCKED.name()).equals("true")) {
                        return HqmeError.ERR_PERMISSION_DENIED.getCode();
                    }
                    
                    if (lock) {
                        FileChannel channel = mAccessor.getChannel();
                        try {
                            mLock = channel.tryLock();
                            if (mLock != null && mLock.isValid()) {
                                mProperties.put(VSDProperties.SProperty.S_LOCKED.name(), "true");
                            }
                        } catch (OverlappingFileLockException e) {
                            Log.w(sTag, "open: Already locked by someone else");
                            return HqmeError.ERR_IO.getCode();
                        } catch (NonWritableChannelException e) {
                            Log.w(sTag, "open: Cannot acquire lock for read only files. mode: " + mode + " lock: " + lock);
                            return HqmeError.ERR_GENERAL.getCode();
                        }
                    }
                }
                return HqmeError.STATUS_SUCCESS.getCode();
            } catch (NullPointerException e) {
                e.printStackTrace();
                return HqmeError.ERR_NOT_FOUND.getCode();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return HqmeError.ERR_NOT_FOUND.getCode();
            } catch (IOException e) {
                e.printStackTrace();
                return HqmeError.ERR_GENERAL.getCode();
            } catch (IllegalArgumentException e) {
                // mode is not one of "r", "rw"
                e.printStackTrace();
                return HqmeError.ERR_INVALID_ARGUMENT.getCode();
            }
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#close()
         */
        @Override
        public int close() throws RemoteException {
            try {
                if (!isGranted()) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (mDataFile == null) {
                    return HqmeError.ERR_NOT_FOUND.getCode();
                } 
                
                if (mAccessor != null) {
                    if (mLock != null && mLock.isValid()) {
                        mLock.release();
                        mLock = null;
                        mProperties.put(VSDProperties.SProperty.S_LOCKED.name(), "false");
                    }
                    mAccessor.close();
                    mAccessor = null;
                }
                return HqmeError.STATUS_SUCCESS.getCode();
            } catch (NullPointerException e) {
                return HqmeError.ERR_NOT_FOUND.getCode();
            } catch (IOException e) {
                return HqmeError.ERR_GENERAL.getCode();
            }
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#remove()
         */
        @Override
        public int remove() throws RemoteException {
            try {
                if (!isGranted()) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } 
                
                close(); // prevents resource leaks
                
                boolean res = true;
                if (mDataFile.exists()) {
                    res = mDataFile.delete();
                }
                if (mMetaFile.exists()) {
                    res &= mMetaFile.delete();
                }
                File parentFolder = mDataFile;
                while ((parentFolder != null ) && (parentFolder = parentFolder.getParentFile()) != null) {
                    if (parentFolder.exists() && parentFolder.isDirectory()) {
                        if (!parentFolder.delete()) {
                            break;
                        }
                    }
                }
                String _origin = mProperties.getProperty(VSDProperties.SProperty.S_ORIGIN.name());
                HashMap<String, UntenCacheObject> objects = sObjects.get(_origin); 
                if (objects != null) {
                    IContentObject obj = objects.remove(mName.toLowerCase());
                    if (obj != null && obj != this) {
                        obj.remove();
                        obj = null;
                    }
                }
                HashMap<String, Properties> properties = sObjectProperties.get(_origin);
                if (properties != null) {
                    Properties pro = properties.remove(mName.toLowerCase());
                    if (pro != null) {
                        pro.clear();
                        pro = null;
                    }
                }
                mProperties = null;
                mDataFile = null;
                mMetaFile = null;
                parentFolder = null;
                return !res ? HqmeError.ERR_GENERAL.getCode() : HqmeError.STATUS_SUCCESS.getCode();
            } catch (NullPointerException e) { 
                return HqmeError.ERR_NOT_FOUND.getCode();
            } catch (SecurityException e) {
                e.printStackTrace();
                Log.e(sTag, "remove: SecurityException fault: " + e);
            }
            return HqmeError.ERR_GENERAL.getCode();
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#read(byte[] buf, int count)
         */
        @Override
        public int read(byte[] buf, int count) throws RemoteException {
            try {
                if (!isGranted()) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (mDataFile == null) {
                    return HqmeError.ERR_NOT_FOUND.getCode();
                } else if (buf.length < count) {
                    return HqmeError.ERR_INVALID_ARGUMENT.getCode(); 
                }
                
                int result = mAccessor.read(buf, 0, count);
                if (result == -1) {
                    return HqmeError.ERR_IO.getCode();
                }
                return result;
            } catch (NullPointerException e) {
                return HqmeError.ERR_IO.getCode();
            } catch (IOException e) {
                e.printStackTrace();
                return HqmeError.ERR_IO.getCode();
            }
        }
        
        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#write(byte[] buf, int count)
         */
        @Override
        public int write(byte[] buf, int count) throws RemoteException {
            try {
                if (!isGranted()) {
                    return HqmeError.ERR_PERMISSION_DENIED.getCode();
                } else if (mDataFile == null) {
                    return HqmeError.ERR_NOT_FOUND.getCode();
                } else if (buf.length < count) {
                    return HqmeError.ERR_INVALID_ARGUMENT.getCode(); 
                }
                
                mAccessor.write(buf, 0, count);
                return count;
            } catch (NullPointerException e) {
                return HqmeError.ERR_IO.getCode();
            } catch (IOException e) {
                e.printStackTrace();
                return HqmeError.ERR_IO.getCode();
            }
        }

        /*
         * (non-Javadoc)
         * @see com.hqme.cm.IContentObject#getStreamingUri()
         */
        @Override
        public String getStreamingUri() throws RemoteException {
            try {
                if (!mDataFile.exists() || !mDataFile.isFile() || !isGranted()) {
                    return null;
                } else {
                    //String uri = "file://" + mDataFile.getAbsolutePath();
                    String uri = String.format("http://localhost:%d/playback.jsp?token=%s", StreamingServer.getServerPortNumber(), PlaybackTokens.newPlaybackToken(this));
                    debugLog(sTag, "getStreamingUri: " + uri);
                    return uri;
                }
            } catch (NullPointerException e) {
                Log.w(sTag, "getStreamingUri: object not found");
                return null;
            } 
        }

        private boolean isGranted() {
            // Superuser
            int res = sPluginContext.checkCallingPermission("com.hqme.cm.core.SU");
            if (res == PackageManager.PERMISSION_GRANTED) {
                return true;
            } 

            // HQME core or VSD itself
            int uid = getCallingUid();
            String nameForUid = sPackageManager.getNameForUid(uid);
            if ("com.hqme.cm.core".equals(nameForUid) || "com.hqme.cm.cache".equals(nameForUid)) {
                return true;
            } 

            // Content owner or creator
            String origin = mProperties.getProperty(VSDProperties.SProperty.S_ORIGIN.name());
            if (origin != null && origin.equalsIgnoreCase(nameForUid)) {
                return true;
            }
            
            // Group user or everyone else
            return false;
        }
    }
    
    private static final String normalize(String path) {
        String _path = path.toLowerCase();
        if (_path.startsWith("/")) {
            _path = _path.replaceFirst("/", "");
        }
        return _path;
    }

    private static class UntenDefaultUncaughtExceptionHandler implements UncaughtExceptionHandler {
        private static UncaughtExceptionHandler sDefaultEH = Thread.getDefaultUncaughtExceptionHandler(); 
        public void uncaughtException(Thread thread, Throwable fault) {
            debugLog(sTag, "uncaughtException: Thread = %d \"%s\"", thread.getId(), thread.getName());
            debugLog(sTag, "uncaughtException: %s", fault);
            sDefaultEH.uncaughtException(thread, fault);
        }
    }
    
    final static ServiceConnection mPluginManagerConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            debugLog(sTag, "onServiceConnected: className = " + className);
            sPluginManagerProxy = IStorageManager.Stub.asInterface(service);

            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                try {
                    debugLog(sTag, "onServiceConnected: registering plugin, myPid = " + Process.myPid());
                    int storageId = sPluginManagerProxy.registerPlugin(sUntenCache);

                    if (sStorageId != storageId) {
                        sStorageId = storageId;
                        sProperties.put("VS_STORAGE_ID", Integer.toString(sStorageId));
                        doSaveProperties(sMetaFile, sProperties);
                    }
                } catch (RemoteException fault) {
                    fault.printStackTrace();
                    Log.e(sTag, "onServiceConnected: RemoteExpection fault: " + fault);
                }
            } else {
                Log.w(sTag, "onServiceConnected: external storage is not mounted");
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            debugLog(sTag, "onServiceDisconnected: className = " + className);
            sPluginManagerProxy = null;
        }
    };
    
    public void onCreate() {
        debugLog(sTag, "onCreate: called");
        
        Thread.setDefaultUncaughtExceptionHandler(new UntenDefaultUncaughtExceptionHandler());
        
        sIsDebugMode = (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
        sPackageManager = getPackageManager();
        sPluginContext = getApplicationContext();
        
        if (sPluginManagerProxy == null || !sPluginManagerProxy.asBinder().isBinderAlive()) {
            debugLog(sTag, "onCreate: bindService() calling...");
            sPluginContext.bindService(new Intent(IStorageManager.class.getName()), mPluginManagerConnection, 0);
        }
        
        if (streamingServerWorker == null) {
            streamingServerWorker = new Thread(streamingServerHandler);
            streamingServerWorker.setDaemon(true);
            streamingServerWorker.start();
        }
        
        doLoad();
    }

    public int onStartCommand(Intent intent, int flags, int startId) {
        debugLog(sTag, "onStartCommand: received start id " + startId + ": " + intent);

        // We want this service to continue running until it is explicitly stopped, so return sticky.
        return START_STICKY;
    }

    public void onDestroy() {
        debugLog(sTag, "onDestroy: called");
        
        if (streamingServerWorker != null) {
            streamingServerHandler.stopServer();
            streamingServerWorker = null;
        }
        
        if (sPluginManagerProxy != null && sPluginManagerProxy.asBinder().isBinderAlive()) {
            try {
                sPluginManagerProxy.unregisterPlugin(sUntenCache);
            } catch (RemoteException fault) {
                fault.printStackTrace();
                Log.e(sTag, "onDestroy: RemoteException fault:" + fault);
            }
        } else {
            Log.w(sTag, "onDestroy: sPluginManagerProxy is invalid!");
        }

        unbindService(mPluginManagerConnection);
    }

    static void doUnload() {
        sProperties.clear();
        sObjects.clear();
        sObjectProperties.clear();
        sLoaded = false;
    }
    
    static void doLoad() {
        if (sLoaded) {
            return;
        }

        String state = Environment.getExternalStorageState();
        if (!Environment.MEDIA_MOUNTED.equals(state)) {
            debugLog(sTag, "doLoad: external storage is not mounted");
            return;
        } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            debugLog(sTag, "doLoad: external storage is not writable");
            return;
        }

        sRoot = new File(Environment.getExternalStorageDirectory(), "HQME/Cache/Default/");
        if (!sRoot.exists()) {
            sRoot.mkdirs();
        }

        sMetaFile = new File(sRoot, "VSDProperties.meta");
        if (!sMetaFile.exists()) {
            try {
                sMetaFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(sTag, "doLoad: metadata file creation failed, fault:" + e);
            }
        } else {
            doLoadProperties(sMetaFile, sProperties);
        }
            
        try {
            String value = sProperties.getProperty("VS_STORAGE_ID", "-1");
            sStorageId = Integer.parseInt(value);
            debugLog(sTag, "doLoad: assigned storage id is " + sStorageId);
        } catch (NumberFormatException e) {
            sStorageId = -1;
        }

        debugLog(sTag, "doLoad: calling into doLoadObjects()...");
        UntenCacheService.doLoadObjects(sRoot);
        debugLog(sTag, "doLoad: doLoadObjects() called: originCount = " + sObjects.size() + ", sObjectCount = " + sObjectCount);
        
        sLoaded = true;
    }

    private static void doLoadObjects(File dir) {
        if (dir.isDirectory()) {
            FilenameFilter filter = new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    return !name.endsWith(".meta");
                }
            };
            String[] children = dir.list(filter);
            for (int i = 0; i < children.length; i++) {
                UntenCacheService.doLoadObjects(new File(dir, children[i]));
            }
        } else {
            String path = dir.getAbsolutePath();
            String root = sRoot.getAbsolutePath();
            path = path.substring(root.length() + 1, path.length() - 5);
            String origin = path.substring(0, path.indexOf('/'));
            String name = path.substring(origin.length() + 1);
            
            HashMap<String, UntenCacheObject> objects = sObjects.get(origin);
            if (objects == null) {
                objects = new HashMap<String, UntenCacheObject>();
                sObjects.put(origin, objects);
            }
            objects.put(name.toLowerCase(), new UntenCacheObject(name, origin));
        }
    }

    private static boolean doLoadProperties(File metaFile, java.util.Properties prop) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(metaFile); 
            prop.load(fis);
            return true;
        } catch (FileNotFoundException e) {
            Log.e(sTag, "doLoadProperties: metadata file not found");
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(sTag, "doLoadProperties: metadata file read failed, fault: " + e);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(sTag, "doLoadProperties: metadata file close failed, fault: " + e);
            }
        }
        return false;
    }
    
    private static boolean doSaveProperties(File metaFile, java.util.Properties prop) {
        FileOutputStream fos = null; 
        try {
            fos = new FileOutputStream(metaFile);
            prop.store(fos, "");
            return true;
        } catch (FileNotFoundException e) {
            Log.e(sTag, "doSaveProperties: metadata file not found");
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(sTag, "doSaveProperties: metadata file write failed, fault: " + e);
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(sTag, "doSaveProperties: metadata file close failed, fault: " + e);
            }
        }
        return false;
    }
    
    public IBinder onBind(Intent intent) {
//        if (IVSD.class.getName().equals(intent.getAction())) {
//            return sUntenCacheProxy;
//        }
        
        // We don't provide binding, so return null      
        return null;
    }

    public boolean onUnbind(Intent intent) {
//        if (IVSD.class.getName().equals(intent.getAction())) {
//            stopSelf(); // We don't stop ourself either
//        }
        
        return false; // No re-binding since no binding
    }
    
    public static void debugLog(String tag, String message) {
        if (sIsDebugMode) {
            Log.d(tag, message);
        }
    }

    public static void debugLog(String tag, String format, Object... args) {
        String message = String.format(format, args);
        debugLog(tag, message);
    }
}




Java Source Code List

com.hqme.cm.Account.java
com.hqme.cm.EventsNotify.java
com.hqme.cm.HqmeError.java
com.hqme.cm.OriginACL.java
com.hqme.cm.Permission.java
com.hqme.cm.Property.java
com.hqme.cm.QueueRequestState.java
com.hqme.cm.ReqEvents.java
com.hqme.cm.VSDEvent.java
com.hqme.cm.VSDFunctionGroups.java
com.hqme.cm.VSDProperties.java
com.hqme.cm.cache.PlaybackTokens.java
com.hqme.cm.cache.StreamingServer.java
com.hqme.cm.cache.UntenCacheService.java
com.hqme.cm.cache.UntenMedia.java
com.hqme.cm.core.CmApplication.java
com.hqme.cm.core.DeviceDescription.java
com.hqme.cm.core.HQMEProvider.java
com.hqme.cm.core.HQME.java
com.hqme.cm.core.Package.java
com.hqme.cm.core.Policy.java
com.hqme.cm.core.ProtocolException.java
com.hqme.cm.core.ProtocolHandlerInputStream.java
com.hqme.cm.core.ProtocolHandler.java
com.hqme.cm.core.ProtocolManager.java
com.hqme.cm.core.ProtocolPluginHttp.java
com.hqme.cm.core.ProtocolPlugin.java
com.hqme.cm.core.QueueRequestObject.java
com.hqme.cm.core.QueueRequestProperties.java
com.hqme.cm.core.RULE_AVAILABLE_SPACE.java
com.hqme.cm.core.RULE_BANDWIDTH_LIMIT.java
com.hqme.cm.core.RULE_CHARGING_STATE.java
com.hqme.cm.core.RULE_CONNECTION_TYPE.java
com.hqme.cm.core.RULE_DOWNLOAD_LIMIT.java
com.hqme.cm.core.RULE_EXPIRE.java
com.hqme.cm.core.RULE_FREE_SPACE.java
com.hqme.cm.core.RULE_FUNCTIONGROUPS.java
com.hqme.cm.core.RULE_MANDATORY_TIME.java
com.hqme.cm.core.RULE_MAX_SIZE.java
com.hqme.cm.core.RULE_POWER_LEVEL.java
com.hqme.cm.core.RULE_PRIORITY.java
com.hqme.cm.core.RULE_ROAMING.java
com.hqme.cm.core.RULE_SCHEDULE.java
com.hqme.cm.core.RULE_TIME.java
com.hqme.cm.core.Record.java
com.hqme.cm.core.RuleBase.java
com.hqme.cm.core.RuleCollection.java
com.hqme.cm.core.Rule.java
com.hqme.cm.core.StorageManager.java
com.hqme.cm.core.WorkOrderManager.java
com.hqme.cm.core.WorkOrder.java
com.hqme.cm.core.policyParser.Expression.java
com.hqme.cm.core.policyParser.HqmePolicyException.java
com.hqme.cm.core.policyParser.LogicalExpression.java
com.hqme.cm.core.policyParser.NegateExpression.java
com.hqme.cm.core.policyParser.Operator.java
com.hqme.cm.core.policyParser.PolicyElementParser.java
com.hqme.cm.core.policyParser.PolicyExpression.java
com.hqme.cm.core.policyParser.Precedence.java
com.hqme.cm.core.policyParser.Token.java
com.hqme.cm.core.policyParser.Tokenizer.java
com.hqme.cm.sample.HqmeClientActivity.java
com.hqme.cm.sample.HqmeClientReceiver.java
com.hqme.cm.util.CmClientUtil.java
com.hqme.cm.util.CmDate.java
com.hqme.cm.util.CmNumber.java
com.hqme.cm.util.CmProperties.java
com.hqme.cm.util.CmUri.java