diff options
Diffstat (limited to 'services/java/com/android/server/PackageManagerService.java')
-rw-r--r-- | services/java/com/android/server/PackageManagerService.java | 6569 |
1 files changed, 6569 insertions, 0 deletions
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java new file mode 100644 index 0000000..fec3608 --- /dev/null +++ b/services/java/com/android/server/PackageManagerService.java @@ -0,0 +1,6569 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * 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.android.server; + +import com.android.internal.app.ResolverActivity; +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.PendingIntent; +import android.app.PendingIntent.CanceledException; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ComponentInfo; +import android.content.pm.IPackageDataObserver; +import android.content.pm.IPackageDeleteObserver; +import android.content.pm.IPackageInstallObserver; +import android.content.pm.IPackageManager; +import android.content.pm.IPackageStatsObserver; +import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageStats; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.pm.PackageManager.PKG_INSTALL_COMPLETE; +import static android.content.pm.PackageManager.PKG_INSTALL_INCOMPLETE; +import android.content.pm.PackageParser; +import android.content.pm.PermissionInfo; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.Signature; +import android.content.pm.PackageParser.Package; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.HandlerThread; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.Environment; +import android.os.FileObserver; +import android.os.FileUtils; +import android.os.Handler; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.util.*; +import android.view.Display; +import android.view.WindowManager; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +class PackageManagerService extends IPackageManager.Stub { + private static final String TAG = "PackageManager"; + private static final boolean DEBUG_SETTINGS = false; + private static final boolean DEBUG_PREFERRED = false; + + private static final boolean MULTIPLE_APPLICATION_UIDS = true; + private static final int RADIO_UID = Process.PHONE_UID; + private static final int FIRST_APPLICATION_UID = + Process.FIRST_APPLICATION_UID; + private static final int MAX_APPLICATION_UIDS = 1000; + + private static final boolean SHOW_INFO = false; + + private static final boolean GET_CERTIFICATES = true; + + private static final int REMOVE_EVENTS = + FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM; + private static final int ADD_EVENTS = + FileObserver.CLOSE_WRITE /*| FileObserver.CREATE*/ | FileObserver.MOVED_TO; + + private static final int OBSERVER_EVENTS = REMOVE_EVENTS | ADD_EVENTS; + + static final int SCAN_MONITOR = 1<<0; + static final int SCAN_NO_DEX = 1<<1; + static final int SCAN_FORCE_DEX = 1<<2; + static final int SCAN_UPDATE_SIGNATURE = 1<<3; + static final int SCAN_FORWARD_LOCKED = 1<<4; + + static final int LOG_BOOT_PROGRESS_PMS_START = 3060; + static final int LOG_BOOT_PROGRESS_PMS_SYSTEM_SCAN_START = 3070; + static final int LOG_BOOT_PROGRESS_PMS_DATA_SCAN_START = 3080; + static final int LOG_BOOT_PROGRESS_PMS_SCAN_END = 3090; + static final int LOG_BOOT_PROGRESS_PMS_READY = 3100; + + final HandlerThread mHandlerThread = new HandlerThread("PackageManager", + Process.THREAD_PRIORITY_BACKGROUND); + final Handler mHandler; + + final int mSdkVersion = SystemProperties.getInt( + "ro.build.version.sdk", 0); + + final Context mContext; + final boolean mFactoryTest; + final DisplayMetrics mMetrics; + final int mDefParseFlags; + final String[] mSeparateProcesses; + + // This is where all application persistent data goes. + final File mAppDataDir; + + // This is the object monitoring the framework dir. + final FileObserver mFrameworkInstallObserver; + + // This is the object monitoring the system app dir. + final FileObserver mSystemInstallObserver; + + // This is the object monitoring mAppInstallDir. + final FileObserver mAppInstallObserver; + + // This is the object monitoring mDrmAppPrivateInstallDir. + final FileObserver mDrmAppInstallObserver; + + // Used for priviledge escalation. MUST NOT BE CALLED WITH mPackages + // LOCK HELD. Can be called with mInstallLock held. + final Installer mInstaller; + + final File mFrameworkDir; + final File mSystemAppDir; + final File mAppInstallDir; + + // Directory containing the private parts (e.g. code and non-resource assets) of forward-locked + // apps. + final File mDrmAppPrivateInstallDir; + + // ---------------------------------------------------------------- + + // Lock for state used when installing and doing other long running + // operations. Methods that must be called with this lock held have + // the prefix "LI". + final Object mInstallLock = new Object(); + + // These are the directories in the 3rd party applications installed dir + // that we have currently loaded packages from. Keys are the application's + // installed zip file (absolute codePath), and values are Package. + final HashMap<String, PackageParser.Package> mAppDirs = + new HashMap<String, PackageParser.Package>(); + + // Information for the parser to write more useful error messages. + File mScanningPath; + int mLastScanError; + + final int[] mOutPermissions = new int[3]; + + // ---------------------------------------------------------------- + + // Keys are String (package name), values are Package. This also serves + // as the lock for the global state. Methods that must be called with + // this lock held have the prefix "LP". + final HashMap<String, PackageParser.Package> mPackages = + new HashMap<String, PackageParser.Package>(); + + final Settings mSettings; + boolean mRestoredSettings; + boolean mReportedUidError; + + // Group-ids that are given to all packages as read from etc/permissions/*.xml. + int[] mGlobalGids; + + // These are the built-in uid -> permission mappings that were read from the + // etc/permissions.xml file. + final SparseArray<HashSet<String>> mSystemPermissions = + new SparseArray<HashSet<String>>(); + + // These are the built-in shared libraries that were read from the + // etc/permissions.xml file. + final HashMap<String, String> mSharedLibraries = new HashMap<String, String>(); + + // All available activities, for your resolving pleasure. + final ActivityIntentResolver mActivities = + new ActivityIntentResolver(); + + // All available receivers, for your resolving pleasure. + final ActivityIntentResolver mReceivers = + new ActivityIntentResolver(); + + // All available services, for your resolving pleasure. + final ServiceIntentResolver mServices = new ServiceIntentResolver(); + + // Keys are String (provider class name), values are Provider. + final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent = + new HashMap<ComponentName, PackageParser.Provider>(); + + // Mapping from provider base names (first directory in content URI codePath) + // to the provider information. + final HashMap<String, PackageParser.Provider> mProviders = + new HashMap<String, PackageParser.Provider>(); + + // Mapping from instrumentation class names to info about them. + final HashMap<ComponentName, PackageParser.Instrumentation> mInstrumentation = + new HashMap<ComponentName, PackageParser.Instrumentation>(); + + // Mapping from permission names to info about them. + final HashMap<String, PackageParser.PermissionGroup> mPermissionGroups = + new HashMap<String, PackageParser.PermissionGroup>(); + + boolean mSystemReady; + boolean mSafeMode; + boolean mHasSystemUidErrors; + + ApplicationInfo mAndroidApplication; + final ActivityInfo mResolveActivity = new ActivityInfo(); + final ResolveInfo mResolveInfo = new ResolveInfo(); + ComponentName mResolveComponentName; + PackageParser.Package mPlatformPackage; + + public static final IPackageManager main(Context context, boolean factoryTest) { + PackageManagerService m = new PackageManagerService(context, factoryTest); + ServiceManager.addService("package", m); + return m; + } + + static String[] splitString(String str, char sep) { + int count = 1; + int i = 0; + while ((i=str.indexOf(sep, i)) >= 0) { + count++; + i++; + } + + String[] res = new String[count]; + i=0; + count = 0; + int lastI=0; + while ((i=str.indexOf(sep, i)) >= 0) { + res[count] = str.substring(lastI, i); + count++; + i++; + lastI = i; + } + res[count] = str.substring(lastI, str.length()); + return res; + } + + public PackageManagerService(Context context, boolean factoryTest) { + EventLog.writeEvent(LOG_BOOT_PROGRESS_PMS_START, + SystemClock.uptimeMillis()); + + if (mSdkVersion <= 0) { + Log.w(TAG, "**** ro.build.version.sdk not set!"); + } + + mContext = context; + mFactoryTest = factoryTest; + mMetrics = new DisplayMetrics(); + mSettings = new Settings(); + mSettings.addSharedUserLP("android.uid.system", + Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM); + mSettings.addSharedUserLP("android.uid.phone", + MULTIPLE_APPLICATION_UIDS + ? RADIO_UID : FIRST_APPLICATION_UID, + ApplicationInfo.FLAG_SYSTEM); + + String separateProcesses = SystemProperties.get("debug.separate_processes"); + if (separateProcesses != null && separateProcesses.length() > 0) { + if ("*".equals(separateProcesses)) { + mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES; + mSeparateProcesses = null; + Log.w(TAG, "Running with debug.separate_processes: * (ALL)"); + } else { + mDefParseFlags = 0; + mSeparateProcesses = separateProcesses.split(","); + Log.w(TAG, "Running with debug.separate_processes: " + + separateProcesses); + } + } else { + mDefParseFlags = 0; + mSeparateProcesses = null; + } + + Installer installer = new Installer(); + // Little hacky thing to check if installd is here, to determine + // whether we are running on the simulator and thus need to take + // care of building the /data file structure ourself. + // (apparently the sim now has a working installer) + if (installer.ping() && Process.supportsProcesses()) { + mInstaller = installer; + } else { + mInstaller = null; + } + + WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); + Display d = wm.getDefaultDisplay(); + d.getMetrics(mMetrics); + + synchronized (mInstallLock) { + synchronized (mPackages) { + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + + File dataDir = Environment.getDataDirectory(); + mAppDataDir = new File(dataDir, "data"); + mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); + + if (mInstaller == null) { + // Make sure these dirs exist, when we are running in + // the simulator. + // Make a wide-open directory for random misc stuff. + File miscDir = new File(dataDir, "misc"); + miscDir.mkdirs(); + mAppDataDir.mkdirs(); + mDrmAppPrivateInstallDir.mkdirs(); + } + + readPermissions(); + + mRestoredSettings = mSettings.readLP(); + long startTime = SystemClock.uptimeMillis(); + + EventLog.writeEvent(LOG_BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, + startTime); + + int scanMode = SCAN_MONITOR; + + final HashSet<String> libFiles = new HashSet<String>(); + + mFrameworkDir = new File(Environment.getRootDirectory(), "framework"); + + if (mInstaller != null) { + /** + * Out of paranoia, ensure that everything in the boot class + * path has been dexed. + */ + String bootClassPath = System.getProperty("java.boot.class.path"); + if (bootClassPath != null) { + String[] paths = splitString(bootClassPath, ':'); + for (int i=0; i<paths.length; i++) { + try { + if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) { + libFiles.add(paths[i]); + mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true); + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Boot class path not found: " + paths[i]); + } catch (IOException e) { + Log.w(TAG, "Exception reading boot class path: " + paths[i], e); + } + } + } else { + Log.w(TAG, "No BOOTCLASSPATH found!"); + } + + /** + * Also ensure all external libraries have had dexopt run on them. + */ + if (mSharedLibraries.size() > 0) { + Iterator<String> libs = mSharedLibraries.values().iterator(); + while (libs.hasNext()) { + String lib = libs.next(); + try { + if (dalvik.system.DexFile.isDexOptNeeded(lib)) { + libFiles.add(lib); + mInstaller.dexopt(lib, Process.SYSTEM_UID, true); + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Library not found: " + lib); + } catch (IOException e) { + Log.w(TAG, "Exception reading library: " + lib, e); + } + } + } + + // Gross hack for now: we know this file doesn't contain any + // code, so don't dexopt it to avoid the resulting log spew. + libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk"); + + /** + * And there are a number of commands implemented in Java, which + * we currently need to do the dexopt on so that they can be + * run from a non-root shell. + */ + String[] frameworkFiles = mFrameworkDir.list(); + if (frameworkFiles != null && mInstaller != null) { + for (int i=0; i<frameworkFiles.length; i++) { + File libPath = new File(mFrameworkDir, frameworkFiles[i]); + String path = libPath.getPath(); + // Skip the file if we alrady did it. + if (libFiles.contains(path)) { + continue; + } + // Skip the file if it is not a type we want to dexopt. + if (!path.endsWith(".apk") && !path.endsWith(".jar")) { + continue; + } + try { + if (dalvik.system.DexFile.isDexOptNeeded(path)) { + mInstaller.dexopt(path, Process.SYSTEM_UID, true); + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Jar not found: " + path); + } catch (IOException e) { + Log.w(TAG, "Exception reading jar: " + path, e); + } + } + } + } + + mFrameworkInstallObserver = new AppDirObserver( + mFrameworkDir.getPath(), OBSERVER_EVENTS, true); + mFrameworkInstallObserver.startWatching(); + scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM, + scanMode | SCAN_NO_DEX); + mSystemAppDir = new File(Environment.getRootDirectory(), "app"); + mSystemInstallObserver = new AppDirObserver( + mSystemAppDir.getPath(), OBSERVER_EVENTS, true); + mSystemInstallObserver.startWatching(); + scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode); + mAppInstallDir = new File(dataDir, "app"); + if (mInstaller == null) { + // Make sure these dirs exist, when we are running in + // the simulator. + mAppInstallDir.mkdirs(); // scanDirLI() assumes this dir exists + } + //look for any incomplete package installations + ArrayList<String> deletePkgsList = mSettings.getListOfIncompleteInstallPackages(); + //clean up list + for(int i = 0; i < deletePkgsList.size(); i++) { + //clean up here + cleanupInstallFailedPackage(deletePkgsList.get(i)); + } + //delete tmp files + deleteTempPackageFiles(); + + EventLog.writeEvent(LOG_BOOT_PROGRESS_PMS_DATA_SCAN_START, + SystemClock.uptimeMillis()); + mAppInstallObserver = new AppDirObserver( + mAppInstallDir.getPath(), OBSERVER_EVENTS, false); + mAppInstallObserver.startWatching(); + scanDirLI(mAppInstallDir, 0, scanMode); + + mDrmAppInstallObserver = new AppDirObserver( + mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false); + mDrmAppInstallObserver.startWatching(); + scanDirLI(mDrmAppPrivateInstallDir, 0, scanMode); + + EventLog.writeEvent(LOG_BOOT_PROGRESS_PMS_SCAN_END, + SystemClock.uptimeMillis()); + Log.i(TAG, "Time to scan packages: " + + ((SystemClock.uptimeMillis()-startTime)/1000f) + + " seconds"); + + updatePermissionsLP(); + + mSettings.writeLP(); + + EventLog.writeEvent(LOG_BOOT_PROGRESS_PMS_READY, + SystemClock.uptimeMillis()); + + // Now after opening every single application zip, make sure they + // are all flushed. Not really needed, but keeps things nice and + // tidy. + Runtime.getRuntime().gc(); + } // synchronized (mPackages) + } // synchronized (mInstallLock) + } + + @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (RuntimeException e) { + if (!(e instanceof SecurityException) && !(e instanceof IllegalArgumentException)) { + Log.e(TAG, "Package Manager Crash", e); + } + throw e; + } + } + + void cleanupInstallFailedPackage(String packageName) { + if (mInstaller != null) { + int retCode = mInstaller.remove(packageName); + if (retCode < 0) { + Log.w(TAG, "Couldn't remove app data directory for package: " + + packageName + ", retcode=" + retCode); + } + } else { + //for emulator + PackageParser.Package pkg = mPackages.get(packageName); + File dataDir = new File(pkg.applicationInfo.dataDir); + dataDir.delete(); + } + mSettings.removePackageLP(packageName); + } + + void readPermissions() { + // Read permissions from .../etc/permission directory. + File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions"); + if (!libraryDir.exists() || !libraryDir.isDirectory()) { + Log.w(TAG, "No directory " + libraryDir + ", skipping"); + return; + } + if (!libraryDir.canRead()) { + Log.w(TAG, "Directory " + libraryDir + " cannot be read"); + return; + } + + // Iterate over the files in the directory and scan .xml files + for (File f : libraryDir.listFiles()) { + // We'll read platform.xml last + if (f.getPath().endsWith("etc/permissions/platform.xml")) { + continue; + } + + if (!f.getPath().endsWith(".xml")) { + Log.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); + continue; + } + if (!f.canRead()) { + Log.w(TAG, "Permissions library file " + f + " cannot be read"); + continue; + } + + readPermissionsFromXml(f); + } + + // Read permissions from .../etc/permissions/platform.xml last so it will take precedence + final File permFile = new File(Environment.getRootDirectory(), + "etc/permissions/platform.xml"); + readPermissionsFromXml(permFile); + } + + private void readPermissionsFromXml(File permFile) { + FileReader permReader = null; + try { + permReader = new FileReader(permFile); + } catch (FileNotFoundException e) { + Log.w(TAG, "Couldn't find or open permissions file " + permFile); + return; + } + + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(permReader); + + XmlUtils.beginDocument(parser, "permissions"); + + while (true) { + XmlUtils.nextElement(parser); + if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { + break; + } + + String name = parser.getName(); + if ("group".equals(name)) { + String gidStr = parser.getAttributeValue(null, "gid"); + if (gidStr != null) { + int gid = Integer.parseInt(gidStr); + mGlobalGids = appendInt(mGlobalGids, gid); + } else { + Log.w(TAG, "<group> without gid at " + + parser.getPositionDescription()); + } + + XmlUtils.skipCurrentTag(parser); + continue; + } else if ("permission".equals(name)) { + String perm = parser.getAttributeValue(null, "name"); + if (perm == null) { + Log.w(TAG, "<permission> without name at " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + perm = perm.intern(); + readPermission(parser, perm); + + } else if ("assign-permission".equals(name)) { + String perm = parser.getAttributeValue(null, "name"); + if (perm == null) { + Log.w(TAG, "<assign-permission> without name at " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + String uidStr = parser.getAttributeValue(null, "uid"); + if (uidStr == null) { + Log.w(TAG, "<assign-permission> without uid at " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + int uid = Process.getUidForName(uidStr); + if (uid < 0) { + Log.w(TAG, "<assign-permission> with unknown uid \"" + + uidStr + "\" at " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + perm = perm.intern(); + HashSet<String> perms = mSystemPermissions.get(uid); + if (perms == null) { + perms = new HashSet<String>(); + mSystemPermissions.put(uid, perms); + } + perms.add(perm); + XmlUtils.skipCurrentTag(parser); + + } else if ("library".equals(name)) { + String lname = parser.getAttributeValue(null, "name"); + String lfile = parser.getAttributeValue(null, "file"); + if (lname == null) { + Log.w(TAG, "<library> without name at " + + parser.getPositionDescription()); + } else if (lfile == null) { + Log.w(TAG, "<library> without file at " + + parser.getPositionDescription()); + } else { + Log.i(TAG, "Got library " + lname + " in " + lfile); + this.mSharedLibraries.put(lname, lfile); + } + XmlUtils.skipCurrentTag(parser); + continue; + + } else { + XmlUtils.skipCurrentTag(parser); + continue; + } + + } + } catch (XmlPullParserException e) { + Log.w(TAG, "Got execption parsing permissions.", e); + } catch (IOException e) { + Log.w(TAG, "Got execption parsing permissions.", e); + } + } + + void readPermission(XmlPullParser parser, String name) + throws IOException, XmlPullParserException { + + name = name.intern(); + + BasePermission bp = mSettings.mPermissions.get(name); + if (bp == null) { + bp = new BasePermission(name, null, BasePermission.TYPE_BUILTIN); + mSettings.mPermissions.put(name, bp); + } + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if ("group".equals(tagName)) { + String gidStr = parser.getAttributeValue(null, "gid"); + if (gidStr != null) { + int gid = Process.getGidForName(gidStr); + bp.gids = appendInt(bp.gids, gid); + } else { + Log.w(TAG, "<group> without gid at " + + parser.getPositionDescription()); + } + } + XmlUtils.skipCurrentTag(parser); + } + } + + static int[] appendInt(int[] cur, int val) { + if (cur == null) { + return new int[] { val }; + } + final int N = cur.length; + for (int i=0; i<N; i++) { + if (cur[i] == val) { + return cur; + } + } + int[] ret = new int[N+1]; + System.arraycopy(cur, 0, ret, 0, N); + ret[N] = val; + return ret; + } + + static int[] appendInts(int[] cur, int[] add) { + if (add == null) return cur; + if (cur == null) return add; + final int N = add.length; + for (int i=0; i<N; i++) { + cur = appendInt(cur, add[i]); + } + return cur; + } + + PackageInfo generatePackageInfo(PackageParser.Package p, int flags) { + final PackageSetting ps = (PackageSetting)p.mExtras; + if (ps == null) { + return null; + } + final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; + return PackageParser.generatePackageInfo(p, gp.gids, flags); + } + + public PackageInfo getPackageInfo(String packageName, int flags) { + synchronized (mPackages) { + PackageParser.Package p = mPackages.get(packageName); + if (Config.LOGV) Log.v( + TAG, "getApplicationInfo " + packageName + + ": " + p); + if (p != null) { + return generatePackageInfo(p, flags); + } + if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + return generatePackageInfoFromSettingsLP(packageName, flags); + } + } + return null; + } + + public int getPackageUid(String packageName) { + synchronized (mPackages) { + PackageParser.Package p = mPackages.get(packageName); + if(p != null) { + return p.applicationInfo.uid; + } + PackageSetting ps = mSettings.mPackages.get(packageName); + if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) { + return -1; + } + p = ps.pkg; + return p != null ? p.applicationInfo.uid : -1; + } + } + + public int[] getPackageGids(String packageName) { + synchronized (mPackages) { + PackageParser.Package p = mPackages.get(packageName); + if (Config.LOGV) Log.v( + TAG, "getApplicationInfo " + packageName + + ": " + p); + if (p != null) { + final PackageSetting ps = (PackageSetting)p.mExtras; + final SharedUserSetting suid = ps.sharedUser; + return suid != null ? suid.gids : ps.gids; + } + } + // stupid thing to indicate an error. + return new int[0]; + } + + public PermissionInfo getPermissionInfo(String name, int flags) { + synchronized (mPackages) { + final BasePermission p = mSettings.mPermissions.get(name); + if (p != null && p.perm != null) { + return PackageParser.generatePermissionInfo(p.perm, flags); + } + return null; + } + } + + public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) { + synchronized (mPackages) { + ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10); + for (BasePermission p : mSettings.mPermissions.values()) { + if (group == null) { + if (p.perm.info.group == null) { + out.add(PackageParser.generatePermissionInfo(p.perm, flags)); + } + } else { + if (group.equals(p.perm.info.group)) { + out.add(PackageParser.generatePermissionInfo(p.perm, flags)); + } + } + } + + if (out.size() > 0) { + return out; + } + return mPermissionGroups.containsKey(group) ? out : null; + } + } + + public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) { + synchronized (mPackages) { + return PackageParser.generatePermissionGroupInfo( + mPermissionGroups.get(name), flags); + } + } + + public List<PermissionGroupInfo> getAllPermissionGroups(int flags) { + synchronized (mPackages) { + final int N = mPermissionGroups.size(); + ArrayList<PermissionGroupInfo> out + = new ArrayList<PermissionGroupInfo>(N); + for (PackageParser.PermissionGroup pg : mPermissionGroups.values()) { + out.add(PackageParser.generatePermissionGroupInfo(pg, flags)); + } + return out; + } + } + + private ApplicationInfo generateApplicationInfoFromSettingsLP(String packageName, int flags) { + PackageSetting ps = mSettings.mPackages.get(packageName); + if(ps != null) { + if(ps.pkg == null) { + PackageInfo pInfo = generatePackageInfoFromSettingsLP(packageName, flags); + if(pInfo != null) { + return pInfo.applicationInfo; + } + return null; + } + return PackageParser.generateApplicationInfo(ps.pkg, flags); + } + return null; + } + + private PackageInfo generatePackageInfoFromSettingsLP(String packageName, int flags) { + PackageSetting ps = mSettings.mPackages.get(packageName); + if(ps != null) { + if(ps.pkg == null) { + ps.pkg = new PackageParser.Package(packageName); + ps.pkg.applicationInfo.packageName = packageName; + } + return generatePackageInfo(ps.pkg, flags); + } + return null; + } + + public ApplicationInfo getApplicationInfo(String packageName, int flags) { + synchronized (mPackages) { + PackageParser.Package p = mPackages.get(packageName); + if (Config.LOGV) Log.v( + TAG, "getApplicationInfo " + packageName + + ": " + p); + if (p != null) { + // Note: isEnabledLP() does not apply here - always return info + return PackageParser.generateApplicationInfo(p, flags); + } + if ("android".equals(packageName)||"system".equals(packageName)) { + return mAndroidApplication; + } + if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + return generateApplicationInfoFromSettingsLP(packageName, flags); + } + } + return null; + } + + + public void freeStorageAndNotify(final long freeStorageSize, final IPackageDataObserver observer) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CLEAR_APP_CACHE, null); + // Queue up an async operation since clearing cache may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + int retCode = -1; + if (mInstaller != null) { + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Log.w(TAG, "Couldn't clear application caches"); + } + } //end if mInstaller + if (observer != null) { + try { + observer.onRemoveCompleted(null, (retCode >= 0)); + } catch (RemoteException e) { + Log.w(TAG, "RemoveException when invoking call back"); + } + } + } + }); + } + + public void freeStorage(final long freeStorageSize, final PendingIntent opFinishedIntent) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CLEAR_APP_CACHE, null); + // Queue up an async operation since clearing cache may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + int retCode = -1; + if (mInstaller != null) { + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Log.w(TAG, "Couldn't clear application caches"); + } + } + if(opFinishedIntent != null) { + try { + // Callback via pending intent + opFinishedIntent.send((retCode >= 0) ? 1 : 0); + } catch (CanceledException e1) { + Log.i(TAG, "Failed to send pending intent"); + } + } + } + }); + } + + public ActivityInfo getActivityInfo(ComponentName component, int flags) { + synchronized (mPackages) { + PackageParser.Activity a = mActivities.mActivities.get(component); + if (Config.LOGV) Log.v( + TAG, "getActivityInfo " + component + ": " + a); + if (a != null && mSettings.isEnabledLP(a.info, flags)) { + return PackageParser.generateActivityInfo(a, flags); + } + if (mResolveComponentName.equals(component)) { + return mResolveActivity; + } + } + return null; + } + + public ActivityInfo getReceiverInfo(ComponentName component, int flags) { + synchronized (mPackages) { + PackageParser.Activity a = mReceivers.mActivities.get(component); + if (Config.LOGV) Log.v( + TAG, "getReceiverInfo " + component + ": " + a); + if (a != null && mSettings.isEnabledLP(a.info, flags)) { + return PackageParser.generateActivityInfo(a, flags); + } + } + return null; + } + + public ServiceInfo getServiceInfo(ComponentName component, int flags) { + synchronized (mPackages) { + PackageParser.Service s = mServices.mServices.get(component); + if (Config.LOGV) Log.v( + TAG, "getServiceInfo " + component + ": " + s); + if (s != null && mSettings.isEnabledLP(s.info, flags)) { + return PackageParser.generateServiceInfo(s, flags); + } + } + return null; + } + + public String[] getSystemSharedLibraryNames() { + Set<String> libSet; + synchronized (mPackages) { + libSet = mSharedLibraries.keySet(); + } + int size = libSet.size(); + if (size > 0) { + String[] libs = new String[size]; + libSet.toArray(libs); + return libs; + } + return null; + } + + public int checkPermission(String permName, String pkgName) { + synchronized (mPackages) { + PackageParser.Package p = mPackages.get(pkgName); + if (p != null && p.mExtras != null) { + PackageSetting ps = (PackageSetting)p.mExtras; + if (ps.sharedUser != null) { + if (ps.sharedUser.grantedPermissions.contains(permName)) { + return PackageManager.PERMISSION_GRANTED; + } + } else if (ps.grantedPermissions.contains(permName)) { + return PackageManager.PERMISSION_GRANTED; + } + } + } + return PackageManager.PERMISSION_DENIED; + } + + public int checkUidPermission(String permName, int uid) { + synchronized (mPackages) { + Object obj = mSettings.getUserIdLP(uid); + if (obj != null) { + if (obj instanceof SharedUserSetting) { + SharedUserSetting sus = (SharedUserSetting)obj; + if (sus.grantedPermissions.contains(permName)) { + return PackageManager.PERMISSION_GRANTED; + } + } else if (obj instanceof PackageSetting) { + PackageSetting ps = (PackageSetting)obj; + if (ps.grantedPermissions.contains(permName)) { + return PackageManager.PERMISSION_GRANTED; + } + } + } else { + HashSet<String> perms = mSystemPermissions.get(uid); + if (perms != null && perms.contains(permName)) { + return PackageManager.PERMISSION_GRANTED; + } + } + } + return PackageManager.PERMISSION_DENIED; + } + + private BasePermission findPermissionTreeLP(String permName) { + for(BasePermission bp : mSettings.mPermissionTrees.values()) { + if (permName.startsWith(bp.name) && + permName.length() > bp.name.length() && + permName.charAt(bp.name.length()) == '.') { + return bp; + } + } + return null; + } + + private BasePermission checkPermissionTreeLP(String permName) { + if (permName != null) { + BasePermission bp = findPermissionTreeLP(permName); + if (bp != null) { + if (bp.uid == Binder.getCallingUid()) { + return bp; + } + throw new SecurityException("Calling uid " + + Binder.getCallingUid() + + " is not allowed to add to permission tree " + + bp.name + " owned by uid " + bp.uid); + } + } + throw new SecurityException("No permission tree found for " + permName); + } + + public boolean addPermission(PermissionInfo info) { + synchronized (mPackages) { + if (info.labelRes == 0 && info.nonLocalizedLabel == null) { + throw new SecurityException("Label must be specified in permission"); + } + BasePermission tree = checkPermissionTreeLP(info.name); + BasePermission bp = mSettings.mPermissions.get(info.name); + boolean added = bp == null; + if (added) { + bp = new BasePermission(info.name, tree.sourcePackage, + BasePermission.TYPE_DYNAMIC); + } else if (bp.type != BasePermission.TYPE_DYNAMIC) { + throw new SecurityException( + "Not allowed to modify non-dynamic permission " + + info.name); + } + bp.perm = new PackageParser.Permission(tree.perm.owner, + new PermissionInfo(info)); + bp.perm.info.packageName = tree.perm.info.packageName; + bp.uid = tree.uid; + if (added) { + mSettings.mPermissions.put(info.name, bp); + } + mSettings.writeLP(); + return added; + } + } + + public void removePermission(String name) { + synchronized (mPackages) { + checkPermissionTreeLP(name); + BasePermission bp = mSettings.mPermissions.get(name); + if (bp != null) { + if (bp.type != BasePermission.TYPE_DYNAMIC) { + throw new SecurityException( + "Not allowed to modify non-dynamic permission " + + name); + } + mSettings.mPermissions.remove(name); + mSettings.writeLP(); + } + } + } + + public int checkSignatures(String pkg1, String pkg2) { + synchronized (mPackages) { + PackageParser.Package p1 = mPackages.get(pkg1); + PackageParser.Package p2 = mPackages.get(pkg2); + if (p1 == null || p1.mExtras == null + || p2 == null || p2.mExtras == null) { + return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; + } + return checkSignaturesLP(p1, p2); + } + } + + int checkSignaturesLP(PackageParser.Package p1, PackageParser.Package p2) { + if (p1.mSignatures == null) { + return p2.mSignatures == null + ? PackageManager.SIGNATURE_NEITHER_SIGNED + : PackageManager.SIGNATURE_FIRST_NOT_SIGNED; + } + if (p2.mSignatures == null) { + return PackageManager.SIGNATURE_SECOND_NOT_SIGNED; + } + final int N1 = p1.mSignatures.length; + final int N2 = p2.mSignatures.length; + for (int i=0; i<N1; i++) { + boolean match = false; + for (int j=0; j<N2; j++) { + if (p1.mSignatures[i].equals(p2.mSignatures[j])) { + match = true; + break; + } + } + if (!match) { + return PackageManager.SIGNATURE_NO_MATCH; + } + } + return PackageManager.SIGNATURE_MATCH; + } + + public String[] getPackagesForUid(int uid) { + synchronized (mPackages) { + Object obj = mSettings.getUserIdLP(uid); + if (obj instanceof SharedUserSetting) { + SharedUserSetting sus = (SharedUserSetting)obj; + final int N = sus.packages.size(); + String[] res = new String[N]; + Iterator<PackageSetting> it = sus.packages.iterator(); + int i=0; + while (it.hasNext()) { + res[i++] = it.next().name; + } + return res; + } else if (obj instanceof PackageSetting) { + PackageSetting ps = (PackageSetting)obj; + return new String[] { ps.name }; + } + } + return null; + } + + public String getNameForUid(int uid) { + synchronized (mPackages) { + Object obj = mSettings.getUserIdLP(uid); + if (obj instanceof SharedUserSetting) { + SharedUserSetting sus = (SharedUserSetting)obj; + return sus.name + ":" + sus.userId; + } else if (obj instanceof PackageSetting) { + PackageSetting ps = (PackageSetting)obj; + return ps.name; + } + } + return null; + } + + public int getUidForSharedUser(String sharedUserName) { + if(sharedUserName == null) { + return -1; + } + synchronized (mPackages) { + SharedUserSetting suid = mSettings.getSharedUserLP(sharedUserName, 0, false); + if(suid == null) { + return -1; + } + return suid.userId; + } + } + + public ResolveInfo resolveIntent(Intent intent, String resolvedType, + int flags) { + List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags); + if (query != null) { + final int N = query.size(); + if (N == 1) { + return query.get(0); + } else if (N > 1) { + // If there is more than one activity with the same priority, + // then let the user decide between them. + ResolveInfo r0 = query.get(0); + ResolveInfo r1 = query.get(1); + if (false) { + System.out.println(r0.activityInfo.name + + "=" + r0.priority + " vs " + + r1.activityInfo.name + + "=" + r1.priority); + } + // If the first activity has a higher priority, or a different + // default, then it is always desireable to pick it. + if (r0.priority != r1.priority + || r0.preferredOrder != r1.preferredOrder + || r0.isDefault != r1.isDefault) { + return query.get(0); + } + // If we have saved a preference for a preferred activity for + // this Intent, use that. + ResolveInfo ri = findPreferredActivity(intent, resolvedType, + flags, query, r0.priority); + if (ri != null) { + return ri; + } + return mResolveInfo; + } + } + return null; + } + + ResolveInfo findPreferredActivity(Intent intent, String resolvedType, + int flags, List<ResolveInfo> query, int priority) { + synchronized (mPackages) { + if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); + List<PreferredActivity> prefs = + mSettings.mPreferredActivities.queryIntent(null, + intent, resolvedType, + (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); + if (prefs != null && prefs.size() > 0) { + // First figure out how good the original match set is. + // We will only allow preferred activities that came + // from the same match quality. + int match = 0; + final int N = query.size(); + if (DEBUG_PREFERRED) Log.v(TAG, "Figuring out best match..."); + for (int j=0; j<N; j++) { + ResolveInfo ri = query.get(j); + if (DEBUG_PREFERRED) Log.v(TAG, "Match for " + ri.activityInfo + + ": 0x" + Integer.toHexString(match)); + if (ri.match > match) match = ri.match; + } + if (DEBUG_PREFERRED) Log.v(TAG, "Best match: 0x" + + Integer.toHexString(match)); + match &= IntentFilter.MATCH_CATEGORY_MASK; + final int M = prefs.size(); + for (int i=0; i<M; i++) { + PreferredActivity pa = prefs.get(i); + if (pa.mMatch != match) { + continue; + } + ActivityInfo ai = getActivityInfo(pa.mActivity, flags); + if (DEBUG_PREFERRED) { + Log.v(TAG, "Got preferred activity:"); + ai.dump(new LogPrinter(Log.INFO, TAG), " "); + } + if (ai != null) { + for (int j=0; j<N; j++) { + ResolveInfo ri = query.get(j); + if (!ri.activityInfo.applicationInfo.packageName + .equals(ai.applicationInfo.packageName)) { + continue; + } + if (!ri.activityInfo.name.equals(ai.name)) { + continue; + } + + // Okay we found a previously set preferred app. + // If the result set is different from when this + // was created, we need to clear it and re-ask the + // user their preference. + if (!pa.sameSet(query, priority)) { + Log.i(TAG, "Result set changed, dropping preferred activity for " + + intent + " type " + resolvedType); + mSettings.mPreferredActivities.removeFilter(pa); + return null; + } + + // Yay! + return ri; + } + } + } + } + } + return null; + } + + public List<ResolveInfo> queryIntentActivities(Intent intent, + String resolvedType, int flags) { + ComponentName comp = intent.getComponent(); + if (comp != null) { + List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); + ActivityInfo ai = getActivityInfo(comp, flags); + if (ai != null) { + ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = ai; + list.add(ri); + } + return list; + } + + synchronized (mPackages) { + return (List<ResolveInfo>)mActivities. + queryIntent(null, intent, resolvedType, flags); + } + } + + public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, + Intent[] specifics, String[] specificTypes, Intent intent, + String resolvedType, int flags) { + final String resultsAction = intent.getAction(); + + List<ResolveInfo> results = queryIntentActivities( + intent, resolvedType, flags|PackageManager.GET_RESOLVED_FILTER); + if (Config.LOGV) Log.v(TAG, "Query " + intent + ": " + results); + + int specificsPos = 0; + int N; + + // todo: note that the algorithm used here is O(N^2). This + // isn't a problem in our current environment, but if we start running + // into situations where we have more than 5 or 10 matches then this + // should probably be changed to something smarter... + + // First we go through and resolve each of the specific items + // that were supplied, taking care of removing any corresponding + // duplicate items in the generic resolve list. + if (specifics != null) { + for (int i=0; i<specifics.length; i++) { + final Intent sintent = specifics[i]; + if (sintent == null) { + continue; + } + + if (Config.LOGV) Log.v(TAG, "Specific #" + i + ": " + sintent); + String action = sintent.getAction(); + if (resultsAction != null && resultsAction.equals(action)) { + // If this action was explicitly requested, then don't + // remove things that have it. + action = null; + } + ComponentName comp = sintent.getComponent(); + ResolveInfo ri = null; + ActivityInfo ai = null; + if (comp == null) { + ri = resolveIntent( + sintent, + specificTypes != null ? specificTypes[i] : null, + flags); + if (ri == null) { + continue; + } + if (ri == mResolveInfo) { + // ACK! Must do something better with this. + } + ai = ri.activityInfo; + comp = new ComponentName(ai.applicationInfo.packageName, + ai.name); + } else { + ai = getActivityInfo(comp, flags); + if (ai == null) { + continue; + } + } + + // Look for any generic query activities that are duplicates + // of this specific one, and remove them from the results. + if (Config.LOGV) Log.v(TAG, "Specific #" + i + ": " + ai); + N = results.size(); + int j; + for (j=specificsPos; j<N; j++) { + ResolveInfo sri = results.get(j); + if ((sri.activityInfo.name.equals(comp.getClassName()) + && sri.activityInfo.applicationInfo.packageName.equals( + comp.getPackageName())) + || (action != null && sri.filter.matchAction(action))) { + results.remove(j); + if (Config.LOGV) Log.v( + TAG, "Removing duplicate item from " + j + + " due to specific " + specificsPos); + if (ri == null) { + ri = sri; + } + j--; + N--; + } + } + + // Add this specific item to its proper place. + if (ri == null) { + ri = new ResolveInfo(); + ri.activityInfo = ai; + } + results.add(specificsPos, ri); + ri.specificIndex = i; + specificsPos++; + } + } + + // Now we go through the remaining generic results and remove any + // duplicate actions that are found here. + N = results.size(); + for (int i=specificsPos; i<N-1; i++) { + final ResolveInfo rii = results.get(i); + if (rii.filter == null) { + continue; + } + + // Iterate over all of the actions of this result's intent + // filter... typically this should be just one. + final Iterator<String> it = rii.filter.actionsIterator(); + if (it == null) { + continue; + } + while (it.hasNext()) { + final String action = it.next(); + if (resultsAction != null && resultsAction.equals(action)) { + // If this action was explicitly requested, then don't + // remove things that have it. + continue; + } + for (int j=i+1; j<N; j++) { + final ResolveInfo rij = results.get(j); + if (rij.filter != null && rij.filter.hasAction(action)) { + results.remove(j); + if (Config.LOGV) Log.v( + TAG, "Removing duplicate item from " + j + + " due to action " + action + " at " + i); + j--; + N--; + } + } + } + + // If the caller didn't request filter information, drop it now + // so we don't have to marshall/unmarshall it. + if ((flags&PackageManager.GET_RESOLVED_FILTER) == 0) { + rii.filter = null; + } + } + + // Filter out the caller activity if so requested. + if (caller != null) { + N = results.size(); + for (int i=0; i<N; i++) { + ActivityInfo ainfo = results.get(i).activityInfo; + if (caller.getPackageName().equals(ainfo.applicationInfo.packageName) + && caller.getClassName().equals(ainfo.name)) { + results.remove(i); + break; + } + } + } + + // If the caller didn't request filter information, + // drop them now so we don't have to + // marshall/unmarshall it. + if ((flags&PackageManager.GET_RESOLVED_FILTER) == 0) { + N = results.size(); + for (int i=0; i<N; i++) { + results.get(i).filter = null; + } + } + + if (Config.LOGV) Log.v(TAG, "Result: " + results); + return results; + } + + public List<ResolveInfo> queryIntentReceivers(Intent intent, + String resolvedType, int flags) { + synchronized (mPackages) { + return (List<ResolveInfo>)mReceivers. + queryIntent(null, intent, resolvedType, flags); + } + } + + public ResolveInfo resolveService(Intent intent, String resolvedType, + int flags) { + List<ResolveInfo> query = queryIntentServices(intent, resolvedType, + flags); + if (query != null) { + if (query.size() >= 1) { + // If there is more than one service with the same priority, + // just arbitrarily pick the first one. + return query.get(0); + } + } + return null; + } + + public List<ResolveInfo> queryIntentServices(Intent intent, + String resolvedType, int flags) { + ComponentName comp = intent.getComponent(); + if (comp != null) { + List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); + ServiceInfo si = getServiceInfo(comp, flags); + if (si != null) { + ResolveInfo ri = new ResolveInfo(); + ri.serviceInfo = si; + list.add(ri); + } + return list; + } + + synchronized (mPackages) { + return (List<ResolveInfo>)mServices. + queryIntent(null, intent, resolvedType, flags); + } + } + + public List<PackageInfo> getInstalledPackages(int flags) { + ArrayList<PackageInfo> finalList = new ArrayList<PackageInfo>(); + + synchronized (mPackages) { + if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + Iterator<PackageSetting> i = mSettings.mPackages.values().iterator(); + while (i.hasNext()) { + final PackageSetting ps = i.next(); + PackageInfo psPkg = generatePackageInfoFromSettingsLP(ps.name, flags); + if(psPkg != null) { + finalList.add(psPkg); + } + } + } + else { + Iterator<PackageParser.Package> i = mPackages.values().iterator(); + while (i.hasNext()) { + final PackageParser.Package p = i.next(); + if (p.applicationInfo != null) { + PackageInfo pi = generatePackageInfo(p, flags); + if(pi != null) { + finalList.add(pi); + } + } + } + } + } + return finalList; + } + + public List<ApplicationInfo> getInstalledApplications(int flags) { + ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>(); + synchronized(mPackages) { + if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + Iterator<PackageSetting> i = mSettings.mPackages.values().iterator(); + while (i.hasNext()) { + final PackageSetting ps = i.next(); + ApplicationInfo ai = generateApplicationInfoFromSettingsLP(ps.name, flags); + if(ai != null) { + finalList.add(ai); + } + } + } + else { + Iterator<PackageParser.Package> i = mPackages.values().iterator(); + while (i.hasNext()) { + final PackageParser.Package p = i.next(); + if (p.applicationInfo != null) { + ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags); + if(ai != null) { + finalList.add(ai); + } + } + } + } + } + return finalList; + } + + public List<ApplicationInfo> getPersistentApplications(int flags) { + ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>(); + + synchronized (mPackages) { + Iterator<PackageParser.Package> i = mPackages.values().iterator(); + while (i.hasNext()) { + PackageParser.Package p = i.next(); + if (p.applicationInfo != null + && (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0 + && (!mSafeMode || (p.applicationInfo.flags + &ApplicationInfo.FLAG_SYSTEM) != 0)) { + finalList.add(p.applicationInfo); + } + } + } + + return finalList; + } + + public ProviderInfo resolveContentProvider(String name, int flags) { + synchronized (mPackages) { + final PackageParser.Provider provider = mProviders.get(name); + return provider != null + && mSettings.isEnabledLP(provider.info, flags) + && (!mSafeMode || (provider.info.applicationInfo.flags + &ApplicationInfo.FLAG_SYSTEM) != 0) + ? PackageParser.generateProviderInfo(provider, flags) + : null; + } + } + + public void querySyncProviders(List outNames, List outInfo) { + synchronized (mPackages) { + Iterator<Map.Entry<String, PackageParser.Provider>> i + = mProviders.entrySet().iterator(); + + while (i.hasNext()) { + Map.Entry<String, PackageParser.Provider> entry = i.next(); + PackageParser.Provider p = entry.getValue(); + + if (p.syncable + && (!mSafeMode || (p.info.applicationInfo.flags + &ApplicationInfo.FLAG_SYSTEM) != 0)) { + outNames.add(entry.getKey()); + outInfo.add(PackageParser.generateProviderInfo(p, 0)); + } + } + } + } + + public List<ProviderInfo> queryContentProviders(String processName, + int uid, int flags) { + ArrayList<ProviderInfo> finalList = null; + + synchronized (mPackages) { + Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator(); + while (i.hasNext()) { + PackageParser.Provider p = i.next(); + if (p.info.authority != null + && (processName == null || + (p.info.processName.equals(processName) + && p.info.applicationInfo.uid == uid)) + && mSettings.isEnabledLP(p.info, flags) + && (!mSafeMode || (p.info.applicationInfo.flags + &ApplicationInfo.FLAG_SYSTEM) != 0)) { + if (finalList == null) { + finalList = new ArrayList<ProviderInfo>(3); + } + finalList.add(PackageParser.generateProviderInfo(p, + flags)); + } + } + } + + if (finalList != null) { + Collections.sort(finalList, mProviderInitOrderSorter); + } + + return finalList; + } + + public InstrumentationInfo getInstrumentationInfo(ComponentName name, + int flags) { + synchronized (mPackages) { + final PackageParser.Instrumentation i = mInstrumentation.get(name); + return PackageParser.generateInstrumentationInfo(i, flags); + } + } + + public List<InstrumentationInfo> queryInstrumentation(String targetPackage, + int flags) { + ArrayList<InstrumentationInfo> finalList = + new ArrayList<InstrumentationInfo>(); + + synchronized (mPackages) { + Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator(); + while (i.hasNext()) { + PackageParser.Instrumentation p = i.next(); + if (targetPackage == null + || targetPackage.equals(p.info.targetPackage)) { + finalList.add(PackageParser.generateInstrumentationInfo(p, + flags)); + } + } + } + + return finalList; + } + + private void scanDirLI(File dir, int flags, int scanMode) { + Log.d(TAG, "Scanning app dir " + dir); + + String[] files = dir.list(); + + int i; + for (i=0; i<files.length; i++) { + File file = new File(dir, files[i]); + PackageParser.Package pkg = scanPackageLI(file, file, file, + flags|PackageParser.PARSE_MUST_BE_APK, scanMode); + } + } + + private static void reportSettingsProblem(int priority, String msg) { + try { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + File fname = new File(systemDir, "uiderrors.txt"); + FileOutputStream out = new FileOutputStream(fname, true); + PrintWriter pw = new PrintWriter(out); + pw.println(msg); + pw.close(); + FileUtils.setPermissions( + fname.toString(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH, + -1, -1); + } catch (java.io.IOException e) { + } + Log.println(priority, TAG, msg); + } + + private boolean collectCertificatesLI(PackageParser pp, PackageSetting ps, + PackageParser.Package pkg, File srcFile, int parseFlags) { + if (GET_CERTIFICATES) { + if (ps == null || !ps.codePath.equals(srcFile) + || ps.getTimeStamp() != srcFile.lastModified()) { + Log.i(TAG, srcFile.toString() + " changed; collecting certs"); + if (!pp.collectCertificates(pkg, parseFlags)) { + mLastScanError = pp.getParseError(); + return false; + } + } + } + return true; + } + + /* + * Scan a package and return the newly parsed package. + * Returns null in case of errors and the error code is stored in mLastScanError + */ + private PackageParser.Package scanPackageLI(File scanFile, + File destCodeFile, File destResourceFile, int parseFlags, + int scanMode) { + mLastScanError = PackageManager.INSTALL_SUCCEEDED; + parseFlags |= mDefParseFlags; + PackageParser pp = new PackageParser(scanFile.getPath()); + pp.setSeparateProcesses(mSeparateProcesses); + pp.setSdkVersion(mSdkVersion); + final PackageParser.Package pkg = pp.parsePackage(scanFile, + destCodeFile.getAbsolutePath(), mMetrics, parseFlags); + if (pkg == null) { + mLastScanError = pp.getParseError(); + return null; + } + PackageSetting ps; + PackageSetting updatedPkg; + synchronized (mPackages) { + ps = mSettings.peekPackageLP(pkg.packageName, + scanFile.toString()); + updatedPkg = mSettings.mDisabledSysPackages.get(pkg.packageName); + } + if (updatedPkg != null) { + // An updated system app will not have the PARSE_IS_SYSTEM flag set initially + parseFlags |= PackageParser.PARSE_IS_SYSTEM; + } + if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { + // Check for updated system applications here + if ((updatedPkg != null) && (ps == null)) { + // The system package has been updated and the code path does not match + // Ignore entry. Just return + Log.w(TAG, "Package:" + pkg.packageName + + " has been updated. Ignoring the one from path:"+scanFile); + mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; + return null; + } + } + if (!collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags)) { + Log.i(TAG, "Failed verifying certificates for package:" + pkg.packageName); + return null; + } + // The apk is forward locked (not public) if its code and resources + // are kept in different files. + if (ps != null && !ps.codePath.equals(ps.resourcePath)) { + scanMode |= SCAN_FORWARD_LOCKED; + } + // Note that we invoke the following method only if we are about to unpack an application + return scanPackageLI(scanFile, destCodeFile, destResourceFile, + pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE); + } + + private static String fixProcessName(String defProcessName, + String processName, int uid) { + if (processName == null) { + return defProcessName; + } + return processName; + } + + private boolean verifySignaturesLP(PackageSetting pkgSetting, + PackageParser.Package pkg, int parseFlags, boolean updateSignature) { + if (pkg.mSignatures != null) { + if (!pkgSetting.signatures.updateSignatures(pkg.mSignatures, + updateSignature)) { + Log.e(TAG, "Package " + pkg.packageName + + " signatures do not match the previously installed version; ignoring!"); + mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; + return false; + } + + if (pkgSetting.sharedUser != null) { + if (!pkgSetting.sharedUser.signatures.mergeSignatures( + pkg.mSignatures, updateSignature)) { + Log.e(TAG, "Package " + pkg.packageName + + " has no signatures that match those in shared user " + + pkgSetting.sharedUser.name + "; ignoring!"); + mLastScanError = PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; + return false; + } + } + } else { + pkg.mSignatures = pkgSetting.signatures.mSignatures; + } + return true; + } + + private PackageParser.Package scanPackageLI( + File scanFile, File destCodeFile, File destResourceFile, + PackageParser.Package pkg, int parseFlags, int scanMode) { + + mScanningPath = scanFile; + if (pkg == null) { + mLastScanError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; + return null; + } + + final String pkgName = pkg.applicationInfo.packageName; + if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; + } + + if (pkgName.equals("android")) { + synchronized (mPackages) { + if (mAndroidApplication != null) { + Log.w(TAG, "*************************************************"); + Log.w(TAG, "Core android package being redefined. Skipping."); + Log.w(TAG, " file=" + mScanningPath); + Log.w(TAG, "*************************************************"); + mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; + return null; + } + + // Set up information for our fall-back user intent resolution + // activity. + mPlatformPackage = pkg; + pkg.mVersionCode = mSdkVersion; + mAndroidApplication = pkg.applicationInfo; + mResolveActivity.applicationInfo = mAndroidApplication; + mResolveActivity.name = ResolverActivity.class.getName(); + mResolveActivity.packageName = mAndroidApplication.packageName; + mResolveActivity.processName = mAndroidApplication.processName; + mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; + mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; + mResolveActivity.theme = com.android.internal.R.style.Theme_Dialog_Alert; + mResolveActivity.exported = true; + mResolveActivity.enabled = true; + mResolveInfo.activityInfo = mResolveActivity; + mResolveInfo.priority = 0; + mResolveInfo.preferredOrder = 0; + mResolveInfo.match = 0; + mResolveComponentName = new ComponentName( + mAndroidApplication.packageName, mResolveActivity.name); + } + } + + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) Log.d( + TAG, "Scanning package " + pkgName); + if (mPackages.containsKey(pkgName) || mSharedLibraries.containsKey(pkgName)) { + Log.w(TAG, "*************************************************"); + Log.w(TAG, "Application package " + pkgName + + " already installed. Skipping duplicate."); + Log.w(TAG, "*************************************************"); + mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; + return null; + } + + SharedUserSetting suid = null; + PackageSetting pkgSetting = null; + + boolean removeExisting = false; + + synchronized (mPackages) { + // Check all shared libraries and map to their actual file path. + if (pkg.usesLibraryFiles != null) { + for (int i=0; i<pkg.usesLibraryFiles.length; i++) { + String file = mSharedLibraries.get(pkg.usesLibraryFiles[i]); + if (file == null) { + Log.e(TAG, "Package " + pkg.packageName + + " requires unavailable shared library " + + pkg.usesLibraryFiles[i] + "; ignoring!"); + mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; + return null; + } + pkg.usesLibraryFiles[i] = file; + } + } + + if (pkg.mSharedUserId != null) { + suid = mSettings.getSharedUserLP(pkg.mSharedUserId, + pkg.applicationInfo.flags, true); + if (suid == null) { + Log.w(TAG, "Creating application package " + pkgName + + " for shared user failed"); + mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return null; + } + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) { + Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + + suid.userId + "): packages=" + suid.packages); + } + } + + // Just create the setting, don't add it yet + pkgSetting = mSettings.getPackageLP(pkg, suid, destCodeFile, + destResourceFile, pkg.applicationInfo.flags, true, false); + if (pkgSetting == null) { + Log.w(TAG, "Creating application package " + pkgName + " failed"); + mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return null; + } + if(mSettings.mDisabledSysPackages.get(pkg.packageName) != null) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + } + + pkg.applicationInfo.uid = pkgSetting.userId; + pkg.mExtras = pkgSetting; + + if (!verifySignaturesLP(pkgSetting, pkg, parseFlags, + (scanMode&SCAN_UPDATE_SIGNATURE) != 0)) { + if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) == 0) { + mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; + return null; + } + // The signature has changed, but this package is in the system + // image... let's recover! + pkg.mSignatures = pkgSetting.signatures.mSignatures; + // However... if this package is part of a shared user, but it + // doesn't match the signature of the shared user, let's fail. + // What this means is that you can't change the signatures + // associated with an overall shared user, which doesn't seem all + // that unreasonable. + if (pkgSetting.sharedUser != null) { + if (!pkgSetting.sharedUser.signatures.mergeSignatures( + pkg.mSignatures, false)) { + mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; + return null; + } + } + removeExisting = true; + } + } + + if (removeExisting) { + if (mInstaller != null) { + int ret = mInstaller.remove(pkgName); + if (ret != 0) { + String msg = "System package " + pkg.packageName + + " could not have data directory erased after signature change."; + reportSettingsProblem(Log.WARN, msg); + mLastScanError = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; + return null; + } + } + Log.w(TAG, "System package " + pkg.packageName + + " signature changed: existing data removed."); + mLastScanError = PackageManager.INSTALL_SUCCEEDED; + } + + long scanFileTime = scanFile.lastModified(); + final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0; + final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.getTimeStamp(); + pkg.applicationInfo.processName = fixProcessName( + pkg.applicationInfo.packageName, + pkg.applicationInfo.processName, + pkg.applicationInfo.uid); + pkg.applicationInfo.publicSourceDir = pkgSetting.resourcePathString; + + File dataPath; + if (mPlatformPackage == pkg) { + // The system package is special. + dataPath = new File (Environment.getDataDirectory(), "system"); + pkg.applicationInfo.dataDir = dataPath.getPath(); + } else { + // This is a normal package, need to make its data directory. + dataPath = new File(mAppDataDir, pkgName); + if (dataPath.exists()) { + mOutPermissions[1] = 0; + FileUtils.getPermissions(dataPath.getPath(), mOutPermissions); + if (mOutPermissions[1] == pkg.applicationInfo.uid + || !Process.supportsProcesses()) { + pkg.applicationInfo.dataDir = dataPath.getPath(); + } else { + boolean recovered = false; + if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { + // If this is a system app, we can at least delete its + // current data so the application will still work. + if (mInstaller != null) { + int ret = mInstaller.remove(pkgName); + if(ret >= 0) { + // Old data gone! + String msg = "System package " + pkg.packageName + + " has changed from uid: " + + mOutPermissions[1] + " to " + + pkg.applicationInfo.uid + "; old data erased"; + reportSettingsProblem(Log.WARN, msg); + recovered = true; + + // And now re-install the app. + ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, + pkg.applicationInfo.uid); + if (ret == -1) { + // Ack should not happen! + msg = "System package " + pkg.packageName + + " could not have data directory re-created after delete."; + reportSettingsProblem(Log.WARN, msg); + mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return null; + } + } + } + if (!recovered) { + mHasSystemUidErrors = true; + } + } + if (!recovered) { + pkg.applicationInfo.dataDir = "/mismatched_uid/settings_" + + pkg.applicationInfo.uid + "/fs_" + + mOutPermissions[1]; + String msg = "Package " + pkg.packageName + + " has mismatched uid: " + + mOutPermissions[1] + " on disk, " + + pkg.applicationInfo.uid + " in settings"; + synchronized (mPackages) { + if (!mReportedUidError) { + mReportedUidError = true; + msg = msg + "; read messages:\n" + + mSettings.getReadMessagesLP(); + } + reportSettingsProblem(Log.ERROR, msg); + } + } + } + pkg.applicationInfo.dataDir = dataPath.getPath(); + } else { + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGV) + Log.v(TAG, "Want this data dir: " + dataPath); + //invoke installer to do the actual installation + if (mInstaller != null) { + int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid, + pkg.applicationInfo.uid); + if(ret < 0) { + // Error from installer + mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return null; + } + } else { + dataPath.mkdirs(); + if (dataPath.exists()) { + FileUtils.setPermissions( + dataPath.toString(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, + pkg.applicationInfo.uid, pkg.applicationInfo.uid); + } + } + if (dataPath.exists()) { + pkg.applicationInfo.dataDir = dataPath.getPath(); + } else { + Log.w(TAG, "Unable to create data directory: " + dataPath); + pkg.applicationInfo.dataDir = null; + } + } + } + + // Perform shared library installation and dex validation and + // optimization, if this is not a system app. + if (mInstaller != null) { + String path = scanFile.getPath(); + if (scanFileNewer) { + Log.i(TAG, path + " changed; unpacking"); + try { + cachePackageSharedLibsLI(pkg, dataPath, scanFile); + } catch (IOException e) { + Log.e(TAG, "Failure extracting shared libs", e); + if(mInstaller != null) { + mInstaller.remove(pkgName); + } else { + dataPath.delete(); + } + mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return null; + } + } + + if ((scanMode&SCAN_NO_DEX) == 0 + && (pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { + int ret = 0; + try { + if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { + ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, + (scanMode&SCAN_FORWARD_LOCKED) == 0); + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Apk not found for dexopt: " + path); + ret = -1; + } catch (IOException e) { + Log.w(TAG, "Exception reading apk: " + path, e); + ret = -1; + } + if (ret < 0) { + //error from installer + mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; + return null; + } + } + } + + if (mFactoryTest && pkg.requestedPermissions.contains( + android.Manifest.permission.FACTORY_TEST)) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST; + } + + if ((scanMode&SCAN_MONITOR) != 0) { + pkg.mPath = destCodeFile.getAbsolutePath(); + mAppDirs.put(pkg.mPath, pkg); + } + + synchronized (mPackages) { + // We don't expect installation to fail beyond this point + // Add the new setting to mSettings + mSettings.insertPackageSettingLP(pkgSetting, pkg.packageName, suid); + // Add the new setting to mPackages + mPackages.put(pkg.applicationInfo.packageName, pkg); + int N = pkg.providers.size(); + StringBuilder r = null; + int i; + for (i=0; i<N; i++) { + PackageParser.Provider p = pkg.providers.get(i); + p.info.processName = fixProcessName(pkg.applicationInfo.processName, + p.info.processName, pkg.applicationInfo.uid); + mProvidersByComponent.put(new ComponentName(p.info.packageName, + p.info.name), p); + p.syncable = p.info.isSyncable; + String names[] = p.info.authority.split(";"); + p.info.authority = null; + for (int j = 0; j < names.length; j++) { + if (j == 1 && p.syncable) { + // We only want the first authority for a provider to possibly be + // syncable, so if we already added this provider using a different + // authority clear the syncable flag. We copy the provider before + // changing it because the mProviders object contains a reference + // to a provider that we don't want to change. + // Only do this for the second authority since the resulting provider + // object can be the same for all future authorities for this provider. + p = new PackageParser.Provider(p); + p.syncable = false; + } + if (!mProviders.containsKey(names[j])) { + mProviders.put(names[j], p); + if (p.info.authority == null) { + p.info.authority = names[j]; + } else { + p.info.authority = p.info.authority + ";" + names[j]; + } + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) + Log.d(TAG, "Registered content provider: " + names[j] + + ", className = " + p.info.name + + ", isSyncable = " + p.info.isSyncable); + } else { + Log.w(TAG, "Skipping provider name " + names[j] + + " (in package " + pkg.applicationInfo.packageName + + "): name already used"); + } + } + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(p.info.name); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Providers: " + r); + } + + N = pkg.services.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Service s = pkg.services.get(i); + s.info.processName = fixProcessName(pkg.applicationInfo.processName, + s.info.processName, pkg.applicationInfo.uid); + mServices.addService(s); + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(s.info.name); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Services: " + r); + } + + N = pkg.receivers.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Activity a = pkg.receivers.get(i); + a.info.processName = fixProcessName(pkg.applicationInfo.processName, + a.info.processName, pkg.applicationInfo.uid); + mReceivers.addActivity(a, "receiver"); + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(a.info.name); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Receivers: " + r); + } + + N = pkg.activities.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Activity a = pkg.activities.get(i); + a.info.processName = fixProcessName(pkg.applicationInfo.processName, + a.info.processName, pkg.applicationInfo.uid); + mActivities.addActivity(a, "activity"); + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(a.info.name); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Activities: " + r); + } + + N = pkg.permissionGroups.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i); + PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name); + if (cur == null) { + mPermissionGroups.put(pg.info.name, pg); + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(pg.info.name); + } + } else { + Log.w(TAG, "Permission group " + pg.info.name + " from package " + + pg.info.packageName + " ignored: original from " + + cur.info.packageName); + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append("DUP:"); + r.append(pg.info.name); + } + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Permission Groups: " + r); + } + + N = pkg.permissions.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Permission p = pkg.permissions.get(i); + HashMap<String, BasePermission> permissionMap = + p.tree ? mSettings.mPermissionTrees + : mSettings.mPermissions; + p.group = mPermissionGroups.get(p.info.group); + if (p.info.group == null || p.group != null) { + BasePermission bp = permissionMap.get(p.info.name); + if (bp == null) { + bp = new BasePermission(p.info.name, p.info.packageName, + BasePermission.TYPE_NORMAL); + permissionMap.put(p.info.name, bp); + } + if (bp.perm == null) { + if (bp.sourcePackage == null + || bp.sourcePackage.equals(p.info.packageName)) { + BasePermission tree = findPermissionTreeLP(p.info.name); + if (tree == null + || tree.sourcePackage.equals(p.info.packageName)) { + bp.perm = p; + bp.uid = pkg.applicationInfo.uid; + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(p.info.name); + } + } else { + Log.w(TAG, "Permission " + p.info.name + " from package " + + p.info.packageName + " ignored: base tree " + + tree.name + " is from package " + + tree.sourcePackage); + } + } else { + Log.w(TAG, "Permission " + p.info.name + " from package " + + p.info.packageName + " ignored: original from " + + bp.sourcePackage); + } + } else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append("DUP:"); + r.append(p.info.name); + } + } else { + Log.w(TAG, "Permission " + p.info.name + " from package " + + p.info.packageName + " ignored: no group " + + p.group); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Permissions: " + r); + } + + N = pkg.instrumentation.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Instrumentation a = pkg.instrumentation.get(i); + a.info.packageName = pkg.applicationInfo.packageName; + a.info.sourceDir = pkg.applicationInfo.sourceDir; + a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir; + a.info.dataDir = pkg.applicationInfo.dataDir; + mInstrumentation.put(a.component, a); + if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(a.info.name); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Instrumentation: " + r); + } + + pkgSetting.setTimeStamp(scanFileTime); + } + + return pkg; + } + + private void cachePackageSharedLibsLI(PackageParser.Package pkg, + File dataPath, File scanFile) throws IOException { + File sharedLibraryDir = new File(dataPath.getPath() + "/lib"); + final String sharedLibraryABI = "armeabi"; + final String apkLibraryDirectory = "lib/" + sharedLibraryABI + "/"; + final String apkSharedLibraryPrefix = apkLibraryDirectory + "lib"; + final String sharedLibrarySuffix = ".so"; + boolean createdSharedLib = false; + try { + ZipFile zipFile = new ZipFile(scanFile); + Enumeration<ZipEntry> entries = + (Enumeration<ZipEntry>) zipFile.entries(); + + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + if (entry.isDirectory()) { + continue; + } + String entryName = entry.getName(); + if (! (entryName.startsWith(apkSharedLibraryPrefix) + && entryName.endsWith(sharedLibrarySuffix))) { + continue; + } + String libFileName = entryName.substring( + apkLibraryDirectory.length()); + if (libFileName.contains("/") + || (!FileUtils.isFilenameSafe(new File(libFileName)))) { + continue; + } + String sharedLibraryFilePath = sharedLibraryDir.getPath() + + File.separator + libFileName; + File sharedLibraryFile = new File(sharedLibraryFilePath); + if (! sharedLibraryFile.exists() || + sharedLibraryFile.length() != entry.getSize() || + sharedLibraryFile.lastModified() != entry.getTime()) { + if (Config.LOGD) { + Log.d(TAG, "Caching shared lib " + entry.getName()); + } + if (mInstaller == null) { + sharedLibraryDir.mkdir(); + createdSharedLib = true; + } + cacheSharedLibLI(pkg, zipFile, entry, sharedLibraryDir, + sharedLibraryFile); + } + } + } catch (IOException e) { + Log.e(TAG, "Failed to cache package shared libs", e); + if(createdSharedLib) { + sharedLibraryDir.delete(); + } + throw e; + } + } + + private void cacheSharedLibLI(PackageParser.Package pkg, + ZipFile zipFile, ZipEntry entry, + File sharedLibraryDir, + File sharedLibraryFile) throws IOException { + InputStream inputStream = zipFile.getInputStream(entry); + try { + File tempFile = File.createTempFile("tmp", "tmp", sharedLibraryDir); + String tempFilePath = tempFile.getPath(); + // XXX package manager can't change owner, so the lib files for + // now need to be left as world readable and owned by the system. + if (! FileUtils.copyToFile(inputStream, tempFile) || + ! tempFile.setLastModified(entry.getTime()) || + FileUtils.setPermissions(tempFilePath, + FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP + |FileUtils.S_IROTH, -1, -1) != 0 || + ! tempFile.renameTo(sharedLibraryFile)) { + // Failed to properly write file. + tempFile.delete(); + throw new IOException("Couldn't create cached shared lib " + + sharedLibraryFile + " in " + sharedLibraryDir); + } + } finally { + inputStream.close(); + } + } + + void removePackageLI(PackageParser.Package pkg, boolean chatty) { + if (chatty && Config.LOGD) Log.d( + TAG, "Removing package " + pkg.applicationInfo.packageName ); + + synchronized (mPackages) { + if (pkg.mPreferredOrder > 0) { + mSettings.mPreferredPackages.remove(pkg); + pkg.mPreferredOrder = 0; + updatePreferredIndicesLP(); + } + + clearPackagePreferredActivitiesLP(pkg.packageName); + + mPackages.remove(pkg.applicationInfo.packageName); + if (pkg.mPath != null) { + mAppDirs.remove(pkg.mPath); + } + + PackageSetting ps = (PackageSetting)pkg.mExtras; + if (ps != null && ps.sharedUser != null) { + // XXX don't do this until the data is removed. + if (false) { + ps.sharedUser.packages.remove(ps); + if (ps.sharedUser.packages.size() == 0) { + // Remove. + } + } + } + + int N = pkg.providers.size(); + StringBuilder r = null; + int i; + for (i=0; i<N; i++) { + PackageParser.Provider p = pkg.providers.get(i); + mProvidersByComponent.remove(new ComponentName(p.info.packageName, + p.info.name)); + if (p.info.authority == null) { + + /* The is another ContentProvider with this authority when + * this app was installed so this authority is null, + * Ignore it as we don't have to unregister the provider. + */ + continue; + } + String names[] = p.info.authority.split(";"); + for (int j = 0; j < names.length; j++) { + if (mProviders.get(names[j]) == p) { + mProviders.remove(names[j]); + if (chatty && Config.LOGD) Log.d( + TAG, "Unregistered content provider: " + names[j] + + ", className = " + p.info.name + + ", isSyncable = " + p.info.isSyncable); + } + } + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(p.info.name); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Providers: " + r); + } + + N = pkg.services.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Service s = pkg.services.get(i); + mServices.removeService(s); + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(s.info.name); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Services: " + r); + } + + N = pkg.receivers.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Activity a = pkg.receivers.get(i); + mReceivers.removeActivity(a, "receiver"); + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(a.info.name); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Receivers: " + r); + } + + N = pkg.activities.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Activity a = pkg.activities.get(i); + mActivities.removeActivity(a, "activity"); + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(a.info.name); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Activities: " + r); + } + + N = pkg.permissions.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Permission p = pkg.permissions.get(i); + boolean tree = false; + BasePermission bp = mSettings.mPermissions.get(p.info.name); + if (bp == null) { + tree = true; + bp = mSettings.mPermissionTrees.get(p.info.name); + } + if (bp != null && bp.perm == p) { + if (bp.type != BasePermission.TYPE_BUILTIN) { + if (tree) { + mSettings.mPermissionTrees.remove(p.info.name); + } else { + mSettings.mPermissions.remove(p.info.name); + } + } else { + bp.perm = null; + } + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(p.info.name); + } + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Permissions: " + r); + } + + N = pkg.instrumentation.size(); + r = null; + for (i=0; i<N; i++) { + PackageParser.Instrumentation a = pkg.instrumentation.get(i); + mInstrumentation.remove(a.component); + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(a.info.name); + } + } + if (r != null) { + if (Config.LOGD) Log.d(TAG, " Instrumentation: " + r); + } + } + } + + private static final boolean isPackageFilename(String name) { + return name != null && name.endsWith(".apk"); + } + + private void updatePermissionsLP() { + // Make sure there are no dangling permission trees. + Iterator<BasePermission> it = mSettings.mPermissionTrees + .values().iterator(); + while (it.hasNext()) { + BasePermission bp = it.next(); + if (bp.perm == null) { + Log.w(TAG, "Removing dangling permission tree: " + bp.name + + " from package " + bp.sourcePackage); + it.remove(); + } + } + + // Make sure all dynamic permissions have been assigned to a package, + // and make sure there are no dangling permissions. + it = mSettings.mPermissions.values().iterator(); + while (it.hasNext()) { + BasePermission bp = it.next(); + if (bp.type == BasePermission.TYPE_DYNAMIC) { + if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" + + bp.name + " pkg=" + bp.sourcePackage + + " info=" + bp.pendingInfo); + if (bp.perm == null && bp.pendingInfo != null) { + BasePermission tree = findPermissionTreeLP(bp.name); + if (tree != null) { + bp.perm = new PackageParser.Permission(tree.perm.owner, + new PermissionInfo(bp.pendingInfo)); + bp.perm.info.packageName = tree.perm.info.packageName; + bp.perm.info.name = bp.name; + bp.uid = tree.uid; + } + } + } + if (bp.perm == null) { + Log.w(TAG, "Removing dangling permission: " + bp.name + + " from package " + bp.sourcePackage); + it.remove(); + } + } + + // Now update the permissions for all packages, in particular + // replace the granted permissions of the system packages. + for (PackageParser.Package pkg : mPackages.values()) { + grantPermissionsLP(pkg, false); + } + } + + private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) { + final PackageSetting ps = (PackageSetting)pkg.mExtras; + if (ps == null) { + return; + } + final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; + boolean addedPermission = false; + + if (replace) { + ps.permissionsFixed = false; + if (gp == ps) { + gp.grantedPermissions.clear(); + gp.gids = mGlobalGids; + } + } + + if (gp.gids == null) { + gp.gids = mGlobalGids; + } + + final int N = pkg.requestedPermissions.size(); + for (int i=0; i<N; i++) { + String name = pkg.requestedPermissions.get(i); + BasePermission bp = mSettings.mPermissions.get(name); + PackageParser.Permission p = bp != null ? bp.perm : null; + if (false) { + if (gp != ps) { + Log.i(TAG, "Package " + pkg.packageName + " checking " + name + + ": " + p); + } + } + if (p != null) { + final String perm = p.info.name; + boolean allowed; + if (p.info.protectionLevel == PermissionInfo.PROTECTION_NORMAL + || p.info.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) { + allowed = true; + } else if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE + || p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { + allowed = (checkSignaturesLP(p.owner, pkg) + == PackageManager.SIGNATURE_MATCH) + || (checkSignaturesLP(mPlatformPackage, pkg) + == PackageManager.SIGNATURE_MATCH); + if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { + // For updated system applications, the signatureOrSystem permission + // is granted only if it had been defined by the original application. + if ((pkg.applicationInfo.flags + & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + PackageSetting sysPs = mSettings.getDisabledSystemPkg(pkg.packageName); + if(sysPs.grantedPermissions.contains(perm)) { + allowed = true; + } else { + allowed = false; + } + } else { + allowed = true; + } + } + } + } else { + allowed = false; + } + if (false) { + if (gp != ps) { + Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); + } + } + if (allowed) { + if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0 + && ps.permissionsFixed) { + // If this is an existing, non-system package, then + // we can't add any new permissions to it. + if (!gp.loadedPermissions.contains(perm)) { + allowed = false; + } + } + if (allowed) { + if (!gp.grantedPermissions.contains(perm)) { + addedPermission = true; + gp.grantedPermissions.add(perm); + gp.gids = appendInts(gp.gids, bp.gids); + } + } else { + Log.w(TAG, "Not granting permission " + perm + + " to package " + pkg.packageName + + " because it was previously installed without"); + } + } else { + Log.w(TAG, "Not granting permission " + perm + + " to package " + pkg.packageName + + " (protectionLevel=" + p.info.protectionLevel + + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + + ")"); + } + } else { + Log.w(TAG, "Unknown permission " + name + + " in package " + pkg.packageName); + } + } + + if ((addedPermission || replace) && !ps.permissionsFixed && + (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { + // This is the first that we have heard about this package, so the + // permissions we have now selected are fixed until explicitly + // changed. + ps.permissionsFixed = true; + gp.loadedPermissions = new HashSet<String>(gp.grantedPermissions); + } + } + + private final class ActivityIntentResolver + extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> { + public List queryIntent(ContentResolver resolver, Intent intent, + String resolvedType, boolean defaultOnly) { + mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; + return super.queryIntent(resolver, intent, resolvedType, defaultOnly); + } + + public List queryIntent(ContentResolver resolver, Intent intent, + String resolvedType, int flags) { + mFlags = flags; + return super.queryIntent( + resolver, intent, resolvedType, + (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); + } + + public final void addActivity(PackageParser.Activity a, String type) { + mActivities.put(a.component, a); + if (SHOW_INFO || Config.LOGV) Log.v( + TAG, " " + type + " " + + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); + if (SHOW_INFO || Config.LOGV) Log.v(TAG, " Class=" + a.info.name); + int NI = a.intents.size(); + int j; + for (j=0; j<NI; j++) { + PackageParser.ActivityIntentInfo intent = a.intents.get(j); + if (SHOW_INFO || Config.LOGV) { + Log.v(TAG, " IntentFilter:"); + intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); + } + if (!intent.debugCheck()) { + Log.w(TAG, "==> For Activity " + a.info.name); + } + addFilter(intent); + } + } + + public final void removeActivity(PackageParser.Activity a, String type) { + mActivities.remove(a.component); + if (SHOW_INFO || Config.LOGV) Log.v( + TAG, " " + type + " " + + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); + if (SHOW_INFO || Config.LOGV) Log.v(TAG, " Class=" + a.info.name); + int NI = a.intents.size(); + int j; + for (j=0; j<NI; j++) { + PackageParser.ActivityIntentInfo intent = a.intents.get(j); + if (SHOW_INFO || Config.LOGV) { + Log.v(TAG, " IntentFilter:"); + intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); + } + removeFilter(intent); + } + } + + @Override + protected boolean allowFilterResult( + PackageParser.ActivityIntentInfo filter, List<ResolveInfo> dest) { + ActivityInfo filterAi = filter.activity.info; + for (int i=dest.size()-1; i>=0; i--) { + ActivityInfo destAi = dest.get(i).activityInfo; + if (destAi.name == filterAi.name + && destAi.packageName == filterAi.packageName) { + return false; + } + } + return true; + } + + @Override + protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info, + int match) { + if (!mSettings.isEnabledLP(info.activity.info, mFlags)) { + return null; + } + final PackageParser.Activity activity = info.activity; + if (mSafeMode && (activity.info.applicationInfo.flags + &ApplicationInfo.FLAG_SYSTEM) == 0) { + return null; + } + final ResolveInfo res = new ResolveInfo(); + res.activityInfo = PackageParser.generateActivityInfo(activity, + mFlags); + if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) { + res.filter = info; + } + res.priority = info.getPriority(); + res.preferredOrder = activity.owner.mPreferredOrder; + //System.out.println("Result: " + res.activityInfo.className + + // " = " + res.priority); + res.match = match; + res.isDefault = info.hasDefault; + res.labelRes = info.labelRes; + res.nonLocalizedLabel = info.nonLocalizedLabel; + res.icon = info.icon; + return res; + } + + @Override + protected void sortResults(List<ResolveInfo> results) { + Collections.sort(results, mResolvePrioritySorter); + } + + @Override + protected void dumpFilter(Printer out, String prefix, + PackageParser.ActivityIntentInfo filter) { + out.println(prefix + + Integer.toHexString(System.identityHashCode(filter.activity)) + + " " + filter.activity.component.flattenToShortString()); + } + +// List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) { +// final Iterator<ResolveInfo> i = resolveInfoList.iterator(); +// final List<ResolveInfo> retList = Lists.newArrayList(); +// while (i.hasNext()) { +// final ResolveInfo resolveInfo = i.next(); +// if (isEnabledLP(resolveInfo.activityInfo)) { +// retList.add(resolveInfo); +// } +// } +// return retList; +// } + + // Keys are String (activity class name), values are Activity. + private final HashMap<ComponentName, PackageParser.Activity> mActivities + = new HashMap<ComponentName, PackageParser.Activity>(); + private int mFlags; + } + + private final class ServiceIntentResolver + extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> { + public List queryIntent(ContentResolver resolver, Intent intent, + String resolvedType, boolean defaultOnly) { + mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; + return super.queryIntent(resolver, intent, resolvedType, defaultOnly); + } + + public List queryIntent(ContentResolver resolver, Intent intent, + String resolvedType, int flags) { + mFlags = flags; + return super.queryIntent( + resolver, intent, resolvedType, + (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); + } + + public final void addService(PackageParser.Service s) { + mServices.put(s.component, s); + if (SHOW_INFO || Config.LOGV) Log.v( + TAG, " " + (s.info.nonLocalizedLabel != null + ? s.info.nonLocalizedLabel : s.info.name) + ":"); + if (SHOW_INFO || Config.LOGV) Log.v( + TAG, " Class=" + s.info.name); + int NI = s.intents.size(); + int j; + for (j=0; j<NI; j++) { + PackageParser.ServiceIntentInfo intent = s.intents.get(j); + if (SHOW_INFO || Config.LOGV) { + Log.v(TAG, " IntentFilter:"); + intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); + } + if (!intent.debugCheck()) { + Log.w(TAG, "==> For Service " + s.info.name); + } + addFilter(intent); + } + } + + public final void removeService(PackageParser.Service s) { + mServices.remove(s.component); + if (SHOW_INFO || Config.LOGV) Log.v( + TAG, " " + (s.info.nonLocalizedLabel != null + ? s.info.nonLocalizedLabel : s.info.name) + ":"); + if (SHOW_INFO || Config.LOGV) Log.v( + TAG, " Class=" + s.info.name); + int NI = s.intents.size(); + int j; + for (j=0; j<NI; j++) { + PackageParser.ServiceIntentInfo intent = s.intents.get(j); + if (SHOW_INFO || Config.LOGV) { + Log.v(TAG, " IntentFilter:"); + intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); + } + removeFilter(intent); + } + } + + @Override + protected boolean allowFilterResult( + PackageParser.ServiceIntentInfo filter, List<ResolveInfo> dest) { + ServiceInfo filterSi = filter.service.info; + for (int i=dest.size()-1; i>=0; i--) { + ServiceInfo destAi = dest.get(i).serviceInfo; + if (destAi.name == filterSi.name + && destAi.packageName == filterSi.packageName) { + return false; + } + } + return true; + } + + @Override + protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter, + int match) { + final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo)filter; + if (!mSettings.isEnabledLP(info.service.info, mFlags)) { + return null; + } + final PackageParser.Service service = info.service; + if (mSafeMode && (service.info.applicationInfo.flags + &ApplicationInfo.FLAG_SYSTEM) == 0) { + return null; + } + final ResolveInfo res = new ResolveInfo(); + res.serviceInfo = PackageParser.generateServiceInfo(service, + mFlags); + if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) { + res.filter = filter; + } + res.priority = info.getPriority(); + res.preferredOrder = service.owner.mPreferredOrder; + //System.out.println("Result: " + res.activityInfo.className + + // " = " + res.priority); + res.match = match; + res.isDefault = info.hasDefault; + res.labelRes = info.labelRes; + res.nonLocalizedLabel = info.nonLocalizedLabel; + res.icon = info.icon; + return res; + } + + @Override + protected void sortResults(List<ResolveInfo> results) { + Collections.sort(results, mResolvePrioritySorter); + } + + @Override + protected void dumpFilter(Printer out, String prefix, + PackageParser.ServiceIntentInfo filter) { + out.println(prefix + + Integer.toHexString(System.identityHashCode(filter.service)) + + " " + filter.service.component.flattenToShortString()); + } + +// List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) { +// final Iterator<ResolveInfo> i = resolveInfoList.iterator(); +// final List<ResolveInfo> retList = Lists.newArrayList(); +// while (i.hasNext()) { +// final ResolveInfo resolveInfo = (ResolveInfo) i; +// if (isEnabledLP(resolveInfo.serviceInfo)) { +// retList.add(resolveInfo); +// } +// } +// return retList; +// } + + // Keys are String (activity class name), values are Activity. + private final HashMap<ComponentName, PackageParser.Service> mServices + = new HashMap<ComponentName, PackageParser.Service>(); + private int mFlags; + }; + + private static final Comparator<ResolveInfo> mResolvePrioritySorter = + new Comparator<ResolveInfo>() { + public int compare(ResolveInfo r1, ResolveInfo r2) { + int v1 = r1.priority; + int v2 = r2.priority; + //System.out.println("Comparing: q1=" + q1 + " q2=" + q2); + if (v1 != v2) { + return (v1 > v2) ? -1 : 1; + } + v1 = r1.preferredOrder; + v2 = r2.preferredOrder; + if (v1 != v2) { + return (v1 > v2) ? -1 : 1; + } + if (r1.isDefault != r2.isDefault) { + return r1.isDefault ? -1 : 1; + } + v1 = r1.match; + v2 = r2.match; + //System.out.println("Comparing: m1=" + m1 + " m2=" + m2); + return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0); + } + }; + + private static final Comparator<ProviderInfo> mProviderInitOrderSorter = + new Comparator<ProviderInfo>() { + public int compare(ProviderInfo p1, ProviderInfo p2) { + final int v1 = p1.initOrder; + final int v2 = p2.initOrder; + return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0); + } + }; + + private static final void sendPackageBroadcast(String action, String pkg, Bundle extras) { + IActivityManager am = ActivityManagerNative.getDefault(); + if (am != null) { + try { + final Intent intent = new Intent(action, + pkg != null ? Uri.fromParts("package", pkg, null) : null); + if (extras != null) { + intent.putExtras(extras); + } + am.broadcastIntent( + null, intent, + null, null, 0, null, null, null, false, false); + } catch (RemoteException ex) { + } + } + } + + private final class AppDirObserver extends FileObserver { + public AppDirObserver(String path, int mask, boolean isrom) { + super(path, mask); + mRootDir = path; + mIsRom = isrom; + } + + public void onEvent(int event, String path) { + String removedPackage = null; + int removedUid = -1; + String addedPackage = null; + int addedUid = -1; + + synchronized (mInstallLock) { + String fullPathStr = null; + File fullPath = null; + if (path != null) { + fullPath = new File(mRootDir, path); + fullPathStr = fullPath.getPath(); + } + + if (Config.LOGV) Log.v( + TAG, "File " + fullPathStr + " changed: " + + Integer.toHexString(event)); + + if (!isPackageFilename(path)) { + if (Config.LOGV) Log.v( + TAG, "Ignoring change of non-package file: " + fullPathStr); + return; + } + + if ((event&REMOVE_EVENTS) != 0) { + synchronized (mInstallLock) { + PackageParser.Package p = mAppDirs.get(fullPathStr); + if (p != null) { + removePackageLI(p, true); + removedPackage = p.applicationInfo.packageName; + removedUid = p.applicationInfo.uid; + } + } + } + + if ((event&ADD_EVENTS) != 0) { + PackageParser.Package p = mAppDirs.get(fullPathStr); + if (p == null) { + p = scanPackageLI(fullPath, fullPath, fullPath, + (mIsRom ? PackageParser.PARSE_IS_SYSTEM : 0) | + PackageParser.PARSE_CHATTY | + PackageParser.PARSE_MUST_BE_APK, + SCAN_MONITOR); + if (p != null) { + synchronized (mPackages) { + grantPermissionsLP(p, false); + } + addedPackage = p.applicationInfo.packageName; + addedUid = p.applicationInfo.uid; + } + } + } + + synchronized (mPackages) { + mSettings.writeLP(); + } + } + + if (removedPackage != null) { + Bundle extras = new Bundle(1); + extras.putInt(Intent.EXTRA_UID, removedUid); + extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false); + sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras); + } + if (addedPackage != null) { + Bundle extras = new Bundle(1); + extras.putInt(Intent.EXTRA_UID, addedUid); + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage, extras); + } + } + + private final String mRootDir; + private final boolean mIsRom; + } + + /* Called when a downloaded package installation has been confirmed by the user */ + public void installPackage( + final Uri packageURI, final IPackageInstallObserver observer, final int flags) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INSTALL_PACKAGES, null); + + // Queue up an async operation since the package installation may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + PackageInstalledInfo res; + synchronized (mInstallLock) { + res = installPackageLI(packageURI, flags); + } + if (observer != null) { + try { + observer.packageInstalled(res.name, res.returnCode); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } + } + // There appears to be a subtle deadlock condition if the sendPackageBroadcast + // call appears in the synchronized block above. + if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { + res.removedInfo.sendBroadcast(false, true); + Bundle extras = new Bundle(1); + extras.putInt(Intent.EXTRA_UID, res.uid); + if (res.removedInfo.removedPackage != null) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, + res.pkg.applicationInfo.packageName, + extras); + } + Runtime.getRuntime().gc(); + } + }); + } + + class PackageInstalledInfo { + String name; + int uid; + PackageParser.Package pkg; + int returnCode; + PackageRemovedInfo removedInfo; + } + + /* + * Install a non-existing package. + */ + private void installNewPackageLI(String pkgName, + File tmpPackageFile, + String destFilePath, File destPackageFile, File destResourceFile, + PackageParser.Package pkg, boolean forwardLocked, + PackageInstalledInfo res) { + // Remember this for later, in case we need to rollback this install + boolean dataDirExists = (new File(mAppDataDir, pkgName)).exists(); + res.name = pkgName; + synchronized(mPackages) { + if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(destFilePath)) { + // Don't allow installation over an existing package with the same name. + Log.w(TAG, "Attempt to re-install " + pkgName + + " without first uninstalling."); + res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; + return; + } + } + if (destPackageFile.exists()) { + // It's safe to do this because we know (from the above check) that the file + // isn't currently used for an installed package. + destPackageFile.delete(); + } + mLastScanError = PackageManager.INSTALL_SUCCEEDED; + PackageParser.Package newPackage = scanPackageLI(tmpPackageFile, destPackageFile, + destResourceFile, pkg, 0, + SCAN_MONITOR | SCAN_FORCE_DEX + | SCAN_UPDATE_SIGNATURE + | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)); + if (newPackage == null) { + Log.w(TAG, "Package couldn't be installed in " + destPackageFile); + if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; + } + } else { + updateSettingsLI(pkgName, tmpPackageFile, + destFilePath, destPackageFile, + destResourceFile, pkg, + newPackage, + true, + forwardLocked, + res); + // delete the partially installed application. the data directory will have to be + // restored if it was already existing + if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) { + // remove package from internal structures. Note that we want deletePackageX to + // delete the package data and cache directories that it created in + // scanPackageLocked, unless those directories existed before we even tried to + // install. + deletePackageLI( + pkgName, true, + dataDirExists ? PackageManager.DONT_DELETE_DATA : 0, + res.removedInfo); + } + } + } + + private void replacePackageLI(String pkgName, + File tmpPackageFile, + String destFilePath, File destPackageFile, File destResourceFile, + PackageParser.Package pkg, boolean forwardLocked, + PackageInstalledInfo res) { + PackageParser.Package deletedPackage; + // First find the old package info and check signatures + synchronized(mPackages) { + deletedPackage = mPackages.get(pkgName); + if(checkSignaturesLP(pkg, deletedPackage) != PackageManager.SIGNATURE_MATCH) { + res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; + return; + } + } + boolean sysPkg = ((deletedPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); + if(sysPkg) { + replaceSystemPackageLI(deletedPackage, + tmpPackageFile, destFilePath, + destPackageFile, destResourceFile, pkg, forwardLocked, res); + } else { + replaceNonSystemPackageLI(deletedPackage, tmpPackageFile, destFilePath, + destPackageFile, destResourceFile, pkg, forwardLocked, res); + } + } + + private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, + File tmpPackageFile, + String destFilePath, File destPackageFile, File destResourceFile, + PackageParser.Package pkg, boolean forwardLocked, + PackageInstalledInfo res) { + PackageParser.Package newPackage = null; + String pkgName = deletedPackage.packageName; + boolean deletedPkg = true; + boolean updatedSettings = false; + int parseFlags = PackageManager.REPLACE_EXISTING_PACKAGE; + // First delete the existing package while retaining the data directory + if (!deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA, + res.removedInfo)) { + // If the existing package was'nt successfully deleted + res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; + deletedPkg = false; + } else { + // Successfully deleted the old package. Now proceed with re-installation + mLastScanError = PackageManager.INSTALL_SUCCEEDED; + newPackage = scanPackageLI(tmpPackageFile, destPackageFile, + destResourceFile, pkg, parseFlags, + SCAN_MONITOR | SCAN_FORCE_DEX + | SCAN_UPDATE_SIGNATURE + | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)); + if (newPackage == null) { + Log.w(TAG, "Package couldn't be installed in " + destPackageFile); + if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; + } + } else { + updateSettingsLI(pkgName, tmpPackageFile, + destFilePath, destPackageFile, + destResourceFile, pkg, + newPackage, + true, + forwardLocked, + res); + updatedSettings = true; + } + } + + if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { + // If we deleted an exisiting package, the old source and resource files that we + // were keeping around in case we needed them (see below) can now be deleted + final ApplicationInfo deletedPackageAppInfo = deletedPackage.applicationInfo; + final ApplicationInfo installedPackageAppInfo = + newPackage.applicationInfo; + if (!deletedPackageAppInfo.sourceDir + .equals(installedPackageAppInfo.sourceDir)) { + new File(deletedPackageAppInfo.sourceDir).delete(); + } + if (!deletedPackageAppInfo.publicSourceDir + .equals(installedPackageAppInfo.publicSourceDir)) { + new File(deletedPackageAppInfo.publicSourceDir).delete(); + } + //update signature on the new package setting + //this should always succeed, since we checked the + //signature earlier. + synchronized(mPackages) { + verifySignaturesLP(mSettings.mPackages.get(pkgName), pkg, + parseFlags, true); + } + } else { + // remove package from internal structures. Note that we want deletePackageX to + // delete the package data and cache directories that it created in + // scanPackageLocked, unless those directories existed before we even tried to + // install. + if(updatedSettings) { + deletePackageLI( + pkgName, true, + PackageManager.DONT_DELETE_DATA, + res.removedInfo); + } + // Since we failed to install the new package we need to restore the old + // package that we deleted. + if(deletedPkg) { + installPackageLI( + Uri.fromFile(new File(deletedPackage.mPath)), + isForwardLocked(deletedPackage) + ? PackageManager.FORWARD_LOCK_PACKAGE + : 0); + } + } + } + + private void replaceSystemPackageLI(PackageParser.Package deletedPackage, + File tmpPackageFile, + String destFilePath, File destPackageFile, File destResourceFile, + PackageParser.Package pkg, boolean forwardLocked, + PackageInstalledInfo res) { + PackageParser.Package newPackage = null; + boolean updatedSettings = false; + int parseFlags = PackageManager.REPLACE_EXISTING_PACKAGE | + PackageParser.PARSE_IS_SYSTEM; + String packageName = deletedPackage.packageName; + res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; + if (packageName == null) { + Log.w(TAG, "Attempt to delete null packageName."); + return; + } + PackageParser.Package oldPkg; + PackageSetting oldPkgSetting; + synchronized (mPackages) { + oldPkg = mPackages.get(packageName); + oldPkgSetting = mSettings.mPackages.get(packageName); + if((oldPkg == null) || (oldPkg.applicationInfo == null) || + (oldPkgSetting == null)) { + Log.w(TAG, "Could'nt find package:"+packageName+" information"); + return; + } + } + res.removedInfo.uid = oldPkg.applicationInfo.uid; + res.removedInfo.removedPackage = packageName; + // Remove existing system package + removePackageLI(oldPkg, true); + synchronized (mPackages) { + res.removedInfo.removedUid = mSettings.disableSystemPackageLP(packageName); + } + + // Successfully disabled the old package. Now proceed with re-installation + mLastScanError = PackageManager.INSTALL_SUCCEEDED; + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + newPackage = scanPackageLI(tmpPackageFile, destPackageFile, + destResourceFile, pkg, parseFlags, + SCAN_MONITOR | SCAN_FORCE_DEX + | SCAN_UPDATE_SIGNATURE + | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)); + if (newPackage == null) { + Log.w(TAG, "Package couldn't be installed in " + destPackageFile); + if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; + } + } else { + updateSettingsLI(packageName, tmpPackageFile, + destFilePath, destPackageFile, + destResourceFile, pkg, + newPackage, + true, + forwardLocked, + res); + updatedSettings = true; + } + + if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { + //update signature on the new package setting + //this should always succeed, since we checked the + //signature earlier. + synchronized(mPackages) { + verifySignaturesLP(mSettings.mPackages.get(packageName), pkg, + parseFlags, true); + } + } else { + // Re installation failed. Restore old information + // Remove new pkg information + removePackageLI(newPackage, true); + // Add back the old system package + scanPackageLI(oldPkgSetting.codePath, oldPkgSetting.codePath, + oldPkgSetting.resourcePath, + oldPkg, parseFlags, + SCAN_MONITOR + | SCAN_UPDATE_SIGNATURE + | (forwardLocked ? SCAN_FORWARD_LOCKED : 0)); + // Restore the old system information in Settings + synchronized(mPackages) { + if(updatedSettings) { + mSettings.enableSystemPackageLP(packageName); + } + mSettings.writeLP(); + } + } + } + + private void updateSettingsLI(String pkgName, File tmpPackageFile, + String destFilePath, File destPackageFile, + File destResourceFile, + PackageParser.Package pkg, + PackageParser.Package newPackage, + boolean replacingExistingPackage, + boolean forwardLocked, + PackageInstalledInfo res) { + synchronized (mPackages) { + //write settings. the installStatus will be incomplete at this stage. + //note that the new package setting would have already been + //added to mPackages. It hasn't been persisted yet. + mSettings.setInstallStatus(pkgName, PKG_INSTALL_INCOMPLETE); + mSettings.writeLP(); + } + + int retCode = 0; + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { + retCode = mInstaller.movedex(tmpPackageFile.toString(), + destPackageFile.toString()); + if (retCode != 0) { + Log.e(TAG, "Couldn't rename dex file: " + destPackageFile); + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + return; + } + } + // XXX There are probably some big issues here: upon doing + // the rename, we have reached the point of no return (the + // original .apk is gone!), so we can't fail. Yet... we can. + if (!tmpPackageFile.renameTo(destPackageFile)) { + Log.e(TAG, "Couldn't move package file to: " + destPackageFile); + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } else { + res.returnCode = setPermissionsLI(pkgName, newPackage, destFilePath, + destResourceFile, + forwardLocked); + if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) { + return; + } else { + Log.d(TAG, "New package installed in " + destPackageFile); + } + } + if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) { + if (mInstaller != null) { + mInstaller.rmdex(tmpPackageFile.getPath()); + } + } + + synchronized (mPackages) { + grantPermissionsLP(newPackage, true); + res.name = pkgName; + res.uid = newPackage.applicationInfo.uid; + res.pkg = newPackage; + mSettings.setInstallStatus(pkgName, PKG_INSTALL_COMPLETE); + res.returnCode = PackageManager.INSTALL_SUCCEEDED; + //to update install status + mSettings.writeLP(); + } + } + + private PackageInstalledInfo installPackageLI(Uri pPackageURI, int pFlags) { + File tmpPackageFile = null; + String pkgName = null; + boolean forwardLocked = false; + boolean replacingExistingPackage = false; + // Result object to be returned + PackageInstalledInfo res = new PackageInstalledInfo(); + res.returnCode = PackageManager.INSTALL_SUCCEEDED; + res.uid = -1; + res.pkg = null; + res.removedInfo = new PackageRemovedInfo(); + + main_flow: try { + tmpPackageFile = createTempPackageFile(); + if (tmpPackageFile == null) { + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + break main_flow; + } + tmpPackageFile.deleteOnExit(); // paranoia + if (pPackageURI.getScheme().equals("file")) { + final File srcPackageFile = new File(pPackageURI.getPath()); + // We copy the source package file to a temp file and then rename it to the + // destination file in order to eliminate a window where the package directory + // scanner notices the new package file but it's not completely copied yet. + if (!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) { + Log.e(TAG, "Couldn't copy package file to temp file."); + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + break main_flow; + } + } else if (pPackageURI.getScheme().equals("content")) { + ParcelFileDescriptor fd; + try { + fd = mContext.getContentResolver().openFileDescriptor(pPackageURI, "r"); + } catch (FileNotFoundException e) { + Log.e(TAG, "Couldn't open file descriptor from download service."); + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + break main_flow; + } + if (fd == null) { + Log.e(TAG, "Couldn't open file descriptor from download service (null)."); + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + break main_flow; + } + if (Config.LOGV) { + Log.v(TAG, "Opened file descriptor from download service."); + } + ParcelFileDescriptor.AutoCloseInputStream + dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd); + // We copy the source package file to a temp file and then rename it to the + // destination file in order to eliminate a window where the package directory + // scanner notices the new package file but it's not completely copied yet. + if (!FileUtils.copyToFile(dlStream, tmpPackageFile)) { + Log.e(TAG, "Couldn't copy package stream to temp file."); + res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + break main_flow; + } + } else { + Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI); + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_URI; + break main_flow; + } + pkgName = PackageParser.parsePackageName( + tmpPackageFile.getAbsolutePath(), 0); + if (pkgName == null) { + Log.e(TAG, "Couldn't find a package name in : " + tmpPackageFile); + res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; + break main_flow; + } + res.name = pkgName; + //initialize some variables before installing pkg + final String pkgFileName = pkgName + ".apk"; + final File destDir = ((pFlags&PackageManager.FORWARD_LOCK_PACKAGE) != 0) + ? mDrmAppPrivateInstallDir + : mAppInstallDir; + final File destPackageFile = new File(destDir, pkgFileName); + final String destFilePath = destPackageFile.getAbsolutePath(); + File destResourceFile; + if ((pFlags&PackageManager.FORWARD_LOCK_PACKAGE) != 0) { + final String publicZipFileName = pkgName + ".zip"; + destResourceFile = new File(mAppInstallDir, publicZipFileName); + forwardLocked = true; + } else { + destResourceFile = destPackageFile; + } + // Retrieve PackageSettings and parse package + int parseFlags = PackageParser.PARSE_CHATTY; + parseFlags |= mDefParseFlags; + PackageParser pp = new PackageParser(tmpPackageFile.getPath()); + pp.setSeparateProcesses(mSeparateProcesses); + pp.setSdkVersion(mSdkVersion); + final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile, + destPackageFile.getAbsolutePath(), mMetrics, parseFlags); + if (pkg == null) { + res.returnCode = pp.getParseError(); + break main_flow; + } + if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) { + res.returnCode = pp.getParseError(); + break main_flow; + } + + synchronized (mPackages) { + //check if installing already existing package + if ((pFlags&PackageManager.REPLACE_EXISTING_PACKAGE) != 0 + && mPackages.containsKey(pkgName)) { + replacingExistingPackage = true; + } + } + + if(replacingExistingPackage) { + replacePackageLI(pkgName, + tmpPackageFile, + destFilePath, destPackageFile, destResourceFile, + pkg, forwardLocked, + res); + } else { + installNewPackageLI(pkgName, + tmpPackageFile, + destFilePath, destPackageFile, destResourceFile, + pkg, forwardLocked, + res); + } + } finally { + if (tmpPackageFile != null && tmpPackageFile.exists()) { + tmpPackageFile.delete(); + } + return res; + } + } + + private int setPermissionsLI(String pkgName, + PackageParser.Package newPackage, + String destFilePath, + File destResourceFile, + boolean forwardLocked) { + int retCode; + if (forwardLocked) { + try { + extractPublicFiles(newPackage, destResourceFile); + } catch (IOException e) { + Log.e(TAG, "Couldn't create a new zip file for the public parts of a" + + " forward-locked app."); + return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; + } finally { + //TODO clean up the extracted public files + } + if (mInstaller != null) { + retCode = mInstaller.setForwardLockPerm(pkgName, + newPackage.applicationInfo.uid); + } else { + final int filePermissions = + FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP; + retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1, + newPackage.applicationInfo.uid); + } + } else { + final int filePermissions = + FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP + |FileUtils.S_IROTH; + retCode = FileUtils.setPermissions(destFilePath, filePermissions, -1, -1); + } + if (retCode != 0) { + Log.e(TAG, "Couldn't set new package file permissions for " + destFilePath + + ". The return code was: " + retCode); + } + return PackageManager.INSTALL_SUCCEEDED; + } + + private boolean isForwardLocked(PackageParser.Package deletedPackage) { + final ApplicationInfo applicationInfo = deletedPackage.applicationInfo; + return applicationInfo.sourceDir.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath()); + } + + private void extractPublicFiles(PackageParser.Package newPackage, + File publicZipFile) throws IOException { + final ZipOutputStream publicZipOutStream = + new ZipOutputStream(new FileOutputStream(publicZipFile)); + final ZipFile privateZip = new ZipFile(newPackage.mPath); + + // Copy manifest, resources.arsc and res directory to public zip + + final Enumeration<? extends ZipEntry> privateZipEntries = privateZip.entries(); + while (privateZipEntries.hasMoreElements()) { + final ZipEntry zipEntry = privateZipEntries.nextElement(); + final String zipEntryName = zipEntry.getName(); + if ("AndroidManifest.xml".equals(zipEntryName) + || "resources.arsc".equals(zipEntryName) + || zipEntryName.startsWith("res/")) { + try { + copyZipEntry(zipEntry, privateZip, publicZipOutStream); + } catch (IOException e) { + try { + publicZipOutStream.close(); + throw e; + } finally { + publicZipFile.delete(); + } + } + } + } + + publicZipOutStream.close(); + FileUtils.setPermissions( + publicZipFile.getAbsolutePath(), + FileUtils.S_IRUSR|FileUtils.S_IWUSR|FileUtils.S_IRGRP|FileUtils.S_IROTH, + -1, -1); + } + + private static void copyZipEntry(ZipEntry zipEntry, + ZipFile inZipFile, + ZipOutputStream outZipStream) throws IOException { + byte[] buffer = new byte[4096]; + int num; + + ZipEntry newEntry; + if (zipEntry.getMethod() == ZipEntry.STORED) { + // Preserve the STORED method of the input entry. + newEntry = new ZipEntry(zipEntry); + } else { + // Create a new entry so that the compressed len is recomputed. + newEntry = new ZipEntry(zipEntry.getName()); + } + outZipStream.putNextEntry(newEntry); + + InputStream data = inZipFile.getInputStream(zipEntry); + while ((num = data.read(buffer)) > 0) { + outZipStream.write(buffer, 0, num); + } + outZipStream.flush(); + } + + private void deleteTempPackageFiles() { + FilenameFilter filter = new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.startsWith("vmdl") && name.endsWith(".tmp"); + } + }; + String tmpFilesList[] = mAppInstallDir.list(filter); + if(tmpFilesList == null) { + return; + } + for(int i = 0; i < tmpFilesList.length; i++) { + File tmpFile = new File(mAppInstallDir, tmpFilesList[i]); + tmpFile.delete(); + } + } + + private File createTempPackageFile() { + File tmpPackageFile; + try { + tmpPackageFile = File.createTempFile("vmdl", ".tmp", mAppInstallDir); + } catch (IOException e) { + Log.e(TAG, "Couldn't create temp file for downloaded package file."); + return null; + } + try { + FileUtils.setPermissions( + tmpPackageFile.getCanonicalPath(), FileUtils.S_IRUSR|FileUtils.S_IWUSR, + -1, -1); + } catch (IOException e) { + Log.e(TAG, "Trouble getting the canoncical path for a temp file."); + return null; + } + return tmpPackageFile; + } + + public void deletePackage(final String packageName, + final IPackageDeleteObserver observer, + final int flags) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.DELETE_PACKAGES, null); + // Queue up an async operation since the package deletion may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + final boolean succeded = deletePackageX(packageName, true, true, flags); + if (observer != null) { + try { + observer.packageDeleted(succeded); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } //end catch + } //end if + } //end run + }); + } + + /** + * This method is an internal method that could be get invoked either + * to delete an installed package or to clean up a failed installation. + * After deleting an installed package, a broadcast is sent to notify any + * listeners that the package has been installed. For cleaning up a failed + * installation, the broadcast is not necessary since the package's + * installation wouldn't have sent the initial broadcast either + * The key steps in deleting a package are + * deleting the package information in internal structures like mPackages, + * deleting the packages base directories through installd + * updating mSettings to reflect current status + * persisting settings for later use + * sending a broadcast if necessary + */ + + private boolean deletePackageX(String packageName, boolean sendBroadCast, + boolean deleteCodeAndResources, int flags) { + PackageRemovedInfo info = new PackageRemovedInfo(); + boolean res = false; + + synchronized (mInstallLock) { + res = deletePackageLI(packageName, deleteCodeAndResources, flags, info); + } + + if(res && sendBroadCast) { + info.sendBroadcast(deleteCodeAndResources, false); + } + return res; + } + + static class PackageRemovedInfo { + String removedPackage; + int uid = -1; + int removedUid = -1; + + void sendBroadcast(boolean fullRemove, boolean replacing) { + Bundle extras = new Bundle(1); + extras.putInt(Intent.EXTRA_UID, removedUid >= 0 ? removedUid : uid); + extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove); + if (replacing) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } + if (removedPackage != null) { + sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras); + } + if (removedUid >= 0) { + sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras); + } + } + } + + /* + * This method deletes the package from internal data structures. If the DONT_DELETE_DATA + * flag is not set, the data directory is removed as well. + * make sure this flag is set for partially installed apps. If not its meaningless to + * delete a partially installed application. + */ + private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo, + int flags) { + String packageName = p.packageName; + outInfo.removedPackage = packageName; + removePackageLI(p, true); + // Retrieve object to delete permissions for shared user later on + PackageSetting deletedPs; + synchronized (mPackages) { + deletedPs = mSettings.mPackages.get(packageName); + } + if ((flags&PackageManager.DONT_DELETE_DATA) == 0) { + if (mInstaller != null) { + int retCode = mInstaller.remove(packageName); + if (retCode < 0) { + Log.w(TAG, "Couldn't remove app data or cache directory for package: " + + packageName + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion + } + } else { + //for emulator + PackageParser.Package pkg = mPackages.get(packageName); + File dataDir = new File(pkg.applicationInfo.dataDir); + dataDir.delete(); + } + synchronized (mPackages) { + outInfo.removedUid = mSettings.removePackageLP(packageName); + } + } + synchronized (mPackages) { + if ( (deletedPs != null) && (deletedPs.sharedUser != null)) { + // remove permissions associated with package + mSettings.updateSharedUserPerms (deletedPs); + } + // Save settings now + mSettings.writeLP (); + } + } + + /* + * Tries to delete system package. + */ + private boolean deleteSystemPackageLI(PackageParser.Package p, + boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) { + ApplicationInfo applicationInfo = p.applicationInfo; + //applicable for non-partially installed applications only + if (applicationInfo == null) { + Log.w(TAG, "Package " + p.packageName + " has no applicationInfo."); + return false; + } + PackageSetting ps = null; + // Confirm if the system package has been updated + // An updated system app can be deleted. This will also have to restore + // the system pkg from system partition + synchronized (mPackages) { + ps = mSettings.getDisabledSystemPkg(p.packageName); + } + if (ps == null) { + Log.w(TAG, "Attempt to delete system package "+ p.packageName); + return false; + } else { + Log.i(TAG, "Deleting system pkg from data partition"); + } + // Delete the updated package + boolean ret = deleteInstalledPackageLI(p, deleteCodeAndResources, flags, outInfo); + if (!ret) { + return false; + } + synchronized (mPackages) { + // Reinstate the old system package + mSettings.enableSystemPackageLP(p.packageName); + } + // Install the system package + PackageParser.Package newPkg = scanPackageLI(ps.codePath, ps.codePath, ps.resourcePath, + PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM, + SCAN_MONITOR); + + if (newPkg == null) { + Log.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError); + return false; + } + synchronized (mPackages) { + mSettings.writeLP(); + } + return true; + } + + private boolean deleteInstalledPackageLI(PackageParser.Package p, + boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) { + ApplicationInfo applicationInfo = p.applicationInfo; + if (applicationInfo == null) { + Log.w(TAG, "Package " + p.packageName + " has no applicationInfo."); + return false; + } + // Delete application's source directory + File sourceFile = new File(applicationInfo.sourceDir); + if (!sourceFile.exists()) { + Log.w(TAG, "Package source " + applicationInfo.sourceDir + " does not exist."); + } + outInfo.uid = applicationInfo.uid; + + // Delete package data from internal structures and also remove data if flag is set + removePackageDataLI(p, outInfo, flags); + + // Delete application code and resources + if (deleteCodeAndResources) { + sourceFile.delete(); + final File publicSourceFile = new File(applicationInfo.publicSourceDir); + if (publicSourceFile.exists()) { + publicSourceFile.delete(); + } + if (mInstaller != null) { + int retCode = mInstaller.rmdex(sourceFile.toString()); + if (retCode < 0) { + Log.w(TAG, "Couldn't remove dex file for package: " + + p.packageName + " at location " + sourceFile.toString() + ", retcode=" + retCode); + // we don't consider this to be a failure of the core package deletion + } + } + } + return true; + } + + /* + * This method handles package deletion in general + */ + private boolean deletePackageLI(String packageName, + boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo) { + if (packageName == null) { + Log.w(TAG, "Attempt to delete null packageName."); + return false; + } + PackageParser.Package p; + boolean dataOnly = false; + synchronized (mPackages) { + p = mPackages.get(packageName); + if (p == null) { + //this retrieves partially installed apps + dataOnly = true; + PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps == null) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; + } + p = ps.pkg; + } + } + if (p == null) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; + } + + if (dataOnly) { + // Delete application data first + removePackageDataLI(p, outInfo, flags); + return true; + } + // At this point the package should have ApplicationInfo associated with it + if (p.applicationInfo == null) { + Log.w(TAG, "Package " + p.packageName + " has no applicationInfo."); + return false; + } + if ( (p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + Log.i(TAG, "Removing system package:"+p.packageName); + // When an updated system application is deleted we delete the existing resources as well and + // fall back to existing code in system partition + return deleteSystemPackageLI(p, true, flags, outInfo); + } + Log.i(TAG, "Removing non-system package:"+p.packageName); + return deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo); + } + + public void clearApplicationUserData(final String packageName, + final IPackageDataObserver observer) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CLEAR_APP_USER_DATA, null); + // Queue up an async operation since the package deletion may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + final boolean succeeded; + synchronized (mInstallLock) { + succeeded = clearApplicationUserDataLI(packageName); + } + if (succeeded) { + // invoke DeviceStorageMonitor's update method to clear any notifications + DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) + ServiceManager.getService(DeviceStorageMonitorService.SERVICE); + if (dsm != null) { + dsm.updateMemory(); + } + } + if(observer != null) { + try { + observer.onRemoveCompleted(packageName, succeeded); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } + } //end if observer + } //end run + }); + } + + private boolean clearApplicationUserDataLI(String packageName) { + if (packageName == null) { + Log.w(TAG, "Attempt to delete null packageName."); + return false; + } + PackageParser.Package p; + boolean dataOnly = false; + synchronized (mPackages) { + p = mPackages.get(packageName); + if(p == null) { + dataOnly = true; + PackageSetting ps = mSettings.mPackages.get(packageName); + if((ps == null) || (ps.pkg == null)) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; + } + p = ps.pkg; + } + } + if(!dataOnly) { + //need to check this only for fully installed applications + if (p == null) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; + } + final ApplicationInfo applicationInfo = p.applicationInfo; + if (applicationInfo == null) { + Log.w(TAG, "Package " + packageName + " has no applicationInfo."); + return false; + } + } + if (mInstaller != null) { + int retCode = mInstaller.clearUserData(packageName); + if (retCode < 0) { + Log.w(TAG, "Couldn't remove cache files for package: " + + packageName); + return false; + } + } + return true; + } + + public void deleteApplicationCacheFiles(final String packageName, + final IPackageDataObserver observer) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.DELETE_CACHE_FILES, null); + // Queue up an async operation since the package deletion may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + final boolean succeded; + synchronized (mInstallLock) { + succeded = deleteApplicationCacheFilesLI(packageName); + } + if(observer != null) { + try { + observer.onRemoveCompleted(packageName, succeded); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } + } //end if observer + } //end run + }); + } + + private boolean deleteApplicationCacheFilesLI(String packageName) { + if (packageName == null) { + Log.w(TAG, "Attempt to delete null packageName."); + return false; + } + PackageParser.Package p; + synchronized (mPackages) { + p = mPackages.get(packageName); + } + if (p == null) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; + } + final ApplicationInfo applicationInfo = p.applicationInfo; + if (applicationInfo == null) { + Log.w(TAG, "Package " + packageName + " has no applicationInfo."); + return false; + } + if (mInstaller != null) { + int retCode = mInstaller.deleteCacheFiles(packageName); + if (retCode < 0) { + Log.w(TAG, "Couldn't remove cache files for package: " + + packageName); + return false; + } + } + return true; + } + + public void getPackageSizeInfo(final String packageName, + final IPackageStatsObserver observer) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.GET_PACKAGE_SIZE, null); + // Queue up an async operation since the package deletion may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + PackageStats lStats = new PackageStats(packageName); + final boolean succeded; + synchronized (mInstallLock) { + succeded = getPackageSizeInfoLI(packageName, lStats); + } + if(observer != null) { + try { + observer.onGetStatsCompleted(lStats, succeded); + } catch (RemoteException e) { + Log.i(TAG, "Observer no longer exists."); + } + } //end if observer + } //end run + }); + } + + private boolean getPackageSizeInfoLI(String packageName, PackageStats pStats) { + if (packageName == null) { + Log.w(TAG, "Attempt to get size of null packageName."); + return false; + } + PackageParser.Package p; + boolean dataOnly = false; + synchronized (mPackages) { + p = mPackages.get(packageName); + if(p == null) { + dataOnly = true; + PackageSetting ps = mSettings.mPackages.get(packageName); + if((ps == null) || (ps.pkg == null)) { + Log.w(TAG, "Package named '" + packageName +"' doesn't exist."); + return false; + } + p = ps.pkg; + } + } + String publicSrcDir = null; + if(!dataOnly) { + final ApplicationInfo applicationInfo = p.applicationInfo; + if (applicationInfo == null) { + Log.w(TAG, "Package " + packageName + " has no applicationInfo."); + return false; + } + publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null; + } + if (mInstaller != null) { + int res = mInstaller.getSizeInfo(packageName, p.mPath, + publicSrcDir, pStats); + if (res < 0) { + return false; + } else { + return true; + } + } + return true; + } + + + public void addPackageToPreferred(String packageName) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + + synchronized (mPackages) { + PackageParser.Package p = mPackages.get(packageName); + if (p == null) { + return; + } + PackageSetting ps = (PackageSetting)p.mExtras; + if (ps != null) { + mSettings.mPreferredPackages.remove(ps); + mSettings.mPreferredPackages.add(0, ps); + updatePreferredIndicesLP(); + mSettings.writeLP(); + } + } + } + + public void removePackageFromPreferred(String packageName) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + + synchronized (mPackages) { + PackageParser.Package p = mPackages.get(packageName); + if (p == null) { + return; + } + if (p.mPreferredOrder > 0) { + PackageSetting ps = (PackageSetting)p.mExtras; + if (ps != null) { + mSettings.mPreferredPackages.remove(ps); + p.mPreferredOrder = 0; + updatePreferredIndicesLP(); + mSettings.writeLP(); + } + } + } + } + + private void updatePreferredIndicesLP() { + final ArrayList<PackageSetting> pkgs + = mSettings.mPreferredPackages; + final int N = pkgs.size(); + for (int i=0; i<N; i++) { + pkgs.get(i).pkg.mPreferredOrder = N - i; + } + } + + public List<PackageInfo> getPreferredPackages(int flags) { + synchronized (mPackages) { + final ArrayList<PackageInfo> res = new ArrayList<PackageInfo>(); + final ArrayList<PackageSetting> pref = mSettings.mPreferredPackages; + final int N = pref.size(); + for (int i=0; i<N; i++) { + res.add(generatePackageInfo(pref.get(i).pkg, flags)); + } + return res; + } + } + + public void addPreferredActivity(IntentFilter filter, int match, + ComponentName[] set, ComponentName activity) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + + synchronized (mPackages) { + Log.i(TAG, "Adding preferred activity " + activity + ":"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); + mSettings.mPreferredActivities.addFilter( + new PreferredActivity(filter, match, set, activity)); + mSettings.writeLP(); + } + } + + public void clearPackagePreferredActivities(String packageName) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + + synchronized (mPackages) { + if (clearPackagePreferredActivitiesLP(packageName)) { + mSettings.writeLP(); + } + } + } + + boolean clearPackagePreferredActivitiesLP(String packageName) { + boolean changed = false; + Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); + while (it.hasNext()) { + PreferredActivity pa = it.next(); + if (pa.mActivity.getPackageName().equals(packageName)) { + it.remove(); + changed = true; + } + } + return changed; + } + + public int getPreferredActivities(List<IntentFilter> outFilters, + List<ComponentName> outActivities, String packageName) { + + int num = 0; + synchronized (mPackages) { + Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator(); + while (it.hasNext()) { + PreferredActivity pa = it.next(); + if (packageName == null + || pa.mActivity.getPackageName().equals(packageName)) { + if (outFilters != null) { + outFilters.add(new IntentFilter(pa)); + } + if (outActivities != null) { + outActivities.add(pa.mActivity); + } + } + } + } + + return num; + } + + public void setApplicationEnabledSetting(String appPackageName, + int newState, int flags) { + setEnabledSetting(appPackageName, null, newState, flags); + } + + public void setComponentEnabledSetting(ComponentName componentName, + int newState, int flags) { + setEnabledSetting(componentName.getPackageName(), + componentName.getClassName(), newState, flags); + } + + private void setEnabledSetting( + final String packageNameStr, String classNameStr, int newState, final int flags) { + if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT + || newState == COMPONENT_ENABLED_STATE_ENABLED + || newState == COMPONENT_ENABLED_STATE_DISABLED)) { + throw new IllegalArgumentException("Invalid new component state: " + + newState); + } + PackageSetting pkgSetting; + final int uid = Binder.getCallingUid(); + final int permission = mContext.checkCallingPermission( + android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); + final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); + int packageUid = -1; + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageNameStr); + if (pkgSetting == null) { + if (classNameStr == null) { + throw new IllegalArgumentException( + "Unknown package: " + packageNameStr); + } + throw new IllegalArgumentException( + "Unknown component: " + packageNameStr + + "/" + classNameStr); + } + if (!allowedByPermission && (uid != pkgSetting.userId)) { + throw new SecurityException( + "Permission Denial: attempt to change component state from pid=" + + Binder.getCallingPid() + + ", uid=" + uid + ", package uid=" + pkgSetting.userId); + } + packageUid = pkgSetting.userId; + if (classNameStr == null) { + // We're dealing with an application/package level state change + pkgSetting.enabled = newState; + } else { + // We're dealing with a component level state change + switch (newState) { + case COMPONENT_ENABLED_STATE_ENABLED: + pkgSetting.enableComponentLP(classNameStr); + break; + case COMPONENT_ENABLED_STATE_DISABLED: + pkgSetting.disableComponentLP(classNameStr); + break; + case COMPONENT_ENABLED_STATE_DEFAULT: + pkgSetting.restoreComponentLP(classNameStr); + break; + default: + Log.e(TAG, "Invalid new component state: " + newState); + } + } + mSettings.writeLP(); + } + + long callingId = Binder.clearCallingIdentity(); + try { + Bundle extras = new Bundle(2); + extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, + (flags&PackageManager.DONT_KILL_APP) != 0); + extras.putInt(Intent.EXTRA_UID, packageUid); + sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageNameStr, extras); + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + public int getApplicationEnabledSetting(String appPackageName) { + synchronized (mPackages) { + PackageSetting pkg = mSettings.mPackages.get(appPackageName); + if (pkg == null) { + throw new IllegalArgumentException("Unknown package: " + appPackageName); + } + return pkg.enabled; + } + } + + public int getComponentEnabledSetting(ComponentName componentName) { + synchronized (mPackages) { + final String packageNameStr = componentName.getPackageName(); + PackageSetting pkg = mSettings.mPackages.get(packageNameStr); + if (pkg == null) { + throw new IllegalArgumentException("Unknown component: " + componentName); + } + final String classNameStr = componentName.getClassName(); + return pkg.currentEnabledStateLP(classNameStr); + } + } + + public void enterSafeMode() { + if (!mSystemReady) { + mSafeMode = true; + } + } + + public void systemReady() { + mSystemReady = true; + } + + public boolean isSafeMode() { + return mSafeMode; + } + + public boolean hasSystemUidErrors() { + return mHasSystemUidErrors; + } + + static String arrayToString(int[] array) { + StringBuffer buf = new StringBuffer(128); + buf.append('['); + if (array != null) { + for (int i=0; i<array.length; i++) { + if (i > 0) buf.append(", "); + buf.append(array[i]); + } + } + buf.append(']'); + return buf.toString(); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump ActivityManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " without permission " + + android.Manifest.permission.DUMP); + return; + } + + Printer printer = new PrintWriterPrinter(pw); + synchronized (mPackages) { + pw.println("Activity Resolver Table:"); + mActivities.dump(printer, " "); + pw.println(" "); + pw.println("Receiver Resolver Table:"); + mReceivers.dump(printer, " "); + pw.println(" "); + pw.println("Service Resolver Table:"); + mServices.dump(printer, " "); + pw.println(" "); + pw.println("Preferred Activities:"); + mSettings.mPreferredActivities.dump(printer, " "); + pw.println(" "); + pw.println("Preferred Packages:"); + { + for (PackageSetting ps : mSettings.mPreferredPackages) { + pw.println(" " + ps.name); + } + } + pw.println(" "); + pw.println("Permissions:"); + { + for (BasePermission p : mSettings.mPermissions.values()) { + pw.println(" Permission [" + p.name + "] (" + + Integer.toHexString(System.identityHashCode(p)) + + "):"); + pw.println(" sourcePackage=" + p.sourcePackage); + pw.println(" uid=" + p.uid + + " gids=" + arrayToString(p.gids) + + " type=" + p.type); + } + } + pw.println(" "); + pw.println("Packages:"); + { + for (PackageSetting ps : mSettings.mPackages.values()) { + pw.println(" Package [" + ps.name + "] (" + + Integer.toHexString(System.identityHashCode(ps)) + + "):"); + pw.println(" userId=" + ps.userId + + " gids=" + arrayToString(ps.gids)); + pw.println(" sharedUser=" + ps.sharedUser); + pw.println(" pkg=" + ps.pkg); + pw.println(" codePath=" + ps.codePathString); + pw.println(" resourcePath=" + ps.resourcePathString); + if (ps.pkg != null) { + pw.println(" dataDir=" + ps.pkg.applicationInfo.dataDir); + } + pw.println(" timeStamp=" + ps.getTimeStampStr()); + pw.println(" signatures=" + ps.signatures); + pw.println(" permissionsFixed=" + ps.permissionsFixed + + " pkgFlags=0x" + Integer.toHexString(ps.pkgFlags) + + " installStatus=" + ps.installStatus + + " enabled=" + ps.enabled); + if (ps.disabledComponents.size() > 0) { + pw.println(" disabledComponents:"); + for (String s : ps.disabledComponents) { + pw.println(" " + s); + } + } + if (ps.enabledComponents.size() > 0) { + pw.println(" enabledComponents:"); + for (String s : ps.enabledComponents) { + pw.println(" " + s); + } + } + pw.println(" grantedPermissions:"); + for (String s : ps.grantedPermissions) { + pw.println(" " + s); + } + pw.println(" loadedPermissions:"); + for (String s : ps.loadedPermissions) { + pw.println(" " + s); + } + } + } + pw.println(" "); + pw.println("Shared Users:"); + { + for (SharedUserSetting su : mSettings.mSharedUsers.values()) { + pw.println(" SharedUser [" + su.name + "] (" + + Integer.toHexString(System.identityHashCode(su)) + + "):"); + pw.println(" userId=" + su.userId + + " gids=" + arrayToString(su.gids)); + pw.println(" grantedPermissions:"); + for (String s : su.grantedPermissions) { + pw.println(" " + s); + } + pw.println(" loadedPermissions:"); + for (String s : su.loadedPermissions) { + pw.println(" " + s); + } + } + } + pw.println(" "); + pw.println("Settings parse messages:"); + pw.println(mSettings.mReadMessages.toString()); + } + } + + static final class BasePermission { + final static int TYPE_NORMAL = 0; + final static int TYPE_BUILTIN = 1; + final static int TYPE_DYNAMIC = 2; + + final String name; + final String sourcePackage; + final int type; + PackageParser.Permission perm; + PermissionInfo pendingInfo; + int uid; + int[] gids; + + BasePermission(String _name, String _sourcePackage, int _type) { + name = _name; + sourcePackage = _sourcePackage; + type = _type; + } + } + + static class PackageSignatures { + private Signature[] mSignatures; + + PackageSignatures(Signature[] sigs) { + assignSignatures(sigs); + } + + PackageSignatures() { + } + + void writeXml(XmlSerializer serializer, String tagName, + ArrayList<Signature> pastSignatures) throws IOException { + if (mSignatures == null) { + return; + } + serializer.startTag(null, tagName); + serializer.attribute(null, "count", + Integer.toString(mSignatures.length)); + for (int i=0; i<mSignatures.length; i++) { + serializer.startTag(null, "cert"); + final Signature sig = mSignatures[i]; + final int sigHash = sig.hashCode(); + final int numPast = pastSignatures.size(); + int j; + for (j=0; j<numPast; j++) { + Signature pastSig = pastSignatures.get(j); + if (pastSig.hashCode() == sigHash && pastSig.equals(sig)) { + serializer.attribute(null, "index", Integer.toString(j)); + break; + } + } + if (j >= numPast) { + pastSignatures.add(sig); + serializer.attribute(null, "index", Integer.toString(numPast)); + serializer.attribute(null, "key", sig.toCharsString()); + } + serializer.endTag(null, "cert"); + } + serializer.endTag(null, tagName); + } + + void readXml(XmlPullParser parser, ArrayList<Signature> pastSignatures) + throws IOException, XmlPullParserException { + String countStr = parser.getAttributeValue(null, "count"); + if (countStr == null) { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <signatures> has" + + " no count at " + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + } + final int count = Integer.parseInt(countStr); + mSignatures = new Signature[count]; + int pos = 0; + + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("cert")) { + if (pos < count) { + String index = parser.getAttributeValue(null, "index"); + if (index != null) { + try { + int idx = Integer.parseInt(index); + String key = parser.getAttributeValue(null, "key"); + if (key == null) { + if (idx >= 0 && idx < pastSignatures.size()) { + Signature sig = pastSignatures.get(idx); + if (sig != null) { + mSignatures[pos] = pastSignatures.get(idx); + pos++; + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <cert> " + + "index " + index + " is not defined at " + + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <cert> " + + "index " + index + " is out of bounds at " + + parser.getPositionDescription()); + } + } else { + while (pastSignatures.size() <= idx) { + pastSignatures.add(null); + } + Signature sig = new Signature(key); + pastSignatures.set(idx, sig); + mSignatures[pos] = sig; + pos++; + } + } catch (NumberFormatException e) { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <cert> " + + "index " + index + " is not a number at " + + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <cert> has" + + " no index at " + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: too " + + "many <cert> tags, expected " + count + + " at " + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <cert>: " + + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + + if (pos < count) { + // Should never happen -- there is an error in the written + // settings -- but if it does we don't want to generate + // a bad array. + Signature[] newSigs = new Signature[pos]; + System.arraycopy(mSignatures, 0, newSigs, 0, pos); + mSignatures = newSigs; + } + } + + /** + * If any of the given 'sigs' is contained in the existing signatures, + * then completely replace the current signatures with the ones in + * 'sigs'. This is used for updating an existing package to a newly + * installed version. + */ + boolean updateSignatures(Signature[] sigs, boolean update) { + if (mSignatures == null) { + if (update) { + assignSignatures(sigs); + } + return true; + } + if (sigs == null) { + return false; + } + + for (int i=0; i<sigs.length; i++) { + Signature sig = sigs[i]; + for (int j=0; j<mSignatures.length; j++) { + if (mSignatures[j].equals(sig)) { + if (update) { + assignSignatures(sigs); + } + return true; + } + } + } + return false; + } + + /** + * If any of the given 'sigs' is contained in the existing signatures, + * then add in any new signatures found in 'sigs'. This is used for + * including a new package into an existing shared user id. + */ + boolean mergeSignatures(Signature[] sigs, boolean update) { + if (mSignatures == null) { + if (update) { + assignSignatures(sigs); + } + return true; + } + if (sigs == null) { + return false; + } + + Signature[] added = null; + int addedCount = 0; + boolean haveMatch = false; + for (int i=0; i<sigs.length; i++) { + Signature sig = sigs[i]; + boolean found = false; + for (int j=0; j<mSignatures.length; j++) { + if (mSignatures[j].equals(sig)) { + found = true; + haveMatch = true; + break; + } + } + + if (!found) { + if (added == null) { + added = new Signature[sigs.length]; + } + added[i] = sig; + addedCount++; + } + } + + if (!haveMatch) { + // Nothing matched -- reject the new signatures. + return false; + } + if (added == null) { + // Completely matched -- nothing else to do. + return true; + } + + // Add additional signatures in. + if (update) { + Signature[] total = new Signature[addedCount+mSignatures.length]; + System.arraycopy(mSignatures, 0, total, 0, mSignatures.length); + int j = mSignatures.length; + for (int i=0; i<added.length; i++) { + if (added[i] != null) { + total[j] = added[i]; + j++; + } + } + mSignatures = total; + } + return true; + } + + private void assignSignatures(Signature[] sigs) { + if (sigs == null) { + mSignatures = null; + return; + } + mSignatures = new Signature[sigs.length]; + for (int i=0; i<sigs.length; i++) { + mSignatures[i] = sigs[i]; + } + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(128); + buf.append("PackageSignatures{"); + buf.append(Integer.toHexString(System.identityHashCode(this))); + buf.append(" ["); + if (mSignatures != null) { + for (int i=0; i<mSignatures.length; i++) { + if (i > 0) buf.append(", "); + buf.append(Integer.toHexString( + System.identityHashCode(mSignatures[i]))); + } + } + buf.append("]}"); + return buf.toString(); + } + } + + static class PreferredActivity extends IntentFilter { + final int mMatch; + final String[] mSetPackages; + final String[] mSetClasses; + final String[] mSetComponents; + final ComponentName mActivity; + final String mShortActivity; + String mParseError; + + PreferredActivity(IntentFilter filter, int match, ComponentName[] set, + ComponentName activity) { + super(filter); + mMatch = match&IntentFilter.MATCH_CATEGORY_MASK; + mActivity = activity; + mShortActivity = activity.flattenToShortString(); + mParseError = null; + if (set != null) { + final int N = set.length; + String[] myPackages = new String[N]; + String[] myClasses = new String[N]; + String[] myComponents = new String[N]; + for (int i=0; i<N; i++) { + ComponentName cn = set[i]; + if (cn == null) { + mSetPackages = null; + mSetClasses = null; + mSetComponents = null; + return; + } + myPackages[i] = cn.getPackageName().intern(); + myClasses[i] = cn.getClassName().intern(); + myComponents[i] = cn.flattenToShortString().intern(); + } + mSetPackages = myPackages; + mSetClasses = myClasses; + mSetComponents = myComponents; + } else { + mSetPackages = null; + mSetClasses = null; + mSetComponents = null; + } + } + + PreferredActivity(XmlPullParser parser) throws XmlPullParserException, + IOException { + mShortActivity = parser.getAttributeValue(null, "name"); + mActivity = ComponentName.unflattenFromString(mShortActivity); + if (mActivity == null) { + mParseError = "Bad activity name " + mShortActivity; + } + String matchStr = parser.getAttributeValue(null, "match"); + mMatch = matchStr != null ? Integer.parseInt(matchStr, 16) : 0; + String setCountStr = parser.getAttributeValue(null, "set"); + int setCount = setCountStr != null ? Integer.parseInt(setCountStr) : 0; + + String[] myPackages = setCount > 0 ? new String[setCount] : null; + String[] myClasses = setCount > 0 ? new String[setCount] : null; + String[] myComponents = setCount > 0 ? new String[setCount] : null; + + int setPos = 0; + + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + //Log.i(TAG, "Parse outerDepth=" + outerDepth + " depth=" + // + parser.getDepth() + " tag=" + tagName); + if (tagName.equals("set")) { + String name = parser.getAttributeValue(null, "name"); + if (name == null) { + if (mParseError == null) { + mParseError = "No name in set tag in preferred activity " + + mShortActivity; + } + } else if (setPos >= setCount) { + if (mParseError == null) { + mParseError = "Too many set tags in preferred activity " + + mShortActivity; + } + } else { + ComponentName cn = ComponentName.unflattenFromString(name); + if (cn == null) { + if (mParseError == null) { + mParseError = "Bad set name " + name + " in preferred activity " + + mShortActivity; + } + } else { + myPackages[setPos] = cn.getPackageName(); + myClasses[setPos] = cn.getClassName(); + myComponents[setPos] = name; + setPos++; + } + } + XmlUtils.skipCurrentTag(parser); + } else if (tagName.equals("filter")) { + //Log.i(TAG, "Starting to parse filter..."); + readFromXml(parser); + //Log.i(TAG, "Finished filter: outerDepth=" + outerDepth + " depth=" + // + parser.getDepth() + " tag=" + parser.getName()); + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <preferred-activities>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + if (setPos != setCount) { + if (mParseError == null) { + mParseError = "Not enough set tags (expected " + setCount + + " but found " + setPos + ") in " + mShortActivity; + } + } + + mSetPackages = myPackages; + mSetClasses = myClasses; + mSetComponents = myComponents; + } + + public void writeToXml(XmlSerializer serializer) throws IOException { + final int NS = mSetClasses != null ? mSetClasses.length : 0; + serializer.attribute(null, "name", mShortActivity); + serializer.attribute(null, "match", Integer.toHexString(mMatch)); + serializer.attribute(null, "set", Integer.toString(NS)); + for (int s=0; s<NS; s++) { + serializer.startTag(null, "set"); + serializer.attribute(null, "name", mSetComponents[s]); + serializer.endTag(null, "set"); + } + serializer.startTag(null, "filter"); + super.writeToXml(serializer); + serializer.endTag(null, "filter"); + } + + boolean sameSet(List<ResolveInfo> query, int priority) { + if (mSetPackages == null) return false; + final int NQ = query.size(); + final int NS = mSetPackages.length; + int numMatch = 0; + for (int i=0; i<NQ; i++) { + ResolveInfo ri = query.get(i); + if (ri.priority != priority) continue; + ActivityInfo ai = ri.activityInfo; + boolean good = false; + for (int j=0; j<NS; j++) { + if (mSetPackages[j].equals(ai.packageName) + && mSetClasses[j].equals(ai.name)) { + numMatch++; + good = true; + break; + } + } + if (!good) return false; + } + return numMatch == NS; + } + } + + static class GrantedPermissions { + final int pkgFlags; + + HashSet<String> grantedPermissions = new HashSet<String>(); + int[] gids; + + HashSet<String> loadedPermissions = new HashSet<String>(); + + GrantedPermissions(int pkgFlags) { + this.pkgFlags = pkgFlags & ApplicationInfo.FLAG_SYSTEM; + } + } + + /** + * Settings base class for pending and resolved classes. + */ + static class PackageSettingBase extends GrantedPermissions { + final String name; + final File codePath; + final String codePathString; + final File resourcePath; + final String resourcePathString; + private long timeStamp; + private String timeStampString = "0"; + + PackageSignatures signatures = new PackageSignatures(); + + boolean permissionsFixed; + + /* Explicitly disabled components */ + HashSet<String> disabledComponents = new HashSet<String>(0); + /* Explicitly enabled components */ + HashSet<String> enabledComponents = new HashSet<String>(0); + int enabled = COMPONENT_ENABLED_STATE_DEFAULT; + int installStatus = PKG_INSTALL_COMPLETE; + + PackageSettingBase(String name, File codePath, File resourcePath, + int pkgFlags) { + super(pkgFlags); + this.name = name; + this.codePath = codePath; + this.codePathString = codePath.toString(); + this.resourcePath = resourcePath; + this.resourcePathString = resourcePath.toString(); + } + + public void setInstallStatus(int newStatus) { + installStatus = newStatus; + } + + public int getInstallStatus() { + return installStatus; + } + + public void setTimeStamp(long newStamp) { + if (newStamp != timeStamp) { + timeStamp = newStamp; + timeStampString = Long.toString(newStamp); + } + } + + public void setTimeStamp(long newStamp, String newStampStr) { + timeStamp = newStamp; + timeStampString = newStampStr; + } + + public long getTimeStamp() { + return timeStamp; + } + + public String getTimeStampStr() { + return timeStampString; + } + + public void copyFrom(PackageSettingBase base) { + grantedPermissions = base.grantedPermissions; + gids = base.gids; + loadedPermissions = base.loadedPermissions; + + timeStamp = base.timeStamp; + timeStampString = base.timeStampString; + signatures = base.signatures; + permissionsFixed = base.permissionsFixed; + disabledComponents = base.disabledComponents; + enabledComponents = base.enabledComponents; + enabled = base.enabled; + installStatus = base.installStatus; + } + + void enableComponentLP(String componentClassName) { + disabledComponents.remove(componentClassName); + enabledComponents.add(componentClassName); + } + + void disableComponentLP(String componentClassName) { + enabledComponents.remove(componentClassName); + disabledComponents.add(componentClassName); + } + + void restoreComponentLP(String componentClassName) { + enabledComponents.remove(componentClassName); + disabledComponents.remove(componentClassName); + } + + int currentEnabledStateLP(String componentName) { + if (enabledComponents.contains(componentName)) { + return COMPONENT_ENABLED_STATE_ENABLED; + } else if (disabledComponents.contains(componentName)) { + return COMPONENT_ENABLED_STATE_DISABLED; + } else { + return COMPONENT_ENABLED_STATE_DEFAULT; + } + } + } + + /** + * Settings data for a particular package we know about. + */ + static final class PackageSetting extends PackageSettingBase { + int userId; + PackageParser.Package pkg; + SharedUserSetting sharedUser; + + PackageSetting(String name, File codePath, File resourcePath, + int pkgFlags) { + super(name, codePath, resourcePath, pkgFlags); + } + + @Override + public String toString() { + return "PackageSetting{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + name + "/" + userId + "}"; + } + } + + /** + * Settings data for a particular shared user ID we know about. + */ + static final class SharedUserSetting extends GrantedPermissions { + final String name; + int userId; + final HashSet<PackageSetting> packages = new HashSet<PackageSetting>(); + final PackageSignatures signatures = new PackageSignatures(); + + SharedUserSetting(String _name, int _pkgFlags) { + super(_pkgFlags); + name = _name; + } + + @Override + public String toString() { + return "SharedUserSetting{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + name + "/" + userId + "}"; + } + } + + /** + * Holds information about dynamic settings. + */ + private static final class Settings { + private final File mSettingsFilename; + private final File mBackupSettingsFilename; + private final HashMap<String, PackageSetting> mPackages = + new HashMap<String, PackageSetting>(); + // The user's preferred packages/applications, in order of preference. + // First is the most preferred. + private final ArrayList<PackageSetting> mPreferredPackages = + new ArrayList<PackageSetting>(); + // List of replaced system applications + final HashMap<String, PackageSetting> mDisabledSysPackages = + new HashMap<String, PackageSetting>(); + + // The user's preferred activities associated with particular intent + // filters. + private final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities = + new IntentResolver<PreferredActivity, PreferredActivity>() { + @Override + protected void dumpFilter(Printer out, String prefix, + PreferredActivity filter) { + out.println(prefix + + Integer.toHexString(System.identityHashCode(filter)) + + " " + filter.mActivity.flattenToShortString() + + " match=0x" + Integer.toHexString(filter.mMatch)); + if (filter.mSetComponents != null) { + out.println(prefix + " Selected from:"); + for (int i=0; i<filter.mSetComponents.length; i++) { + out.println(prefix + " " + filter.mSetComponents[i]); + } + } + } + }; + private final HashMap<String, SharedUserSetting> mSharedUsers = + new HashMap<String, SharedUserSetting>(); + private final ArrayList<Object> mUserIds = new ArrayList<Object>(); + private final SparseArray<Object> mOtherUserIds = + new SparseArray<Object>(); + + // For reading/writing settings file. + private final ArrayList<Signature> mPastSignatures = + new ArrayList<Signature>(); + + // Mapping from permission names to info about them. + final HashMap<String, BasePermission> mPermissions = + new HashMap<String, BasePermission>(); + + // Mapping from permission tree names to info about them. + final HashMap<String, BasePermission> mPermissionTrees = + new HashMap<String, BasePermission>(); + + private final ArrayList<String> mPendingPreferredPackages + = new ArrayList<String>(); + + private final StringBuilder mReadMessages = new StringBuilder(); + + private static final class PendingPackage extends PackageSettingBase { + final int sharedId; + + PendingPackage(String name, File codePath, File resourcePath, + int sharedId, int pkgFlags) { + super(name, codePath, resourcePath, pkgFlags); + this.sharedId = sharedId; + } + } + private final ArrayList<PendingPackage> mPendingPackages + = new ArrayList<PendingPackage>(); + + Settings() { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + systemDir.mkdirs(); + FileUtils.setPermissions(systemDir.toString(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG + |FileUtils.S_IROTH|FileUtils.S_IXOTH, + -1, -1); + mSettingsFilename = new File(systemDir, "packages.xml"); + mBackupSettingsFilename = new File(systemDir, "packages-backup.xml"); + } + + PackageSetting getPackageLP(PackageParser.Package pkg, + SharedUserSetting sharedUser, File codePath, File resourcePath, + int pkgFlags, boolean create, boolean add) { + final String name = pkg.packageName; + PackageSetting p = getPackageLP(name, sharedUser, codePath, + resourcePath, pkgFlags, create, add); + + if (p != null) { + p.pkg = pkg; + } + return p; + } + + PackageSetting peekPackageLP(String name, String codePath) { + PackageSetting p = mPackages.get(name); + if (p != null && p.codePath.getPath().equals(codePath)) { + return p; + } + return null; + } + + void setInstallStatus(String pkgName, int status) { + PackageSetting p = mPackages.get(pkgName); + if(p != null) { + if(p.getInstallStatus() != status) { + p.setInstallStatus(status); + } + } + } + + int getInstallStatus(String pkgName) { + PackageSetting p = mPackages.get(pkgName); + if(p != null) { + return p.getInstallStatus(); + } + return -1; + } + + SharedUserSetting getSharedUserLP(String name, + int pkgFlags, boolean create) { + SharedUserSetting s = mSharedUsers.get(name); + if (s == null) { + if (!create) { + return null; + } + s = new SharedUserSetting(name, pkgFlags); + if (MULTIPLE_APPLICATION_UIDS) { + s.userId = newUserIdLP(s); + } else { + s.userId = FIRST_APPLICATION_UID; + } + Log.i(TAG, "New shared user " + name + ": id=" + s.userId); + // < 0 means we couldn't assign a userid; fall out and return + // s, which is currently null + if (s.userId >= 0) { + mSharedUsers.put(name, s); + } + } + + return s; + } + + int disableSystemPackageLP(String name) { + PackageSetting p = mPackages.get(name); + if(p == null) { + Log.w(TAG, "Package:"+name+" is not an installed package"); + return -1; + } + PackageSetting dp = mDisabledSysPackages.get(name); + // always make sure the system package code and resource paths dont change + if(dp == null) { + if((p.pkg != null) && (p.pkg.applicationInfo != null)) { + p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + } + mDisabledSysPackages.put(name, p); + } + return removePackageLP(name); + } + + PackageSetting enableSystemPackageLP(String name) { + PackageSetting p = mDisabledSysPackages.get(name); + if(p == null) { + Log.w(TAG, "Package:"+name+" is not disabled"); + return null; + } + // Reset flag in ApplicationInfo object + if((p.pkg != null) && (p.pkg.applicationInfo != null)) { + p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + } + PackageSetting ret = addPackageLP(name, p.codePath, + p.resourcePath, p.userId, p.pkgFlags); + mDisabledSysPackages.remove(name); + return ret; + } + + PackageSetting addPackageLP(String name, File codePath, + File resourcePath, int uid, int pkgFlags) { + PackageSetting p = mPackages.get(name); + if (p != null) { + if (p.userId == uid) { + return p; + } + reportSettingsProblem(Log.ERROR, + "Adding duplicate package, keeping first: " + name); + return null; + } + p = new PackageSetting(name, codePath, resourcePath, pkgFlags); + p.userId = uid; + if (addUserIdLP(uid, p, name)) { + mPackages.put(name, p); + return p; + } + return null; + } + + SharedUserSetting addSharedUserLP(String name, int uid, int pkgFlags) { + SharedUserSetting s = mSharedUsers.get(name); + if (s != null) { + if (s.userId == uid) { + return s; + } + reportSettingsProblem(Log.ERROR, + "Adding duplicate shared user, keeping first: " + name); + return null; + } + s = new SharedUserSetting(name, pkgFlags); + s.userId = uid; + if (addUserIdLP(uid, s, name)) { + mSharedUsers.put(name, s); + return s; + } + return null; + } + + private PackageSetting getPackageLP(String name, + SharedUserSetting sharedUser, File codePath, File resourcePath, + int pkgFlags, boolean create, boolean add) { + PackageSetting p = mPackages.get(name); + if (p != null) { + if (!p.codePath.equals(codePath)) { + // Check to see if its a disabled system app + PackageSetting ps = mDisabledSysPackages.get(name); + if((ps != null) && ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0)) { + // Could be a replaced system package + // Note that if the user replaced a system app, the user has to physically + // delete the new one in order to revert to the system app. So even + // if the user updated the system app via an update, the user still + // has to delete the one installed in the data partition in order to pick up the + // new system package. + return p; + } else { + reportSettingsProblem(Log.WARN, + "Package " + name + " codePath changed from " + p.codePath + + " to " + codePath + "; replacing with new"); + p = null; + } + } else if (p.sharedUser != sharedUser) { + reportSettingsProblem(Log.WARN, + "Package " + name + " shared user changed from " + + (p.sharedUser != null ? p.sharedUser.name : "<nothing>") + + " to " + + (sharedUser != null ? sharedUser.name : "<nothing>") + + "; replacing with new"); + p = null; + } + } + if (p == null) { + // Create a new PackageSettings entry. this can end up here because + // of code path mismatch or user id mismatch of an updated system partition + if (!create) { + return null; + } + p = new PackageSetting(name, codePath, resourcePath, pkgFlags); + p.setTimeStamp(codePath.lastModified()); + if (sharedUser != null) { + p.userId = sharedUser.userId; + } else if (MULTIPLE_APPLICATION_UIDS) { + p.userId = newUserIdLP(p); + } else { + p.userId = FIRST_APPLICATION_UID; + } + if (p.userId < 0) { + reportSettingsProblem(Log.WARN, + "Package " + name + " could not be assigned a valid uid"); + return null; + } + if (add) { + // Finish adding new package by adding it and updating shared + // user preferences + insertPackageSettingLP(p, name, sharedUser); + } + } + return p; + } + + // Utility method that adds a PackageSetting to mPackages and + // completes updating the shared user attributes + private void insertPackageSettingLP(PackageSetting p, String name, + SharedUserSetting sharedUser) { + mPackages.put(name, p); + if (sharedUser != null) { + if (p.sharedUser != null && p.sharedUser != sharedUser) { + reportSettingsProblem(Log.ERROR, + "Package " + p.name + " was user " + + p.sharedUser + " but is now " + sharedUser + + "; I am not changing its files so it will probably fail!"); + p.sharedUser.packages.remove(p); + } else if (p.userId != sharedUser.userId) { + reportSettingsProblem(Log.ERROR, + "Package " + p.name + " was user id " + p.userId + + " but is now user " + sharedUser + + " with id " + sharedUser.userId + + "; I am not changing its files so it will probably fail!"); + } + + sharedUser.packages.add(p); + p.sharedUser = sharedUser; + p.userId = sharedUser.userId; + } + } + + private void updateSharedUserPerms (PackageSetting deletedPs) { + if ( (deletedPs == null) || (deletedPs.pkg == null)) { + Log.i(TAG, "Trying to update info for null package. Just ignoring"); + return; + } + // No sharedUserId + if (deletedPs.sharedUser == null) { + return; + } + SharedUserSetting sus = deletedPs.sharedUser; + // Update permissions + for (String eachPerm: deletedPs.pkg.requestedPermissions) { + boolean used = false; + if (!sus.grantedPermissions.contains (eachPerm)) { + continue; + } + for (PackageSetting pkg:sus.packages) { + if (pkg.grantedPermissions.contains (eachPerm)) { + used = true; + break; + } + } + if (!used) { + // can safely delete this permission from list + sus.grantedPermissions.remove(eachPerm); + sus.loadedPermissions.remove(eachPerm); + } + } + // Update gids + int newGids[] = null; + for (PackageSetting pkg:sus.packages) { + newGids = appendInts(newGids, pkg.gids); + } + sus.gids = newGids; + } + + private int removePackageLP(String name) { + PackageSetting p = mPackages.get(name); + if (p != null) { + mPackages.remove(name); + if (p.sharedUser != null) { + p.sharedUser.packages.remove(p); + if (p.sharedUser.packages.size() == 0) { + mSharedUsers.remove(p.sharedUser.name); + removeUserIdLP(p.sharedUser.userId); + return p.sharedUser.userId; + } + } else { + removeUserIdLP(p.userId); + return p.userId; + } + } + return -1; + } + + private boolean addUserIdLP(int uid, Object obj, Object name) { + if (uid >= FIRST_APPLICATION_UID + MAX_APPLICATION_UIDS) { + return false; + } + + if (uid >= FIRST_APPLICATION_UID) { + int N = mUserIds.size(); + final int index = uid - FIRST_APPLICATION_UID; + while (index >= N) { + mUserIds.add(null); + N++; + } + if (mUserIds.get(index) != null) { + reportSettingsProblem(Log.ERROR, + "Adding duplicate shared id: " + uid + + " name=" + name); + return false; + } + mUserIds.set(index, obj); + } else { + if (mOtherUserIds.get(uid) != null) { + reportSettingsProblem(Log.ERROR, + "Adding duplicate shared id: " + uid + + " name=" + name); + return false; + } + mOtherUserIds.put(uid, obj); + } + return true; + } + + public Object getUserIdLP(int uid) { + if (uid >= FIRST_APPLICATION_UID) { + int N = mUserIds.size(); + final int index = uid - FIRST_APPLICATION_UID; + return index < N ? mUserIds.get(index) : null; + } else { + return mOtherUserIds.get(uid); + } + } + + private void removeUserIdLP(int uid) { + if (uid >= FIRST_APPLICATION_UID) { + int N = mUserIds.size(); + final int index = uid - FIRST_APPLICATION_UID; + if (index < N) mUserIds.set(index, null); + } else { + mOtherUserIds.remove(uid); + } + } + + void writeLP() { + //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024); + + // Keep the old settings around until we know the new ones have + // been successfully written. + if (mSettingsFilename.exists()) { + if (mBackupSettingsFilename.exists()) { + mBackupSettingsFilename.delete(); + } + mSettingsFilename.renameTo(mBackupSettingsFilename); + } + + mPastSignatures.clear(); + + try { + FileOutputStream str = new FileOutputStream(mSettingsFilename); + + //XmlSerializer serializer = XmlUtils.serializerInstance(); + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(str, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, "packages"); + + serializer.startTag(null, "permission-trees"); + for (BasePermission bp : mPermissionTrees.values()) { + writePermission(serializer, bp); + } + serializer.endTag(null, "permission-trees"); + + serializer.startTag(null, "permissions"); + for (BasePermission bp : mPermissions.values()) { + writePermission(serializer, bp); + } + serializer.endTag(null, "permissions"); + + for (PackageSetting pkg : mPackages.values()) { + writePackage(serializer, pkg); + } + + for (PackageSetting pkg : mDisabledSysPackages.values()) { + writeDisabledSysPackage(serializer, pkg); + } + + serializer.startTag(null, "preferred-packages"); + int N = mPreferredPackages.size(); + for (int i=0; i<N; i++) { + PackageSetting pkg = mPreferredPackages.get(i); + serializer.startTag(null, "item"); + serializer.attribute(null, "name", pkg.name); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "preferred-packages"); + + serializer.startTag(null, "preferred-activities"); + for (PreferredActivity pa : mPreferredActivities.filterSet()) { + serializer.startTag(null, "item"); + pa.writeToXml(serializer); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "preferred-activities"); + + for (SharedUserSetting usr : mSharedUsers.values()) { + serializer.startTag(null, "shared-user"); + serializer.attribute(null, "name", usr.name); + serializer.attribute(null, "userId", + Integer.toString(usr.userId)); + usr.signatures.writeXml(serializer, "sigs", mPastSignatures); + serializer.startTag(null, "perms"); + for (String name : usr.grantedPermissions) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "perms"); + serializer.endTag(null, "shared-user"); + } + + serializer.endTag(null, "packages"); + + serializer.endDocument(); + + str.flush(); + str.close(); + + // New settings successfully written, old ones are no longer + // needed. + mBackupSettingsFilename.delete(); + FileUtils.setPermissions(mSettingsFilename.toString(), + FileUtils.S_IRUSR|FileUtils.S_IWUSR + |FileUtils.S_IRGRP|FileUtils.S_IWGRP + |FileUtils.S_IROTH, + -1, -1); + + } catch(XmlPullParserException e) { + Log.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); + + } catch(java.io.IOException e) { + Log.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); + + } + + //Debug.stopMethodTracing(); + } + + void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg) + throws java.io.IOException { + serializer.startTag(null, "updated-package"); + serializer.attribute(null, "name", pkg.name); + serializer.attribute(null, "codePath", pkg.codePathString); + serializer.attribute(null, "ts", pkg.getTimeStampStr()); + if (!pkg.resourcePathString.equals(pkg.codePathString)) { + serializer.attribute(null, "resourcePath", pkg.resourcePathString); + } + if (pkg.sharedUser == null) { + serializer.attribute(null, "userId", + Integer.toString(pkg.userId)); + } else { + serializer.attribute(null, "sharedUserId", + Integer.toString(pkg.userId)); + } + serializer.startTag(null, "perms"); + if (pkg.sharedUser == null) { + // If this is a shared user, the permissions will + // be written there. We still need to write an + // empty permissions list so permissionsFixed will + // be set. + for (final String name : pkg.grantedPermissions) { + BasePermission bp = mPermissions.get(name); + if ((bp != null) && (bp.perm != null) && (bp.perm.info != null)) { + // We only need to write signature or system permissions but this wont + // match the semantics of grantedPermissions. So write all permissions. + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + } + } + serializer.endTag(null, "perms"); + serializer.endTag(null, "updated-package"); + } + + void writePackage(XmlSerializer serializer, final PackageSetting pkg) + throws java.io.IOException { + serializer.startTag(null, "package"); + serializer.attribute(null, "name", pkg.name); + serializer.attribute(null, "codePath", pkg.codePathString); + if (!pkg.resourcePathString.equals(pkg.codePathString)) { + serializer.attribute(null, "resourcePath", pkg.resourcePathString); + } + serializer.attribute(null, "system", + (pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0 + ? "true" : "false"); + serializer.attribute(null, "ts", pkg.getTimeStampStr()); + if (pkg.sharedUser == null) { + serializer.attribute(null, "userId", + Integer.toString(pkg.userId)); + } else { + serializer.attribute(null, "sharedUserId", + Integer.toString(pkg.userId)); + } + if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { + serializer.attribute(null, "enabled", + pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED + ? "true" : "false"); + } + if(pkg.installStatus == PKG_INSTALL_INCOMPLETE) { + serializer.attribute(null, "installStatus", "false"); + } + pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); + if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { + serializer.startTag(null, "perms"); + if (pkg.sharedUser == null) { + // If this is a shared user, the permissions will + // be written there. We still need to write an + // empty permissions list so permissionsFixed will + // be set. + for (final String name : pkg.grantedPermissions) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + } + serializer.endTag(null, "perms"); + } + if (pkg.disabledComponents.size() > 0) { + serializer.startTag(null, "disabled-components"); + for (final String name : pkg.disabledComponents) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "disabled-components"); + } + if (pkg.enabledComponents.size() > 0) { + serializer.startTag(null, "enabled-components"); + for (final String name : pkg.enabledComponents) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", name); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "enabled-components"); + } + serializer.endTag(null, "package"); + } + + void writePermission(XmlSerializer serializer, BasePermission bp) + throws XmlPullParserException, java.io.IOException { + if (bp.type != BasePermission.TYPE_BUILTIN + && bp.sourcePackage != null) { + serializer.startTag(null, "item"); + serializer.attribute(null, "name", bp.name); + serializer.attribute(null, "package", bp.sourcePackage); + if (DEBUG_SETTINGS) Log.v(TAG, + "Writing perm: name=" + bp.name + " type=" + bp.type); + if (bp.type == BasePermission.TYPE_DYNAMIC) { + PermissionInfo pi = bp.perm != null ? bp.perm.info + : bp.pendingInfo; + if (pi != null) { + serializer.attribute(null, "type", "dynamic"); + if (pi.icon != 0) { + serializer.attribute(null, "icon", + Integer.toString(pi.icon)); + } + if (pi.nonLocalizedLabel != null) { + serializer.attribute(null, "label", + pi.nonLocalizedLabel.toString()); + } + if (pi.protectionLevel != + PermissionInfo.PROTECTION_NORMAL) { + serializer.attribute(null, "protection", + Integer.toString(pi.protectionLevel)); + } + } + } + serializer.endTag(null, "item"); + } + } + + String getReadMessagesLP() { + return mReadMessages.toString(); + } + + ArrayList<String> getListOfIncompleteInstallPackages() { + HashSet<String> kList = new HashSet<String>(mPackages.keySet()); + Iterator<String> its = kList.iterator(); + ArrayList<String> ret = new ArrayList<String>(); + while(its.hasNext()) { + String key = its.next(); + PackageSetting ps = mPackages.get(key); + if(ps.getInstallStatus() == PKG_INSTALL_INCOMPLETE) { + ret.add(key); + } + } + return ret; + } + + boolean readLP() { + FileInputStream str = null; + if (mBackupSettingsFilename.exists()) { + try { + str = new FileInputStream(mBackupSettingsFilename); + mReadMessages.append("Reading from backup settings file\n"); + Log.i(TAG, "Reading from backup settings file!"); + } catch (java.io.IOException e) { + // We'll try for the normal settings file. + } + } + + mPastSignatures.clear(); + + try { + if (str == null) { + if (!mSettingsFilename.exists()) { + mReadMessages.append("No settings file found\n"); + Log.i(TAG, "No current settings file!"); + return false; + } + str = new FileInputStream(mSettingsFilename); + } + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(str, null); + + int type; + while ((type=parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + mReadMessages.append("No start tag found in settings file\n"); + Log.e(TAG, "No start tag found in package manager settings"); + return false; + } + + int outerDepth = parser.getDepth(); + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("package")) { + readPackageLP(parser); + } else if (tagName.equals("permissions")) { + readPermissionsLP(mPermissions, parser); + } else if (tagName.equals("permission-trees")) { + readPermissionsLP(mPermissionTrees, parser); + } else if (tagName.equals("shared-user")) { + readSharedUserLP(parser); + } else if (tagName.equals("preferred-packages")) { + readPreferredPackagesLP(parser); + } else if (tagName.equals("preferred-activities")) { + readPreferredActivitiesLP(parser); + } else if(tagName.equals("updated-package")) { + readDisabledSysPackageLP(parser); + } else { + Log.w(TAG, "Unknown element under <packages>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + str.close(); + + } catch(XmlPullParserException e) { + mReadMessages.append("Error reading: " + e.toString()); + Log.e(TAG, "Error reading package manager settings", e); + + } catch(java.io.IOException e) { + mReadMessages.append("Error reading: " + e.toString()); + Log.e(TAG, "Error reading package manager settings", e); + + } + + int N = mPendingPackages.size(); + for (int i=0; i<N; i++) { + final PendingPackage pp = mPendingPackages.get(i); + Object idObj = getUserIdLP(pp.sharedId); + if (idObj != null && idObj instanceof SharedUserSetting) { + PackageSetting p = getPackageLP(pp.name, + (SharedUserSetting)idObj, pp.codePath, pp.resourcePath, + pp.pkgFlags, true, true); + if (p == null) { + Log.w(TAG, "Unable to create application package for " + + pp.name); + continue; + } + p.copyFrom(pp); + } else if (idObj != null) { + String msg = "Bad package setting: package " + pp.name + + " has shared uid " + pp.sharedId + + " that is not a shared uid\n"; + mReadMessages.append(msg); + Log.e(TAG, msg); + } else { + String msg = "Bad package setting: package " + pp.name + + " has shared uid " + pp.sharedId + + " that is not defined\n"; + mReadMessages.append(msg); + Log.e(TAG, msg); + } + } + mPendingPackages.clear(); + + N = mPendingPreferredPackages.size(); + mPreferredPackages.clear(); + for (int i=0; i<N; i++) { + final String name = mPendingPreferredPackages.get(i); + final PackageSetting p = mPackages.get(name); + if (p != null) { + mPreferredPackages.add(p); + } else { + Log.w(TAG, "Unknown preferred package: " + name); + } + } + mPendingPreferredPackages.clear(); + + mReadMessages.append("Read completed successfully: " + + mPackages.size() + " packages, " + + mSharedUsers.size() + " shared uids\n"); + + return true; + } + + private int readInt(XmlPullParser parser, String ns, String name, + int defValue) { + String v = parser.getAttributeValue(ns, name); + try { + if (v == null) { + return defValue; + } + return Integer.parseInt(v); + } catch (NumberFormatException e) { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: attribute " + + name + " has bad integer value " + v + " at " + + parser.getPositionDescription()); + } + return defValue; + } + + private void readPermissionsLP(HashMap<String, BasePermission> out, + XmlPullParser parser) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + String name = parser.getAttributeValue(null, "name"); + String sourcePackage = parser.getAttributeValue(null, "package"); + String ptype = parser.getAttributeValue(null, "type"); + if (name != null && sourcePackage != null) { + boolean dynamic = "dynamic".equals(ptype); + BasePermission bp = new BasePermission(name, sourcePackage, + dynamic + ? BasePermission.TYPE_DYNAMIC + : BasePermission.TYPE_NORMAL); + if (dynamic) { + PermissionInfo pi = new PermissionInfo(); + pi.packageName = sourcePackage.intern(); + pi.name = name.intern(); + pi.icon = readInt(parser, null, "icon", 0); + pi.nonLocalizedLabel = parser.getAttributeValue( + null, "label"); + pi.protectionLevel = readInt(parser, null, "protection", + PermissionInfo.PROTECTION_NORMAL); + bp.pendingInfo = pi; + } + out.put(bp.name, bp); + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: permissions has" + + " no name at " + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Unknown element reading permissions: " + + parser.getName() + " at " + + parser.getPositionDescription()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readDisabledSysPackageLP(XmlPullParser parser) + throws XmlPullParserException, IOException { + String name = parser.getAttributeValue(null, "name"); + String codePathStr = parser.getAttributeValue(null, "codePath"); + String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); + if(resourcePathStr == null) { + resourcePathStr = codePathStr; + } + + int pkgFlags = 0; + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + PackageSetting ps = new PackageSetting(name, + new File(codePathStr), + new File(resourcePathStr), pkgFlags); + String timeStampStr = parser.getAttributeValue(null, "ts"); + if (timeStampStr != null) { + try { + long timeStamp = Long.parseLong(timeStampStr); + ps.setTimeStamp(timeStamp, timeStampStr); + } catch (NumberFormatException e) { + } + } + String idStr = parser.getAttributeValue(null, "userId"); + ps.userId = idStr != null ? Integer.parseInt(idStr) : 0; + if(ps.userId <= 0) { + String sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); + ps.userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; + } + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("perms")) { + readGrantedPermissionsLP(parser, + ps.grantedPermissions); + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <updated-package>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + mDisabledSysPackages.put(name, ps); + } + + private void readPackageLP(XmlPullParser parser) + throws XmlPullParserException, IOException { + String name = null; + String idStr = null; + String sharedIdStr = null; + String codePathStr = null; + String resourcePathStr = null; + String systemStr = null; + int pkgFlags = 0; + String timeStampStr; + long timeStamp = 0; + PackageSettingBase packageSetting = null; + try { + name = parser.getAttributeValue(null, "name"); + idStr = parser.getAttributeValue(null, "userId"); + sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); + codePathStr = parser.getAttributeValue(null, "codePath"); + resourcePathStr = parser.getAttributeValue(null, "resourcePath"); + systemStr = parser.getAttributeValue(null, "system"); + if (systemStr != null) { + if ("true".equals(systemStr)) { + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + } + } else { + // Old settings that don't specify system... just treat + // them as system, good enough. + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + } + timeStampStr = parser.getAttributeValue(null, "ts"); + if (timeStampStr != null) { + try { + timeStamp = Long.parseLong(timeStampStr); + } catch (NumberFormatException e) { + } + } + if (DEBUG_SETTINGS) Log.v(TAG, "Reading package: " + name + + " userId=" + idStr + " sharedUserId=" + sharedIdStr); + int userId = idStr != null ? Integer.parseInt(idStr) : 0; + if (resourcePathStr == null) { + resourcePathStr = codePathStr; + } + if (name == null) { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <package> has no name at " + + parser.getPositionDescription()); + } else if (codePathStr == null) { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <package> has no codePath at " + + parser.getPositionDescription()); + } else if (userId > 0) { + packageSetting = addPackageLP(name.intern(), new File(codePathStr), + new File(resourcePathStr), userId, pkgFlags); + if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name + + ": userId=" + userId + " pkg=" + packageSetting); + if (packageSetting == null) { + reportSettingsProblem(Log.ERROR, + "Failure adding uid " + userId + + " while parsing settings at " + + parser.getPositionDescription()); + } else { + packageSetting.setTimeStamp(timeStamp, timeStampStr); + } + } else if (sharedIdStr != null) { + userId = sharedIdStr != null + ? Integer.parseInt(sharedIdStr) : 0; + if (userId > 0) { + packageSetting = new PendingPackage(name.intern(), new File(codePathStr), + new File(resourcePathStr), userId, pkgFlags); + packageSetting.setTimeStamp(timeStamp, timeStampStr); + mPendingPackages.add((PendingPackage) packageSetting); + if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name + + ": sharedUserId=" + userId + " pkg=" + + packageSetting); + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + + name + " has bad sharedId " + sharedIdStr + + " at " + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + + name + " has bad userId " + idStr + " at " + + parser.getPositionDescription()); + } + } catch (NumberFormatException e) { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + + name + " has bad userId " + idStr + " at " + + parser.getPositionDescription()); + } + if (packageSetting != null) { + final String enabledStr = parser.getAttributeValue(null, "enabled"); + if (enabledStr != null) { + if (enabledStr.equalsIgnoreCase("true")) { + packageSetting.enabled = COMPONENT_ENABLED_STATE_ENABLED; + } else if (enabledStr.equalsIgnoreCase("false")) { + packageSetting.enabled = COMPONENT_ENABLED_STATE_DISABLED; + } else if (enabledStr.equalsIgnoreCase("default")) { + packageSetting.enabled = COMPONENT_ENABLED_STATE_DEFAULT; + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + + name + " has bad enabled value: " + idStr + + " at " + parser.getPositionDescription()); + } + } else { + packageSetting.enabled = COMPONENT_ENABLED_STATE_DEFAULT; + } + final String installStatusStr = parser.getAttributeValue(null, "installStatus"); + if (installStatusStr != null) { + if (installStatusStr.equalsIgnoreCase("false")) { + packageSetting.installStatus = PKG_INSTALL_INCOMPLETE; + } else { + packageSetting.installStatus = PKG_INSTALL_COMPLETE; + } + } + + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("disabled-components")) { + readDisabledComponentsLP(packageSetting, parser); + } else if (tagName.equals("enabled-components")) { + readEnabledComponentsLP(packageSetting, parser); + } else if (tagName.equals("sigs")) { + packageSetting.signatures.readXml(parser, mPastSignatures); + } else if (tagName.equals("perms")) { + readGrantedPermissionsLP(parser, + packageSetting.loadedPermissions); + packageSetting.permissionsFixed = true; + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <package>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } else { + XmlUtils.skipCurrentTag(parser); + } + } + + private void readDisabledComponentsLP(PackageSettingBase packageSetting, + XmlPullParser parser) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + packageSetting.disabledComponents.add(name.intern()); + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <disabled-components> has" + + " no name at " + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <disabled-components>: " + + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readEnabledComponentsLP(PackageSettingBase packageSetting, + XmlPullParser parser) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + packageSetting.enabledComponents.add(name.intern()); + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <enabled-components> has" + + " no name at " + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <enabled-components>: " + + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readSharedUserLP(XmlPullParser parser) + throws XmlPullParserException, IOException { + String name = null; + String idStr = null; + int pkgFlags = 0; + SharedUserSetting su = null; + try { + name = parser.getAttributeValue(null, "name"); + idStr = parser.getAttributeValue(null, "userId"); + int userId = idStr != null ? Integer.parseInt(idStr) : 0; + if ("true".equals(parser.getAttributeValue(null, "system"))) { + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + } + if (name == null) { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <shared-user> has no name at " + + parser.getPositionDescription()); + } else if (userId == 0) { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: shared-user " + + name + " has bad userId " + idStr + " at " + + parser.getPositionDescription()); + } else { + if ((su=addSharedUserLP(name.intern(), userId, pkgFlags)) == null) { + reportSettingsProblem(Log.ERROR, + "Occurred while parsing settings at " + + parser.getPositionDescription()); + } + } + } catch (NumberFormatException e) { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + + name + " has bad userId " + idStr + " at " + + parser.getPositionDescription()); + }; + + if (su != null) { + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("sigs")) { + su.signatures.readXml(parser, mPastSignatures); + } else if (tagName.equals("perms")) { + readGrantedPermissionsLP(parser, su.loadedPermissions); + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <shared-user>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + } else { + XmlUtils.skipCurrentTag(parser); + } + } + + private void readGrantedPermissionsLP(XmlPullParser parser, + HashSet<String> outPerms) throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + outPerms.add(name.intern()); + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <perms> has" + + " no name at " + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <perms>: " + + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readPreferredPackagesLP(XmlPullParser parser) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + String name = parser.getAttributeValue(null, "name"); + if (name != null) { + mPendingPreferredPackages.add(name); + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <preferred-package> has no name at " + + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <preferred-packages>: " + + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readPreferredActivitiesLP(XmlPullParser parser) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + PreferredActivity pa = new PreferredActivity(parser); + if (pa.mParseError == null) { + mPreferredActivities.addFilter(pa); + } else { + reportSettingsProblem(Log.WARN, + "Error in package manager settings: <preferred-activity> " + + pa.mParseError + " at " + + parser.getPositionDescription()); + } + } else { + reportSettingsProblem(Log.WARN, + "Unknown element under <preferred-activities>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } + + // Returns -1 if we could not find an available UserId to assign + private int newUserIdLP(Object obj) { + // Let's be stupidly inefficient for now... + final int N = mUserIds.size(); + for (int i=0; i<N; i++) { + if (mUserIds.get(i) == null) { + mUserIds.set(i, obj); + return FIRST_APPLICATION_UID + i; + } + } + + // None left? + if (N >= MAX_APPLICATION_UIDS) { + return -1; + } + + mUserIds.add(obj); + return FIRST_APPLICATION_UID + N; + } + + public PackageSetting getDisabledSystemPkg(String name) { + synchronized(mPackages) { + PackageSetting ps = mDisabledSysPackages.get(name); + return ps; + } + } + + boolean isEnabledLP(ComponentInfo componentInfo, int flags) { + final PackageSetting packageSettings = mPackages.get(componentInfo.packageName); + if (Config.LOGV) { + Log.v(TAG, "isEnabledLock - packageName = " + componentInfo.packageName + + " componentName = " + componentInfo.name); + Log.v(TAG, "enabledComponents: " + + Arrays.toString(packageSettings.enabledComponents.toArray())); + Log.v(TAG, "disabledComponents: " + + Arrays.toString(packageSettings.disabledComponents.toArray())); + } + return ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) + || ((componentInfo.enabled + && ((packageSettings.enabled == COMPONENT_ENABLED_STATE_ENABLED) + || (componentInfo.applicationInfo.enabled + && packageSettings.enabled != COMPONENT_ENABLED_STATE_DISABLED)) + && !packageSettings.disabledComponents.contains(componentInfo.name)) + || packageSettings.enabledComponents.contains(componentInfo.name)); + } + } +} |