summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server')
-rw-r--r--services/java/com/android/server/AppWidgetService.java4
-rw-r--r--services/java/com/android/server/BackupManagerService.java1476
-rw-r--r--services/java/com/android/server/BatteryService.java2
-rw-r--r--services/java/com/android/server/ConnectivityService.java207
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java5
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java27
-rw-r--r--services/java/com/android/server/EntropyService.java4
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java92
-rw-r--r--services/java/com/android/server/IntentResolver.java3
-rw-r--r--services/java/com/android/server/LightsService.java3
-rw-r--r--services/java/com/android/server/MountService.java8
-rw-r--r--services/java/com/android/server/NetStatService.java96
-rw-r--r--services/java/com/android/server/NetworkManagementService.java110
-rw-r--r--services/java/com/android/server/ProcessStats.java3
-rw-r--r--services/java/com/android/server/RandomBlock.java4
-rw-r--r--services/java/com/android/server/StatusBarManagerService.java5
-rw-r--r--services/java/com/android/server/SystemBackupAgent.java80
-rw-r--r--services/java/com/android/server/SystemServer.java65
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java1
-rw-r--r--services/java/com/android/server/ThrottleService.java197
-rw-r--r--services/java/com/android/server/Watchdog.java3
-rw-r--r--services/java/com/android/server/WifiService.java13
-rw-r--r--services/java/com/android/server/WifiWatchdogService.java62
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityInputFilter.java111
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java (renamed from services/java/com/android/server/AccessibilityManagerService.java)245
-rw-r--r--services/java/com/android/server/accessibility/TouchExplorer.java1458
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java568
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java77
-rw-r--r--services/java/com/android/server/am/ActivityStack.java270
-rw-r--r--services/java/com/android/server/am/BackupRecord.java1
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java13
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java7
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java24
-rw-r--r--services/java/com/android/server/am/TaskAccessInfo.java35
-rw-r--r--services/java/com/android/server/am/TaskRecord.java4
-rw-r--r--services/java/com/android/server/am/ThumbnailHolder.java24
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java159
-rwxr-xr-xservices/java/com/android/server/location/ComprehensiveCountryDetector.java30
-rw-r--r--services/java/com/android/server/location/GpsXtraDownloader.java1
-rwxr-xr-xservices/java/com/android/server/location/LocationBasedCountryDetector.java88
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java382
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java403
-rw-r--r--services/java/com/android/server/pm/BasePermission.java59
-rw-r--r--services/java/com/android/server/pm/GrantedPermissions.java50
-rw-r--r--services/java/com/android/server/pm/Installer.java (renamed from services/java/com/android/server/Installer.java)272
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java (renamed from services/java/com/android/server/PackageManagerService.java)4854
-rw-r--r--services/java/com/android/server/pm/PackageSetting.java55
-rw-r--r--services/java/com/android/server/pm/PackageSettingBase.java207
-rw-r--r--services/java/com/android/server/pm/PackageSignatures.java198
-rw-r--r--services/java/com/android/server/pm/PendingPackage.java30
-rw-r--r--services/java/com/android/server/pm/PreferredActivity.java73
-rw-r--r--services/java/com/android/server/pm/Settings.java2230
-rw-r--r--services/java/com/android/server/pm/SharedUserSetting.java43
-rw-r--r--services/java/com/android/server/pm/UserManager.java401
-rw-r--r--services/java/com/android/server/usb/UsbDeviceManager.java431
-rw-r--r--services/java/com/android/server/usb/UsbHostManager.java222
-rw-r--r--services/java/com/android/server/usb/UsbService.java588
-rw-r--r--services/java/com/android/server/usb/UsbSettingsManager.java (renamed from services/java/com/android/server/usb/UsbDeviceSettingsManager.java)6
-rw-r--r--services/java/com/android/server/wm/InputFilter.java256
-rw-r--r--services/java/com/android/server/wm/InputManager.java89
-rw-r--r--services/java/com/android/server/wm/ViewServer.java27
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java5
62 files changed, 11277 insertions, 5189 deletions
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 65f8b34..fd502d8 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -542,7 +542,9 @@ class AppWidgetService extends IAppWidgetService.Stub
IRemoteViewsFactory.Stub.asInterface(service);
try {
cb.onDestroy(intent);
- } catch (Exception e) {
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (RuntimeException e) {
e.printStackTrace();
}
mContext.unbindService(this);
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index ea38fbb..cd58b9b 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -23,10 +23,14 @@ import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.IBackupAgent;
import android.app.PendingIntent;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.FullBackup;
import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
+import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -35,6 +39,7 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -42,6 +47,7 @@ import android.content.pm.Signature;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -60,6 +66,7 @@ import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.StringBuilderPrinter;
import com.android.internal.backup.BackupConstants;
import com.android.internal.backup.IBackupTransport;
@@ -69,22 +76,31 @@ import com.android.server.PackageManagerBackupAgent.Metadata;
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
class BackupManagerService extends IBackupManager.Stub {
private static final String TAG = "BackupManagerService";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
+
+ // Name and current contents version of the full-backup manifest file
+ static final String BACKUP_MANIFEST_FILENAME = "_manifest";
+ static final int BACKUP_MANIFEST_VERSION = 1;
// How often we perform a backup pass. Privileged external callers can
// trigger an immediate pass.
@@ -108,14 +124,20 @@ class BackupManagerService extends IBackupManager.Stub {
private static final int MSG_RUN_GET_RESTORE_SETS = 6;
private static final int MSG_TIMEOUT = 7;
private static final int MSG_RESTORE_TIMEOUT = 8;
+ private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
+ private static final int MSG_RUN_FULL_RESTORE = 10;
// Timeout interval for deciding that a bind or clear-data has taken too long
static final long TIMEOUT_INTERVAL = 10 * 1000;
// Timeout intervals for agent backup & restore operations
static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
+ static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
+ // User confirmation timeout for a full backup/restore operation
+ static final long TIMEOUT_FULL_CONFIRMATION = 30 * 1000;
+
private Context mContext;
private PackageManager mPackageManager;
IPackageManager mPackageManagerBinder;
@@ -138,15 +160,13 @@ class BackupManagerService extends IBackupManager.Stub {
// set of backup services that have pending changes
class BackupRequest {
public ApplicationInfo appInfo;
- public boolean fullBackup;
- BackupRequest(ApplicationInfo app, boolean isFull) {
+ BackupRequest(ApplicationInfo app) {
appInfo = app;
- fullBackup = isFull;
}
public String toString() {
- return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
+ return "BackupRequest{app=" + appInfo + "}";
}
}
// Backups that we haven't started yet. Keys are package names.
@@ -232,6 +252,38 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ class FullParams {
+ public ParcelFileDescriptor fd;
+ public final AtomicBoolean latch;
+ public IFullBackupRestoreObserver observer;
+
+ FullParams() {
+ latch = new AtomicBoolean(false);
+ }
+ }
+
+ class FullBackupParams extends FullParams {
+ public boolean includeApks;
+ public boolean includeShared;
+ public boolean allApps;
+ public String[] packages;
+
+ FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
+ boolean doAllApps, String[] pkgList) {
+ fd = output;
+ includeApks = saveApks;
+ includeShared = saveShared;
+ allApps = doAllApps;
+ packages = pkgList;
+ }
+ }
+
+ class FullRestoreParams extends FullParams {
+ FullRestoreParams(ParcelFileDescriptor input) {
+ fd = input;
+ }
+ }
+
// Bookkeeping of in-flight operations for timeout etc. purposes. The operation
// token is the index of the entry in the pending-operations list.
static final int OP_PENDING = 0;
@@ -242,6 +294,8 @@ class BackupManagerService extends IBackupManager.Stub {
final Object mCurrentOpLock = new Object();
final Random mTokenGenerator = new Random();
+ final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
+
// Where we keep our journal files and other bookkeeping
File mBaseStateDir;
File mDataDir;
@@ -264,6 +318,17 @@ class BackupManagerService extends IBackupManager.Stub {
static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
HashSet<String> mPendingInits = new HashSet<String>(); // transport names
+ // Utility: build a new random integer token
+ int generateToken() {
+ int token;
+ do {
+ synchronized (mTokenGenerator) {
+ token = mTokenGenerator.nextInt();
+ }
+ } while (token < 0);
+ return token;
+ }
+
// ----- Asynchronous backup/restore handler thread -----
private class BackupHandler extends Handler {
@@ -321,7 +386,13 @@ class BackupManagerService extends IBackupManager.Stub {
}
case MSG_RUN_FULL_BACKUP:
+ {
+ FullBackupParams params = (FullBackupParams)msg.obj;
+ (new PerformFullBackupTask(params.fd, params.observer, params.includeApks,
+ params.includeShared, params.allApps, params.packages,
+ params.latch)).run();
break;
+ }
case MSG_RUN_RESTORE:
{
@@ -333,6 +404,13 @@ class BackupManagerService extends IBackupManager.Stub {
break;
}
+ case MSG_RUN_FULL_RESTORE:
+ {
+ FullRestoreParams params = (FullRestoreParams)msg.obj;
+ (new PerformFullRestoreTask(params.fd, params.observer, params.latch)).run();
+ break;
+ }
+
case MSG_RUN_CLEAR:
{
ClearParams params = (ClearParams)msg.obj;
@@ -416,6 +494,34 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
}
+
+ case MSG_FULL_CONFIRMATION_TIMEOUT:
+ {
+ synchronized (mFullConfirmations) {
+ FullParams params = mFullConfirmations.get(msg.arg1);
+ if (params != null) {
+ Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
+
+ // Release the waiter; timeout == completion
+ signalFullBackupRestoreCompletion(params);
+
+ // Remove the token from the set
+ mFullConfirmations.delete(msg.arg1);
+
+ // Report a timeout to the observer, if any
+ if (params.observer != null) {
+ try {
+ params.observer.onTimeout();
+ } catch (RemoteException e) {
+ /* don't care if the app has gone away */
+ }
+ }
+ } else {
+ Slog.d(TAG, "couldn't find params for token " + msg.arg1);
+ }
+ }
+ break;
+ }
}
}
}
@@ -760,15 +866,15 @@ class BackupManagerService extends IBackupManager.Stub {
sf.delete();
}
}
+ }
- // Enqueue a new backup of every participant
- int N = mBackupParticipants.size();
- for (int i=0; i<N; i++) {
- int uid = mBackupParticipants.keyAt(i);
- HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
- for (ApplicationInfo app: participants) {
- dataChangedImpl(app.packageName);
- }
+ // Enqueue a new backup of every participant
+ int N = mBackupParticipants.size();
+ for (int i=0; i<N; i++) {
+ int uid = mBackupParticipants.keyAt(i);
+ HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
+ for (ApplicationInfo app: participants) {
+ dataChangedImpl(app.packageName);
}
}
}
@@ -1143,7 +1249,7 @@ class BackupManagerService extends IBackupManager.Stub {
Slog.d(TAG, "awaiting agent for " + app);
// success; wait for the agent to arrive
- // only wait 10 seconds for the clear data to happen
+ // only wait 10 seconds for the bind to happen
long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
while (mConnecting && mConnectedAgent == null
&& (System.currentTimeMillis() < timeoutMark)) {
@@ -1253,9 +1359,11 @@ class BackupManagerService extends IBackupManager.Stub {
void prepareOperationTimeout(int token, long interval) {
if (DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
+ " interval=" + interval);
- mCurrentOperations.put(token, OP_PENDING);
- Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
- mBackupHandler.sendMessageDelayed(msg, interval);
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.put(token, OP_PENDING);
+ Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
+ mBackupHandler.sendMessageDelayed(msg, interval);
+ }
}
// ----- Back up a set of applications via a worker thread -----
@@ -1313,7 +1421,7 @@ class BackupManagerService extends IBackupManager.Stub {
if (status == BackupConstants.TRANSPORT_OK) {
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
mPackageManager, allAgentPackages());
- BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+ BackupRequest pmRequest = new BackupRequest(new ApplicationInfo());
pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
status = processOneBackup(pmRequest,
IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
@@ -1407,12 +1515,10 @@ class BackupManagerService extends IBackupManager.Stub {
}
IBackupAgent agent = null;
- int mode = (request.fullBackup)
- ? IApplicationThread.BACKUP_MODE_FULL
- : IApplicationThread.BACKUP_MODE_INCREMENTAL;
try {
mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
- agent = bindToAgentSynchronous(request.appInfo, mode);
+ agent = bindToAgentSynchronous(request.appInfo,
+ IApplicationThread.BACKUP_MODE_INCREMENTAL);
if (agent != null) {
int result = processOneBackup(request, agent, transport);
if (result != BackupConstants.TRANSPORT_OK) return result;
@@ -1446,7 +1552,7 @@ class BackupManagerService extends IBackupManager.Stub {
ParcelFileDescriptor newState = null;
PackageInfo packInfo;
- int token = mTokenGenerator.nextInt();
+ final int token = generateToken();
try {
// Look up the package info & signatures. This is first so that if it
// throws an exception, there's no file setup yet that would need to
@@ -1461,12 +1567,11 @@ class BackupManagerService extends IBackupManager.Stub {
}
// In a full backup, we pass a null ParcelFileDescriptor as
- // the saved-state "file"
- if (!request.fullBackup) {
- savedState = ParcelFileDescriptor.open(savedStateName,
- ParcelFileDescriptor.MODE_READ_ONLY |
- ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
- }
+ // the saved-state "file". This is by definition an incremental,
+ // so we build a saved state file to pass.
+ savedState = ParcelFileDescriptor.open(savedStateName,
+ ParcelFileDescriptor.MODE_READ_ONLY |
+ ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_WRITE |
@@ -1480,7 +1585,8 @@ class BackupManagerService extends IBackupManager.Stub {
// Initiate the target's backup pass
prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
- agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder);
+ agent.doBackup(savedState, backupData, newState, false,
+ token, mBackupManagerBinder);
boolean success = waitUntilOperationComplete(token);
if (!success) {
@@ -1552,6 +1658,1120 @@ class BackupManagerService extends IBackupManager.Stub {
}
+ // ----- Full backup to a file/socket -----
+
+ class PerformFullBackupTask implements Runnable {
+ ParcelFileDescriptor mOutputFile;
+ IFullBackupRestoreObserver mObserver;
+ boolean mIncludeApks;
+ boolean mIncludeShared;
+ boolean mAllApps;
+ String[] mPackages;
+ AtomicBoolean mLatchObject;
+ File mFilesDir;
+ File mManifestFile;
+
+ PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
+ boolean includeApks, boolean includeShared,
+ boolean doAllApps, String[] packages, AtomicBoolean latch) {
+ mOutputFile = fd;
+ mObserver = observer;
+ mIncludeApks = includeApks;
+ mIncludeShared = includeShared;
+ mAllApps = doAllApps;
+ mPackages = packages;
+ mLatchObject = latch;
+
+ mFilesDir = new File("/data/system");
+ mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
+ }
+
+ @Override
+ public void run() {
+ final List<PackageInfo> packagesToBackup;
+
+ Slog.i(TAG, "--- Performing full-dataset restore ---");
+ sendStartBackup();
+
+ // doAllApps supersedes the package set if any
+ if (mAllApps) {
+ packagesToBackup = mPackageManager.getInstalledPackages(
+ PackageManager.GET_SIGNATURES);
+ } else {
+ packagesToBackup = new ArrayList<PackageInfo>();
+ for (String pkgName : mPackages) {
+ try {
+ packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
+ PackageManager.GET_SIGNATURES));
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
+ }
+ }
+ }
+
+ // Now back up the app data via the agent mechanism
+ PackageInfo pkg = null;
+ try {
+ int N = packagesToBackup.size();
+ for (int i = 0; i < N; i++) {
+ pkg = packagesToBackup.get(i);
+
+ Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
+
+ IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+ IApplicationThread.BACKUP_MODE_FULL);
+ if (agent != null) {
+ try {
+ ApplicationInfo app = pkg.applicationInfo;
+ boolean sendApk = mIncludeApks
+ && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
+ && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+ (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+
+ sendOnBackupPackage(pkg.packageName);
+
+ {
+ BackupDataOutput output = new BackupDataOutput(
+ mOutputFile.getFileDescriptor());
+
+ if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
+ writeAppManifest(pkg, mManifestFile, sendApk);
+ FullBackup.backupToTar(pkg.packageName, null, null,
+ mFilesDir.getAbsolutePath(),
+ mManifestFile.getAbsolutePath(),
+ output);
+ }
+
+ if (DEBUG) Slog.d(TAG, "Calling doBackup()");
+ final int token = generateToken();
+ prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
+ agent.doBackup(null, mOutputFile, null, sendApk,
+ token, mBackupManagerBinder);
+ boolean success = waitUntilOperationComplete(token);
+ if (!success) {
+ Slog.d(TAG, "Full backup failed on package " + pkg.packageName);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Error backing up " + pkg.packageName, e);
+ }
+ } else {
+ Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
+ }
+ tearDown(pkg);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "App died during full backup");
+ } finally {
+ if (pkg != null) {
+ tearDown(pkg);
+ }
+ try {
+ mOutputFile.close();
+ } catch (IOException e) {
+ /* nothing we can do about this */
+ }
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.clear();
+ }
+ synchronized (mLatchObject) {
+ mLatchObject.set(true);
+ mLatchObject.notifyAll();
+ }
+ sendEndBackup();
+ mWakelock.release();
+ if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
+ }
+ }
+
+ private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
+ throws IOException {
+ // Manifest format. All data are strings ending in LF:
+ // BACKUP_MANIFEST_VERSION, currently 1
+ //
+ // Version 1:
+ // package name
+ // package's versionCode
+ // platform versionCode
+ // getInstallerPackageName() for this package (maybe empty)
+ // boolean: "1" if archive includes .apk; any other string means not
+ // number of signatures == N
+ // N*: signature byte array in ascii format per Signature.toCharsString()
+ StringBuilder builder = new StringBuilder(4096);
+ StringBuilderPrinter printer = new StringBuilderPrinter(builder);
+
+ printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
+ printer.println(pkg.packageName);
+ printer.println(Integer.toString(pkg.versionCode));
+ printer.println(Integer.toString(Build.VERSION.SDK_INT));
+
+ String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
+ printer.println((installerName != null) ? installerName : "");
+
+ printer.println(withApk ? "1" : "0");
+ if (pkg.signatures == null) {
+ printer.println("0");
+ } else {
+ printer.println(Integer.toString(pkg.signatures.length));
+ for (Signature sig : pkg.signatures) {
+ printer.println(sig.toCharsString());
+ }
+ }
+
+ FileOutputStream outstream = new FileOutputStream(manifestFile);
+ outstream.write(builder.toString().getBytes());
+ outstream.close();
+ }
+
+ private void tearDown(PackageInfo pkg) {
+ final ApplicationInfo app = pkg.applicationInfo;
+ try {
+ // unbind and tidy up even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(app);
+
+ // The agent was running with a stub Application object, so shut it down.
+ // !!! We hardcode the confirmation UI's package name here rather than use a
+ // manifest flag! TODO something less direct.
+ if (app.uid != Process.SYSTEM_UID
+ && !pkg.packageName.equals("com.android.backupconfirm")) {
+ if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
+ mActivityManager.killApplicationProcess(app.processName, app.uid);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Lost app trying to shut down");
+ }
+ }
+
+ // wrappers for observer use
+ void sendStartBackup() {
+ if (mObserver != null) {
+ try {
+ mObserver.onStartBackup();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full backup observer went away: startBackup");
+ mObserver = null;
+ }
+ }
+ }
+
+ void sendOnBackupPackage(String name) {
+ if (mObserver != null) {
+ try {
+ // TODO: use a more user-friendly name string
+ mObserver.onBackupPackage(name);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full backup observer went away: backupPackage");
+ mObserver = null;
+ }
+ }
+ }
+
+ void sendEndBackup() {
+ if (mObserver != null) {
+ try {
+ mObserver.onEndBackup();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full backup observer went away: endBackup");
+ mObserver = null;
+ }
+ }
+ }
+ }
+
+
+ // ----- Full restore from a file/socket -----
+
+ // Description of a file in the restore datastream
+ static class FileMetadata {
+ String packageName; // name of the owning app
+ String installerPackageName; // name of the market-type app that installed the owner
+ int type; // e.g. FullBackup.TYPE_DIRECTORY
+ String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN
+ String path; // subpath within the semantic domain
+ long mode; // e.g. 0666 (actually int)
+ long mtime; // last mod time, UTC time_t (actually int)
+ long size; // bytes of content
+ }
+
+ enum RestorePolicy {
+ IGNORE,
+ ACCEPT,
+ ACCEPT_IF_APK
+ }
+
+ class PerformFullRestoreTask implements Runnable {
+ ParcelFileDescriptor mInputFile;
+ IFullBackupRestoreObserver mObserver;
+ AtomicBoolean mLatchObject;
+ IBackupAgent mAgent;
+ String mAgentPackage;
+ ApplicationInfo mTargetApp;
+ ParcelFileDescriptor[] mPipes = null;
+
+ // possible handling states for a given package in the restore dataset
+ final HashMap<String, RestorePolicy> mPackagePolicies
+ = new HashMap<String, RestorePolicy>();
+
+ // installer package names for each encountered app, derived from the manifests
+ final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
+
+ // Signatures for a given package found in its manifest file
+ final HashMap<String, Signature[]> mManifestSignatures
+ = new HashMap<String, Signature[]>();
+
+ // Packages we've already wiped data on when restoring their first file
+ final HashSet<String> mClearedPackages = new HashSet<String>();
+
+ PerformFullRestoreTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
+ AtomicBoolean latch) {
+ mInputFile = fd;
+ mObserver = observer;
+ mLatchObject = latch;
+ mAgent = null;
+ mAgentPackage = null;
+ mTargetApp = null;
+
+ // Which packages we've already wiped data on. We prepopulate this
+ // with a whitelist of packages known to be unclearable.
+ mClearedPackages.add("android");
+ mClearedPackages.add("com.android.backupconfirm");
+ mClearedPackages.add("com.android.providers.settings");
+ }
+
+ class RestoreFileRunnable implements Runnable {
+ IBackupAgent mAgent;
+ FileMetadata mInfo;
+ ParcelFileDescriptor mSocket;
+ int mToken;
+
+ RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
+ ParcelFileDescriptor socket, int token) throws IOException {
+ mAgent = agent;
+ mInfo = info;
+ mToken = token;
+
+ // This class is used strictly for process-local binder invocations. The
+ // semantics of ParcelFileDescriptor differ in this case; in particular, we
+ // do not automatically get a 'dup'ed descriptor that we can can continue
+ // to use asynchronously from the caller. So, we make sure to dup it ourselves
+ // before proceeding to do the restore.
+ mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
+ }
+
+ @Override
+ public void run() {
+ try {
+ mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
+ mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
+ mToken, mBackupManagerBinder);
+ } catch (RemoteException e) {
+ // never happens; this is used strictly for local binder calls
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ Slog.i(TAG, "--- Performing full-dataset restore ---");
+ sendStartRestore();
+
+ try {
+ byte[] buffer = new byte[32 * 1024];
+ FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
+
+ boolean didRestore;
+ do {
+ didRestore = restoreOneFile(instream, buffer);
+ } while (didRestore);
+
+ if (DEBUG) Slog.v(TAG, "Done consuming input tarfile");
+ } finally {
+ tearDownPipes();
+ tearDownAgent(mTargetApp);
+
+ try {
+ mInputFile.close();
+ } catch (IOException e) {
+ /* nothing we can do about this */
+ }
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.clear();
+ }
+ synchronized (mLatchObject) {
+ mLatchObject.set(true);
+ mLatchObject.notifyAll();
+ }
+ sendEndRestore();
+ mWakelock.release();
+ if (DEBUG) Slog.d(TAG, "Full restore pass complete.");
+ }
+ }
+
+ boolean restoreOneFile(InputStream instream, byte[] buffer) {
+ FileMetadata info;
+ try {
+ info = readTarHeaders(instream);
+ if (info != null) {
+ if (DEBUG) {
+ dumpFileMetadata(info);
+ }
+
+ final String pkg = info.packageName;
+ if (!pkg.equals(mAgentPackage)) {
+ // okay, change in package; set up our various
+ // bookkeeping if we haven't seen it yet
+ if (!mPackagePolicies.containsKey(pkg)) {
+ mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
+ }
+
+ // Clean up the previous agent relationship if necessary,
+ // and let the observer know we're considering a new app.
+ if (mAgent != null) {
+ if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
+ tearDownPipes();
+ tearDownAgent(mTargetApp);
+ mTargetApp = null;
+ mAgentPackage = null;
+ }
+ }
+
+ if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
+ mPackagePolicies.put(pkg, readAppManifest(info, instream));
+ mPackageInstallers.put(pkg, info.installerPackageName);
+ // We've read only the manifest content itself at this point,
+ // so consume the footer before looping around to the next
+ // input file
+ skipTarPadding(info.size, instream);
+ sendOnRestorePackage(pkg);
+ } else {
+ // Non-manifest, so it's actual file data. Is this a package
+ // we're ignoring?
+ boolean okay = true;
+ RestorePolicy policy = mPackagePolicies.get(pkg);
+ switch (policy) {
+ case IGNORE:
+ okay = false;
+ break;
+
+ case ACCEPT_IF_APK:
+ // If we're in accept-if-apk state, then the first file we
+ // see MUST be the apk.
+ if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
+ if (DEBUG) Slog.d(TAG, "APK file; installing");
+ // Try to install the app.
+ String installerName = mPackageInstallers.get(pkg);
+ okay = installApk(info, installerName, instream);
+ // good to go; promote to ACCEPT
+ mPackagePolicies.put(pkg, (okay)
+ ? RestorePolicy.ACCEPT
+ : RestorePolicy.IGNORE);
+ // At this point we've consumed this file entry
+ // ourselves, so just strip the tar footer and
+ // go on to the next file in the input stream
+ skipTarPadding(info.size, instream);
+ return true;
+ } else {
+ // File data before (or without) the apk. We can't
+ // handle it coherently in this case so ignore it.
+ mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
+ okay = false;
+ }
+ break;
+
+ case ACCEPT:
+ if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
+ if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
+ // we can take the data without the apk, so we
+ // *want* to do so. skip the apk by declaring this
+ // one file not-okay without changing the restore
+ // policy for the package.
+ okay = false;
+ }
+ break;
+
+ default:
+ // Something has gone dreadfully wrong when determining
+ // the restore policy from the manifest. Ignore the
+ // rest of this package's data.
+ Slog.e(TAG, "Invalid policy from manifest");
+ okay = false;
+ mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
+ break;
+ }
+
+ // If the policy is satisfied, go ahead and set up to pipe the
+ // data to the agent.
+ if (DEBUG && okay && mAgent != null) {
+ Slog.i(TAG, "Reusing existing agent instance");
+ }
+ if (okay && mAgent == null) {
+ if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
+
+ try {
+ mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
+
+ // If we haven't sent any data to this app yet, we probably
+ // need to clear it first. Check that.
+ if (!mClearedPackages.contains(pkg)) {
+ // apps with their own full backup agents are
+ // responsible for coherently managing a full
+ // restore.
+ if (mTargetApp.fullBackupAgentName == null) {
+ if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
+ clearApplicationDataSynchronous(pkg);
+ } else {
+ if (DEBUG) Slog.d(TAG, "full backup agent ("
+ + mTargetApp.fullBackupAgentName + ") => no clear");
+ }
+ mClearedPackages.add(pkg);
+ } else {
+ if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
+ }
+
+ // All set; now set up the IPC and launch the agent
+ setUpPipes();
+ mAgent = bindToAgentSynchronous(mTargetApp,
+ IApplicationThread.BACKUP_MODE_RESTORE_FULL);
+ mAgentPackage = pkg;
+ } catch (IOException e) {
+ // fall through to error handling
+ } catch (NameNotFoundException e) {
+ // fall through to error handling
+ }
+
+ if (mAgent == null) {
+ if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
+ okay = false;
+ tearDownPipes();
+ mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
+ }
+ }
+
+ // Sanity check: make sure we never give data to the wrong app. This
+ // should never happen but a little paranoia here won't go amiss.
+ if (okay && !pkg.equals(mAgentPackage)) {
+ Slog.e(TAG, "Restoring data for " + pkg
+ + " but agent is for " + mAgentPackage);
+ okay = false;
+ }
+
+ // At this point we have an agent ready to handle the full
+ // restore data as well as a pipe for sending data to
+ // that agent. Tell the agent to start reading from the
+ // pipe.
+ if (okay) {
+ boolean agentSuccess = true;
+ long toCopy = info.size;
+ final int token = generateToken();
+ try {
+ if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
+ + info.path);
+ prepareOperationTimeout(token,
+ TIMEOUT_FULL_BACKUP_INTERVAL);
+ // fire up the app's agent listening on the socket. If
+ // the agent is running in the system process we can't
+ // just invoke it asynchronously, so we provide a thread
+ // for it here.
+ if (mTargetApp.processName.equals("system")) {
+ Slog.d(TAG, "system process agent - spinning a thread");
+ RestoreFileRunnable runner = new RestoreFileRunnable(
+ mAgent, info, mPipes[0], token);
+ new Thread(runner).start();
+ } else {
+ mAgent.doRestoreFile(mPipes[0], info.size, info.type,
+ info.domain, info.path, info.mode, info.mtime,
+ token, mBackupManagerBinder);
+ }
+ } catch (IOException e) {
+ // couldn't dup the socket for a process-local restore
+ Slog.d(TAG, "Couldn't establish restore");
+ agentSuccess = false;
+ okay = false;
+ } catch (RemoteException e) {
+ // whoops, remote agent went away. We'll eat the content
+ // ourselves, then, and not copy it over.
+ Slog.e(TAG, "Agent crashed during full restore");
+ agentSuccess = false;
+ okay = false;
+ }
+
+ // Copy over the data if the agent is still good
+ if (okay) {
+ boolean pipeOkay = true;
+ FileOutputStream pipe = new FileOutputStream(
+ mPipes[1].getFileDescriptor());
+ if (DEBUG) Slog.d(TAG, "Piping data to agent");
+ while (toCopy > 0) {
+ int toRead = (toCopy > buffer.length)
+ ? buffer.length : (int)toCopy;
+ int nRead = instream.read(buffer, 0, toRead);
+ if (nRead <= 0) break;
+ toCopy -= nRead;
+
+ // send it to the output pipe as long as things
+ // are still good
+ if (pipeOkay) {
+ try {
+ pipe.write(buffer, 0, nRead);
+ } catch (IOException e) {
+ Slog.e(TAG,
+ "Failed to write to restore pipe", e);
+ pipeOkay = false;
+ }
+ }
+ }
+
+ // done sending that file! Now we just need to consume
+ // the delta from info.size to the end of block.
+ skipTarPadding(info.size, instream);
+
+ // and now that we've sent it all, wait for the remote
+ // side to acknowledge receipt
+ agentSuccess = waitUntilOperationComplete(token);
+ }
+
+ // okay, if the remote end failed at any point, deal with
+ // it by ignoring the rest of the restore on it
+ if (!agentSuccess) {
+ mBackupHandler.removeMessages(MSG_TIMEOUT);
+ tearDownPipes();
+ tearDownAgent(mTargetApp);
+ mAgent = null;
+ mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
+ }
+ }
+
+ // Problems setting up the agent communication, or an already-
+ // ignored package: skip to the next tar stream entry by
+ // reading and discarding this file.
+ if (!okay) {
+ if (DEBUG) Slog.d(TAG, "[discarding file content]");
+ long bytesToConsume = (info.size + 511) & ~511;
+ while (bytesToConsume > 0) {
+ int toRead = (bytesToConsume > buffer.length)
+ ? buffer.length : (int)bytesToConsume;
+ long nRead = instream.read(buffer, 0, toRead);
+ if (nRead <= 0) break;
+ bytesToConsume -= nRead;
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "io exception on restore socket read", e);
+ // treat as EOF
+ info = null;
+ }
+
+ return (info != null);
+ }
+
+ void setUpPipes() throws IOException {
+ mPipes = ParcelFileDescriptor.createPipe();
+ }
+
+ void tearDownPipes() {
+ if (mPipes != null) {
+ if (mPipes[0] != null) {
+ try {
+ mPipes[0].close();
+ mPipes[0] = null;
+ mPipes[1].close();
+ mPipes[1] = null;
+ } catch (IOException e) {
+ Slog.w(TAG, "Couldn't close agent pipes", e);
+ }
+ }
+ mPipes = null;
+ }
+ }
+
+ void tearDownAgent(ApplicationInfo app) {
+ if (mAgent != null) {
+ try {
+ // unbind and tidy up even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(app);
+
+ // The agent was running with a stub Application object, so shut it down.
+ // !!! We hardcode the confirmation UI's package name here rather than use a
+ // manifest flag! TODO something less direct.
+ if (app.uid != Process.SYSTEM_UID
+ && !app.packageName.equals("com.android.backupconfirm")) {
+ if (DEBUG) Slog.d(TAG, "Killing host process");
+ mActivityManager.killApplicationProcess(app.processName, app.uid);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Not killing after full restore");
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Lost app trying to shut down");
+ }
+ mAgent = null;
+ }
+ }
+
+ class RestoreInstallObserver extends IPackageInstallObserver.Stub {
+ final AtomicBoolean mDone = new AtomicBoolean();
+ int mResult;
+
+ public void reset() {
+ synchronized (mDone) {
+ mDone.set(false);
+ }
+ }
+
+ public void waitForCompletion() {
+ synchronized (mDone) {
+ while (mDone.get() == false) {
+ try {
+ mDone.wait();
+ } catch (InterruptedException e) { }
+ }
+ }
+ }
+
+ int getResult() {
+ return mResult;
+ }
+
+ @Override
+ public void packageInstalled(String packageName, int returnCode)
+ throws RemoteException {
+ synchronized (mDone) {
+ mResult = returnCode;
+ mDone.set(true);
+ mDone.notifyAll();
+ }
+ }
+ }
+ final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
+
+ boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
+ boolean okay = true;
+
+ if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
+
+ // The file content is an .apk file. Copy it out to a staging location and
+ // attempt to install it.
+ File apkFile = new File(mDataDir, info.packageName);
+ try {
+ FileOutputStream apkStream = new FileOutputStream(apkFile);
+ byte[] buffer = new byte[32 * 1024];
+ long size = info.size;
+ while (size > 0) {
+ long toRead = (buffer.length < size) ? buffer.length : size;
+ int didRead = instream.read(buffer, 0, (int)toRead);
+ apkStream.write(buffer, 0, didRead);
+ size -= didRead;
+ }
+ apkStream.close();
+
+ // make sure the installer can read it
+ apkFile.setReadable(true, false);
+
+ // Now install it
+ Uri packageUri = Uri.fromFile(apkFile);
+ mInstallObserver.reset();
+ mPackageManager.installPackage(packageUri, mInstallObserver,
+ PackageManager.INSTALL_REPLACE_EXISTING, installerPackage);
+ mInstallObserver.waitForCompletion();
+
+ if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
+ // The only time we continue to accept install of data even if the
+ // apk install failed is if we had already determined that we could
+ // accept the data regardless.
+ if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
+ okay = false;
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to transcribe restored apk for install");
+ okay = false;
+ } finally {
+ apkFile.delete();
+ }
+
+ return okay;
+ }
+
+ // Given an actual file content size, consume the post-content padding mandated
+ // by the tar format.
+ void skipTarPadding(long size, InputStream instream) throws IOException {
+ long partial = (size + 512) % 512;
+ if (partial > 0) {
+ byte[] buffer = new byte[512];
+ instream.read(buffer, 0, 512 - (int)partial);
+ }
+ }
+
+ // Returns a policy constant; takes a buffer arg to reduce memory churn
+ RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
+ throws IOException {
+ // Fail on suspiciously large manifest files
+ if (info.size > 64 * 1024) {
+ throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
+ }
+ byte[] buffer = new byte[(int) info.size];
+ int nRead = 0;
+ while (nRead < info.size) {
+ nRead += instream.read(buffer, nRead, (int)info.size - nRead);
+ }
+
+ RestorePolicy policy = RestorePolicy.IGNORE;
+ String[] str = new String[1];
+ int offset = 0;
+
+ try {
+ offset = extractLine(buffer, offset, str);
+ int version = Integer.parseInt(str[0]);
+ if (version == BACKUP_MANIFEST_VERSION) {
+ offset = extractLine(buffer, offset, str);
+ String manifestPackage = str[0];
+ // TODO: handle <original-package>
+ if (manifestPackage.equals(info.packageName)) {
+ offset = extractLine(buffer, offset, str);
+ version = Integer.parseInt(str[0]); // app version
+ offset = extractLine(buffer, offset, str);
+ int platformVersion = Integer.parseInt(str[0]);
+ offset = extractLine(buffer, offset, str);
+ info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
+ offset = extractLine(buffer, offset, str);
+ boolean hasApk = str[0].equals("1");
+ offset = extractLine(buffer, offset, str);
+ int numSigs = Integer.parseInt(str[0]);
+ Signature[] sigs = null;
+ if (numSigs > 0) {
+ sigs = new Signature[numSigs];
+ for (int i = 0; i < numSigs; i++) {
+ offset = extractLine(buffer, offset, str);
+ sigs[i] = new Signature(str[0]);
+ }
+
+ // Okay, got the manifest info we need...
+ try {
+ // Verify signatures against any installed version; if they
+ // don't match, then we fall though and ignore the data. The
+ // signatureMatch() method explicitly ignores the signature
+ // check for packages installed on the system partition, because
+ // such packages are signed with the platform cert instead of
+ // the app developer's cert, so they're different on every
+ // device.
+ PackageInfo pkgInfo = mPackageManager.getPackageInfo(
+ info.packageName, PackageManager.GET_SIGNATURES);
+ if (signaturesMatch(sigs, pkgInfo)) {
+ if (pkgInfo.versionCode >= version) {
+ Slog.i(TAG, "Sig + version match; taking data");
+ policy = RestorePolicy.ACCEPT;
+ } else {
+ // The data is from a newer version of the app than
+ // is presently installed. That means we can only
+ // use it if the matching apk is also supplied.
+ Slog.d(TAG, "Data version " + version
+ + " is newer than installed version "
+ + pkgInfo.versionCode + " - requiring apk");
+ policy = RestorePolicy.ACCEPT_IF_APK;
+ }
+ }
+ } catch (NameNotFoundException e) {
+ // Okay, the target app isn't installed. We can process
+ // the restore properly only if the dataset provides the
+ // apk file and we can successfully install it.
+ if (DEBUG) Slog.i(TAG, "Package " + info.packageName
+ + " not installed; requiring apk in dataset");
+ policy = RestorePolicy.ACCEPT_IF_APK;
+ }
+
+ if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
+ Slog.i(TAG, "Cannot restore package " + info.packageName
+ + " without the matching .apk");
+ }
+ } else {
+ Slog.i(TAG, "Missing signature on backed-up package "
+ + info.packageName);
+ }
+ } else {
+ Slog.i(TAG, "Expected package " + info.packageName
+ + " but restore manifest claims " + manifestPackage);
+ }
+ } else {
+ Slog.i(TAG, "Unknown restore manifest version " + version
+ + " for package " + info.packageName);
+ }
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
+ }
+
+ return policy;
+ }
+
+ // Builds a line from a byte buffer starting at 'offset', and returns
+ // the index of the next unconsumed data in the buffer.
+ int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
+ final int end = buffer.length;
+ if (offset >= end) throw new IOException("Incomplete data");
+
+ int pos;
+ for (pos = offset; pos < end; pos++) {
+ byte c = buffer[pos];
+ // at LF we declare end of line, and return the next char as the
+ // starting point for the next time through
+ if (c == '\n') {
+ break;
+ }
+ }
+ outStr[0] = new String(buffer, offset, pos - offset);
+ pos++; // may be pointing an extra byte past the end but that's okay
+ return pos;
+ }
+
+ void dumpFileMetadata(FileMetadata info) {
+ if (DEBUG) {
+ StringBuilder b = new StringBuilder(128);
+
+ // mode string
+ b.append((info.type == FullBackup.TYPE_DIRECTORY) ? 'd' : '-');
+ b.append(((info.mode & 0400) != 0) ? 'r' : '-');
+ b.append(((info.mode & 0200) != 0) ? 'w' : '-');
+ b.append(((info.mode & 0100) != 0) ? 'x' : '-');
+ b.append(((info.mode & 0040) != 0) ? 'r' : '-');
+ b.append(((info.mode & 0020) != 0) ? 'w' : '-');
+ b.append(((info.mode & 0010) != 0) ? 'x' : '-');
+ b.append(((info.mode & 0004) != 0) ? 'r' : '-');
+ b.append(((info.mode & 0002) != 0) ? 'w' : '-');
+ b.append(((info.mode & 0001) != 0) ? 'x' : '-');
+ b.append(String.format(" %9d ", info.size));
+
+ Date stamp = new Date(info.mtime);
+ b.append(new SimpleDateFormat("MMM dd kk:mm:ss ").format(stamp));
+
+ b.append(info.packageName);
+ b.append(" :: ");
+ b.append(info.domain);
+ b.append(" :: ");
+ b.append(info.path);
+
+ Slog.i(TAG, b.toString());
+ }
+ }
+ // Consume a tar file header block [sequence] and accumulate the relevant metadata
+ FileMetadata readTarHeaders(InputStream instream) throws IOException {
+ byte[] block = new byte[512];
+ FileMetadata info = null;
+
+ boolean gotHeader = readTarHeader(instream, block);
+ if (gotHeader) {
+ // okay, presume we're okay, and extract the various metadata
+ info = new FileMetadata();
+ info.size = extractRadix(block, 124, 12, 8);
+ info.mtime = extractRadix(block, 136, 12, 8);
+ info.mode = extractRadix(block, 100, 8, 8);
+
+ info.path = extractString(block, 345, 155); // prefix
+ String path = extractString(block, 0, 100);
+ if (path.length() > 0) {
+ if (info.path.length() > 0) info.path += '/';
+ info.path += path;
+ }
+
+ // tar link indicator field: 1 byte at offset 156 in the header.
+ int typeChar = block[156];
+ if (typeChar == 'x') {
+ // pax extended header, so we need to read that
+ gotHeader = readPaxExtendedHeader(instream, info);
+ if (gotHeader) {
+ // and after a pax extended header comes another real header -- read
+ // that to find the real file type
+ gotHeader = readTarHeader(instream, block);
+ }
+ if (!gotHeader) throw new IOException("Bad or missing pax header");
+
+ typeChar = block[156];
+ }
+
+ switch (typeChar) {
+ case '0': info.type = FullBackup.TYPE_FILE; break;
+ case '5': info.type = FullBackup.TYPE_DIRECTORY; break;
+ case 0: {
+ // presume EOF
+ return null;
+ }
+ default: {
+ Slog.e(TAG, "Unknown tar entity type: " + typeChar);
+ throw new IOException("Unknown entity type " + typeChar);
+ }
+ }
+
+ // Parse out the path
+ //
+ // first: apps/shared/unrecognized
+ if (FullBackup.SHARED_PREFIX.regionMatches(0,
+ info.path, 0, FullBackup.SHARED_PREFIX.length())) {
+ // File in shared storage. !!! TODO: implement this.
+ info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
+ info.domain = FullBackup.SHARED_STORAGE_TOKEN;
+ } else if (FullBackup.APPS_PREFIX.regionMatches(0,
+ info.path, 0, FullBackup.APPS_PREFIX.length())) {
+ // App content! Parse out the package name and domain
+
+ // strip the apps/ prefix
+ info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
+
+ // extract the package name
+ int slash = info.path.indexOf('/');
+ if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
+ info.packageName = info.path.substring(0, slash);
+ info.path = info.path.substring(slash+1);
+
+ // if it's a manifest we're done, otherwise parse out the domains
+ if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) {
+ slash = info.path.indexOf('/');
+ if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
+ info.domain = info.path.substring(0, slash);
+ // validate that it's one of the domains we understand
+ if (!info.domain.equals(FullBackup.APK_TREE_TOKEN)
+ && !info.domain.equals(FullBackup.DATA_TREE_TOKEN)
+ && !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN)
+ && !info.domain.equals(FullBackup.ROOT_TREE_TOKEN)
+ && !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)
+ && !info.domain.equals(FullBackup.OBB_TREE_TOKEN)
+ && !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
+ throw new IOException("Unrecognized domain " + info.domain);
+ }
+
+ info.path = info.path.substring(slash + 1);
+ }
+ }
+ }
+ return info;
+ }
+
+ boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
+ int nRead = instream.read(block, 0, 512);
+ if (nRead > 0 && nRead != 512) {
+ // if we read only a partial block, then things are
+ // clearly screwed up. terminate the restore.
+ throw new IOException("Partial header block: " + nRead);
+ }
+ return (nRead > 0);
+ }
+
+ // overwrites 'info' fields based on the pax extended header
+ boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
+ throws IOException {
+ // We should never see a pax extended header larger than this
+ if (info.size > 32*1024) {
+ Slog.w(TAG, "Suspiciously large pax header size " + info.size
+ + " - aborting");
+ throw new IOException("Sanity failure: pax header size " + info.size);
+ }
+
+ // read whole blocks, not just the content size
+ int numBlocks = (int)((info.size + 511) >> 9);
+ byte[] data = new byte[numBlocks * 512];
+ int nRead = instream.read(data);
+ if (nRead != data.length) {
+ return false;
+ }
+
+ final int contentSize = (int) info.size;
+ int offset = 0;
+ do {
+ // extract the line at 'offset'
+ int eol = offset+1;
+ while (eol < contentSize && data[eol] != ' ') eol++;
+ if (eol >= contentSize) {
+ // error: we just hit EOD looking for the end of the size field
+ throw new IOException("Invalid pax data");
+ }
+ // eol points to the space between the count and the key
+ int linelen = (int) extractRadix(data, offset, eol - offset, 10);
+ int key = eol + 1; // start of key=value
+ eol = offset + linelen - 1; // trailing LF
+ int value;
+ for (value = key+1; data[value] != '=' && value <= eol; value++);
+ if (value > eol) {
+ throw new IOException("Invalid pax declaration");
+ }
+
+ // pax requires that key/value strings be in UTF-8
+ String keyStr = new String(data, key, value-key, "UTF-8");
+ // -1 to strip the trailing LF
+ String valStr = new String(data, value+1, eol-value-1, "UTF-8");
+
+ if ("path".equals(keyStr)) {
+ info.path = valStr;
+ } else if ("size".equals(keyStr)) {
+ info.size = Long.parseLong(valStr);
+ } else {
+ if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
+ }
+
+ offset += linelen;
+ } while (offset < contentSize);
+
+ return true;
+ }
+
+ long extractRadix(byte[] data, int offset, int maxChars, int radix)
+ throws IOException {
+ long value = 0;
+ final int end = offset + maxChars;
+ for (int i = offset; i < end; i++) {
+ final byte b = data[i];
+ if (b == 0 || b == ' ') break;
+ if (b < '0' || b > ('0' + radix - 1)) {
+ throw new IOException("Invalid number in header");
+ }
+ value = radix * value + (b - '0');
+ }
+ return value;
+ }
+
+ String extractString(byte[] data, int offset, int maxChars) throws IOException {
+ final int end = offset + maxChars;
+ int eos = offset;
+ // tar string fields can end with either NUL or SPC
+ while (eos < end && data[eos] != 0 && data[eos] != ' ') eos++;
+ return new String(data, offset, eos-offset, "US-ASCII");
+ }
+
+ void sendStartRestore() {
+ if (mObserver != null) {
+ try {
+ mObserver.onStartRestore();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full restore observer went away: startRestore");
+ mObserver = null;
+ }
+ }
+ }
+
+ void sendOnRestorePackage(String name) {
+ if (mObserver != null) {
+ try {
+ // TODO: use a more user-friendly name string
+ mObserver.onRestorePackage(name);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full restore observer went away: restorePackage");
+ mObserver = null;
+ }
+ }
+ }
+
+ void sendEndRestore() {
+ if (mObserver != null) {
+ try {
+ mObserver.onEndRestore();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full restore observer went away: endRestore");
+ mObserver = null;
+ }
+ }
+ }
+ }
+
// ----- Restore handling -----
private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
@@ -1893,7 +3113,7 @@ class BackupManagerService extends IBackupManager.Stub {
ParcelFileDescriptor backupData = null;
ParcelFileDescriptor newState = null;
- int token = mTokenGenerator.nextInt();
+ final int token = generateToken();
try {
// Run the transport's restore pass
backupData = ParcelFileDescriptor.open(backupDataName,
@@ -1957,7 +3177,9 @@ class BackupManagerService extends IBackupManager.Stub {
try { if (backupData != null) backupData.close(); } catch (IOException e) {}
try { if (newState != null) newState.close(); } catch (IOException e) {}
backupData = newState = null;
- mCurrentOperations.delete(token);
+ synchronized (mCurrentOperations) {
+ mCurrentOperations.delete(token);
+ }
// If we know a priori that we'll need to perform a full post-restore backup
// pass, clear the new state file data. This means we're discarding work that
@@ -2092,7 +3314,7 @@ class BackupManagerService extends IBackupManager.Stub {
if (app.packageName.equals(packageName)) {
// Add the caller to the set of pending backups. If there is
// one already there, then overwrite it, but no harm done.
- BackupRequest req = new BackupRequest(app, false);
+ BackupRequest req = new BackupRequest(app);
if (mPendingBackups.put(app.packageName, req) == null) {
// Journal this request in case of crash. The put()
// operation returned null when this package was not already
@@ -2239,10 +3461,196 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ // Run a *full* backup pass for the given package, writing the resulting data stream
+ // to the supplied file descriptor. This method is synchronous and does not return
+ // to the caller until the backup has been completed.
+ public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
+ boolean doAllApps, String[] pkgList) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+
+ // Validate
+ if (!doAllApps) {
+ if (!includeShared) {
+ // If we're backing up shared data (sdcard or equivalent), then we can run
+ // without any supplied app names. Otherwise, we'd be doing no work, so
+ // report the error.
+ if (pkgList == null || pkgList.length == 0) {
+ throw new IllegalArgumentException(
+ "Backup requested but neither shared nor any apps named");
+ }
+ }
+ }
+
+ if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
+ + " shared=" + includeShared + " all=" + doAllApps
+ + " pkgs=" + pkgList);
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
+ doAllApps, pkgList);
+ final int token = generateToken();
+ synchronized (mFullConfirmations) {
+ mFullConfirmations.put(token, params);
+ }
+
+ // start up the confirmation UI
+ if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
+ if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
+ Slog.e(TAG, "Unable to launch full backup confirmation");
+ mFullConfirmations.delete(token);
+ return;
+ }
+
+ // make sure the screen is lit for the user interaction
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+
+ // start the confirmation countdown
+ startConfirmationTimeout(token, params);
+
+ // wait for the backup to be performed
+ if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
+ waitForCompletion(params);
+ } finally {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ // just eat it
+ }
+ Binder.restoreCallingIdentity(oldId);
+ }
+ if (DEBUG) Slog.d(TAG, "Full backup done; returning to caller");
+ }
+
+ public void fullRestore(ParcelFileDescriptor fd) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+ Slog.i(TAG, "Beginning full restore...");
+
+ long oldId = Binder.clearCallingIdentity();
+
+ try {
+ FullRestoreParams params = new FullRestoreParams(fd);
+ final int token = generateToken();
+ synchronized (mFullConfirmations) {
+ mFullConfirmations.put(token, params);
+ }
+
+ // start up the confirmation UI
+ if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
+ if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
+ Slog.e(TAG, "Unable to launch full restore confirmation");
+ mFullConfirmations.delete(token);
+ return;
+ }
+
+ // make sure the screen is lit for the user interaction
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+
+ // start the confirmation countdown
+ startConfirmationTimeout(token, params);
+
+ // wait for the restore to be performed
+ if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
+ waitForCompletion(params);
+ } finally {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Error trying to close fd after full restore: " + e);
+ }
+ Binder.restoreCallingIdentity(oldId);
+ Slog.i(TAG, "Full restore completed");
+ }
+ }
+
+ boolean startConfirmationUi(int token, String action) {
+ try {
+ Intent confIntent = new Intent(action);
+ confIntent.setClassName("com.android.backupconfirm",
+ "com.android.backupconfirm.BackupRestoreConfirmation");
+ confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
+ confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(confIntent);
+ } catch (ActivityNotFoundException e) {
+ return false;
+ }
+ return true;
+ }
+
+ void startConfirmationTimeout(int token, FullParams params) {
+ if (DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
+ + TIMEOUT_FULL_CONFIRMATION + " millis");
+ Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
+ token, 0, params);
+ mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
+ }
+
+ void waitForCompletion(FullParams params) {
+ synchronized (params.latch) {
+ while (params.latch.get() == false) {
+ try {
+ params.latch.wait();
+ } catch (InterruptedException e) { /* never interrupted */ }
+ }
+ }
+ }
+
+ void signalFullBackupRestoreCompletion(FullParams params) {
+ synchronized (params.latch) {
+ params.latch.set(true);
+ params.latch.notifyAll();
+ }
+ }
+
+ // Confirm that the previously-requested full backup/restore operation can proceed. This
+ // is used to require a user-facing disclosure about the operation.
+ public void acknowledgeFullBackupOrRestore(int token, boolean allow,
+ IFullBackupRestoreObserver observer) {
+ if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
+ + " allow=" + allow);
+
+ // TODO: possibly require not just this signature-only permission, but even
+ // require that the specific designated confirmation-UI app uid is the caller?
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+
+ FullParams params;
+ synchronized (mFullConfirmations) {
+ params = mFullConfirmations.get(token);
+ if (params != null) {
+ mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
+ mFullConfirmations.delete(token);
+
+ if (allow) {
+ params.observer = observer;
+ final int verb = params instanceof FullBackupParams
+ ? MSG_RUN_FULL_BACKUP
+ : MSG_RUN_FULL_RESTORE;
+
+ if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(verb, params);
+ mBackupHandler.sendMessage(msg);
+ } else {
+ Slog.w(TAG, "User rejected full backup/restore operation");
+ // indicate completion without having actually transferred any data
+ signalFullBackupRestoreCompletion(params);
+ }
+ } else {
+ Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
// Enable/disable the backup service
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupEnabled");
+ "setBackupEnabled");
Slog.i(TAG, "Backup enabled => " + enable);
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 47599c8..1aff9a2 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -390,7 +390,7 @@ class BatteryService extends Binder {
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
- if (true) {
+ if (false) {
Slog.d(TAG, "level:" + mBatteryLevel +
" scale:" + BATTERY_SCALE + " status:" + mBatteryStatus +
" health:" + mBatteryHealth + " present:" + mBatteryPresent +
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 55ce80b..dd76eb8 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,6 +16,11 @@
package com.android.server;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+
import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.ContentResolver;
import android.content.Context;
@@ -26,11 +31,13 @@ import android.net.ConnectivityManager;
import android.net.DummyDataStateTracker;
import android.net.EthernetDataTracker;
import android.net.IConnectivityManager;
-import android.net.LinkAddress;
+import android.net.INetworkPolicyListener;
+import android.net.INetworkPolicyManager;
import android.net.LinkProperties;
import android.net.MobileDataStateTracker;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
import android.net.NetworkStateTracker;
import android.net.NetworkUtils;
import android.net.Proxy;
@@ -39,6 +46,7 @@ import android.net.RouteInfo;
import android.net.vpn.VpnManager;
import android.net.wifi.WifiStateTracker;
import android.os.Binder;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -53,22 +61,21 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
+import android.util.SparseIntArray;
import com.android.internal.telephony.Phone;
import com.android.server.connectivity.Tethering;
import java.io.FileDescriptor;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
-import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.GregorianCalendar;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* @hide
@@ -78,6 +85,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final boolean DBG = true;
private static final String TAG = "ConnectivityService";
+ private static final boolean LOGD_RULES = false;
+
// how long to wait before switching back to a radio's default network
private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
// system property that can override the above value
@@ -91,6 +100,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private Tethering mTethering;
private boolean mTetheringConfigValid = false;
+ /** Currently active network rules by UID. */
+ private SparseIntArray mUidRules = new SparseIntArray();
+
/**
* Sometimes we want to refer to the individual network state
* trackers separately, and sometimes we just want to treat them
@@ -128,6 +140,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private AtomicBoolean mBackgroundDataEnabled = new AtomicBoolean(true);
private INetworkManagementService mNetd;
+ private INetworkPolicyManager mPolicyManager;
private static final int ENABLED = 1;
private static final int DISABLED = 0;
@@ -250,14 +263,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
RadioAttributes[] mRadioAttributes;
- public static synchronized ConnectivityService getInstance(Context context) {
- if (sServiceInstance == null) {
- sServiceInstance = new ConnectivityService(context);
- }
- return sServiceInstance;
- }
-
- private ConnectivityService(Context context) {
+ public ConnectivityService(
+ Context context, INetworkManagementService netd, INetworkPolicyManager policyManager) {
if (DBG) log("ConnectivityService starting up");
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
@@ -290,9 +297,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loge("Error setting defaultDns using " + dns);
}
- mContext = context;
+ mContext = checkNotNull(context, "missing Context");
+ mNetd = checkNotNull(netd, "missing INetworkManagementService");
+ mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
- PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ try {
+ mPolicyManager.registerListener(mPolicyListener);
+ } catch (RemoteException e) {
+ // ouch, no rules updates means some processes may never get network
+ Slog.e(TAG, "unable to register INetworkPolicyListener", e);
+ }
+
+ final PowerManager powerManager = (PowerManager) context.getSystemService(
+ Context.POWER_SERVICE);
mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
com.android.internal.R.integer.config_networkTransitionTimeout);
@@ -430,7 +447,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- mTethering = new Tethering(mContext, mHandler.getLooper());
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
+
+ mTethering = new Tethering(mContext, nmService, mHandler.getLooper());
mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) ||
!mTethering.isDunRequired()) &&
(mTethering.getTetherableUsbRegexs().length != 0 ||
@@ -533,32 +553,92 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
/**
+ * Check if UID is blocked from using the given {@link NetworkInfo}.
+ */
+ private boolean isNetworkBlocked(NetworkInfo info, int uid) {
+ synchronized (mUidRules) {
+ return isNetworkBlockedLocked(info, uid);
+ }
+ }
+
+ /**
+ * Check if UID is blocked from using the given {@link NetworkInfo}.
+ */
+ private boolean isNetworkBlockedLocked(NetworkInfo info, int uid) {
+ // TODO: expand definition of "paid" network to cover tethered or paid
+ // hotspot use cases.
+ final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
+ final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+
+ if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
+ return true;
+ }
+
+ // no restrictive rules; network is visible
+ return false;
+ }
+
+ /**
* Return NetworkInfo for the active (i.e., connected) network interface.
* It is assumed that at most one network is active at a time. If more
* than one is active, it is indeterminate which will be returned.
* @return the info for the active network, or {@code null} if none is
* active
*/
+ @Override
public NetworkInfo getActiveNetworkInfo() {
- return getNetworkInfo(mActiveDefaultNetwork);
+ enforceAccessPermission();
+ final int uid = Binder.getCallingUid();
+ return getNetworkInfo(mActiveDefaultNetwork, uid);
+ }
+
+ @Override
+ public NetworkInfo getActiveNetworkInfoForUid(int uid) {
+ enforceConnectivityInternalPermission();
+ return getNetworkInfo(mActiveDefaultNetwork, uid);
}
+ @Override
public NetworkInfo getNetworkInfo(int networkType) {
enforceAccessPermission();
- if (ConnectivityManager.isNetworkTypeValid(networkType)) {
- NetworkStateTracker t = mNetTrackers[networkType];
- if (t != null)
- return t.getNetworkInfo();
+ final int uid = Binder.getCallingUid();
+ return getNetworkInfo(networkType, uid);
+ }
+
+ private NetworkInfo getNetworkInfo(int networkType, int uid) {
+ NetworkInfo info = null;
+ if (isNetworkTypeValid(networkType)) {
+ final NetworkStateTracker tracker = mNetTrackers[networkType];
+ if (tracker != null) {
+ info = tracker.getNetworkInfo();
+ if (isNetworkBlocked(info, uid)) {
+ // network is blocked; clone and override state
+ info = new NetworkInfo(info);
+ info.setDetailedState(DetailedState.BLOCKED, null, null);
+ }
+ }
}
- return null;
+ return info;
}
+ @Override
public NetworkInfo[] getAllNetworkInfo() {
enforceAccessPermission();
- NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
+ final int uid = Binder.getCallingUid();
+ final NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
int i = 0;
- for (NetworkStateTracker t : mNetTrackers) {
- if(t != null) result[i++] = t.getNetworkInfo();
+ synchronized (mUidRules) {
+ for (NetworkStateTracker tracker : mNetTrackers) {
+ if (tracker != null) {
+ NetworkInfo info = tracker.getNetworkInfo();
+ if (isNetworkBlockedLocked(info, uid)) {
+ // network is blocked; clone and override state
+ info = new NetworkInfo(info);
+ info.setDetailedState(DetailedState.BLOCKED, null, null);
+ }
+ result[i++] = info;
+ }
+ }
}
return result;
}
@@ -571,15 +651,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* @return the ip properties for the active network, or {@code null} if
* none is active
*/
+ @Override
public LinkProperties getActiveLinkProperties() {
return getLinkProperties(mActiveDefaultNetwork);
}
+ @Override
public LinkProperties getLinkProperties(int networkType) {
enforceAccessPermission();
- if (ConnectivityManager.isNetworkTypeValid(networkType)) {
- NetworkStateTracker t = mNetTrackers[networkType];
- if (t != null) return t.getLinkProperties();
+ if (isNetworkTypeValid(networkType)) {
+ final NetworkStateTracker tracker = mNetTrackers[networkType];
+ if (tracker != null) {
+ return tracker.getLinkProperties();
+ }
}
return null;
}
@@ -1024,6 +1108,30 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
+ private INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
+ @Override
+ public void onRulesChanged(int uid, int uidRules) {
+ // only someone like NPMS should only be calling us
+ // TODO: create permission for modifying data policy
+ mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+ if (LOGD_RULES) {
+ Slog.d(TAG, "onRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")");
+ }
+
+ synchronized (mUidRules) {
+ // skip update when we've already applied rules
+ final int oldRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+ if (oldRules == uidRules) return;
+
+ mUidRules.put(uid, uidRules);
+ }
+
+ // TODO: dispatch into NMS to push rules towards kernel module
+ // TODO: notify UID when it has requested targeted updates
+ }
+ };
+
/**
* @see ConnectivityManager#setMobileDataEnabled(boolean)
*/
@@ -1281,9 +1389,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
void systemReady() {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- mNetd = INetworkManagementService.Stub.asInterface(b);
-
synchronized(this) {
mSystemReady = true;
if (mInitialBroadcast != null) {
@@ -1378,13 +1483,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
addDefaultRoute(mNetTrackers[netType]);
} else {
// many radios add a default route even when we don't want one.
- // remove the default interface unless we need it for our active network
+ // remove the default route unless we need it for our active network
if (mActiveDefaultNetwork != -1) {
- LinkProperties linkProperties =
+ LinkProperties defaultLinkProperties =
mNetTrackers[mActiveDefaultNetwork].getLinkProperties();
LinkProperties newLinkProperties =
mNetTrackers[netType].getLinkProperties();
- String defaultIface = linkProperties.getInterfaceName();
+ String defaultIface = defaultLinkProperties.getInterfaceName();
if (defaultIface != null &&
!defaultIface.equals(newLinkProperties.getInterfaceName())) {
removeDefaultRoute(mNetTrackers[netType]);
@@ -1549,12 +1654,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (values.length == 6) {
final String prefix = "/sys/kernel/ipv4/tcp_";
- stringToFile(prefix + "rmem_min", values[0]);
- stringToFile(prefix + "rmem_def", values[1]);
- stringToFile(prefix + "rmem_max", values[2]);
- stringToFile(prefix + "wmem_min", values[3]);
- stringToFile(prefix + "wmem_def", values[4]);
- stringToFile(prefix + "wmem_max", values[5]);
+ FileUtils.stringToFile(prefix + "rmem_min", values[0]);
+ FileUtils.stringToFile(prefix + "rmem_def", values[1]);
+ FileUtils.stringToFile(prefix + "rmem_max", values[2]);
+ FileUtils.stringToFile(prefix + "wmem_min", values[3]);
+ FileUtils.stringToFile(prefix + "wmem_def", values[4]);
+ FileUtils.stringToFile(prefix + "wmem_max", values[5]);
} else {
loge("Invalid buffersize string: " + bufferSizes);
}
@@ -1563,23 +1668,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- /**
- * Writes string to file. Basically same as "echo -n $string > $filename"
- *
- * @param filename
- * @param string
- * @throws IOException
- */
- private void stringToFile(String filename, String string) throws IOException {
- FileWriter out = new FileWriter(filename);
- try {
- out.write(string);
- } finally {
- out.close();
- }
- }
-
-
/**
* Adjust the per-process dns entries (net.dns<x>.<pid>) based
* on the highest priority active net which this process requested.
@@ -2269,4 +2357,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
return networkType;
}
+
+ private static <T> T checkNotNull(T value, String message) {
+ if (value == null) {
+ throw new NullPointerException(message);
+ }
+ return value;
+ }
}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 3bd8215..3181a9d 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -1925,9 +1925,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Check for permissions if a particular caller is specified
if (who != null) {
// When checking for a single caller, status is based on caller's request
- ActiveAdmin ap = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_ENCRYPTED_STORAGE);
- return ap.encryptionRequested;
+ ActiveAdmin ap = getActiveAdminUncheckedLocked(who);
+ return ap != null ? ap.encryptionRequested : false;
}
// If no particular caller is specified, return the aggregate set of requests.
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 0fba7c3..d34087f 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -34,7 +34,6 @@ import android.os.StatFs;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.util.Config;
import android.util.EventLog;
import android.util.Slog;
@@ -56,10 +55,10 @@ import android.util.Slog;
* settings parameter with a default value of 2MB), the free memory is
* logged to the event log.
*/
-class DeviceStorageMonitorService extends Binder {
+public class DeviceStorageMonitorService extends Binder {
private static final String TAG = "DeviceStorageMonitorService";
private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+ private static final boolean localLOGV = false;
private static final int DEVICE_MEMORY_WHAT = 1;
private static final int MONITOR_INTERVAL = 1; //in minutes
private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
@@ -99,7 +98,7 @@ class DeviceStorageMonitorService extends Binder {
/**
* This string is used for ServiceManager access to this class.
*/
- static final String SERVICE = "devicestoragemonitor";
+ public static final String SERVICE = "devicestoragemonitor";
/**
* Handler that checks the amount of disk space on the device and sends a
@@ -398,4 +397,24 @@ class DeviceStorageMonitorService extends Binder {
// force an early check
postCheckMemoryMsg(true, 0);
}
+
+ /**
+ * Callable from other things in the system service to obtain the low memory
+ * threshold.
+ *
+ * @return low memory threshold in bytes
+ */
+ public long getMemoryLowThreshold() {
+ return mMemLowThreshold;
+ }
+
+ /**
+ * Callable from other things in the system process to check whether memory
+ * is low.
+ *
+ * @return true is memory is low
+ */
+ public boolean isMemoryLow() {
+ return mLowMemFlag;
+ }
}
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java
index 0f1fc78..788a2f5 100644
--- a/services/java/com/android/server/EntropyService.java
+++ b/services/java/com/android/server/EntropyService.java
@@ -96,7 +96,7 @@ public class EntropyService extends Binder {
private void loadInitialEntropy() {
try {
- RandomBlock.fromFile(entropyFile).toFile(randomDevice);
+ RandomBlock.fromFile(entropyFile).toFile(randomDevice, false);
} catch (IOException e) {
Slog.w(TAG, "unable to load initial entropy (first boot?)", e);
}
@@ -104,7 +104,7 @@ public class EntropyService extends Binder {
private void writeEntropy() {
try {
- RandomBlock.fromFile(randomDevice).toFile(entropyFile);
+ RandomBlock.fromFile(randomDevice).toFile(entropyFile, true);
} catch (IOException e) {
Slog.w(TAG, "unable to write entropy", e);
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 7a0dc3e..f4308cd 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -64,7 +64,9 @@ import android.provider.Settings;
import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
+import android.text.style.SuggestionSpan;
import android.util.EventLog;
+import android.util.LruCache;
import android.util.Pair;
import android.util.Slog;
import android.util.PrintWriterPrinter;
@@ -117,15 +119,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final long TIME_TO_RECONNECT = 10*1000;
+ static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
+
private static final int NOT_A_SUBTYPE_ID = -1;
private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
private static final String SUBTYPE_MODE_VOICE = "voice";
- // TODO: Will formalize this value as API
- private static final String SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME =
- "excludeFromLastInputMethod";
-
final Context mContext;
final Resources mRes;
final Handler mHandler;
@@ -141,6 +141,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// lock for this class.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<InputMethodInfo>();
final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<String, InputMethodInfo>();
+ private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
+ new LruCache<SuggestionSpan, InputMethodInfo>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
class SessionState {
final ClientState client;
@@ -965,6 +967,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
public void updateStatusIcon(IBinder token, String packageName, int iconId) {
int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
@@ -989,6 +992,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
@@ -1008,6 +1012,42 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
+ synchronized (mMethodMap) {
+ final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
+ for (int i = 0; i < spans.length; ++i) {
+ SuggestionSpan ss = spans[i];
+ if (!TextUtils.isEmpty(ss.getNotificationTargetClassName())) {
+ mSecureSuggestionSpans.put(ss, currentImi);
+ final InputMethodInfo targetImi = mSecureSuggestionSpans.get(ss);
+ }
+ }
+ }
+ }
+
+ public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
+ synchronized (mMethodMap) {
+ final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span);
+ // TODO: Do not send the intent if the process of the targetImi is already dead.
+ if (targetImi != null) {
+ final String[] suggestions = span.getSuggestions();
+ if (index < 0 || index >= suggestions.length) return false;
+ final String className = span.getNotificationTargetClassName();
+ final Intent intent = new Intent();
+ // Ensures that only a class in the original IME package will receive the
+ // notification.
+ intent.setClassName(targetImi.getPackageName(), className);
+ intent.setAction(SuggestionSpan.ACTION_SUGGESTION_PICKED);
+ intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, originalString);
+ intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, suggestions[index]);
+ intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, span.hashCode());
+ mContext.sendBroadcast(intent);
+ return true;
+ }
+ }
+ return false;
+ }
+
void updateFromSettingsLocked() {
// We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
// ENABLED_INPUT_METHODS is taking care of keeping them correctly in
@@ -1102,6 +1142,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
public boolean showSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) {
int uid = Binder.getCallingUid();
@@ -1167,6 +1208,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return res;
}
+ @Override
public boolean hideSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) {
int uid = Binder.getCallingUid();
@@ -1229,6 +1271,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return res;
}
+ @Override
public void windowGainedFocus(IInputMethodClient client, IBinder windowToken,
boolean viewHasFocus, boolean isTextEditor, int softInputMode,
boolean first, int windowFlags) {
@@ -1330,6 +1373,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
public void showInputMethodPickerFromClient(IInputMethodClient client) {
synchronized (mMethodMap) {
if (mCurClient == null || client == null
@@ -1344,10 +1388,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
public void setInputMethod(IBinder token, String id) {
setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID);
}
+ @Override
public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
synchronized (mMethodMap) {
if (subtype != null) {
@@ -1359,6 +1405,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
public void showInputMethodAndSubtypeEnablerFromClient(
IInputMethodClient client, String inputMethodId) {
synchronized (mMethodMap) {
@@ -1438,6 +1485,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ public InputMethodSubtype getLastInputMethodSubtype() {
+ synchronized (mMethodMap) {
+ final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
+ // TODO: Handle the case of the last IME with no subtypes
+ if (lastIme == null || TextUtils.isEmpty(lastIme.first)
+ || TextUtils.isEmpty(lastIme.second)) return null;
+ final InputMethodInfo lastImi = mMethodMap.get(lastIme.first);
+ if (lastImi == null) return null;
+ try {
+ final int lastSubtypeHash = Integer.valueOf(lastIme.second);
+ return lastImi.getSubtypeAt(getSubtypeIdFromHashCode(
+ lastImi, lastSubtypeHash));
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+ }
+
private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
synchronized (mMethodMap) {
if (token == null) {
@@ -1463,6 +1528,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
public void hideMySoftInput(IBinder token, int flags) {
synchronized (mMethodMap) {
if (token == null || mCurToken != token) {
@@ -1479,6 +1545,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
public void showMySoftInput(IBinder token, int flags) {
synchronized (mMethodMap) {
if (token == null || mCurToken != token) {
@@ -1515,6 +1582,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
public boolean handleMessage(Message msg) {
HandlerCaller.SomeArgs args;
switch (msg.what) {
@@ -1811,13 +1879,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final int subtypeCount = imi.getSubtypeCount();
for (int j = 0; j < subtypeCount; ++j) {
InputMethodSubtype subtype = imi.getSubtypeAt(j);
- if (enabledSubtypeSet.contains(String.valueOf(subtype.hashCode()))) {
+ if (enabledSubtypeSet.contains(String.valueOf(subtype.hashCode()))
+ && !subtype.isAuxiliary()) {
final CharSequence title;
int nameResId = subtype.getNameResId();
String mode = subtype.getMode();
if (nameResId != 0) {
- title = TextUtils.concat(pm.getText(imi.getPackageName(),
- nameResId, imi.getServiceInfo().applicationInfo),
+ title = TextUtils.concat(subtype.getDisplayName(context,
+ imi.getPackageName(), imi.getServiceInfo().applicationInfo),
(TextUtils.isEmpty(label) ? "" : " (" + label + ")"));
} else {
CharSequence language = subtype.getLocale();
@@ -1858,6 +1927,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
hideInputMethodMenu();
}
@@ -1869,6 +1939,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mDialogBuilder = new AlertDialog.Builder(context)
.setTitle(com.android.internal.R.string.select_input_method)
.setOnCancelListener(new OnCancelListener() {
+ @Override
public void onCancel(DialogInterface dialog) {
hideInputMethodMenu();
}
@@ -1879,6 +1950,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mDialogBuilder.setSingleChoiceItems(mItems, checkedItem,
new AlertDialog.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
synchronized (mMethodMap) {
if (mIms == null || mIms.length <= which
@@ -1903,6 +1975,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mDialogBuilder.setPositiveButton(
com.android.internal.R.string.configure_input_methods,
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int whichButton) {
showConfigureInputMethods();
}
@@ -1938,6 +2011,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// ----------------------------------------------------------------------
+ @Override
public boolean setInputMethodEnabled(String id, boolean enabled) {
synchronized (mMethodMap) {
if (mContext.checkCallingOrSelfPermission(
@@ -2001,7 +2075,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private boolean canAddToLastInputMethod(InputMethodSubtype subtype) {
if (subtype == null) return true;
- return !subtype.containsExtraValueKey(SUBTYPE_EXTRAVALUE_EXCLUDE_FROM_LAST_IME);
+ return !subtype.isAuxiliary();
}
private void saveCurrentInputMethodAndSubtypeToHistory() {
@@ -2268,6 +2342,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
/**
* @return Return the current subtype of this input method.
*/
+ @Override
public InputMethodSubtype getCurrentInputMethodSubtype() {
boolean subtypeIsSelected = false;
try {
@@ -2350,6 +2425,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
synchronized (mMethodMap) {
if (subtype != null && mCurMethodId != null) {
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index b78389b..1d3e3ac 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -35,7 +35,6 @@ import android.util.Slog;
import android.util.LogPrinter;
import android.util.Printer;
-import android.util.Config;
import android.content.Intent;
import android.content.IntentFilter;
@@ -45,7 +44,7 @@ import android.content.IntentFilter;
public class IntentResolver<F extends IntentFilter, R extends Object> {
final private static String TAG = "IntentResolver";
final private static boolean DEBUG = false;
- final private static boolean localLOGV = DEBUG || Config.LOGV;
+ final private static boolean localLOGV = DEBUG || false;
public void addFilter(F f) {
if (localLOGV) {
diff --git a/services/java/com/android/server/LightsService.java b/services/java/com/android/server/LightsService.java
index 21f2bcf..1e95f3e 100644
--- a/services/java/com/android/server/LightsService.java
+++ b/services/java/com/android/server/LightsService.java
@@ -148,7 +148,6 @@ public class LightsService {
fis.close();
return (result != '0');
} catch (Exception e) {
- Slog.e(TAG, "getFlashlightEnabled failed", e);
return false;
}
}
@@ -168,7 +167,7 @@ public class LightsService {
fos.write(bytes);
fos.close();
} catch (Exception e) {
- Slog.e(TAG, "setFlashlightEnabled failed", e);
+ // fail silently
}
}
};
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 8570e6e..f78dca9 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -19,6 +19,7 @@ package com.android.server;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.util.XmlUtils;
import com.android.server.am.ActivityManagerService;
+import com.android.server.pm.PackageManagerService;
import android.Manifest;
import android.content.BroadcastReceiver;
@@ -98,6 +99,9 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
private static final String VOLD_TAG = "VoldConnector";
+ /** Maximum number of ASEC containers allowed to be mounted. */
+ private static final int MAX_CONTAINERS = 250;
+
/*
* Internal vold volume state constants
*/
@@ -524,7 +528,6 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
}
}
};
-
private final class MountServiceBinderListener implements IBinder.DeathRecipient {
final IMountServiceListener mListener;
@@ -1207,8 +1210,7 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC
* amount of containers we'd ever expect to have. This keeps an
* "asec list" from blocking a thread repeatedly.
*/
- mConnector = new NativeDaemonConnector(this, "vold",
- PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);
+ mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG);
mReady = false;
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
diff --git a/services/java/com/android/server/NetStatService.java b/services/java/com/android/server/NetStatService.java
deleted file mode 100644
index 7fe6743..0000000
--- a/services/java/com/android/server/NetStatService.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2008 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 android.content.Context;
-import android.net.TrafficStats;
-import android.os.INetStatService;
-import android.os.SystemClock;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-public class NetStatService extends INetStatService.Stub {
- private final Context mContext;
-
- public NetStatService(Context context) {
- mContext = context;
- }
-
- public long getMobileTxPackets() {
- return TrafficStats.getMobileTxPackets();
- }
-
- public long getMobileRxPackets() {
- return TrafficStats.getMobileRxPackets();
- }
-
- public long getMobileTxBytes() {
- return TrafficStats.getMobileTxBytes();
- }
-
- public long getMobileRxBytes() {
- return TrafficStats.getMobileRxBytes();
- }
-
- public long getTotalTxPackets() {
- return TrafficStats.getTotalTxPackets();
- }
-
- public long getTotalRxPackets() {
- return TrafficStats.getTotalRxPackets();
- }
-
- public long getTotalTxBytes() {
- return TrafficStats.getTotalTxBytes();
- }
-
- public long getTotalRxBytes() {
- return TrafficStats.getTotalRxBytes();
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- // This data is accessible to any app -- no permission check needed.
-
- pw.print("Elapsed: total=");
- pw.print(SystemClock.elapsedRealtime());
- pw.print("ms awake=");
- pw.print(SystemClock.uptimeMillis());
- pw.println("ms");
-
- pw.print("Mobile: Tx=");
- pw.print(getMobileTxBytes());
- pw.print("B/");
- pw.print(getMobileTxPackets());
- pw.print("Pkts Rx=");
- pw.print(getMobileRxBytes());
- pw.print("B/");
- pw.print(getMobileRxPackets());
- pw.println("Pkts");
-
- pw.print("Total: Tx=");
- pw.print(getTotalTxBytes());
- pw.print("B/");
- pw.print(getTotalTxPackets());
- pw.print("Pkts Rx=");
- pw.print(getTotalRxBytes());
- pw.print("B/");
- pw.print(getTotalRxPackets());
- pw.println("Pkts");
- }
-}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 0b4b958..8f179f5 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -16,59 +16,52 @@
package com.android.server;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
+import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
+import android.net.NetworkStats;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.os.Binder;
import android.os.INetworkManagementService;
-import android.os.Handler;
+import android.os.SystemClock;
import android.os.SystemProperties;
-import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
-import java.util.ArrayList;
-import java.util.NoSuchElementException;
-import java.util.StringTokenizer;
-import android.provider.Settings;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.InputStreamReader;
import java.io.IOException;
-import java.lang.IllegalStateException;
-import java.net.InetAddress;
+import java.io.InputStreamReader;
import java.net.Inet4Address;
-import java.net.UnknownHostException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
+import libcore.io.IoUtils;
+
/**
* @hide
*/
class NetworkManagementService extends INetworkManagementService.Stub {
-
- private static final String TAG = "NetworkManagmentService";
+ private static final String TAG = "NetworkManagementService";
private static final boolean DBG = false;
private static final String NETD_TAG = "NetdConnector";
private static final int ADD = 1;
private static final int REMOVE = 2;
+ /** Base path to UID-granularity network statistics. */
+ private static final File PATH_PROC_UID_STAT = new File("/proc/uid_stat");
+
class NetdResponseCode {
public static final int InterfaceListResult = 110;
public static final int TetherInterfaceListResult = 111;
@@ -883,12 +876,62 @@ class NetworkManagementService extends INetworkManagementService.Stub {
return -1;
}
- public long getInterfaceRxCounter(String iface) {
- return getInterfaceCounter(iface, true);
+ @Override
+ public NetworkStats getNetworkStatsSummary() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+ final String[] ifaces = listInterfaces();
+ final NetworkStats.Builder stats = new NetworkStats.Builder(
+ SystemClock.elapsedRealtime(), ifaces.length);
+
+ for (String iface : ifaces) {
+ final long rx = getInterfaceCounter(iface, true);
+ final long tx = getInterfaceCounter(iface, false);
+ stats.addEntry(iface, NetworkStats.UID_ALL, rx, tx);
+ }
+
+ return stats.build();
+ }
+
+ @Override
+ public NetworkStats getNetworkStatsDetail() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+ final String[] knownUids = PATH_PROC_UID_STAT.list();
+ final NetworkStats.Builder stats = new NetworkStats.Builder(
+ SystemClock.elapsedRealtime(), knownUids.length);
+
+ for (String uid : knownUids) {
+ final int uidInt = Integer.parseInt(uid);
+ collectNetworkStatsDetail(stats, uidInt);
+ }
+
+ return stats.build();
+ }
+
+ @Override
+ public NetworkStats getNetworkStatsUidDetail(int uid) {
+ if (Binder.getCallingUid() != uid) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+ }
+
+ final NetworkStats.Builder stats = new NetworkStats.Builder(
+ SystemClock.elapsedRealtime(), 1);
+ collectNetworkStatsDetail(stats, uid);
+ return stats.build();
}
- public long getInterfaceTxCounter(String iface) {
- return getInterfaceCounter(iface, false);
+ private void collectNetworkStatsDetail(NetworkStats.Builder stats, int uid) {
+ // TODO: kernel module will provide interface-level stats in future
+ // TODO: migrate these stats to come across netd in bulk, instead of all
+ // these individual file reads.
+ final File uidPath = new File(PATH_PROC_UID_STAT, Integer.toString(uid));
+ final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
+ final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
+ stats.addEntry(NetworkStats.IFACE_ALL, uid, rx, tx);
}
public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
@@ -949,4 +992,19 @@ class NetworkManagementService extends INetworkManagementService.Stub {
public int getInterfaceTxThrottle(String iface) {
return getInterfaceThrottle(iface, false);
}
+
+ /**
+ * Utility method to read a single plain-text {@link Long} from the given
+ * {@link File}, usually from a {@code /proc/} filesystem.
+ */
+ private static long readSingleLongFromFile(File file) {
+ try {
+ final byte[] buffer = IoUtils.readFileAsByteArray(file.toString());
+ return Long.parseLong(new String(buffer).trim());
+ } catch (NumberFormatException e) {
+ return -1;
+ } catch (IOException e) {
+ return -1;
+ }
+ }
}
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index 1a12a84..f693ed1 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -20,7 +20,6 @@ import static android.os.Process.*;
import android.os.Process;
import android.os.SystemClock;
-import android.util.Config;
import android.util.Slog;
import java.io.File;
@@ -35,7 +34,7 @@ import java.util.StringTokenizer;
public class ProcessStats {
private static final String TAG = "ProcessStats";
private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG || Config.LOGV;
+ private static final boolean localLOGV = DEBUG || false;
private static final int[] PROCESS_STATS_FORMAT = new int[] {
PROC_SPACE_TERM,
diff --git a/services/java/com/android/server/RandomBlock.java b/services/java/com/android/server/RandomBlock.java
index cc22bd9..e5d7301 100644
--- a/services/java/com/android/server/RandomBlock.java
+++ b/services/java/com/android/server/RandomBlock.java
@@ -62,11 +62,11 @@ class RandomBlock {
return retval;
}
- void toFile(String filename) throws IOException {
+ void toFile(String filename, boolean sync) throws IOException {
if (DEBUG) Slog.v(TAG, "writing to file " + filename);
RandomAccessFile out = null;
try {
- out = new RandomAccessFile(filename, "rws");
+ out = new RandomAccessFile(filename, sync ? "rws" : "rw");
toDataOut(out);
truncateIfPossible(out);
} finally {
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 8df8177..1d2072c 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -122,6 +122,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub
// ================================================================================
// From IStatusBarService
// ================================================================================
+ public void userActivity() {
+ if (mBar != null) try {
+ mBar.userActivity();
+ } catch (RemoteException ex) {}
+ }
public void expand() {
enforceExpandStatusBar();
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index 80b0174..99c8af6 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -20,6 +20,7 @@ package com.android.server;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupAgentHelper;
+import android.app.backup.FullBackup;
import android.app.backup.WallpaperBackupHelper;
import android.content.Context;
import android.os.ParcelFileDescriptor;
@@ -36,13 +37,29 @@ import java.io.IOException;
public class SystemBackupAgent extends BackupAgentHelper {
private static final String TAG = "SystemBackupAgent";
- // These paths must match what the WallpaperManagerService uses
- private static final String WALLPAPER_IMAGE = "/data/data/com.android.settings/files/wallpaper";
- private static final String WALLPAPER_INFO = "/data/system/wallpaper_info.xml";
+ // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME
+ // are also used in the full-backup file format, so must not change unless steps are
+ // taken to support the legacy backed-up datasets.
+ private static final String WALLPAPER_IMAGE_FILENAME = "wallpaper";
+ private static final String WALLPAPER_INFO_FILENAME = "wallpaper_info.xml";
+
+ private static final String WALLPAPER_IMAGE_DIR = "/data/data/com.android.settings/files";
+ private static final String WALLPAPER_IMAGE = WALLPAPER_IMAGE_DIR + "/" + WALLPAPER_IMAGE_FILENAME;
+
+ private static final String WALLPAPER_INFO_DIR = "/data/system";
+ private static final String WALLPAPER_INFO = WALLPAPER_INFO_DIR + "/" + WALLPAPER_INFO_FILENAME;
+
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
+ if (oldState == null) {
+ // Ah, it's a full backup dataset, being restored piecemeal. Just
+ // pop over to the full restore handling and we're done.
+ runFullBackup(data);
+ return;
+ }
+
// We only back up the data under the current "wallpaper" schema with metadata
WallpaperManagerService wallpaper = (WallpaperManagerService)ServiceManager.getService(
Context.WALLPAPER_SERVICE);
@@ -57,6 +74,21 @@ public class SystemBackupAgent extends BackupAgentHelper {
super.onBackup(oldState, data, newState);
}
+ private void runFullBackup(BackupDataOutput output) {
+ fullWallpaperBackup(output);
+ }
+
+ private void fullWallpaperBackup(BackupDataOutput output) {
+ // Back up the data files directly. We do them in this specific order --
+ // info file followed by image -- because then we need take no special
+ // steps during restore; the restore will happen properly when the individual
+ // files are restored piecemeal.
+ FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null,
+ WALLPAPER_INFO_DIR, WALLPAPER_INFO, output);
+ FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null,
+ WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output);
+ }
+
@Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
throws IOException {
@@ -80,4 +112,46 @@ public class SystemBackupAgent extends BackupAgentHelper {
(new File(WALLPAPER_INFO)).delete();
}
}
+
+ @Override
+ public void onRestoreFile(ParcelFileDescriptor data, long size,
+ int type, String domain, String path, long mode, long mtime)
+ throws IOException {
+ Slog.i(TAG, "Restoring file domain=" + domain + " path=" + path);
+
+ // Bits to indicate postprocessing we may need to perform
+ boolean restoredWallpaper = false;
+
+ File outFile = null;
+ // Various domain+files we understand a priori
+ if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) {
+ if (path.equals(WALLPAPER_INFO_FILENAME)) {
+ outFile = new File(WALLPAPER_INFO);
+ restoredWallpaper = true;
+ } else if (path.equals(WALLPAPER_IMAGE_FILENAME)) {
+ outFile = new File(WALLPAPER_IMAGE);
+ restoredWallpaper = true;
+ }
+ }
+
+ try {
+ if (outFile == null) {
+ Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]");
+ }
+ FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
+
+ if (restoredWallpaper) {
+ WallpaperManagerService wallpaper =
+ (WallpaperManagerService)ServiceManager.getService(
+ Context.WALLPAPER_SERVICE);
+ wallpaper.settingsRestored();
+ }
+ } catch (IOException e) {
+ if (restoredWallpaper) {
+ // Make sure we wind up in a good state
+ (new File(WALLPAPER_IMAGE)).delete();
+ (new File(WALLPAPER_INFO)).delete();
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d160963..596cbac 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,16 +16,6 @@
package com.android.server;
-import com.android.server.am.ActivityManagerService;
-import com.android.server.usb.UsbService;
-import com.android.server.wm.WindowManagerService;
-import com.android.internal.app.ShutdownThread;
-import com.android.internal.os.BinderInternal;
-import com.android.internal.os.SamplingProfilerIntegration;
-
-import dalvik.system.VMRuntime;
-import dalvik.system.Zygote;
-
import android.accounts.AccountManagerService;
import android.app.ActivityManagerNative;
import android.bluetooth.BluetoothAdapter;
@@ -38,25 +28,35 @@ import android.content.pm.IPackageManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.media.AudioService;
-import android.os.Build;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.provider.Contacts.People;
import android.provider.Settings;
import android.server.BluetoothA2dpService;
import android.server.BluetoothService;
import android.server.search.SearchManagerService;
import android.util.DisplayMetrics;
import android.util.EventLog;
-import android.util.Log;
import android.util.Slog;
-import android.view.Display;
import android.view.WindowManager;
+import com.android.internal.app.ShutdownThread;
+import com.android.internal.os.BinderInternal;
+import com.android.internal.os.SamplingProfilerIntegration;
+import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.net.NetworkPolicyManagerService;
+import com.android.server.net.NetworkStatsService;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.usb.UsbService;
+import com.android.server.wm.WindowManagerService;
+
+import dalvik.system.VMRuntime;
+import dalvik.system.Zygote;
+
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
@@ -117,6 +117,10 @@ class ServerThread extends Thread {
LightsService lights = null;
PowerManagerService power = null;
BatteryService battery = null;
+ AlarmManagerService alarm = null;
+ NetworkManagementService networkManagement = null;
+ NetworkStatsService networkStats = null;
+ NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
IPackageManager pm = null;
Context context = null;
@@ -187,7 +191,7 @@ class ServerThread extends Thread {
power.init(context, lights, ActivityManagerService.getDefault(), battery);
Slog.i(TAG, "Alarm Manager");
- AlarmManagerService alarm = new AlarmManagerService(context);
+ alarm = new AlarmManagerService(context);
ServiceManager.addService(Context.ALARM_SERVICE, alarm);
Slog.i(TAG, "Init Watchdog");
@@ -273,24 +277,33 @@ class ServerThread extends Thread {
}
try {
- Slog.i(TAG, "NetStat Service");
- ServiceManager.addService("netstat", new NetStatService(context));
+ Slog.i(TAG, "NetworkManagement Service");
+ networkManagement = NetworkManagementService.create(context);
+ ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting NetworkManagement Service", e);
+ }
+
+ try {
+ Slog.i(TAG, "NetworkStats Service");
+ networkStats = new NetworkStatsService(context, networkManagement, alarm);
+ ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
} catch (Throwable e) {
- Slog.e(TAG, "Failure starting NetStat Service", e);
+ Slog.e(TAG, "Failure starting NetworkStats Service", e);
}
try {
- Slog.i(TAG, "NetworkManagement Service");
- ServiceManager.addService(
- Context.NETWORKMANAGEMENT_SERVICE,
- NetworkManagementService.create(context));
+ Slog.i(TAG, "NetworkPolicy Service");
+ networkPolicy = new NetworkPolicyManagerService(
+ context, ActivityManagerService.self(), power, networkStats);
+ ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
} catch (Throwable e) {
- Slog.e(TAG, "Failure starting NetworkManagement Service", e);
+ Slog.e(TAG, "Failure starting NetworkPolicy Service", e);
}
try {
Slog.i(TAG, "Connectivity Service");
- connectivity = ConnectivityService.getInstance(context);
+ connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Connectivity Service", e);
@@ -526,6 +539,8 @@ class ServerThread extends Thread {
// These are needed to propagate to the runnable below.
final Context contextF = context;
final BatteryService batteryF = battery;
+ final NetworkStatsService networkStatsF = networkStats;
+ final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final ConnectivityService connectivityF = connectivity;
final DockObserver dockF = dock;
final UsbService usbF = usb;
@@ -551,6 +566,8 @@ class ServerThread extends Thread {
startSystemUi(contextF);
if (batteryF != null) batteryF.systemReady();
+ if (networkStatsF != null) networkStatsF.systemReady();
+ if (networkPolicyF != null) networkPolicyF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
if (usbF != null) usbF.systemReady();
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index a8d40b7..dd54c16 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -279,7 +279,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!checkNotifyPermission("notifyServiceState()")){
return;
}
- Slog.i(TAG, "notifyServiceState: " + state);
synchronized (mRecords) {
mServiceState = state;
for (Record r : mRecords) {
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index d841cb3..510ff62 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -16,6 +16,9 @@
package com.android.server;
+import com.android.internal.R;
+import com.android.internal.telephony.TelephonyProperties;
+
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -30,7 +33,7 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.INetworkManagementEventObserver;
import android.net.IThrottleManager;
-import android.net.SntpClient;
+import android.net.NetworkStats;
import android.net.ThrottleManager;
import android.os.Binder;
import android.os.Environment;
@@ -47,10 +50,9 @@ import android.os.SystemProperties;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.NtpTrustedTime;
import android.util.Slog;
-
-import com.android.internal.R;
-import com.android.internal.telephony.TelephonyProperties;
+import android.util.TrustedTime;
import java.io.BufferedWriter;
import java.io.File;
@@ -60,11 +62,11 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
import java.util.GregorianCalendar;
import java.util.Properties;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
// TODO - add comments - reference the ThrottleManager for public API
public class ThrottleService extends IThrottleManager.Stub {
@@ -84,6 +86,11 @@ public class ThrottleService extends IThrottleManager.Stub {
private static final int TESTING_RESET_PERIOD_SEC = 60 * 10;
private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
+ private static final long MAX_NTP_CACHE_AGE = 24 * 60 * 60 * 1000;
+ private static final long MAX_NTP_FETCH_WAIT = 20 * 1000;
+
+ private long mMaxNtpCacheAge = MAX_NTP_CACHE_AGE;
+
private int mPolicyPollPeriodSec;
private AtomicLong mPolicyThreshold;
private AtomicInteger mPolicyThrottleValue;
@@ -121,10 +128,24 @@ public class ThrottleService extends IThrottleManager.Stub {
private static final int THROTTLE_INDEX_UNTHROTTLED = 0;
private static final String PROPERTIES_FILE = "/etc/gps.conf";
- private String mNtpServer;
- private boolean mNtpActive;
+
+ private Intent mPollStickyBroadcast;
+
+ private TrustedTime mTime;
+
+ private static INetworkManagementService getNetworkManagementService() {
+ final IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ return INetworkManagementService.Stub.asInterface(b);
+ }
public ThrottleService(Context context) {
+ // TODO: move to using cached NtpTrustedTime
+ this(context, getNetworkManagementService(), new NtpTrustedTime(),
+ context.getResources().getString(R.string.config_datause_iface));
+ }
+
+ public ThrottleService(Context context, INetworkManagementService nmService, TrustedTime time,
+ String iface) {
if (VDBG) Slog.v(TAG, "Starting ThrottleService");
mContext = context;
@@ -132,17 +153,15 @@ public class ThrottleService extends IThrottleManager.Stub {
mPolicyThrottleValue = new AtomicInteger();
mThrottleIndex = new AtomicInteger();
- mNtpActive = false;
-
- mIface = mContext.getResources().getString(R.string.config_datause_iface);
+ mIface = iface;
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
Intent pollIntent = new Intent(ACTION_POLL, null);
mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
Intent resetIntent = new Intent(ACTION_RESET, null);
mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- mNMService = INetworkManagementService.Stub.asInterface(b);
+ mNMService = nmService;
+ mTime = time;
mNotificationManager = (NotificationManager)mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
@@ -189,7 +208,7 @@ public class ThrottleService extends IThrottleManager.Stub {
mMsg = msg;
}
- void observe(Context context) {
+ void register(Context context) {
ContentResolver resolver = context.getContentResolver();
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.THROTTLE_POLLING_SEC), false, this);
@@ -207,6 +226,11 @@ public class ThrottleService extends IThrottleManager.Stub {
Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC), false, this);
}
+ void unregister(Context context) {
+ final ContentResolver resolver = context.getContentResolver();
+ resolver.unregisterContentObserver(this);
+ }
+
@Override
public void onChange(boolean selfChange) {
mHandler.obtainMessage(mMsg).sendToTarget();
@@ -220,7 +244,9 @@ public class ThrottleService extends IThrottleManager.Stub {
}
private long ntpToWallTime(long ntpTime) {
- long bestNow = getBestTime(true); // do it quickly
+ // get time quickly without worrying about trusted state
+ long bestNow = mTime.hasCache() ? mTime.currentTimeMillis()
+ : System.currentTimeMillis();
long localNow = System.currentTimeMillis();
return localNow + (ntpTime - bestNow);
}
@@ -300,7 +326,7 @@ public class ThrottleService extends IThrottleManager.Stub {
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
+ dispatchPoll();
}
}, new IntentFilter(ACTION_POLL));
@@ -308,7 +334,7 @@ public class ThrottleService extends IThrottleManager.Stub {
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
+ dispatchReset();
}
}, new IntentFilter(ACTION_RESET));
@@ -318,7 +344,10 @@ public class ThrottleService extends IThrottleManager.Stub {
File file = new File(PROPERTIES_FILE);
stream = new FileInputStream(file);
properties.load(stream);
- mNtpServer = properties.getProperty("NTP_SERVER", null);
+ final String ntpServer = properties.getProperty("NTP_SERVER", null);
+ if (mTime instanceof NtpTrustedTime) {
+ ((NtpTrustedTime) mTime).setNtpServer(ntpServer, MAX_NTP_FETCH_WAIT);
+ }
} catch (IOException e) {
Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
} finally {
@@ -343,9 +372,33 @@ public class ThrottleService extends IThrottleManager.Stub {
}
mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
- mSettingsObserver.observe(mContext);
+ mSettingsObserver.register(mContext);
+ }
+
+ void shutdown() {
+ // TODO: eventually connect with ShutdownThread to persist stats during
+ // graceful shutdown.
+
+ if (mThread != null) {
+ mThread.quit();
+ }
+
+ if (mSettingsObserver != null) {
+ mSettingsObserver.unregister(mContext);
+ }
+
+ if (mPollStickyBroadcast != null) {
+ mContext.removeStickyBroadcast(mPollStickyBroadcast);
+ }
}
+ void dispatchPoll() {
+ mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
+ }
+
+ void dispatchReset() {
+ mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
+ }
private static final int EVENT_REBOOT_RECOVERY = 0;
private static final int EVENT_POLICY_CHANGED = 1;
@@ -440,15 +493,17 @@ public class ThrottleService extends IThrottleManager.Stub {
mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
- mMaxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC, MAX_NTP_CACHE_AGE_SEC);
+ final int maxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC,
+ (int) (MAX_NTP_CACHE_AGE / 1000));
+ mMaxNtpCacheAge = maxNtpCacheAgeSec * 1000;
if (VDBG || (mPolicyThreshold.get() != 0)) {
Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" +
mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold.get() +
", value=" + mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay +
- ", noteType=" + mPolicyNotificationsAllowedMask + ", maxNtpCacheAge=" +
- mMaxNtpCacheAgeSec);
+ ", noteType=" + mPolicyNotificationsAllowedMask + ", mMaxNtpCacheAge=" +
+ mMaxNtpCacheAge);
}
// force updates
@@ -464,15 +519,30 @@ public class ThrottleService extends IThrottleManager.Stub {
private void onPollAlarm() {
long now = SystemClock.elapsedRealtime();
- long next = now + mPolicyPollPeriodSec*1000;
+ long next = now + mPolicyPollPeriodSec * 1000;
- checkForAuthoritativeTime();
+ // when trusted cache outdated, try refreshing
+ if (mTime.getCacheAge() > mMaxNtpCacheAge) {
+ if (mTime.forceRefresh()) {
+ if (VDBG) Slog.d(TAG, "updated trusted time, reseting alarm");
+ dispatchReset();
+ }
+ }
long incRead = 0;
long incWrite = 0;
try {
- incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead;
- incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite;
+ final NetworkStats stats = mNMService.getNetworkStatsSummary();
+ final int index = stats.findIndex(mIface, NetworkStats.UID_ALL);
+
+ if (index != -1) {
+ incRead = stats.rx[index] - mLastRead;
+ incWrite = stats.tx[index] - mLastWrite;
+ } else {
+ // missing iface, assume stats are 0
+ Slog.w(TAG, "unable to find stats for iface " + mIface);
+ }
+
// handle iface resets - on some device the 3g iface comes and goes and gets
// totals reset to 0. Deal with it
if ((incRead < 0) || (incWrite < 0)) {
@@ -509,6 +579,7 @@ public class ThrottleService extends IThrottleManager.Stub {
broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
mContext.sendStickyBroadcast(broadcast);
+ mPollStickyBroadcast = broadcast;
mAlarmManager.cancel(mPendingPollIntent);
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
@@ -538,7 +609,8 @@ public class ThrottleService extends IThrottleManager.Stub {
// have we spoken with an ntp server yet?
// this is controversial, but we'd rather err towards not throttling
- if ((mNtpServer != null) && !mNtpActive) {
+ if (!mTime.hasCache()) {
+ Slog.w(TAG, "missing trusted time, skipping throttle check");
return;
}
@@ -697,9 +769,15 @@ public class ThrottleService extends IThrottleManager.Stub {
" bytes read and " + mRecorder.getPeriodTx(0) + " written");
}
- long now = getBestTime(false);
+ // when trusted cache outdated, try refreshing
+ if (mTime.getCacheAge() > mMaxNtpCacheAge) {
+ mTime.forceRefresh();
+ }
- if (mNtpActive || (mNtpServer == null)) {
+ // as long as we have a trusted time cache, we always reset alarms,
+ // even if the refresh above failed.
+ if (mTime.hasCache()) {
+ final long now = mTime.currentTimeMillis();
Calendar end = calculatePeriodEnd(now);
Calendar start = calculatePeriodStart(end);
@@ -714,56 +792,11 @@ public class ThrottleService extends IThrottleManager.Stub {
SystemClock.elapsedRealtime() + offset,
mPendingResetIntent);
} else {
- if (VDBG) Slog.d(TAG, "no authoritative time - not resetting period");
+ if (VDBG) Slog.d(TAG, "no trusted time, not resetting period");
}
}
}
- private void checkForAuthoritativeTime() {
- if (mNtpActive || (mNtpServer == null)) return;
-
- // will try to get the ntp time and switch to it if found.
- // will also cache the time so we don't fetch it repeatedly.
- getBestTime(false);
- }
-
- private static final int MAX_NTP_CACHE_AGE_SEC = 60 * 60 * 24; // 1 day
- private static final int MAX_NTP_FETCH_WAIT = 20 * 1000;
- private int mMaxNtpCacheAgeSec = MAX_NTP_CACHE_AGE_SEC;
- private long cachedNtp;
- private long cachedNtpTimestamp;
-
- // if the request is tied to UI and ANR's are a danger, request a fast result
- // the regular polling should have updated the cached time recently using the
- // slower method (!fast)
- private long getBestTime(boolean fast) {
- if (mNtpServer != null) {
- if (mNtpActive) {
- long ntpAge = SystemClock.elapsedRealtime() - cachedNtpTimestamp;
- if (ntpAge < mMaxNtpCacheAgeSec * 1000 || fast) {
- if (VDBG) Slog.v(TAG, "using cached time");
- return cachedNtp + ntpAge;
- }
- }
- SntpClient client = new SntpClient();
- if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT)) {
- cachedNtp = client.getNtpTime();
- cachedNtpTimestamp = SystemClock.elapsedRealtime();
- if (!mNtpActive) {
- mNtpActive = true;
- if (VDBG) Slog.d(TAG, "found Authoritative time - reseting alarm");
- mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
- }
- if (VDBG) Slog.v(TAG, "using Authoritative time: " + cachedNtp);
- return cachedNtp;
- }
- }
- long time = System.currentTimeMillis();
- if (VDBG) Slog.v(TAG, "using User time: " + time);
- mNtpActive = false;
- return time;
- }
-
// records bytecount data for a given time and accumulates it into larger time windows
// for logging and other purposes
//
@@ -929,7 +962,7 @@ public class ThrottleService extends IThrottleManager.Stub {
private void checkAndDeleteLRUDataFile(File dir) {
File[] files = dir.listFiles();
- if (files.length <= MAX_SIMS_SUPPORTED) return;
+ if (files == null || files.length <= MAX_SIMS_SUPPORTED) return;
if (DBG) Slog.d(TAG, "Too many data files");
do {
File oldest = null;
@@ -949,9 +982,11 @@ public class ThrottleService extends IThrottleManager.Stub {
File newest = null;
File[] files = dir.listFiles();
- for (File f : files) {
- if ((newest == null) || (newest.lastModified() < f.lastModified())) {
- newest = f;
+ if (files != null) {
+ for (File f : files) {
+ if ((newest == null) || (newest.lastModified() < f.lastModified())) {
+ newest = f;
+ }
}
}
if (newest == null) {
@@ -1126,7 +1161,7 @@ public class ThrottleService extends IThrottleManager.Stub {
" seconds.");
pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
pw.println("Current Throttle Index is " + mThrottleIndex.get());
- pw.println("Max NTP Cache Age is " + mMaxNtpCacheAgeSec);
+ pw.println("mMaxNtpCacheAge=" + mMaxNtpCacheAge);
for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 2a25c2a..2d3ac00 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -33,7 +33,6 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.util.Config;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -45,7 +44,7 @@ import java.util.Calendar;
/** This class calls its monitor every minute. Killing this process if they don't return **/
public class Watchdog extends Thread {
static final String TAG = "Watchdog";
- static final boolean localLOGV = false || Config.LOGV;
+ static final boolean localLOGV = false || false;
// Set this to true to use debug default values.
static final boolean DB = false;
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 6acc32f..41ec63a 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -235,6 +235,15 @@ public class WifiService extends IWifiManager.Stub {
}
break;
}
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
+ Slog.d(TAG, "Send failed, client connection lost");
+ } else {
+ Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
+ }
+ mClients.remove((AsyncChannel) msg.obj);
+ break;
+ }
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
AsyncChannel ac = new AsyncChannel();
ac.connect(mContext, this, msg.replyTo);
@@ -889,7 +898,7 @@ public class WifiService extends IWifiManager.Stub {
evaluateTrafficStatsPolling();
mWifiStateMachine.enableRssiPolling(true);
if (mBackgroundScanSupported) {
- mWifiStateMachine.enableBackgroundScan(false);
+ mWifiStateMachine.enableBackgroundScanCommand(false);
}
mWifiStateMachine.enableAllNetworks();
updateWifiState();
@@ -901,7 +910,7 @@ public class WifiService extends IWifiManager.Stub {
evaluateTrafficStatsPolling();
mWifiStateMachine.enableRssiPolling(false);
if (mBackgroundScanSupported) {
- mWifiStateMachine.enableBackgroundScan(true);
+ mWifiStateMachine.enableBackgroundScanCommand(true);
}
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index 94531bb..6ef6963 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -28,12 +28,12 @@ import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
+import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Config;
import android.util.Slog;
import java.io.IOException;
@@ -73,8 +73,8 @@ import java.util.Random;
*/
public class WifiWatchdogService {
private static final String TAG = "WifiWatchdogService";
- private static final boolean V = false || Config.LOGV;
- private static final boolean D = true || Config.LOGD;
+ private static final boolean V = false || false;
+ private static final boolean D = true || false;
private Context mContext;
private ContentResolver mContentResolver;
@@ -509,6 +509,7 @@ public class WifiWatchdogService {
// This access point does not require a watchdog, so queue idle on the main thread
mHandler.idle();
}
+ mHandler.checkWalledGarden(ssid);
}
/**
@@ -1003,6 +1004,8 @@ public class WifiWatchdogService {
* The object will be an {@link AccessPoint}.
*/
static final int ACTION_BACKGROUND_CHECK_AP = 3;
+ /** Check whether the connection is a walled garden */
+ static final int ACTION_CHECK_WALLED_GARDEN = 4;
/**
* Go to sleep for the current network. We are conservative with making
@@ -1023,7 +1026,16 @@ public class WifiWatchdogService {
static final int MESSAGE_DISCONNECTED = 104;
/** Performs a hard-reset on the watchdog state. */
static final int MESSAGE_RESET = 105;
-
+
+ /* Walled garden detection */
+ private String mLastSsid;
+ private long mLastTime;
+ private final long MIN_WALLED_GARDEN_TEST_INTERVAL = 15 * 60 * 1000; //15 minutes
+
+ void checkWalledGarden(String ssid) {
+ sendMessage(obtainMessage(ACTION_CHECK_WALLED_GARDEN, ssid));
+ }
+
void checkAp(AccessPoint ap) {
removeAllActions();
sendMessage(obtainMessage(ACTION_CHECK_AP, ap));
@@ -1085,6 +1097,9 @@ public class WifiWatchdogService {
case ACTION_BACKGROUND_CHECK_AP:
handleBackgroundCheckAp((AccessPoint) msg.obj);
break;
+ case ACTION_CHECK_WALLED_GARDEN:
+ handleWalledGardenCheck((String) msg.obj);
+ break;
case MESSAGE_SLEEP:
handleSleep((String) msg.obj);
break;
@@ -1102,6 +1117,43 @@ public class WifiWatchdogService {
break;
}
}
+
+ private boolean isWalledGardenConnection() {
+ //One way to detect a walled garden is to see if multiple DNS queries
+ //resolve to the same IP address
+ try {
+ String host1 = "www.google.com";
+ String host2 = "www.android.com";
+ String address1 = InetAddress.getByName(host1).getHostAddress();
+ String address2 = InetAddress.getByName(host2).getHostAddress();
+ if (address1.equals(address2)) return true;
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ return false;
+ }
+
+ private void handleWalledGardenCheck(String ssid) {
+ long currentTime = System.currentTimeMillis();
+ //Avoid a walled garden test on the same network if one was already done
+ //within MIN_WALLED_GARDEN_TEST_INTERVAL. This will handle scenarios where
+ //there are frequent network disconnections
+ if (ssid.equals(mLastSsid) &&
+ (currentTime - mLastTime) < MIN_WALLED_GARDEN_TEST_INTERVAL) {
+ return;
+ }
+
+ mLastTime = currentTime;
+ mLastSsid = ssid;
+
+ if (isWalledGardenConnection()) {
+ Uri uri = Uri.parse("http://www.google.com");
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ }
+ }
}
/**
@@ -1264,7 +1316,7 @@ public class WifiWatchdogService {
return false;
} catch (Exception e) {
- if (V || Config.LOGD) {
+ if (V || false) {
Slog.d(TAG, "DnsPinger.isReachable got an unknown exception", e);
}
return false;
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
new file mode 100644
index 0000000..8ba0a0b
--- /dev/null
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 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.accessibility;
+
+import com.android.server.wm.InputFilter;
+
+import android.content.Context;
+import android.util.Slog;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy;
+
+/**
+ * Input filter for accessibility.
+ *
+ * Currently just a stub but will eventually implement touch exploration, etc.
+ */
+public class AccessibilityInputFilter extends InputFilter {
+ private static final String TAG = "AccessibilityInputFilter";
+ private static final boolean DEBUG = false;
+
+ private final Context mContext;
+
+ /**
+ * This is an interface for explorers that take a {@link MotionEvent}
+ * stream and perform touch exploration of the screen content.
+ */
+ public interface Explorer {
+ /**
+ * Handles a {@link MotionEvent}.
+ *
+ * @param event The event to handle.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ public void onMotionEvent(MotionEvent event, int policyFlags);
+
+ /**
+ * Requests that the explorer clears its internal state.
+ *
+ * @param event The last received event.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ public void clear(MotionEvent event, int policyFlags);
+ }
+
+ private TouchExplorer mTouchExplorer;
+ private int mTouchscreenSourceDeviceId;
+
+ public AccessibilityInputFilter(Context context) {
+ super(context.getMainLooper());
+ mContext = context;
+ }
+
+ @Override
+ public void onInstalled() {
+ if (DEBUG) {
+ Slog.d(TAG, "Accessibility input filter installed.");
+ }
+ super.onInstalled();
+ }
+
+ @Override
+ public void onUninstalled() {
+ if (DEBUG) {
+ Slog.d(TAG, "Accessibility input filter uninstalled.");
+ }
+ super.onUninstalled();
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event, int policyFlags) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ + Integer.toHexString(policyFlags));
+ }
+ if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
+ MotionEvent motionEvent = (MotionEvent) event;
+ int deviceId = event.getDeviceId();
+ if (mTouchscreenSourceDeviceId != deviceId) {
+ mTouchscreenSourceDeviceId = deviceId;
+ if (mTouchExplorer != null) {
+ mTouchExplorer.clear(motionEvent, policyFlags);
+ } else {
+ mTouchExplorer = new TouchExplorer(this, mContext);
+ }
+ }
+ if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) != 0) {
+ mTouchExplorer.onMotionEvent(motionEvent, policyFlags);
+ } else {
+ mTouchExplorer.clear(motionEvent, policyFlags);
+ }
+ } else {
+ super.onInputEvent(event, policyFlags);
+ }
+ }
+}
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 04ae490..7801aec 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -14,11 +14,14 @@
** limitations under the License.
*/
-package com.android.server;
+package com.android.server.accessibility;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.HandlerCaller.SomeArgs;
+import com.android.server.wm.WindowManagerService;
+
+import org.xmlpull.v1.XmlPullParserException;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -38,21 +41,21 @@ import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
-import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
-import android.util.Config;
import android.util.Slog;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -73,6 +76,8 @@ import java.util.Set;
public class AccessibilityManagerService extends IAccessibilityManager.Stub
implements HandlerCaller.Callback {
+ private static final boolean DEBUG = false;
+
private static final String LOG_TAG = "AccessibilityManagerService";
private static int sIdCounter = 0;
@@ -92,10 +97,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final List<IAccessibilityManagerClient> mClients =
new ArrayList<IAccessibilityManagerClient>();
- final Map<ComponentName, Service> mComponentNameToServiceMap =
- new HashMap<ComponentName, Service>();
+ final Map<ComponentName, Service> mComponentNameToServiceMap = new HashMap<ComponentName, Service>();
- private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>();
+ private final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<AccessibilityServiceInfo>();
private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
@@ -107,6 +111,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private boolean mIsEnabled;
+ private AccessibilityInputFilter mInputFilter;
+
+ private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList = new ArrayList<AccessibilityServiceInfo>();
+
+ private boolean mHasInputFilter;
+
/**
* Handler for delayed event dispatch.
*/
@@ -131,7 +141,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*
* @param context A {@link Context} instance.
*/
- AccessibilityManagerService(Context context) {
+ public AccessibilityManagerService(Context context) {
mContext = context;
mPackageManager = mContext.getPackageManager();
mCaller = new HandlerCaller(context, this);
@@ -249,6 +259,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
unbindAllServicesLocked();
}
updateClientsLocked();
+ updateInputFilterLocked();
}
}
});
@@ -268,9 +279,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
});
}
- public boolean addClient(IAccessibilityManagerClient client) {
+ public boolean addClient(IAccessibilityManagerClient client) throws RemoteException {
synchronized (mLock) {
- mClients.add(client);
+ final IAccessibilityManagerClient addedClient = client;
+ mClients.add(addedClient);
+ // Clients are registered all the time until their process is
+ // killed, therefore we do not have a corresponding unlinkToDeath.
+ client.asBinder().linkToDeath(new DeathRecipient() {
+ public void binderDied() {
+ synchronized (mLock) {
+ mClients.remove(addedClient);
+ }
+ }
+ }, 0);
return mIsEnabled;
}
}
@@ -290,12 +311,31 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return (OWN_PROCESS_ID != Binder.getCallingPid());
}
- public List<ServiceInfo> getAccessibilityServiceList() {
+ public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
synchronized (mLock) {
return mInstalledServices;
}
}
+ public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
+ List<AccessibilityServiceInfo> result = mEnabledServicesForFeedbackTempList;
+ List<Service> services = mServices;
+ synchronized (mLock) {
+ while (feedbackType != 0) {
+ final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType));
+ feedbackType &= ~feedbackTypeBit;
+ final int serviceCount = services.size();
+ for (int i = 0; i < serviceCount; i++) {
+ Service service = services.get(i);
+ if (service.mFeedbackType == feedbackType) {
+ result.add(service.mAccessibilityServiceInfo);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
public void interrupt() {
synchronized (mLock) {
for (int i = 0, count = mServices.size(); i < count; i++) {
@@ -303,16 +343,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
try {
service.mServiceInterface.onInterrupt();
} catch (RemoteException re) {
- if (re instanceof DeadObjectException) {
- Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
- if (removeDeadServiceLocked(service)) {
- count--;
- i--;
- }
- } else {
- Slog.e(LOG_TAG, "Error during sending interrupt request to "
- + service.mService, re);
- }
+ Slog.e(LOG_TAG, "Error during sending interrupt request to "
+ + service.mService, re);
}
}
}
@@ -327,14 +359,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
Service service = (Service) arguments.arg2;
synchronized (mLock) {
- service.mEventTypes = info.eventTypes;
- service.mFeedbackType = info.feedbackType;
- String[] packageNames = info.packageNames;
- if (packageNames != null) {
- service.mPackageNames.addAll(Arrays.asList(packageNames));
+ // If the XML manifest had data to configure the service its info
+ // should be already set. In such a case update only the dynamically
+ // configurable properties.
+ AccessibilityServiceInfo oldInfo = service.mAccessibilityServiceInfo;
+ if (oldInfo != null) {
+ oldInfo.updateDynamicallyConfigurableProperties(info);
+ service.setAccessibilityServiceInfo(oldInfo);
+ } else {
+ service.setAccessibilityServiceInfo(info);
+ tryAddServiceLocked(service);
}
- service.mNotificationTimeout = info.notificationTimeout;
- service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
+
+ updateInputFilterLocked();
}
return;
default:
@@ -349,10 +386,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mInstalledServices.clear();
List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
- new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
+ new Intent(AccessibilityService.SERVICE_INTERFACE),
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
for (int i = 0, count = installedServices.size(); i < count; i++) {
- mInstalledServices.add(installedServices.get(i).serviceInfo);
+ ResolveInfo resolveInfo = installedServices.get(i);
+ AccessibilityServiceInfo accessibilityServiceInfo;
+ try {
+ accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
+ mInstalledServices.add(accessibilityServiceInfo);
+ } catch (XmlPullParserException xppe) {
+ Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe);
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", ioe);
+ }
}
}
@@ -445,31 +492,49 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
try {
listener.onAccessibilityEvent(event);
- if (Config.DEBUG) {
+ if (DEBUG) {
Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
}
} catch (RemoteException re) {
- if (re instanceof DeadObjectException) {
- Slog.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
- removeDeadServiceLocked(service);
- } else {
- Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
+ Slog.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
+ }
+ }
+
+ /**
+ * Adds a service.
+ *
+ * @param service The service to add.
+ */
+ private void tryAddServiceLocked(Service service) {
+ try {
+ if (mServices.contains(service) || !service.isConfigured()) {
+ return;
}
+ service.linkToOwnDeath();
+ mServices.add(service);
+ mComponentNameToServiceMap.put(service.mComponentName, service);
+ updateInputFilterLocked();
+ } catch (RemoteException e) {
+ /* do nothing */
}
}
/**
- * Removes a dead service.
+ * Removes a service.
*
* @param service The service.
* @return True if the service was removed, false otherwise.
*/
- private boolean removeDeadServiceLocked(Service service) {
- if (Config.DEBUG) {
- Slog.i(LOG_TAG, "Dead service " + service.mService + " removed");
+ private boolean tryRemoveServiceLocked(Service service) {
+ final boolean removed = mServices.remove(service);
+ if (!removed) {
+ return false;
}
+ mComponentNameToServiceMap.remove(service.mComponentName);
mHandler.removeMessages(service.mId);
- return mServices.remove(service);
+ service.unlinkToOwnDeath();
+ updateInputFilterLocked();
+ return removed;
}
/**
@@ -492,11 +557,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return false;
}
- if (!service.mService.isBinderAlive()) {
- removeDeadServiceLocked(service);
- return false;
- }
-
int eventType = event.getEventType();
if ((service.mEventTypes & eventType) != eventType) {
return false;
@@ -574,22 +634,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* @param installedServices All installed {@link AccessibilityService}s.
* @param enabledServices The {@link ComponentName}s of the enabled services.
*/
- private void updateServicesStateLocked(List<ServiceInfo> installedServices,
+ private void updateServicesStateLocked(List<AccessibilityServiceInfo> installedServices,
Set<ComponentName> enabledServices) {
Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
boolean isEnabled = mIsEnabled;
for (int i = 0, count = installedServices.size(); i < count; i++) {
- ServiceInfo intalledService = installedServices.get(i);
- ComponentName componentName = new ComponentName(intalledService.packageName,
- intalledService.name);
+ AccessibilityServiceInfo installedService = installedServices.get(i);
+ ComponentName componentName = ComponentName.unflattenFromString(
+ installedService.getId());
Service service = componentNameToServiceMap.get(componentName);
if (isEnabled) {
if (enabledServices.contains(componentName)) {
if (service == null) {
- service = new Service(componentName);
+ service = new Service(installedService);
}
service.bind();
} else if (!enabledServices.contains(componentName)) {
@@ -621,6 +681,40 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
/**
+ * Updates the input filter state. The filter is enabled if accessibility
+ * is enabled and there is at least one accessibility service providing
+ * spoken feedback.
+ */
+ private void updateInputFilterLocked() {
+ WindowManagerService wm = (WindowManagerService)ServiceManager.getService(
+ Context.WINDOW_SERVICE);
+ if (mIsEnabled) {
+ final boolean hasSpokenFeedbackServices = !getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty();
+ if (hasSpokenFeedbackServices) {
+ if (mHasInputFilter) {
+ return;
+ }
+ if (mInputFilter == null) {
+ mInputFilter = new AccessibilityInputFilter(mContext);
+ }
+ wm.setInputFilter(mInputFilter);
+ mHasInputFilter = true;
+ } else {
+ if (mHasInputFilter) {
+ wm.setInputFilter(null);
+ mHasInputFilter = false;
+ }
+ }
+ } else {
+ if (mHasInputFilter) {
+ wm.setInputFilter(null);
+ mHasInputFilter = false;
+ }
+ }
+ }
+
+ /**
* This class represents an accessibility service. It stores all per service
* data required for the service management, provides API for starting/stopping the
* service and is responsible for adding/removing the service in the data structures
@@ -628,9 +722,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* passed to the service it represents as soon it is bound. It also serves as the
* connection for the service.
*/
- class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
+ class Service extends IAccessibilityServiceConnection.Stub
+ implements ServiceConnection, DeathRecipient {
int mId = 0;
+ AccessibilityServiceInfo mAccessibilityServiceInfo;
+
IBinder mService;
IEventListener mServiceInterface;
@@ -645,8 +742,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
long mNotificationTimeout;
- boolean mIsActive;
-
ComponentName mComponentName;
Intent mIntent;
@@ -655,9 +750,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final SparseArray<AccessibilityEvent> mPendingEvents =
new SparseArray<AccessibilityEvent>();
- Service(ComponentName componentName) {
+ Service(AccessibilityServiceInfo accessibilityServiceInfo) {
mId = sIdCounter++;
- mComponentName = componentName;
+ setAccessibilityServiceInfo(accessibilityServiceInfo);
mIntent = new Intent().setComponent(mComponentName);
mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.accessibility_binding_label);
@@ -665,6 +760,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
}
+ public void setAccessibilityServiceInfo(AccessibilityServiceInfo info) {
+ mAccessibilityServiceInfo = info;
+ ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
+ mComponentName = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+ mEventTypes = info.eventTypes;
+ mFeedbackType = info.feedbackType;
+ String[] packageNames = info.packageNames;
+ if (packageNames != null) {
+ mPackageNames.addAll(Arrays.asList(packageNames));
+ }
+ mNotificationTimeout = info.notificationTimeout;
+ mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
+ }
+
/**
* Binds to the accessibility service.
*
@@ -685,10 +794,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
public boolean unbind() {
if (mService != null) {
- mService = null;
+ tryRemoveServiceLocked(this);
mContext.unbindService(this);
- mComponentNameToServiceMap.remove(mComponentName);
- mServices.remove(this);
+ mService = null;
return true;
}
return false;
@@ -711,14 +819,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public void onServiceConnected(ComponentName componentName, IBinder service) {
mService = service;
mServiceInterface = IEventListener.Stub.asInterface(service);
-
try {
mServiceInterface.setConnection(this);
synchronized (mLock) {
- if (!mServices.contains(this)) {
- mServices.add(this);
- mComponentNameToServiceMap.put(componentName, this);
- }
+ tryAddServiceLocked(this);
}
} catch (RemoteException re) {
Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
@@ -726,9 +830,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
public void onServiceDisconnected(ComponentName componentName) {
+ /* do nothing - #binderDied takes care */
+ }
+
+ public void linkToOwnDeath() throws RemoteException {
+ mService.linkToDeath(this, 0);
+ }
+
+ public void unlinkToOwnDeath() {
+ mService.unlinkToDeath(this, 0);
+ }
+
+ public void binderDied() {
synchronized (mLock) {
- Service service = mComponentNameToServiceMap.remove(componentName);
- mServices.remove(service);
+ tryRemoveServiceLocked(this);
}
}
}
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
new file mode 100644
index 0000000..4c7f595
--- /dev/null
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -0,0 +1,1458 @@
+/*
+ ** Copyright 2011, 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.accessibility;
+
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END;
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START;
+
+import com.android.server.accessibility.AccessibilityInputFilter.Explorer;
+import com.android.server.wm.InputFilter;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.view.WindowManagerPolicy;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import java.util.Arrays;
+
+/**
+ * This class is a strategy for performing touch exploration. It
+ * transforms the motion event stream by modifying, adding, replacing,
+ * and consuming certain events. The interaction model is:
+ *
+ * <ol>
+ * <li>1. One finger moving around performs touch exploration.</li>
+ * <li>2. Two close fingers moving in the same direction perform a drag.</li>
+ * <li>3. Multi-finger gestures are delivered to view hierarchy.</li>
+ * <li>4. Pointers that have not moved more than a specified distance after they
+ * went down are considered inactive.</li>
+ * <li>5. Two fingers moving too far from each other or in different directions
+ * are considered a multi-finger gesture.</li>
+ * <li>6. Tapping on the last touch explored location within given time and
+ * distance slop performs a click.</li>
+ * <li>7. Tapping and holding for a while on the last touch explored location within
+ * given time and distance slop performs a long press.</li>
+ * <ol>
+ *
+ * @hide
+ */
+public class TouchExplorer implements Explorer {
+ private static final boolean DEBUG = false;
+
+ // Tag for logging received events.
+ private static final String LOG_TAG_RECEIVED = "TouchExplorer-RECEIVED";
+ // Tag for logging injected events.
+ private static final String LOG_TAG_INJECTED = "TouchExplorer-INJECTED";
+ // Tag for logging the current state.
+ private static final String LOG_TAG_STATE = "TouchExplorer-STATE";
+
+ // States this explorer can be in.
+ private static final int STATE_TOUCH_EXPLORING = 0x00000001;
+ private static final int STATE_DRAGGING = 0x00000002;
+ private static final int STATE_DELEGATING = 0x00000004;
+
+ // Human readable symbolic names for the states of the explorer.
+ private static final SparseArray<String> sStateSymbolicNames = new SparseArray<String>();
+ static {
+ SparseArray<String> symbolicNames = sStateSymbolicNames;
+ symbolicNames.append(STATE_TOUCH_EXPLORING, "STATE_TOUCH_EXPLORING");
+ symbolicNames.append(STATE_DRAGGING, "STATE_DRAGING");
+ symbolicNames.append(STATE_DELEGATING, "STATE_DELEGATING");
+ }
+
+ // Invalid pointer ID.
+ private static final int INVALID_POINTER_ID = -1;
+
+ // The coefficient by which to multiply
+ // ViewConfiguration.#getScaledTouchExplorationTapSlop()
+ // to compute #mDraggingDistance.
+ private static final int COEFFICIENT_DRAGGING_DISTANCE = 2;
+
+ // The time slop in milliseconds for activating an item after it has
+ // been touch explored. Tapping on an item within this slop will perform
+ // a click and tapping and holding down a long press.
+ private static final long ACTIVATION_TIME_SLOP = 2000;
+
+ // This constant captures the current implementation detail that
+ // pointer IDs are between 0 and 31 inclusive (subject to change).
+ // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+ private static final int MAX_POINTER_COUNT = 32;
+
+ // The minimum of the cosine between the vectors of two moving
+ // pointers so they can be considered moving in the same direction.
+ private static final float MIN_ANGLE_COS = 0.866025404f; // cos(pi/6)
+
+ // The delay for sending a hover enter event.
+ private static final long DELAY_SEND_HOVER_MOVE = 200;
+
+ // Temporary array for storing pointer IDs.
+ private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT];
+
+ // Temporary array for storing PointerProperties
+ private final PointerProperties[] mTempPointerProperties =
+ PointerProperties.createArray(MAX_POINTER_COUNT);
+
+ // Temporary array for storing PointerCoords
+ private final PointerCoords[] mTempPointerCoords =
+ PointerCoords.createArray(MAX_POINTER_COUNT);
+
+ // The maximal distance between two pointers so they are
+ // considered to be performing a drag operation.
+ private final float mDraggingDistance;
+
+ // The distance from the last touch explored location tapping within
+ // which would perform a click and tapping and holding a long press.
+ private final int mTouchExplorationTapSlop;
+
+ // Context handle for accessing resources.
+ private final Context mContext;
+
+ // The InputFilter this tracker is associated with i.e. the filter
+ // which delegates event processing to this touch explorer.
+ private final InputFilter mInputFilter;
+
+ // Helper class for tracking pointers on the screen, for example which
+ // pointers are down, which are active, etc.
+ private final PointerTracker mPointerTracker;
+
+ // Handle to the accessibility manager for firing accessibility events
+ // announcing touch exploration gesture start and end.
+ private final AccessibilityManager mAccessibilityManager;
+
+ // The last event that was received while performing touch exploration.
+ private MotionEvent mLastTouchExploreEvent;
+
+ // The current state of the touch explorer.
+ private int mCurrentState = STATE_TOUCH_EXPLORING;
+
+ // Flag whether a touch exploration gesture is in progress.
+ private boolean mTouchExploreGestureInProgress;
+
+ // The ID of the pointer used for dragging.
+ private int mDraggingPointerId;
+
+ // Handler for performing asynchronous operations.
+ private final Handler mHandler;
+
+ // Command for delayed sending of a hover event.
+ private final SendHoverDelayed mSendHoverDelayed;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param inputFilter The input filter associated with this explorer.
+ * @param context A context handle for accessing resources.
+ */
+ public TouchExplorer(InputFilter inputFilter, Context context) {
+ mInputFilter = inputFilter;
+ mTouchExplorationTapSlop =
+ ViewConfiguration.get(context).getScaledTouchExplorationTapSlop();
+ mDraggingDistance = mTouchExplorationTapSlop * COEFFICIENT_DRAGGING_DISTANCE;
+ mPointerTracker = new PointerTracker(context);
+ mContext = context;
+ mHandler = new Handler(context.getMainLooper());
+ mSendHoverDelayed = new SendHoverDelayed();
+ mAccessibilityManager = AccessibilityManager.getInstance(context);
+ }
+
+ public void clear(MotionEvent event, int policyFlags) {
+ sendUpForInjectedDownPointers(event, policyFlags);
+ clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onMotionEvent(MotionEvent event, int policyFlags) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG_RECEIVED, "Received event: " + event + ", policyFlags=0x"
+ + Integer.toHexString(policyFlags));
+ Slog.d(LOG_TAG_STATE, sStateSymbolicNames.get(mCurrentState));
+ }
+
+ // Keep track of the pointers's state.
+ mPointerTracker.onReceivedMotionEvent(event);
+
+ switch(mCurrentState) {
+ case STATE_TOUCH_EXPLORING: {
+ handleMotionEventStateTouchExploring(event, policyFlags);
+ } break;
+ case STATE_DRAGGING: {
+ handleMotionEventStateDragging(event, policyFlags);
+ } break;
+ case STATE_DELEGATING: {
+ handleMotionEventStateDelegating(event, policyFlags);
+ } break;
+ default: {
+ throw new IllegalStateException("Illegal state: " + mCurrentState);
+ }
+ }
+ }
+
+ /**
+ * Handles a motion event in touch exploring state.
+ *
+ * @param event The event to be handled.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void handleMotionEventStateTouchExploring(MotionEvent event, int policyFlags) {
+ PointerTracker pointerTracker = mPointerTracker;
+ final int activePointerCount = pointerTracker.getActivePointerCount();
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ // Send a hover for every finger down so the user gets feedback
+ // where she is currently touching.
+ mSendHoverDelayed.forceSendAndRemove();
+ mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, 1, policyFlags,
+ DELAY_SEND_HOVER_MOVE);
+ } break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ switch (activePointerCount) {
+ case 0: {
+ throw new IllegalStateException("The must always be one active pointer in"
+ + "touch exploring state!");
+ }
+ case 1: {
+ // Schedule a hover event which will lead to firing an
+ // accessibility event from the hovered view.
+ mSendHoverDelayed.remove();
+ final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerIdBits = (1 << pointerId);
+ final int lastAction = pointerTracker.getLastInjectedHoverAction();
+ // If a schedules hover enter for another pointer is delivered we send move.
+ final int action = (lastAction == MotionEvent.ACTION_HOVER_ENTER)
+ ? MotionEvent.ACTION_HOVER_MOVE
+ : MotionEvent.ACTION_HOVER_ENTER;
+ mSendHoverDelayed.post(event, action, pointerIdBits, policyFlags,
+ DELAY_SEND_HOVER_MOVE);
+
+ if (mLastTouchExploreEvent == null) {
+ break;
+ }
+
+ // If more pointers down on the screen since the last touch
+ // exploration we discard the last cached touch explore event.
+ if (event.getPointerCount() != mLastTouchExploreEvent.getPointerCount()) {
+ mLastTouchExploreEvent = null;
+ }
+ } break;
+ default: {
+ /* do nothing - let the code for ACTION_MOVE decide what to do */
+ } break;
+ }
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerIndex = event.findPointerIndex(pointerId);
+ final int pointerIdBits = (1 << pointerId);
+ switch (activePointerCount) {
+ case 0: {
+ /* do nothing - no active pointers so we swallow the event */
+ } break;
+ case 1: {
+ // Detect touch exploration gesture start by having one active pointer
+ // that moved more than a given distance.
+ if (!mTouchExploreGestureInProgress) {
+ final float deltaX = pointerTracker.getReceivedPointerDownX(pointerId)
+ - event.getX(pointerIndex);
+ final float deltaY = pointerTracker.getReceivedPointerDownY(pointerId)
+ - event.getY(pointerIndex);
+ final double moveDelta = Math.hypot(deltaX, deltaY);
+
+ if (moveDelta > mTouchExplorationTapSlop) {
+ mTouchExploreGestureInProgress = true;
+ sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
+ // Make sure the scheduled down/move event is sent.
+ mSendHoverDelayed.forceSendAndRemove();
+ // If we have transitioned to exploring state from another one
+ // we need to send a hover enter event here.
+ final int lastAction = mPointerTracker.getLastInjectedHoverAction();
+ if (lastAction == MotionEvent.ACTION_HOVER_EXIT) {
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER,
+ pointerIdBits, policyFlags);
+ }
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
+ policyFlags);
+ }
+ } else {
+ // Touch exploration gesture in progress so send a hover event.
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
+ policyFlags);
+ }
+
+ // Detect long press on the last touch explored position.
+ if (!mTouchExploreGestureInProgress && mLastTouchExploreEvent != null) {
+
+ // If the down was not in the time slop => nothing else to do.
+ final long pointerDownTime =
+ pointerTracker.getReceivedPointerDownTime(pointerId);
+ final long lastExploreTime = mLastTouchExploreEvent.getEventTime();
+ final long deltaTimeExplore = pointerDownTime - lastExploreTime;
+ if (deltaTimeExplore > ACTIVATION_TIME_SLOP) {
+ mLastTouchExploreEvent = null;
+ break;
+ }
+
+ // If the pointer moved more than the tap slop => nothing else to do.
+ final float deltaX = mLastTouchExploreEvent.getX(pointerIndex)
+ - event.getX(pointerIndex);
+ final float deltaY = mLastTouchExploreEvent.getY(pointerIndex)
+ - event.getY(pointerIndex);
+ final float moveDelta = (float) Math.hypot(deltaX, deltaY);
+ if (moveDelta > mTouchExplorationTapSlop) {
+ mLastTouchExploreEvent = null;
+ break;
+ }
+
+ // If down for long enough we get a long press.
+ final long deltaTimeMove = event.getEventTime() - pointerDownTime;
+ if (deltaTimeMove > ViewConfiguration.getLongPressTimeout()) {
+ mCurrentState = STATE_DELEGATING;
+ // Make sure the scheduled hover exit is delivered.
+ mSendHoverDelayed.forceSendAndRemove();
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ sendMotionEvent(event, policyFlags);
+ mTouchExploreGestureInProgress = false;
+ mLastTouchExploreEvent = null;
+ }
+ }
+ } break;
+ case 2: {
+ mSendHoverDelayed.forceSendAndRemove();
+ // We want to no longer hover over the location so subsequent
+ // touch at the same spot will generate a hover enter.
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits,
+ policyFlags);
+
+ if (isDraggingGesture(event)) {
+ // Two pointers moving in the same direction within
+ // a given distance perform a drag.
+ mCurrentState = STATE_DRAGGING;
+ if (mTouchExploreGestureInProgress) {
+ sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ mTouchExploreGestureInProgress = false;
+ }
+ mDraggingPointerId = pointerId;
+ sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits,
+ policyFlags);
+ } else {
+ // Two pointers moving arbitrary are delegated to the view hierarchy.
+ mCurrentState = STATE_DELEGATING;
+ if (mTouchExploreGestureInProgress) {
+ sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ mTouchExploreGestureInProgress = false;
+ }
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ }
+ } break;
+ default: {
+ mSendHoverDelayed.forceSendAndRemove();
+ // We want to no longer hover over the location so subsequent
+ // touch at the same spot will generate a hover enter.
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits,
+ policyFlags);
+
+ // More than two pointers are delegated to the view hierarchy.
+ mCurrentState = STATE_DELEGATING;
+ mSendHoverDelayed.remove();
+ if (mTouchExploreGestureInProgress) {
+ sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ mTouchExploreGestureInProgress = false;
+ }
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ }
+ }
+ } break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP: {
+ final int pointerId = pointerTracker.getLastReceivedUpPointerId();
+ final int pointerIdBits = (1 << pointerId);
+ switch (activePointerCount) {
+ case 0: {
+ // If the pointer that went up was not active we have nothing to do.
+ if (!pointerTracker.wasLastReceivedUpPointerActive()) {
+ break;
+ }
+
+ // If touch exploring announce the end of the gesture.
+ if (mTouchExploreGestureInProgress) {
+ sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ mTouchExploreGestureInProgress = false;
+ }
+
+ // Detect whether to activate i.e. click on the last explored location.
+ if (mLastTouchExploreEvent != null) {
+
+ // If the down was not in the time slop => nothing else to do.
+ final long eventTime =
+ pointerTracker.getLastReceivedUpPointerDownTime();
+ final long exploreTime = mLastTouchExploreEvent.getEventTime();
+ final long deltaTime = eventTime - exploreTime;
+ if (deltaTime > ACTIVATION_TIME_SLOP) {
+ mSendHoverDelayed.forceSendAndRemove();
+ final int lastAction = mPointerTracker.getLastInjectedHoverAction();
+ if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT,
+ pointerIdBits, policyFlags);
+ }
+ mLastTouchExploreEvent = MotionEvent.obtain(event);
+ break;
+ }
+
+ // If the pointer moved more than the tap slop => nothing else to do.
+ final int pointerIndex = event.findPointerIndex(pointerId);
+ final float deltaX = pointerTracker.getLastReceivedUpPointerDownX()
+ - event.getX(pointerIndex);
+ final float deltaY = pointerTracker.getLastReceivedUpPointerDownY()
+ - event.getY(pointerIndex);
+ final float deltaMove = (float) Math.hypot(deltaX, deltaY);
+ if (deltaMove > mTouchExplorationTapSlop) {
+ mSendHoverDelayed.forceSendAndRemove();
+ final int lastAction = mPointerTracker.getLastInjectedHoverAction();
+ if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT,
+ pointerIdBits, policyFlags);
+ }
+ mLastTouchExploreEvent = MotionEvent.obtain(event);
+ break;
+ }
+
+ // All preconditions are met, so click the last explored location.
+ mSendHoverDelayed.forceSendAndRemove();
+ sendActionDownAndUp(mLastTouchExploreEvent, policyFlags);
+ mLastTouchExploreEvent = null;
+ } else {
+ mSendHoverDelayed.forceSendAndRemove();
+ final int lastAction = mPointerTracker.getLastInjectedHoverAction();
+ if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT,
+ pointerIdBits, policyFlags);
+ }
+ mLastTouchExploreEvent = MotionEvent.obtain(event);
+ }
+ } break;
+ }
+ } break;
+ case MotionEvent.ACTION_CANCEL: {
+ final int lastAction = pointerTracker.getLastInjectedHoverAction();
+ if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
+ final int pointerId = pointerTracker.getPrimaryActivePointerId();
+ final int pointerIdBits = (1 << pointerId);
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits,
+ policyFlags);
+ }
+ clear();
+ } break;
+ }
+ }
+
+ /**
+ * Handles a motion event in dragging state.
+ *
+ * @param event The event to be handled.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) {
+ final int pointerIdBits = (1 << mDraggingPointerId);
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ throw new IllegalStateException("Dragging state can be reached only if two "
+ + "pointers are already down");
+ }
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ // We are in dragging state so we have two pointers and another one
+ // goes down => delegate the three pointers to the view hierarchy
+ mCurrentState = STATE_DELEGATING;
+ sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ final int activePointerCount = mPointerTracker.getActivePointerCount();
+ switch (activePointerCount) {
+ case 2: {
+ if (isDraggingGesture(event)) {
+ // If still dragging send a drag event.
+ sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits,
+ policyFlags);
+ } else {
+ // The two pointers are moving either in different directions or
+ // no close enough => delegate the gesture to the view hierarchy.
+ mCurrentState = STATE_DELEGATING;
+ // Send an event to the end of the drag gesture.
+ sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
+ policyFlags);
+ // Deliver all active pointers to the view hierarchy.
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ }
+ } break;
+ default: {
+ mCurrentState = STATE_DELEGATING;
+ // Send an event to the end of the drag gesture.
+ sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
+ policyFlags);
+ // Deliver all active pointers to the view hierarchy.
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ }
+ }
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ // Send an event to the end of the drag gesture.
+ sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ } break;
+ case MotionEvent.ACTION_CANCEL: {
+ clear();
+ } break;
+ }
+ }
+
+ /**
+ * Handles a motion event in delegating state.
+ *
+ * @param event The event to be handled.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ public void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ throw new IllegalStateException("Delegating state can only be reached if "
+ + "there is at least one pointer down!");
+ }
+ case MotionEvent.ACTION_UP: {
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ // Check whether some other pointer became active because they have moved
+ // a given distance and if such exist send them to the view hierarchy
+ final int notInjectedCount = mPointerTracker.getNotInjectedActivePointerCount();
+ if (notInjectedCount > 0) {
+ sendDownForAllActiveNotInjectedPointers(event, policyFlags);
+ }
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ // No active pointers => go to initial state.
+ if (mPointerTracker.getActivePointerCount() == 0) {
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ }
+ } break;
+ case MotionEvent.ACTION_CANCEL: {
+ clear();
+ } break;
+ }
+ // Deliver the event striping out inactive pointers.
+ sendMotionEventStripInactivePointers(event, policyFlags);
+ }
+
+ /**
+ * Sends down events to the view hierarchy for all active pointers which are
+ * not already being delivered i.e. pointers that are not yet injected.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendDownForAllActiveNotInjectedPointers(MotionEvent prototype, int policyFlags) {
+ final PointerProperties[] pointerProperties = mTempPointerProperties;
+ final PointerCoords[] pointerCoords = mTempPointerCoords;
+ final PointerTracker pointerTracker = mPointerTracker;
+ int pointerDataIndex = 0;
+
+ final int pinterCount = prototype.getPointerCount();
+ for (int i = 0; i < pinterCount; i++) {
+ final int pointerId = prototype.getPointerId(i);
+
+ // Skip inactive pointers.
+ if (!pointerTracker.isActivePointer(pointerId)) {
+ continue;
+ }
+ // Skip already delivered pointers.
+ if (pointerTracker.isInjectedPointerDown(pointerId)) {
+ continue;
+ }
+
+ // Populate and inject an event for the current pointer.
+ prototype.getPointerProperties(i, pointerProperties[pointerDataIndex]);
+ prototype.getPointerCoords(i, pointerCoords[pointerDataIndex]);
+
+ final long downTime = pointerTracker.getLastInjectedDownEventTime();
+ final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, pointerDataIndex);
+ final int pointerCount = pointerDataIndex + 1;
+ final long eventTime = SystemClock.uptimeMillis();
+
+ MotionEvent event = MotionEvent.obtain(downTime, eventTime,
+ action, pointerCount, pointerProperties, pointerCoords,
+ prototype.getMetaState(), prototype.getButtonState(),
+ prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+ prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags());
+ sendMotionEvent(event, policyFlags);
+ event.recycle();
+
+ pointerDataIndex++;
+ }
+ }
+
+ /**
+ * Sends up events to the view hierarchy for all active pointers which are
+ * already being delivered i.e. pointers that are injected.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
+ final PointerTracker pointerTracker = mPointerTracker;
+ final PointerProperties[] pointerProperties = mTempPointerProperties;
+ final PointerCoords[] pointerCoords = mTempPointerCoords;
+ int pointerDataIndex = 0;
+
+ final int pointerCount = prototype.getPointerCount();
+ for (int i = 0; i < pointerCount; i++) {
+ final int pointerId = prototype.getPointerId(i);
+
+ // Skip non injected down pointers.
+ if (!pointerTracker.isInjectedPointerDown(pointerId)) {
+ continue;
+ }
+
+ // Populate and inject event.
+ prototype.getPointerProperties(i, pointerProperties[pointerDataIndex]);
+ prototype.getPointerCoords(i, pointerCoords[pointerDataIndex]);
+
+ final long downTime = pointerTracker.getLastInjectedDownEventTime();
+ final int action = computeInjectionAction(MotionEvent.ACTION_UP, pointerDataIndex);
+ final int newPointerCount = pointerDataIndex + 1;
+ final long eventTime = SystemClock.uptimeMillis();
+
+ MotionEvent event = MotionEvent.obtain(downTime, eventTime, action,
+ newPointerCount, pointerProperties, pointerCoords,
+ prototype.getMetaState(), prototype.getButtonState(),
+ prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+ prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags());
+
+ sendMotionEvent(event, policyFlags);
+ event.recycle();
+
+ pointerDataIndex++;
+ }
+ }
+
+ /**
+ * Sends a motion event by first stripping the inactive pointers.
+ *
+ * @param prototype The prototype from which to create the injected event.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendMotionEventStripInactivePointers(MotionEvent prototype, int policyFlags) {
+ PointerTracker pointerTracker = mPointerTracker;
+
+ // All pointers active therefore we just inject the event as is.
+ if (prototype.getPointerCount() == pointerTracker.getActivePointerCount()) {
+ sendMotionEvent(prototype, policyFlags);
+ return;
+ }
+
+ // No active pointers and the one that just went up was not
+ // active, therefore we have nothing to do.
+ if (pointerTracker.getActivePointerCount() == 0
+ && !pointerTracker.wasLastReceivedUpPointerActive()) {
+ return;
+ }
+
+ int pointerIdBits = 0;
+ final int pointerCount = prototype.getPointerCount();
+ for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) {
+ final int pointerId = prototype.getPointerId(pointerIndex);
+ // If the pointer is inactive or the pointer that just went up
+ // was inactive we strip the pointer data from the event.
+ if (pointerTracker.isActiveOrWasLastActiveUpPointer(pointerId)) {
+ pointerIdBits |= (1 << pointerId);
+ }
+ }
+
+ MotionEvent event = prototype.split(pointerIdBits);
+ sendMotionEvent(event, policyFlags);
+ event.recycle();
+ }
+
+ /**
+ * Sends an up and down events.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) {
+ final PointerProperties[] pointerProperties = mTempPointerProperties;
+ final PointerCoords[] pointerCoords = mTempPointerCoords;
+ final int pointerId = mPointerTracker.getLastReceivedUpPointerId();
+ final int pointerIndex = prototype.findPointerIndex(pointerId);
+
+ // Send down.
+ prototype.getPointerProperties(pointerIndex, pointerProperties[0]);
+ prototype.getPointerCoords(pointerIndex, pointerCoords[0]);
+
+ final long downTime = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN,
+ 1, pointerProperties, pointerCoords,
+ prototype.getMetaState(), prototype.getButtonState(),
+ prototype.getXPrecision(), prototype.getYPrecision(), prototype.getDeviceId(),
+ prototype.getEdgeFlags(), prototype.getSource(), prototype.getFlags());
+ sendMotionEvent(event, policyFlags);
+
+ // Send up.
+ event.setAction(MotionEvent.ACTION_UP);
+ sendMotionEvent(event, policyFlags);
+ event.recycle();
+ }
+
+ /**
+ * Sends an event.
+ *
+ * @param event The event to send.
+ * @param action The action of the event.
+ * @param pointerIdBits The bits of the pointers to send.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits,
+ int policyFlags) {
+ MotionEvent event = prototype.split(pointerIdBits);
+ event.setDownTime(mPointerTracker.getLastInjectedDownEventTime());
+ event.setAction(action);
+ sendMotionEvent(event, policyFlags);
+ event.recycle();
+ }
+
+ /**
+ * Computes the action for an injected event based on a masked action
+ * and a pointer index.
+ *
+ * @param actionMasked The masked action.
+ * @param pointerIndex The index of the pointer which has changed.
+ * @return The action to be used for injection.
+ */
+ private int computeInjectionAction(int actionMasked, int pointerIndex) {
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ PointerTracker pointerTracker = mPointerTracker;
+ // Compute the action based on how many down pointers are injected.
+ if (pointerTracker.getInjectedPointerDownCount() == 0) {
+ return MotionEvent.ACTION_DOWN;
+ } else {
+ return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+ | MotionEvent.ACTION_POINTER_DOWN;
+ }
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ PointerTracker pointerTracker = mPointerTracker;
+ // Compute the action based on how many down pointers are injected.
+ if (pointerTracker.getInjectedPointerDownCount() == 1) {
+ return MotionEvent.ACTION_UP;
+ } else {
+ return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+ | MotionEvent.ACTION_POINTER_UP;
+ }
+ }
+ default:
+ return actionMasked;
+ }
+ }
+
+ /**
+ * Determines whether a two pointer gesture is a dragging one.
+ *
+ * @param event The event with the pointer data.
+ * @return True if the gesture is a dragging one.
+ */
+ private boolean isDraggingGesture(MotionEvent event) {
+ PointerTracker pointerTracker = mPointerTracker;
+ int[] pointerIds = mTempPointerIds;
+ pointerTracker.populateActivePointerIds(pointerIds);
+
+ final int firstPtrIndex = event.findPointerIndex(pointerIds[0]);
+ final int secondPtrIndex = event.findPointerIndex(pointerIds[1]);
+
+ final float firstPtrX = event.getX(firstPtrIndex);
+ final float firstPtrY = event.getY(firstPtrIndex);
+ final float secondPtrX = event.getX(secondPtrIndex);
+ final float secondPtrY = event.getY(secondPtrIndex);
+
+ // Check if the pointers are close enough.
+ final float deltaX = firstPtrX - secondPtrX;
+ final float deltaY = firstPtrY - secondPtrY;
+ final float deltaMove = (float) Math.hypot(deltaX, deltaY);
+ if (deltaMove > mDraggingDistance) {
+ return false;
+ }
+
+ // Check if the pointers are moving in the same direction.
+ final float firstDeltaX =
+ firstPtrX - pointerTracker.getReceivedPointerDownX(firstPtrIndex);
+ final float firstDeltaY =
+ firstPtrY - pointerTracker.getReceivedPointerDownY(firstPtrIndex);
+ final float firstMagnitude =
+ (float) Math.sqrt(firstDeltaX * firstDeltaX + firstDeltaY * firstDeltaY);
+ final float firstXNormalized =
+ (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
+ final float firstYNormalized =
+ (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
+
+ final float secondDeltaX =
+ secondPtrX - pointerTracker.getReceivedPointerDownX(secondPtrIndex);
+ final float secondDeltaY =
+ secondPtrY - pointerTracker.getReceivedPointerDownY(secondPtrIndex);
+ final float secondMagnitude =
+ (float) Math.sqrt(secondDeltaX * secondDeltaX + secondDeltaY * secondDeltaY);
+ final float secondXNormalized =
+ (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
+ final float secondYNormalized =
+ (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
+
+ final float angleCos =
+ firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
+
+ if (angleCos < MIN_ANGLE_COS) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Sends an event announcing the start/end of a touch exploration gesture.
+ *
+ * @param eventType The type of the event to send.
+ */
+ private void sendAccessibilityEvent(int eventType) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ event.setPackageName(mContext.getPackageName());
+ event.setClassName(getClass().getName());
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+
+ /**
+ * Sends a motion event to the input filter for injection.
+ *
+ * @param event The event to send.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ private void sendMotionEvent(MotionEvent event, int policyFlags) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG_INJECTED, "Injecting event: " + event + ", policyFlags=0x"
+ + Integer.toHexString(policyFlags));
+ }
+ // Make sure that the user will see the event.
+ policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
+ mPointerTracker.onInjectedMotionEvent(event);
+ mInputFilter.sendInputEvent(event, policyFlags);
+ }
+
+ /**
+ * Clears the internal state of this explorer.
+ */
+ private void clear() {
+ mSendHoverDelayed.remove();
+ mPointerTracker.clear();
+ mLastTouchExploreEvent = null;
+ mCurrentState = STATE_TOUCH_EXPLORING;
+ mTouchExploreGestureInProgress = false;
+ mDraggingPointerId = INVALID_POINTER_ID;
+ }
+
+ /**
+ * Helper class for tracking pointers and more specifically which of
+ * them are currently down, which are active, and which are delivered
+ * to the view hierarchy. The enclosing {@link TouchExplorer} uses the
+ * pointer state reported by this class to perform touch exploration.
+ * <p>
+ * The main purpose of this class is to allow the touch explorer to
+ * disregard pointers put down by accident by the user and not being
+ * involved in the interaction. For example, a blind user grabs the
+ * device with her left hand such that she touches the screen and she
+ * uses her right hand's index finger to explore the screen content.
+ * In this scenario the touches generated by the left hand are to be
+ * ignored.
+ */
+ class PointerTracker {
+ private static final String LOG_TAG = "PointerTracker";
+
+ // The coefficient by which to multiply
+ // ViewConfiguration.#getScaledTouchSlop()
+ // to compute #mThresholdActivePointer.
+ private static final int COEFFICIENT_ACTIVE_POINTER = 2;
+
+ // Pointers that moved less than mThresholdActivePointer
+ // are considered active i.e. are ignored.
+ private final double mThresholdActivePointer;
+
+ // Keep track of where and when a pointer went down.
+ private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT];
+ private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT];
+ private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT];
+
+ // Which pointers are down.
+ private int mReceivedPointersDown;
+
+ // Which down pointers are active.
+ private int mActivePointers;
+
+ // Primary active pointer which is either the first that went down
+ // or if it goes up the next active that most recently went down.
+ private int mPrimaryActivePointerId;
+
+ // Flag indicating that there is at least one active pointer moving.
+ private boolean mHasMovingActivePointer;
+
+ // Keep track of which pointers sent to the system are down.
+ private int mInjectedPointersDown;
+
+ // Keep track of the last up pointer data.
+ private float mLastReceivedUpPointerDownX;
+ private float mLastReveivedUpPointerDownY;
+ private long mLastReceivedUpPointerDownTime;
+ private int mLastReceivedUpPointerId;
+ private boolean mLastReceivedUpPointerActive;
+
+ // The time of the last injected down.
+ private long mLastInjectedDownEventTime;
+
+ // The action of the last injected hover event.
+ private int mLastInjectedHoverEventAction = MotionEvent.ACTION_HOVER_EXIT;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context Context for looking up resources.
+ */
+ public PointerTracker(Context context) {
+ mThresholdActivePointer =
+ ViewConfiguration.get(context).getScaledTouchSlop() * COEFFICIENT_ACTIVE_POINTER;
+ }
+
+ /**
+ * Clears the internals state.
+ */
+ public void clear() {
+ Arrays.fill(mReceivedPointerDownX, 0);
+ Arrays.fill(mReceivedPointerDownY, 0);
+ Arrays.fill(mReceivedPointerDownTime, 0);
+ mReceivedPointersDown = 0;
+ mActivePointers = 0;
+ mPrimaryActivePointerId = 0;
+ mHasMovingActivePointer = false;
+ mInjectedPointersDown = 0;
+ mLastReceivedUpPointerDownX = 0;
+ mLastReveivedUpPointerDownY = 0;
+ mLastReceivedUpPointerDownTime = 0;
+ mLastReceivedUpPointerId = 0;
+ mLastReceivedUpPointerActive = false;
+ }
+
+ /**
+ * Processes a received {@link MotionEvent} event.
+ *
+ * @param event The event to process.
+ */
+ public void onReceivedMotionEvent(MotionEvent event) {
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ // New gesture so restart tracking injected down pointers.
+ mInjectedPointersDown = 0;
+ handleReceivedPointerDown(0, event);
+ } break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ handleReceivedPointerDown(event.getActionIndex(), event);
+ } break;
+ case MotionEvent.ACTION_MOVE: {
+ handleReceivedPointerMove(event);
+ } break;
+ case MotionEvent.ACTION_UP: {
+ handleReceivedPointerUp(0, event);
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ handleReceivedPointerUp(event.getActionIndex(), event);
+ } break;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Received pointer: " + toString());
+ }
+ }
+
+ /**
+ * Processes an injected {@link MotionEvent} event.
+ *
+ * @param event The event to process.
+ */
+ public void onInjectedMotionEvent(MotionEvent event) {
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ handleInjectedPointerDown(0, event);
+ } break;
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ handleInjectedPointerDown(event.getActionIndex(), event);
+ } break;
+ case MotionEvent.ACTION_UP: {
+ handleInjectedPointerUp(0, event);
+ } break;
+ case MotionEvent.ACTION_POINTER_UP: {
+ handleInjectedPointerUp(event.getActionIndex(), event);
+ } break;
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_HOVER_EXIT: {
+ mLastInjectedHoverEventAction = event.getActionMasked();
+ } break;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Injected pointer: " + toString());
+ }
+ }
+
+ /**
+ * @return The number of received pointers that are down.
+ */
+ public int getReceivedPointerDownCount() {
+ return Integer.bitCount(mReceivedPointersDown);
+ }
+
+ /**
+ * @return The number of down input pointers that are active.
+ */
+ public int getActivePointerCount() {
+ return Integer.bitCount(mActivePointers);
+ }
+
+ /**
+ * Whether an received pointer is down.
+ *
+ * @param pointerId The unique pointer id.
+ * @return True if the pointer is down.
+ */
+ public boolean isReceivedPointerDown(int pointerId) {
+ final int pointerFlag = (1 << pointerId);
+ return (mReceivedPointersDown & pointerFlag) != 0;
+ }
+
+ /**
+ * Whether an injected pointer is down.
+ *
+ * @param pointerId The unique pointer id.
+ * @return True if the pointer is down.
+ */
+ public boolean isInjectedPointerDown(int pointerId) {
+ final int pointerFlag = (1 << pointerId);
+ return (mInjectedPointersDown & pointerFlag) != 0;
+ }
+
+ /**
+ * @return The number of down pointers injected to the view hierarchy.
+ */
+ public int getInjectedPointerDownCount() {
+ return Integer.bitCount(mInjectedPointersDown);
+ }
+
+ /**
+ * Whether an input pointer is active.
+ *
+ * @param pointerId The unique pointer id.
+ * @return True if the pointer is active.
+ */
+ public boolean isActivePointer(int pointerId) {
+ final int pointerFlag = (1 << pointerId);
+ return (mActivePointers & pointerFlag) != 0;
+ }
+
+ /**
+ * @param pointerId The unique pointer id.
+ * @return The X coordinate where the pointer went down.
+ */
+ public float getReceivedPointerDownX(int pointerId) {
+ return mReceivedPointerDownX[pointerId];
+ }
+
+ /**
+ * @param pointerId The unique pointer id.
+ * @return The Y coordinate where the pointer went down.
+ */
+ public float getReceivedPointerDownY(int pointerId) {
+ return mReceivedPointerDownY[pointerId];
+ }
+
+ /**
+ * @param pointerId The unique pointer id.
+ * @return The time when the pointer went down.
+ */
+ public long getReceivedPointerDownTime(int pointerId) {
+ return mReceivedPointerDownTime[pointerId];
+ }
+
+ /**
+ * @return The id of the primary pointer.
+ */
+ public int getPrimaryActivePointerId() {
+ if (mPrimaryActivePointerId == INVALID_POINTER_ID) {
+ mPrimaryActivePointerId = findPrimaryActivePointer();
+ }
+ return mPrimaryActivePointerId;
+ }
+
+ /**
+ * @return The X coordinate where the last up received pointer went down.
+ */
+ public float getLastReceivedUpPointerDownX() {
+ return mLastReceivedUpPointerDownX;
+ }
+
+ /**
+ * @return The Y coordinate where the last up received pointer went down.
+ */
+ public float getLastReceivedUpPointerDownY() {
+ return mLastReveivedUpPointerDownY;
+ }
+
+ /**
+ * @return The time when the last up received pointer went down.
+ */
+ public long getLastReceivedUpPointerDownTime() {
+ return mLastReceivedUpPointerDownTime;
+ }
+
+ /**
+ * @return The id of the last received pointer that went up.
+ */
+ public int getLastReceivedUpPointerId() {
+ return mLastReceivedUpPointerId;
+ }
+
+ /**
+ * @return Whether the last received pointer that went up was active.
+ */
+ public boolean wasLastReceivedUpPointerActive() {
+ return mLastReceivedUpPointerActive;
+ }
+
+ /**
+ * @return The time of the last injected down event.
+ */
+ public long getLastInjectedDownEventTime() {
+ return mLastInjectedDownEventTime;
+ }
+
+ /**
+ * @return The action of the last injected hover event.
+ */
+ public int getLastInjectedHoverAction() {
+ return mLastInjectedHoverEventAction;
+ }
+
+ /**
+ * Populates the active pointer IDs to the given array.
+ * <p>
+ * Note: The client is responsible for providing large enough array.
+ *
+ * @param outPointerIds The array to which to write the active pointers.
+ */
+ public void populateActivePointerIds(int[] outPointerIds) {
+ int index = 0;
+ for (int idBits = mActivePointers; idBits != 0; ) {
+ final int id = Integer.numberOfTrailingZeros(idBits);
+ idBits &= ~(1 << id);
+ outPointerIds[index] = id;
+ index++;
+ }
+ }
+
+ /**
+ * @return The number of non injected active pointers.
+ */
+ public int getNotInjectedActivePointerCount() {
+ final int pointerState = mActivePointers & ~mInjectedPointersDown;
+ return Integer.bitCount(pointerState);
+ }
+
+ /**
+ * @param pointerId The unique pointer id.
+ * @return Whether the pointer is active or was the last active than went up.
+ */
+ private boolean isActiveOrWasLastActiveUpPointer(int pointerId) {
+ return (isActivePointer(pointerId)
+ || (mLastReceivedUpPointerId == pointerId
+ && mLastReceivedUpPointerActive));
+ }
+
+ /**
+ * Handles a received pointer down event.
+ *
+ * @param pointerIndex The index of the pointer that has changed.
+ * @param event The event to be handled.
+ */
+ private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final int pointerFlag = (1 << pointerId);
+
+ mLastReceivedUpPointerId = 0;
+ mLastReceivedUpPointerDownX = 0;
+ mLastReveivedUpPointerDownY = 0;
+ mLastReceivedUpPointerDownTime = 0;
+ mLastReceivedUpPointerActive = false;
+
+ mReceivedPointersDown |= pointerFlag;
+ mReceivedPointerDownX[pointerId] = event.getX(pointerIndex);
+ mReceivedPointerDownY[pointerId] = event.getY(pointerIndex);
+ mReceivedPointerDownTime[pointerId] = event.getEventTime();
+
+ if (!mHasMovingActivePointer) {
+ // If still no moving active pointers every
+ // down pointer is the only active one.
+ mActivePointers = pointerFlag;
+ mPrimaryActivePointerId = pointerId;
+ } else {
+ // If at least one moving active pointer every
+ // subsequent down pointer is active.
+ mActivePointers |= pointerFlag;
+ }
+ }
+
+ /**
+ * Handles a received pointer move event.
+ *
+ * @param event The event to be handled.
+ */
+ private void handleReceivedPointerMove(MotionEvent event) {
+ detectActivePointers(event);
+ }
+
+ /**
+ * Handles a received pointer up event.
+ *
+ * @param pointerIndex The index of the pointer that has changed.
+ * @param event The event to be handled.
+ */
+ private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final int pointerFlag = (1 << pointerId);
+
+ mLastReceivedUpPointerId = pointerId;
+ mLastReceivedUpPointerDownX = getReceivedPointerDownX(pointerId);
+ mLastReveivedUpPointerDownY = getReceivedPointerDownY(pointerId);
+ mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
+ mLastReceivedUpPointerActive = isActivePointer(pointerId);
+
+ mReceivedPointersDown &= ~pointerFlag;
+ mActivePointers &= ~pointerFlag;
+ mReceivedPointerDownX[pointerId] = 0;
+ mReceivedPointerDownY[pointerId] = 0;
+ mReceivedPointerDownTime[pointerId] = 0;
+
+ if (mActivePointers == 0) {
+ mHasMovingActivePointer = false;
+ }
+ if (mPrimaryActivePointerId == pointerId) {
+ mPrimaryActivePointerId = INVALID_POINTER_ID;
+ }
+ }
+
+ /**
+ * Handles a injected pointer down event.
+ *
+ * @param pointerIndex The index of the pointer that has changed.
+ * @param event The event to be handled.
+ */
+ private void handleInjectedPointerDown(int pointerIndex, MotionEvent event) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final int pointerFlag = (1 << pointerId);
+ mInjectedPointersDown |= pointerFlag;
+ mLastInjectedDownEventTime = event.getEventTime();
+ }
+
+ /**
+ * Handles a injected pointer up event.
+ *
+ * @param pointerIndex The index of the pointer that has changed.
+ * @param event The event to be handled.
+ */
+ private void handleInjectedPointerUp(int pointerIndex, MotionEvent event) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final int pointerFlag = (1 << pointerId);
+ mInjectedPointersDown &= ~pointerFlag;
+ if (mInjectedPointersDown == 0) {
+ mLastInjectedDownEventTime = 0;
+ }
+ }
+
+ /**
+ * Detects the active pointers in an event.
+ *
+ * @param event The event to examine.
+ */
+ private void detectActivePointers(MotionEvent event) {
+ for (int i = 0, count = event.getPointerCount(); i < count; i++) {
+ final int pointerId = event.getPointerId(i);
+ if (mHasMovingActivePointer) {
+ // If already active => nothing to do.
+ if (isActivePointer(pointerId)) {
+ continue;
+ }
+ }
+ // Active pointers are ones that moved more than a given threshold.
+ final float pointerDeltaMove = computePointerDeltaMove(i, event);
+ if (pointerDeltaMove > mThresholdActivePointer) {
+ final int pointerFlag = (1 << pointerId);
+ mActivePointers |= pointerFlag;
+ mHasMovingActivePointer = true;
+ }
+ }
+ }
+
+ /**
+ * @return The primary active pointer.
+ */
+ private int findPrimaryActivePointer() {
+ int primaryActivePointerId = INVALID_POINTER_ID;
+ long minDownTime = Long.MAX_VALUE;
+ // Find the active pointer that went down first.
+ for (int i = 0, count = mReceivedPointerDownTime.length; i < count; i++) {
+ if (isActivePointer(i)) {
+ final long downPointerTime = mReceivedPointerDownTime[i];
+ if (downPointerTime < minDownTime) {
+ minDownTime = downPointerTime;
+ primaryActivePointerId = i;
+ }
+ }
+ }
+ return primaryActivePointerId;
+ }
+
+ /**
+ * Computes the move for a given action pointer index since the
+ * corresponding pointer went down.
+ *
+ * @param pointerIndex The action pointer index.
+ * @param event The event to examine.
+ * @return The distance the pointer has moved.
+ */
+ private float computePointerDeltaMove(int pointerIndex, MotionEvent event) {
+ final int pointerId = event.getPointerId(pointerIndex);
+ final float deltaX = event.getX(pointerIndex) - mReceivedPointerDownX[pointerId];
+ final float deltaY = event.getY(pointerIndex) - mReceivedPointerDownY[pointerId];
+ return (float) Math.hypot(deltaX, deltaY);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("=========================");
+ builder.append("\nDown pointers #");
+ builder.append(getReceivedPointerDownCount());
+ builder.append(" [ ");
+ for (int i = 0; i < MAX_POINTER_COUNT; i++) {
+ if (isReceivedPointerDown(i)) {
+ builder.append(i);
+ builder.append(" ");
+ }
+ }
+ builder.append("]");
+ builder.append("\nActive pointers #");
+ builder.append(getActivePointerCount());
+ builder.append(" [ ");
+ for (int i = 0; i < MAX_POINTER_COUNT; i++) {
+ if (isActivePointer(i)) {
+ builder.append(i);
+ builder.append(" ");
+ }
+ }
+ builder.append("]");
+ builder.append("\nPrimary active pointer id [ ");
+ builder.append(getPrimaryActivePointerId());
+ builder.append(" ]");
+ builder.append("\n=========================");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Class for delayed sending of hover events.
+ */
+ private final class SendHoverDelayed implements Runnable {
+ private static final String LOG_TAG = "SendHoverEnterOrExitDelayed";
+
+ private MotionEvent mEvent;
+ private int mAction;
+ private int mPointerIdBits;
+ private int mPolicyFlags;
+
+ public void post(MotionEvent prototype, int action, int pointerIdBits, int policyFlags,
+ long delay) {
+ remove();
+ mEvent = MotionEvent.obtain(prototype);
+ mAction = action;
+ mPointerIdBits = pointerIdBits;
+ mPolicyFlags = policyFlags;
+ mHandler.postDelayed(this, delay);
+ }
+
+ public void remove() {
+ mHandler.removeCallbacks(this);
+ clear();
+ }
+
+ private boolean isPenidng() {
+ return (mEvent != null);
+ }
+
+ private void clear() {
+ if (!isPenidng()) {
+ return;
+ }
+ mEvent.recycle();
+ mEvent = null;
+ mAction = 0;
+ mPointerIdBits = -1;
+ mPolicyFlags = 0;
+ }
+
+ public void forceSendAndRemove() {
+ if (isPenidng()) {
+ run();
+ remove();
+ }
+ }
+
+ public void run() {
+ if (DEBUG) {
+ if (mAction == MotionEvent.ACTION_HOVER_ENTER) {
+ Slog.d(LOG_TAG, "Injecting: " + MotionEvent.ACTION_HOVER_ENTER);
+ } else if (mAction == MotionEvent.ACTION_HOVER_MOVE) {
+ Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_MOVE");
+ } else if (mAction == MotionEvent.ACTION_HOVER_EXIT) {
+ Slog.d(LOG_TAG, "Injecting: MotionEvent.ACTION_HOVER_EXIT");
+ }
+ }
+
+ sendMotionEvent(mEvent, mAction, mPointerIdBits, mPolicyFlags);
+ clear();
+ }
+ }
+}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 9989d3b..c2a8a1d 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -42,8 +42,10 @@ import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
+import android.app.IProcessObserver;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
+import android.app.IThumbnailRetriever;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
@@ -104,7 +106,6 @@ import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.util.Config;
import android.util.EventLog;
import android.util.Slog;
import android.util.Log;
@@ -164,7 +165,7 @@ public final class ActivityManagerService extends ActivityManagerNative
static final boolean DEBUG_URI_PERMISSION = localLOGV || false;
static final boolean DEBUG_USER_LEAVING = localLOGV || false;
static final boolean DEBUG_RESULTS = localLOGV || false;
- static final boolean DEBUG_BACKUP = localLOGV || false;
+ static final boolean DEBUG_BACKUP = localLOGV || true;
static final boolean DEBUG_CONFIGURATION = localLOGV || false;
static final boolean DEBUG_POWER = localLOGV || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
@@ -442,8 +443,7 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* List of intents that were used to start the most recent tasks.
*/
- final ArrayList<TaskRecord> mRecentTasks
- = new ArrayList<TaskRecord>();
+ final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
/**
* All of the applications we currently have running organized by name.
@@ -451,8 +451,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* returned by the package manager), and the keys are ApplicationRecord
* objects.
*/
- final ProcessMap<ProcessRecord> mProcessNames
- = new ProcessMap<ProcessRecord>();
+ final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
/**
* The currently running heavy-weight process, if any.
@@ -481,8 +480,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* <p>NOTE: This object is protected by its own lock, NOT the global
* activity manager lock!
*/
- final SparseArray<ProcessRecord> mPidsSelfLocked
- = new SparseArray<ProcessRecord>();
+ final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
/**
* All of the processes that have been forced to be foreground. The key
@@ -854,14 +852,6 @@ public final class ActivityManagerService extends ActivityManagerNative
* Current sequence id for process LRU updating.
*/
int mLruSeq = 0;
-
- /**
- * Set to true if the ANDROID_SIMPLE_PROCESS_MANAGEMENT envvar
- * is set, indicating the user wants processes started in such a way
- * that they can use ANDROID_PROCESS_WRAPPER and know what will be
- * running in each process (thus no pre-initialized process, etc).
- */
- boolean mSimpleProcessManagement = false;
/**
* System monitoring: number of processes that died since the last
@@ -885,7 +875,10 @@ public final class ActivityManagerService extends ActivityManagerNative
final RemoteCallbackList<IActivityWatcher> mWatchers
= new RemoteCallbackList<IActivityWatcher>();
-
+
+ final RemoteCallbackList<IProcessObserver> mProcessObservers
+ = new RemoteCallbackList<IProcessObserver>();
+
/**
* Callback of last caller to {@link #requestPss}.
*/
@@ -975,6 +968,8 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int CLEAR_DNS_CACHE = 28;
static final int UPDATE_HTTP_PROXY = 29;
static final int SHOW_COMPAT_MODE_DIALOG_MSG = 30;
+ static final int DISPATCH_FOREGROUND_ACTIVITIES_CHANGED = 31;
+ static final int DISPATCH_PROCESS_DIED = 32;
AlertDialog mUidAlert;
CompatModeDialog mCompatModeDialog;
@@ -1302,6 +1297,20 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
+ break;
+ }
+ case DISPATCH_FOREGROUND_ACTIVITIES_CHANGED: {
+ final int pid = msg.arg1;
+ final int uid = msg.arg2;
+ final boolean foregroundActivities = (Boolean) msg.obj;
+ dispatchForegroundActivitiesChanged(pid, uid, foregroundActivities);
+ break;
+ }
+ case DISPATCH_PROCESS_DIED: {
+ final int pid = msg.arg1;
+ final int uid = msg.arg2;
+ dispatchProcessDied(pid, uid);
+ break;
}
}
}
@@ -1313,6 +1322,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ServiceManager.addService("activity", m);
ServiceManager.addService("meminfo", new MemBinder(m));
+ ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(m));
}
@@ -1438,6 +1448,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ static class GraphicsBinder extends Binder {
+ ActivityManagerService mActivityManagerService;
+ GraphicsBinder(ActivityManagerService activityManagerService) {
+ mActivityManagerService = activityManagerService;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);
+ }
+ }
+
static class CpuBinder extends Binder {
ActivityManagerService mActivityManagerService;
CpuBinder(ActivityManagerService activityManagerService) {
@@ -1455,15 +1477,6 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private ActivityManagerService() {
- String v = System.getenv("ANDROID_SIMPLE_PROCESS_MANAGEMENT");
- if (v != null && Integer.getInteger(v) != 0) {
- mSimpleProcessManagement = true;
- }
- v = System.getenv("ANDROID_DEBUG_APP");
- if (v != null) {
- mSimpleProcessManagement = true;
- }
-
Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
File dataDir = Environment.getDataDirectory();
@@ -1762,7 +1775,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mLruSeq++;
updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0);
}
-
+
final ProcessRecord getProcessRecordLocked(
String processName, int uid) {
if (uid == Process.SYSTEM_UID) {
@@ -1935,8 +1948,7 @@ public final class ActivityManagerService extends ActivityManagerNative
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
int pid = Process.start("android.app.ActivityThread",
- mSimpleProcessManagement ? app.processName : null, uid, uid,
- gids, debugFlags, null);
+ app.processName, uid, uid, gids, debugFlags, null);
BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
synchronized (bs) {
if (bs.isOnBattery()) {
@@ -2162,6 +2174,36 @@ public final class ActivityManagerService extends ActivityManagerNative
mWatchers.finishBroadcast();
}
+ private void dispatchForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+ int i = mProcessObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ observer.onForegroundActivitiesChanged(pid, uid, foregroundActivities);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mProcessObservers.finishBroadcast();
+ }
+
+ private void dispatchProcessDied(int pid, int uid) {
+ int i = mProcessObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ observer.onProcessDied(pid, uid);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mProcessObservers.finishBroadcast();
+ }
+
final void doPendingActivityLaunchesLocked(boolean doResume) {
final int N = mPendingActivityLaunches.size();
if (N <= 0) {
@@ -2668,8 +2710,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " finishing=" + r.finishing);
r.makeFinishing();
mMainStack.mHistory.remove(i);
-
- r.inHistory = false;
+ r.takeFromHistory();
mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
mWindowManager.validateAppTokens(mMainStack.mHistory);
@@ -3047,7 +3088,10 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
- Process.killProcess(app.pid);
+ Slog.w(TAG, "Killing " + app + ": background ANR");
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "background ANR");
+ Process.killProcessQuiet(app.pid);
return;
}
@@ -3299,7 +3343,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (callerUid == Process.SYSTEM_UID) {
synchronized (this) {
ProcessRecord app = getProcessRecordLocked(processName, uid);
- if (app != null) {
+ if (app != null && app.thread != null) {
try {
app.thread.scheduleSuicide();
} catch (RemoteException e) {
@@ -3368,7 +3412,8 @@ public final class ActivityManagerService extends ActivityManagerNative
private final boolean forceStopPackageLocked(String name, int uid,
boolean callerWillRestart, boolean purgeCache, boolean doit) {
- int i, N;
+ int i;
+ int N;
if (uid < 0) {
try {
@@ -3512,7 +3557,9 @@ public final class ActivityManagerService extends ActivityManagerNative
bringDownServiceLocked(sr, true);
}
}
- Process.killProcess(pid);
+ EventLog.writeEvent(EventLogTags.AM_KILL, pid,
+ app.processName, app.setAdj, "start timeout");
+ Process.killProcessQuiet(pid);
if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
Slog.w(TAG, "Unattached app died before backup, skipping");
try {
@@ -3558,7 +3605,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " (IApplicationThread " + thread + "); dropping process");
EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
if (pid > 0 && pid != MY_PID) {
- Process.killProcess(pid);
+ Process.killProcessQuiet(pid);
} else {
try {
thread.scheduleExit();
@@ -3629,6 +3676,7 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean isRestrictedBackupMode = false;
if (mBackupTarget != null && mBackupAppName.equals(processName)) {
isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
+ || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
}
@@ -3877,16 +3925,7 @@ public final class ActivityManagerService extends ActivityManagerNative
r = (ActivityRecord)mMainStack.mHistory.get(index);
r.icicle = icicle;
r.haveState = true;
- if (thumbnail != null) {
- r.thumbnail = thumbnail;
- if (r.task != null) {
- r.task.lastThumbnail = r.thumbnail;
- }
- }
- r.description = description;
- if (r.task != null) {
- r.task.lastDescription = r.description;
- }
+ r.updateThumbnail(thumbnail, description);
r.stopped = true;
r.state = ActivityState.STOPPED;
if (!r.finishing) {
@@ -4910,16 +4949,10 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
- final boolean canReadFb = (flags&ActivityManager.TASKS_GET_THUMBNAILS) != 0
- && checkCallingPermission(
- android.Manifest.permission.READ_FRAME_BUFFER)
- == PackageManager.PERMISSION_GRANTED;
-
int pos = mMainStack.mHistory.size()-1;
ActivityRecord next =
pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
ActivityRecord top = null;
- CharSequence topDescription = null;
TaskRecord curTask = null;
int numActivities = 0;
int numRunning = 0;
@@ -4933,7 +4966,6 @@ public final class ActivityManagerService extends ActivityManagerNative
(top.state == ActivityState.INITIALIZING
&& top.task == r.task)) {
top = r;
- topDescription = r.description;
curTask = r.task;
numActivities = numRunning = 0;
}
@@ -4943,9 +4975,6 @@ public final class ActivityManagerService extends ActivityManagerNative
if (r.app != null && r.app.thread != null) {
numRunning++;
}
- if (topDescription == null) {
- topDescription = r.description;
- }
if (localLOGV) Slog.v(
TAG, r.intent.getComponent().flattenToShortString()
@@ -4959,14 +4988,9 @@ public final class ActivityManagerService extends ActivityManagerNative
ci.id = curTask.taskId;
ci.baseActivity = r.intent.getComponent();
ci.topActivity = top.intent.getComponent();
- if (canReadFb) {
- if (top.thumbnail != null) {
- ci.thumbnail = top.thumbnail;
- } else if (top.state == ActivityState.RESUMED) {
- ci.thumbnail = top.stack.screenshotActivities(top);
- }
+ if (top.thumbHolder != null) {
+ ci.description = top.thumbHolder.lastDescription;
}
- ci.description = topDescription;
ci.numActivities = numActivities;
ci.numRunning = numRunning;
//System.out.println(
@@ -5036,8 +5060,6 @@ public final class ActivityManagerService extends ActivityManagerNative
IPackageManager pm = AppGlobals.getPackageManager();
- ActivityRecord resumed = mMainStack.mResumedActivity;
-
final int N = mRecentTasks.size();
ArrayList<ActivityManager.RecentTaskInfo> res
= new ArrayList<ActivityManager.RecentTaskInfo>(
@@ -5083,24 +5105,121 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- public Bitmap getTaskThumbnail(int id) {
+ private TaskRecord taskForIdLocked(int id) {
+ final int N = mRecentTasks.size();
+ for (int i=0; i<N; i++) {
+ TaskRecord tr = mRecentTasks.get(i);
+ if (tr.taskId == id) {
+ return tr;
+ }
+ }
+ return null;
+ }
+
+ public ActivityManager.TaskThumbnails getTaskThumbnails(int id) {
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
- "getTaskThumbnail()");
- ActivityRecord resumed = mMainStack.mResumedActivity;
- final int N = mRecentTasks.size();
- for (int i=0; i<N; i++) {
- TaskRecord tr = mRecentTasks.get(i);
- if (tr.taskId == id) {
- if (resumed != null && resumed.task == tr) {
- return resumed.stack.screenshotActivities(resumed);
- } else {
- return tr.lastThumbnail;
+ "getTaskThumbnails()");
+ TaskRecord tr = taskForIdLocked(id);
+ if (tr != null) {
+ return mMainStack.getTaskThumbnailsLocked(tr);
+ }
+ }
+ return null;
+ }
+
+ public boolean removeSubTask(int taskId, int subTaskIndex) {
+ synchronized (this) {
+ enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
+ "removeSubTask()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mMainStack.removeTaskActivitiesLocked(taskId, subTaskIndex) != null;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ private void removeTaskProcessesLocked(ActivityRecord root) {
+ TaskRecord tr = root.task;
+ Intent baseIntent = new Intent(
+ tr.intent != null ? tr.intent : tr.affinityIntent);
+ ComponentName component = baseIntent.getComponent();
+ if (component == null) {
+ Slog.w(TAG, "Now component for base intent of task: " + tr);
+ return;
+ }
+
+ // Find any running services associated with this app.
+ ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+ for (ServiceRecord sr : mServices.values()) {
+ if (sr.packageName.equals(component.getPackageName())) {
+ services.add(sr);
+ }
+ }
+
+ // Take care of any running services associated with the app.
+ for (int i=0; i<services.size(); i++) {
+ ServiceRecord sr = services.get(i);
+ if (sr.startRequested) {
+ if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
+ stopServiceLocked(sr);
+ } else {
+ sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
+ sr.makeNextStartId(), baseIntent, -1));
+ if (sr.app != null && sr.app.thread != null) {
+ sendServiceArgsLocked(sr, false);
}
}
}
}
- return null;
+
+ // Find any running processes associated with this app.
+ ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
+ SparseArray<ProcessRecord> appProcs
+ = mProcessNames.getMap().get(component.getPackageName());
+ if (appProcs != null) {
+ for (int i=0; i<appProcs.size(); i++) {
+ procs.add(appProcs.valueAt(i));
+ }
+ }
+
+ // Kill the running processes.
+ for (int i=0; i<procs.size(); i++) {
+ ProcessRecord pr = procs.get(i);
+ if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+ Slog.i(TAG, "Killing " + pr + ": remove task");
+ EventLog.writeEvent(EventLogTags.AM_KILL, pr.pid,
+ pr.processName, pr.setAdj, "remove task");
+ Process.killProcessQuiet(pr.pid);
+ } else {
+ pr.waitingToKill = "remove task";
+ }
+ }
+ }
+
+ public boolean removeTask(int taskId, int flags) {
+ synchronized (this) {
+ enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
+ "removeTask()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ ActivityRecord r = mMainStack.removeTaskActivitiesLocked(taskId, -1);
+ if (r != null) {
+ mRecentTasks.remove(r.task);
+
+ if ((flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0) {
+ removeTaskProcessesLocked(r);
+ }
+
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ return false;
}
private final int findAffinityTaskTopLocked(int startIndex, String affinity) {
@@ -5154,21 +5273,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final long origId = Binder.clearCallingIdentity();
try {
- int N = mRecentTasks.size();
- for (int i=0; i<N; i++) {
- TaskRecord tr = mRecentTasks.get(i);
- if (tr.taskId == task) {
- if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
- mMainStack.mUserLeaving = true;
- }
- if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
- // Caller wants the home activity moved with it. To accomplish this,
- // we'll just move the home task to the top first.
- mMainStack.moveHomeToFrontLocked();
- }
- mMainStack.moveTaskToFrontLocked(tr, null);
- return;
+ TaskRecord tr = taskForIdLocked(task);
+ if (tr != null) {
+ if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
+ mMainStack.mUserLeaving = true;
}
+ if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
+ // Caller wants the home activity moved with it. To accomplish this,
+ // we'll just move the home task to the top first.
+ mMainStack.moveHomeToFrontLocked();
+ }
+ mMainStack.moveTaskToFrontLocked(tr, null);
+ return;
}
for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i);
@@ -5322,9 +5438,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
r = (ActivityRecord)mMainStack.mHistory.get(index);
}
- if (thumbnail == null) {
- thumbnail = r.thumbnail;
- description = r.description;
+ if (thumbnail == null && r.thumbHolder != null) {
+ thumbnail = r.thumbHolder.lastThumbnail;
+ description = r.thumbHolder.lastDescription;
}
if (thumbnail == null && !always) {
// If there is no thumbnail, and this entry is not actually
@@ -6177,6 +6293,14 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ public void registerProcessObserver(IProcessObserver observer) {
+ mProcessObservers.register(observer);
+ }
+
+ public void unregisterProcessObserver(IProcessObserver observer) {
+ mProcessObservers.unregister(observer);
+ }
+
public void setImmersive(IBinder token, boolean immersive) {
synchronized(this) {
int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
@@ -6692,11 +6816,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (app.pid > 0 && app.pid != MY_PID) {
handleAppCrashLocked(app);
- Slog.i(ActivityManagerService.TAG, "Killing "
- + app.processName + " (pid=" + app.pid + "): user's request");
+ Slog.i(ActivityManagerService.TAG, "Killing " + app + ": user's request");
EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
app.processName, app.setAdj, "user's request after error");
- Process.killProcess(app.pid);
+ Process.killProcessQuiet(app.pid);
}
}
}
@@ -8525,7 +8648,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final String[] args = new String[0];
for (int i=list.size()-1; i>=0; i--) {
final ActivityRecord r = (ActivityRecord)list.get(i);
- final boolean full = !brief && (complete || !r.inHistory);
+ final boolean full = !brief && (complete || !r.isInHistory());
if (needNL) {
pw.println(" ");
needNL = false;
@@ -8657,9 +8780,17 @@ public final class ActivityManagerService extends ActivityManagerNative
schedGroup = Integer.toString(r.setSchedGroup);
break;
}
- pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
+ String foreground;
+ if (r.foregroundActivities) {
+ foreground = "A";
+ } else if (r.foregroundServices) {
+ foreground = "S";
+ } else {
+ foreground = " ";
+ }
+ pw.println(String.format("%s%s #%2d: adj=%s/%s%s %s (%s)",
prefix, (r.persistent ? persistentLabel : normalLabel),
- N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
+ N-i, oomAdj, schedGroup, foreground, r.toShortString(), r.adjType));
if (r.adjSource != null || r.adjTarget != null) {
pw.print(prefix);
pw.print(" ");
@@ -8763,6 +8894,37 @@ public final class ActivityManagerService extends ActivityManagerNative
return procs;
}
+ final void dumpGraphicsHardwareUsage(FileDescriptor fd,
+ PrintWriter pw, String[] args) {
+ ArrayList<ProcessRecord> procs = collectProcesses(pw, args);
+ if (procs == null) {
+ return;
+ }
+
+ long uptime = SystemClock.uptimeMillis();
+ long realtime = SystemClock.elapsedRealtime();
+ pw.println("Applications Graphics Acceleration Info:");
+ pw.println("Uptime: " + uptime + " Realtime: " + realtime);
+
+ String callArgs[] = {"graphics"};
+ for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+ ProcessRecord r = procs.get(i);
+ if (r.thread != null) {
+ pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **");
+ pw.flush();
+ try {
+ TransferPipe.goDump(r.thread.asBinder(), fd, callArgs);
+ } catch (IOException e) {
+ pw.println("Failure: " + e);
+ pw.flush();
+ } catch (RemoteException e) {
+ pw.println("Got RemoteException!");
+ pw.flush();
+ }
+ }
+ }
+ }
+
final void dumpApplicationMemoryUsage(FileDescriptor fd,
PrintWriter pw, String prefix, String[] args) {
ArrayList<ProcessRecord> procs = collectProcesses(pw, args);
@@ -8963,7 +9125,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " in dying process " + proc.processName);
EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
capp.processName, capp.setAdj, "dying provider " + proc.processName);
- Process.killProcess(capp.pid);
+ Process.killProcessQuiet(capp.pid);
}
}
@@ -9004,6 +9166,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.thread = null;
app.forcingToForeground = null;
app.foregroundServices = false;
+ app.foregroundActivities = false;
killServicesLocked(app, true);
@@ -9097,6 +9260,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ mHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
+
// If the caller is restarting this app, then leave it in its
// current lists and let the caller take care of it.
if (restarting) {
@@ -9470,7 +9635,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (si.doneExecutingCount > 0) {
flags |= Service.START_FLAG_REDELIVERY;
}
- r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
+ r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
} catch (RemoteException e) {
// Remote process gone... we'll let the normal cleanup take
// care of this.
@@ -9557,11 +9722,8 @@ public final class ActivityManagerService extends ActivityManagerNative
// pending arguments, then fake up one so its onStartCommand() will
// be called.
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
- r.lastStartId++;
- if (r.lastStartId < 1) {
- r.lastStartId = 1;
- }
- r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
+ r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
+ null, -1));
}
sendServiceArgsLocked(r, true);
@@ -9915,11 +10077,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
r.startRequested = true;
r.callStart = false;
- r.lastStartId++;
- if (r.lastStartId < 1) {
- r.lastStartId = 1;
- }
- r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId,
+ r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, targetPermissionUid));
r.lastActivity = SystemClock.uptimeMillis();
synchronized (r.stats.getBatteryStats()) {
@@ -9961,6 +10119,15 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ private void stopServiceLocked(ServiceRecord service) {
+ synchronized (service.stats.getBatteryStats()) {
+ service.stats.stopRunningLocked();
+ }
+ service.startRequested = false;
+ service.callStart = false;
+ bringDownServiceLocked(service, false);
+ }
+
public int stopService(IApplicationThread caller, Intent service,
String resolvedType) {
// Refuse possible leaked file descriptors
@@ -9984,14 +10151,12 @@ public final class ActivityManagerService extends ActivityManagerNative
ServiceLookupResult r = findServiceLocked(service, resolvedType);
if (r != null) {
if (r.record != null) {
- synchronized (r.record.stats.getBatteryStats()) {
- r.record.stats.stopRunningLocked();
- }
- r.record.startRequested = false;
- r.record.callStart = false;
final long origId = Binder.clearCallingIdentity();
- bringDownServiceLocked(r.record, false);
- Binder.restoreCallingIdentity(origId);
+ try {
+ stopServiceLocked(r.record);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
return 1;
}
return -1;
@@ -10053,7 +10218,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- if (r.lastStartId != startId) {
+ if (r.getLastStartId() != startId) {
return false;
}
@@ -10494,7 +10659,7 @@ public final class ActivityManagerService extends ActivityManagerNative
case Service.START_NOT_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, true);
- if (r.lastStartId == startId) {
+ if (r.getLastStartId() == startId) {
// There is no more work, and this service
// doesn't want to hang around if killed.
r.stopIfKilled = true;
@@ -10514,6 +10679,12 @@ public final class ActivityManagerService extends ActivityManagerNative
}
break;
}
+ case Service.START_TASK_REMOVED_COMPLETE: {
+ // Special processing for onTaskRemoved(). Don't
+ // impact normal onStartCommand() processing.
+ r.findDeliveredStart(startId, true);
+ break;
+ }
default:
throw new IllegalArgumentException(
"Unknown service start result: " + res);
@@ -10624,7 +10795,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
BackupRecord r = new BackupRecord(ss, app, backupMode);
- ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName);
+ ComponentName hostingName = (backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL)
+ ? new ComponentName(app.packageName, app.backupAgentName)
+ : new ComponentName("android", "FullBackupAgent");
// startProcessLocked() returns existing proc's record if it's already running
ProcessRecord proc = startProcessLocked(app.processName, app,
false, 0, "backup", hostingName, false);
@@ -12334,25 +12507,30 @@ public final class ActivityManagerService extends ActivityManagerNative
app.keeping = true;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
return (app.curAdj=app.maxAdj);
- }
-
+ }
+
+ final boolean hadForegroundActivities = app.foregroundActivities;
+
app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
app.adjSource = null;
app.adjTarget = null;
app.keeping = false;
app.empty = false;
app.hidden = false;
+ app.foregroundActivities = false;
+
+ final int activitiesSize = app.activities.size();
// Determine the importance of the process, starting with most
// important to least, and assign an appropriate OOM adjustment.
int adj;
int schedGroup;
- int N;
if (app == TOP_APP) {
// The last app on the list is the foreground app.
adj = FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "top-activity";
+ app.foregroundActivities = true;
} else if (app.instrumentationClass != null) {
// Don't want to kill running instrumentation.
adj = FOREGROUND_APP_ADJ;
@@ -12371,54 +12549,64 @@ public final class ActivityManagerService extends ActivityManagerNative
adj = FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "exec-service";
- } else if ((N=app.activities.size()) != 0) {
+ } else if (activitiesSize > 0) {
// This app is in the background with paused activities.
- app.hidden = true;
+ // We inspect activities to potentially upgrade adjustment further below.
adj = hiddenAdj;
schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ app.hidden = true;
app.adjType = "bg-activities";
- N = app.activities.size();
- for (int j=0; j<N; j++) {
- ActivityRecord r = app.activities.get(j);
- if (r.visible) {
- // This app has a visible activity!
- app.hidden = false;
- adj = VISIBLE_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
- app.adjType = "visible";
- break;
- } else if (r.state == ActivityState.PAUSING
- || r.state == ActivityState.PAUSED
- || r.state == ActivityState.STOPPING) {
- adj = PERCEPTIBLE_APP_ADJ;
- app.adjType = "stopping";
- }
- }
} else {
// A very not-needed process. If this is lower in the lru list,
// we will push it in to the empty bucket.
+ adj = hiddenAdj;
+ schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.hidden = true;
app.empty = true;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- adj = hiddenAdj;
app.adjType = "bg-empty";
}
-
+
+ // Examine all activities if not already foreground.
+ if (!app.foregroundActivities && activitiesSize > 0) {
+ for (int j = 0; j < activitiesSize; j++) {
+ final ActivityRecord r = app.activities.get(j);
+ if (r.visible) {
+ // App has a visible activity; only upgrade adjustment.
+ if (adj > VISIBLE_APP_ADJ) {
+ adj = VISIBLE_APP_ADJ;
+ app.adjType = "visible";
+ }
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.hidden = false;
+ app.foregroundActivities = true;
+ break;
+ } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED
+ || r.state == ActivityState.STOPPING) {
+ // Only upgrade adjustment.
+ if (adj > PERCEPTIBLE_APP_ADJ) {
+ adj = PERCEPTIBLE_APP_ADJ;
+ app.adjType = "stopping";
+ }
+ app.foregroundActivities = true;
+ }
+ }
+ }
+
if (adj > PERCEPTIBLE_APP_ADJ) {
if (app.foregroundServices) {
// The user is aware of this app, so make it visible.
adj = PERCEPTIBLE_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "foreground-service";
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
} else if (app.forcingToForeground != null) {
// The user is aware of this app, so make it visible.
adj = PERCEPTIBLE_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "force-foreground";
app.adjSource = app.forcingToForeground;
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
}
}
-
+
if (adj > HEAVY_WEIGHT_APP_ADJ && app == mHeavyWeightProcess) {
// We don't want to kill the current heavy-weight process.
adj = HEAVY_WEIGHT_APP_ADJ;
@@ -12638,7 +12826,12 @@ public final class ActivityManagerService extends ActivityManagerNative
app.curAdj = adj;
app.curSchedGroup = schedGroup;
-
+
+ if (hadForegroundActivities != app.foregroundActivities) {
+ mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED, app.pid, app.info.uid,
+ app.foregroundActivities).sendToTarget();
+ }
+
return adj;
}
@@ -12871,13 +13064,15 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private final boolean updateOomAdjLocked(
- ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
+ ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
app.hiddenAdj = hiddenAdj;
if (app.thread == null) {
return true;
}
+ boolean success = true;
+
final boolean wasKeeping = app.keeping;
int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
@@ -12917,7 +13112,7 @@ public final class ActivityManagerService extends ActivityManagerNative
" oom adj to " + adj);
app.setAdj = adj;
} else {
- return false;
+ success = false;
}
}
if (app.setSchedGroup != app.curSchedGroup) {
@@ -12925,30 +13120,38 @@ public final class ActivityManagerService extends ActivityManagerNative
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
"Setting process group of " + app.processName
+ " to " + app.curSchedGroup);
- if (true) {
- long oldId = Binder.clearCallingIdentity();
- try {
- Process.setProcessGroup(app.pid, app.curSchedGroup);
- } catch (Exception e) {
- Slog.w(TAG, "Failed setting process group of " + app.pid
- + " to " + app.curSchedGroup);
- e.printStackTrace();
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
- if (false) {
- if (app.thread != null) {
+ if (app.waitingToKill != null &&
+ app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+ Slog.i(TAG, "Killing " + app + ": " + app.waitingToKill);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, app.waitingToKill);
+ Process.killProcessQuiet(app.pid);
+ } else {
+ if (true) {
+ long oldId = Binder.clearCallingIdentity();
try {
- app.thread.setSchedulingGroup(app.curSchedGroup);
- } catch (RemoteException e) {
+ Process.setProcessGroup(app.pid, app.curSchedGroup);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed setting process group of " + app.pid
+ + " to " + app.curSchedGroup);
+ e.printStackTrace();
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+ if (false) {
+ if (app.thread != null) {
+ try {
+ app.thread.setSchedulingGroup(app.curSchedGroup);
+ } catch (RemoteException e) {
+ }
}
}
}
}
}
- return true;
+ return success;
}
private final ActivityRecord resumedAppLocked() {
@@ -13064,7 +13267,9 @@ public final class ActivityManagerService extends ActivityManagerNative
+ (app.thread != null ? app.thread.asBinder() : null)
+ ")\n");
if (app.pid > 0 && app.pid != MY_PID) {
- Process.killProcess(app.pid);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "empty");
+ Process.killProcessQuiet(app.pid);
} else {
try {
app.thread.scheduleExit();
@@ -13130,7 +13335,9 @@ public final class ActivityManagerService extends ActivityManagerNative
+ (app.thread != null ? app.thread.asBinder() : null)
+ ")\n");
if (app.pid > 0 && app.pid != MY_PID) {
- Process.killProcess(app.pid);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "empty");
+ Process.killProcessQuiet(app.pid);
} else {
try {
app.thread.scheduleExit();
@@ -13149,7 +13356,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// If we still have too many processes, now from the least
// recently used process we start finishing activities.
- if (Config.LOGV) Slog.v(
+ if (false) Slog.v(
TAG, "*** NOW HAVE " + mLruProcesses.size() +
" of " + curMaxProcs + " processes");
for ( i=0;
@@ -13163,11 +13370,11 @@ public final class ActivityManagerService extends ActivityManagerNative
&& app.services.size() == 0;
int NUMA = app.activities.size();
int j;
- if (Config.LOGV) Slog.v(
+ if (false) Slog.v(
TAG, "Looking to quit " + app.processName);
for (j=0; j<NUMA && canQuit; j++) {
ActivityRecord r = app.activities.get(j);
- if (Config.LOGV) Slog.v(
+ if (false) Slog.v(
TAG, " " + r.intent.getComponent().flattenToShortString()
+ ": frozen=" + r.haveState + ", visible=" + r.visible);
canQuit = (r.haveState || !r.stateNotNeeded)
@@ -13187,7 +13394,9 @@ public final class ActivityManagerService extends ActivityManagerNative
+ (app.thread != null ? app.thread.asBinder() : null)
+ ")\n");
if (app.pid > 0 && app.pid != MY_PID) {
- Process.killProcess(app.pid);
+ EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
+ app.processName, app.setAdj, "old background");
+ Process.killProcessQuiet(app.pid);
} else {
try {
app.thread.scheduleExit();
@@ -13404,4 +13613,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
+
+ // Multi-user methods
+
+ public boolean switchUser(int userid) {
+ // TODO
+ return true;
+ }
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 33c12f4..090e26b 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -48,7 +48,7 @@ import java.util.HashSet;
/**
* An entry in the history stack, representing an activity.
*/
-class ActivityRecord extends IApplicationToken.Stub {
+final class ActivityRecord extends IApplicationToken.Stub {
final ActivityManagerService service; // owner
final ActivityStack stack; // owner
final ActivityInfo info; // all about me
@@ -75,6 +75,7 @@ class ActivityRecord extends IApplicationToken.Stub {
int realTheme; // actual theme resource we will use, never 0.
int windowFlags; // custom window flags for preview window.
TaskRecord task; // the task this is in.
+ ThumbnailHolder thumbHolder; // where our thumbnails should go.
long launchTime; // when we starting launching this activity
long startTime; // last time this activity was started
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
@@ -88,9 +89,7 @@ class ActivityRecord extends IApplicationToken.Stub {
ArrayList newIntents; // any pending new intents for single-top mode
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
UriPermissionOwner uriPermissions; // current special URI access perms.
- ProcessRecord app; // if non-null, hosting application
- Bitmap thumbnail; // icon representation of paused screen
- CharSequence description; // textual description of paused screen
+ ProcessRecord app; // if non-null, hosting application
ActivityState state; // current state we are in
Bundle icicle; // last saved activity state
boolean frontOfTask; // is this the root activity of its task?
@@ -102,7 +101,6 @@ class ActivityRecord extends IApplicationToken.Stub {
boolean configDestroy; // need to destroy due to config change?
int configChangeFlags; // which config values have changed
boolean keysPaused; // has key dispatching been paused for it?
- boolean inHistory; // are we in the history stack?
int launchMode; // the launch mode activity attribute.
boolean visible; // does this activity's window need to be shown?
boolean sleeping; // have we told the activity to sleep?
@@ -117,6 +115,8 @@ class ActivityRecord extends IApplicationToken.Stub {
String stringName; // for caching of toString().
+ private boolean inHistory; // are we in the history stack?
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
pw.print(" processName="); pw.println(processName);
@@ -180,6 +180,7 @@ class ActivityRecord extends IApplicationToken.Stub {
pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded);
pw.print(" forceNewConfig="); pw.println(forceNewConfig);
+ pw.print(prefix); pw.print("thumbHolder="); pw.println(thumbHolder);
if (launchTime != 0 || startTime != 0) {
pw.print(prefix); pw.print("launchTime=");
TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime=");
@@ -333,10 +334,55 @@ class ActivityRecord extends IApplicationToken.Stub {
}
}
+ void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
+ if (inHistory && !finishing) {
+ if (task != null) {
+ task.numActivities--;
+ }
+ if (newTask != null) {
+ newTask.numActivities++;
+ }
+ }
+ if (newThumbHolder == null) {
+ newThumbHolder = newTask;
+ }
+ task = newTask;
+ if (!isRoot && (intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ // This is the start of a new sub-task.
+ if (thumbHolder == null) {
+ thumbHolder = new ThumbnailHolder();
+ }
+ } else {
+ thumbHolder = newThumbHolder;
+ }
+ }
+
+ void putInHistory() {
+ if (!inHistory) {
+ inHistory = true;
+ if (task != null && !finishing) {
+ task.numActivities++;
+ }
+ }
+ }
+
+ void takeFromHistory() {
+ if (inHistory) {
+ inHistory = false;
+ if (task != null && !finishing) {
+ task.numActivities--;
+ }
+ }
+ }
+
+ boolean isInHistory() {
+ return inHistory;
+ }
+
void makeFinishing() {
if (!finishing) {
finishing = true;
- if (task != null) {
+ if (task != null && inHistory) {
task.numActivities--;
}
}
@@ -435,6 +481,25 @@ class ActivityRecord extends IApplicationToken.Stub {
}
}
+ void updateThumbnail(Bitmap newThumbnail, CharSequence description) {
+ if ((intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ // This is a logical break in the task; it repre
+ }
+ if (thumbHolder != null) {
+ if (newThumbnail != null) {
+ thumbHolder.lastThumbnail = newThumbnail;
+ }
+ thumbHolder.lastDescription = description;
+ }
+ }
+
+ void clearThumbnail() {
+ if (thumbHolder != null) {
+ thumbHolder.lastThumbnail = null;
+ thumbHolder.lastDescription = null;
+ }
+ }
+
// IApplicationToken
public boolean mayFreezeScreenLocked(ProcessRecord app) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 5cbb9e1..d8772b8 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -21,8 +21,10 @@ import com.android.internal.os.BatteryStatsImpl;
import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.IActivityManager;
+import android.app.IThumbnailRetriever;
import static android.app.IActivityManager.START_CLASS_NOT_FOUND;
import static android.app.IActivityManager.START_DELIVERED_TO_TOP;
import static android.app.IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
@@ -69,9 +71,9 @@ import java.util.List;
/**
* State and management of a single stack of activities.
*/
-public class ActivityStack {
+final class ActivityStack {
static final String TAG = ActivityManagerService.TAG;
- static final boolean localLOGV = ActivityManagerService.localLOGV;
+ static final boolean localLOGV = ActivityManagerService.localLOGV || true;
static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
static final boolean DEBUG_PAUSE = ActivityManagerService.DEBUG_PAUSE;
static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY;
@@ -135,14 +137,14 @@ public class ActivityStack {
* The back history of all previous (and possibly still
* running) activities. It contains HistoryRecord objects.
*/
- final ArrayList mHistory = new ArrayList();
+ final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>();
/**
* List of running activities, sorted by recent usage.
* The first entry in the list is the least recently used.
* It contains HistoryRecord objects.
*/
- final ArrayList mLRUActivities = new ArrayList();
+ final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<ActivityRecord>();
/**
* List of activities that are waiting for a new activity
@@ -346,7 +348,7 @@ public class ActivityStack {
final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
int i = mHistory.size()-1;
while (i >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (!r.finishing && r != notTop) {
return r;
}
@@ -358,7 +360,7 @@ public class ActivityStack {
final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
int i = mHistory.size()-1;
while (i >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (!r.finishing && !r.delayedResume && r != notTop) {
return r;
}
@@ -379,7 +381,7 @@ public class ActivityStack {
final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
int i = mHistory.size()-1;
while (i >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
// Note: the taskId check depends on real taskId fields being non-zero
if (!r.finishing && (token != r) && (taskId != r.task.taskId)) {
return r;
@@ -425,7 +427,7 @@ public class ActivityStack {
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (!r.finishing && r.task != cp
&& r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
cp = r.task;
@@ -469,7 +471,7 @@ public class ActivityStack {
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (!r.finishing) {
if (r.intent.getComponent().equals(cls)) {
//Slog.i(TAG, "Found matching class!");
@@ -511,6 +513,7 @@ public class ActivityStack {
}
r.app = app;
+ app.waitingToKill = null;
if (localLOGV) Slog.v(TAG, "Launching: " + r);
@@ -688,7 +691,7 @@ public class ActivityStack {
}
// Ensure activities are no longer sleeping.
for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
r.setSleeping(false);
}
mGoingToSleepActivities.clear();
@@ -734,7 +737,7 @@ public class ActivityStack {
// Make sure any stopped but visible activities are now sleeping.
// This ensures that the activity's onStop() is called.
for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
r.setSleeping(true);
}
@@ -799,10 +802,7 @@ public class ActivityStack {
mLastPausedActivity = prev;
prev.state = ActivityState.PAUSING;
prev.task.touchActiveTime();
- prev.thumbnail = screenshotActivities(prev);
- if (prev.task != null) {
- prev.task.lastThumbnail = prev.thumbnail;
- }
+ prev.updateThumbnail(screenshotActivities(prev), null);
mService.updateCpuStats();
@@ -875,7 +875,7 @@ public class ActivityStack {
synchronized (mService) {
int index = indexOfTokenLocked(token);
if (index >= 0) {
- r = (ActivityRecord)mHistory.get(index);
+ r = mHistory.get(index);
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
r.state = ActivityState.PAUSED;
@@ -996,7 +996,7 @@ public class ActivityStack {
mService.reportResumedActivityLocked(next);
}
- next.thumbnail = null;
+ next.clearThumbnail();
if (mMainStack) {
mService.setFocusedActivityLocked(next);
}
@@ -1037,7 +1037,7 @@ public class ActivityStack {
ActivityRecord r;
boolean behindFullscreen = false;
for (; i>=0; i--) {
- r = (ActivityRecord)mHistory.get(i);
+ r = mHistory.get(i);
if (DEBUG_VISBILITY) Slog.v(
TAG, "Make visible? " + r + " finishing=" + r.finishing
+ " state=" + r.state);
@@ -1121,7 +1121,7 @@ public class ActivityStack {
// Now for any activities that aren't visible to the user, make
// sure they no longer are keeping the screen frozen.
while (i >= 0) {
- r = (ActivityRecord)mHistory.get(i);
+ r = mHistory.get(i);
if (DEBUG_VISBILITY) Slog.v(
TAG, "Make invisible? " + r + " finishing=" + r.finishing
+ " state=" + r.state
@@ -1517,7 +1517,7 @@ public class ActivityStack {
// If starting in an existing task, find where that is...
boolean startIt = true;
for (int i = NH-1; i >= 0; i--) {
- ActivityRecord p = (ActivityRecord)mHistory.get(i);
+ ActivityRecord p = mHistory.get(i);
if (p.finishing) {
continue;
}
@@ -1528,8 +1528,7 @@ public class ActivityStack {
addPos = i+1;
if (!startIt) {
mHistory.add(addPos, r);
- r.inHistory = true;
- r.task.numActivities++;
+ r.putInHistory();
mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
r.info.screenOrientation, r.fullscreen);
if (VALIDATE_TOKENS) {
@@ -1561,9 +1560,8 @@ public class ActivityStack {
// Slot the activity into the history stack and proceed
mHistory.add(addPos, r);
- r.inHistory = true;
+ r.putInHistory();
r.frontOfTask = newTask;
- r.task.numActivities++;
if (NH > 0) {
// We want to show the starting preview window if we are
// switching to a new task, or the next activity's process is
@@ -1668,7 +1666,7 @@ public class ActivityStack {
int replyChainEnd = -1;
int lastReparentPos = -1;
for (int i=mHistory.size()-1; i>=-1; i--) {
- ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null;
+ ActivityRecord below = i >= 0 ? mHistory.get(i) : null;
if (below != null && below.finishing) {
continue;
@@ -1722,13 +1720,13 @@ public class ActivityStack {
// bottom of the activity stack. This also keeps it
// correctly ordered with any activities we previously
// moved.
- ActivityRecord p = (ActivityRecord)mHistory.get(0);
+ ActivityRecord p = mHistory.get(0);
if (target.taskAffinity != null
&& target.taskAffinity.equals(p.task.affinity)) {
// If the activity currently at the bottom has the
// same task affinity as the one we are moving,
// then merge it into the same task.
- target.task = p.task;
+ target.setTask(p.task, p.thumbHolder, false);
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to bottom task " + p.task);
} else {
@@ -1736,7 +1734,8 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- target.task = new TaskRecord(mService.mCurTask, target.info, null);
+ target.setTask(new TaskRecord(mService.mCurTask, target.info, null),
+ null, false);
target.task.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to new task " + target.task);
@@ -1746,16 +1745,16 @@ public class ActivityStack {
replyChainEnd = targetI;
}
int dstPos = 0;
+ ThumbnailHolder curThumbHolder = target.thumbHolder;
for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (ActivityRecord)mHistory.get(srcPos);
+ p = mHistory.get(srcPos);
if (p.finishing) {
continue;
}
if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
+ " out to target's task " + target.task);
- task.numActivities--;
- p.task = target.task;
- target.task.numActivities++;
+ p.setTask(target.task, curThumbHolder, false);
+ curThumbHolder = p.thumbHolder;
mHistory.remove(srcPos);
mHistory.add(dstPos, p);
mService.mWindowManager.moveAppToken(dstPos, p);
@@ -1785,7 +1784,7 @@ public class ActivityStack {
// like these are all in the reply chain.
replyChainEnd = targetI+1;
while (replyChainEnd < mHistory.size() &&
- ((ActivityRecord)mHistory.get(
+ (mHistory.get(
replyChainEnd)).task == task) {
replyChainEnd++;
}
@@ -1795,7 +1794,7 @@ public class ActivityStack {
}
ActivityRecord p = null;
for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (ActivityRecord)mHistory.get(srcPos);
+ p = mHistory.get(srcPos);
if (p.finishing) {
continue;
}
@@ -1855,7 +1854,7 @@ public class ActivityStack {
}
ActivityRecord p = null;
for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
- p = (ActivityRecord)mHistory.get(srcPos);
+ p = mHistory.get(srcPos);
if (p.finishing) {
continue;
}
@@ -1873,7 +1872,7 @@ public class ActivityStack {
replyChainEnd = targetI;
}
for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
- ActivityRecord p = (ActivityRecord)mHistory.get(srcPos);
+ ActivityRecord p = mHistory.get(srcPos);
if (p.finishing) {
continue;
}
@@ -1884,12 +1883,10 @@ public class ActivityStack {
lastReparentPos--;
}
mHistory.remove(srcPos);
- p.task.numActivities--;
- p.task = task;
+ p.setTask(task, null, false);
mHistory.add(lastReparentPos, p);
if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
+ " in to resetting task " + task);
- task.numActivities++;
mService.mWindowManager.moveAppToken(lastReparentPos, p);
mService.mWindowManager.setAppGroupId(p, p.task.taskId);
if (VALIDATE_TOKENS) {
@@ -1904,7 +1901,7 @@ public class ActivityStack {
// below so it remains singleTop.
if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
for (int j=lastReparentPos-1; j>=0; j--) {
- ActivityRecord p = (ActivityRecord)mHistory.get(j);
+ ActivityRecord p = mHistory.get(j);
if (p.finishing) {
continue;
}
@@ -1945,7 +1942,7 @@ public class ActivityStack {
// First find the requested task.
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.task.taskId == taskId) {
i++;
break;
@@ -1955,7 +1952,7 @@ public class ActivityStack {
// Now clear it.
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.finishing) {
continue;
}
@@ -1967,7 +1964,7 @@ public class ActivityStack {
ActivityRecord ret = r;
while (i < (mHistory.size()-1)) {
i++;
- r = (ActivityRecord)mHistory.get(i);
+ r = mHistory.get(i);
if (r.task.taskId != taskId) {
break;
}
@@ -2003,6 +2000,28 @@ public class ActivityStack {
}
/**
+ * Completely remove all activities associated with an existing
+ * task starting at a specified index.
+ */
+ private final void performClearTaskAtIndexLocked(int taskId, int i) {
+ while (i < (mHistory.size()-1)) {
+ ActivityRecord r = mHistory.get(i);
+ if (r.task.taskId != taskId) {
+ // Whoops hit the end.
+ return;
+ }
+ if (r.finishing) {
+ i++;
+ continue;
+ }
+ if (!finishActivityLocked(r, i, Activity.RESULT_CANCELED,
+ null, "clear")) {
+ i++;
+ }
+ }
+ }
+
+ /**
* Completely remove all activities associated with an existing task.
*/
private final void performClearTaskLocked(int taskId) {
@@ -2011,37 +2030,23 @@ public class ActivityStack {
// First find the requested task.
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.task.taskId == taskId) {
i++;
break;
}
}
- // Now clear it.
+ // Now find the start and clear it.
while (i > 0) {
i--;
- ActivityRecord r = (ActivityRecord)mHistory.get(i);
+ ActivityRecord r = mHistory.get(i);
if (r.finishing) {
continue;
}
if (r.task.taskId != taskId) {
// We hit the bottom. Now finish it all...
- while (i < (mHistory.size()-1)) {
- i++;
- r = (ActivityRecord)mHistory.get(i);
- if (r.task.taskId != taskId) {
- // Whoops hit the end.
- return;
- }
- if (r.finishing) {
- continue;
- }
- if (finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "clear")) {
- i--;
- }
- }
+ performClearTaskAtIndexLocked(taskId, i+1);
return;
}
}
@@ -2055,7 +2060,7 @@ public class ActivityStack {
int i = mHistory.size();
while (i > 0) {
i--;
- ActivityRecord candidate = (ActivityRecord)mHistory.get(i);
+ ActivityRecord candidate = mHistory.get(i);
if (candidate.task.taskId != task) {
break;
}
@@ -2072,9 +2077,9 @@ public class ActivityStack {
* brought to the front.
*/
private final ActivityRecord moveActivityToFrontLocked(int where) {
- ActivityRecord newTop = (ActivityRecord)mHistory.remove(where);
+ ActivityRecord newTop = mHistory.remove(where);
int top = mHistory.size();
- ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1);
+ ActivityRecord oldTop = mHistory.get(top-1);
mHistory.add(top, newTop);
oldTop.frontOfTask = false;
newTop.frontOfTask = true;
@@ -2117,7 +2122,7 @@ public class ActivityStack {
if (DEBUG_RESULTS) Slog.v(
TAG, "Sending result to " + resultTo + " (index " + index + ")");
if (index >= 0) {
- sourceRecord = (ActivityRecord)mHistory.get(index);
+ sourceRecord = mHistory.get(index);
if (requestCode >= 0 && !sourceRecord.finishing) {
resultRecord = sourceRecord;
}
@@ -2542,11 +2547,11 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- r.task = new TaskRecord(mService.mCurTask, r.info, intent);
+ r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new task " + r.task);
} else {
- r.task = reuseTask;
+ r.setTask(reuseTask, reuseTask, true);
}
newTask = true;
moveHomeToFrontFromLaunchLocked(launchFlags);
@@ -2589,7 +2594,7 @@ public class ActivityStack {
// An existing activity is starting this new activity, so we want
// to keep the new one in the same task as the one that is starting
// it.
- r.task = sourceRecord.task;
+ r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in existing task " + r.task);
@@ -2599,10 +2604,10 @@ public class ActivityStack {
// this case should never happen.
final int N = mHistory.size();
ActivityRecord prev =
- N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
- r.task = prev != null
+ N > 0 ? mHistory.get(N-1) : null;
+ r.setTask(prev != null
? prev.task
- : new TaskRecord(mService.mCurTask, r.info, intent);
+ : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new guessed " + r.task);
}
@@ -3044,7 +3049,7 @@ public class ActivityStack {
// Get the activity record.
int index = indexOfTokenLocked(token);
if (index >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = mHistory.get(index);
if (fromTimeout) {
reportActivityLaunchedLocked(fromTimeout, r, -1, -1);
@@ -3176,12 +3181,12 @@ public class ActivityStack {
if (index < 0) {
return false;
}
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = mHistory.get(index);
// Is this the last activity left?
boolean lastActivity = true;
for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord p = (ActivityRecord)mHistory.get(i);
+ ActivityRecord p = mHistory.get(i);
if (!p.finishing && p != r) {
lastActivity = false;
break;
@@ -3216,7 +3221,7 @@ public class ActivityStack {
System.identityHashCode(r),
r.task.taskId, r.shortComponentName, reason);
if (index < (mHistory.size()-1)) {
- ActivityRecord next = (ActivityRecord)mHistory.get(index+1);
+ ActivityRecord next = mHistory.get(index+1);
if (next.task == r.task) {
if (r.frontOfTask) {
// The next activity is now the front of the task.
@@ -3272,7 +3277,7 @@ public class ActivityStack {
if (mResumedActivity == r) {
boolean endTask = index <= 0
- || ((ActivityRecord)mHistory.get(index-1)).task != r.task;
+ || (mHistory.get(index-1)).task != r.task;
if (DEBUG_TRANSITION) Slog.v(TAG,
"Prepare close transition: finishing " + r);
mService.mWindowManager.prepareAppTransition(endTask
@@ -3414,13 +3419,14 @@ public class ActivityStack {
// Get rid of any pending idle timeouts.
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
+ mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
}
private final void removeActivityFromHistoryLocked(ActivityRecord r) {
if (r.state != ActivityState.DESTROYED) {
r.makeFinishing();
mHistory.remove(r);
- r.inHistory = false;
+ r.takeFromHistory();
r.state = ActivityState.DESTROYED;
mService.mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
@@ -3539,7 +3545,7 @@ public class ActivityStack {
int index = indexOfTokenLocked(token);
if (index >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(index);
+ ActivityRecord r = mHistory.get(index);
if (r.state == ActivityState.DESTROYING) {
final long origId = Binder.clearCallingIdentity();
removeActivityFromHistoryLocked(r);
@@ -3581,7 +3587,7 @@ public class ActivityStack {
final void moveHomeToFrontLocked() {
TaskRecord homeTask = null;
for (int i=mHistory.size()-1; i>=0; i--) {
- ActivityRecord hr = (ActivityRecord)mHistory.get(i);
+ ActivityRecord hr = mHistory.get(i);
if (hr.isHomeActivity) {
homeTask = hr.task;
break;
@@ -3599,7 +3605,7 @@ public class ActivityStack {
final int task = tr.taskId;
int top = mHistory.size()-1;
- if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) {
+ if (top < 0 || (mHistory.get(top)).task.taskId == task) {
// nothing to do!
return;
}
@@ -3614,7 +3620,7 @@ public class ActivityStack {
// Shift all activities with this task up to the top
// of the stack, keeping them in the same internal order.
while (pos >= 0) {
- ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+ ActivityRecord r = mHistory.get(pos);
if (localLOGV) Slog.v(
TAG, "At " + pos + " ckp " + r.task + ": " + r);
if (r.task.taskId == task) {
@@ -3703,7 +3709,7 @@ public class ActivityStack {
// Shift all activities with this task down to the bottom
// of the stack, keeping them in the same internal order.
while (pos < N) {
- ActivityRecord r = (ActivityRecord)mHistory.get(pos);
+ ActivityRecord r = mHistory.get(pos);
if (localLOGV) Slog.v(
TAG, "At " + pos + " ckp " + r.task + ": " + r);
if (r.task.taskId == task) {
@@ -3737,6 +3743,106 @@ public class ActivityStack {
return true;
}
+ public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) {
+ TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true);
+ ActivityRecord resumed = mResumedActivity;
+ if (resumed != null && resumed.thumbHolder == tr) {
+ info.mainThumbnail = resumed.stack.screenshotActivities(resumed);
+ } else {
+ info.mainThumbnail = tr.lastThumbnail;
+ }
+ return info;
+ }
+
+ public ActivityRecord removeTaskActivitiesLocked(int taskId, int subTaskIndex) {
+ TaskAccessInfo info = getTaskAccessInfoLocked(taskId, false);
+ if (info.root == null) {
+ Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId);
+ return null;
+ }
+
+ if (subTaskIndex < 0) {
+ // Just remove the entire task.
+ performClearTaskAtIndexLocked(taskId, info.rootIndex);
+ return info.root;
+ }
+
+ if (subTaskIndex >= info.subtasks.size()) {
+ Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex);
+ return null;
+ }
+
+ // Remove all of this task's activies starting at the sub task.
+ TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex);
+ performClearTaskAtIndexLocked(taskId, subtask.index);
+ return subtask.activity;
+ }
+
+ public TaskAccessInfo getTaskAccessInfoLocked(int taskId, boolean inclThumbs) {
+ ActivityRecord resumed = mResumedActivity;
+ final TaskAccessInfo thumbs = new TaskAccessInfo();
+ // How many different sub-thumbnails?
+ final int NA = mHistory.size();
+ int j = 0;
+ ThumbnailHolder holder = null;
+ while (j < NA) {
+ ActivityRecord ar = mHistory.get(j);
+ if (!ar.finishing && ar.task.taskId == taskId) {
+ holder = ar.thumbHolder;
+ break;
+ }
+ j++;
+ }
+
+ if (j >= NA) {
+ return thumbs;
+ }
+
+ thumbs.root = mHistory.get(j);
+ thumbs.rootIndex = j;
+
+ ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>();
+ thumbs.subtasks = subtasks;
+ ActivityRecord lastActivity = null;
+ while (j < NA) {
+ ActivityRecord ar = mHistory.get(j);
+ j++;
+ if (ar.finishing) {
+ continue;
+ }
+ if (ar.task.taskId != taskId) {
+ break;
+ }
+ lastActivity = ar;
+ if (ar.thumbHolder != holder && holder != null) {
+ thumbs.numSubThumbbails++;
+ holder = ar.thumbHolder;
+ TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask();
+ sub.thumbnail = holder.lastThumbnail;
+ sub.activity = ar;
+ sub.index = j-1;
+ subtasks.add(sub);
+ }
+ }
+ if (lastActivity != null && subtasks.size() > 0) {
+ if (resumed == lastActivity) {
+ TaskAccessInfo.SubTask sub = subtasks.get(subtasks.size()-1);
+ sub.thumbnail = lastActivity.stack.screenshotActivities(lastActivity);
+ }
+ }
+ if (thumbs.numSubThumbbails > 0) {
+ thumbs.retriever = new IThumbnailRetriever.Stub() {
+ public Bitmap getThumbnail(int index) {
+ if (index < 0 || index >= thumbs.subtasks.size()) {
+ return null;
+ }
+ return thumbs.subtasks.get(index).thumbnail;
+ }
+ };
+ }
+ return thumbs;
+ }
+
private final void logStartActivity(int tag, ActivityRecord r,
TaskRecord task) {
EventLog.writeEvent(tag,
diff --git a/services/java/com/android/server/am/BackupRecord.java b/services/java/com/android/server/am/BackupRecord.java
index 6590b91..7e73106 100644
--- a/services/java/com/android/server/am/BackupRecord.java
+++ b/services/java/com/android/server/am/BackupRecord.java
@@ -26,6 +26,7 @@ class BackupRecord {
public static final int BACKUP_NORMAL = 0;
public static final int BACKUP_FULL = 1;
public static final int RESTORE = 2;
+ public static final int RESTORE_FULL = 3;
final BatteryStatsImpl.Uid.Pkg.Serv stats;
String stringName; // cached toString() output
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 963a691..b4fdc9f 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -449,6 +449,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
boolean isCheckin = false;
+ boolean noOutput = false;
if (args != null) {
for (String arg : args) {
if ("--checkin".equals(arg)) {
@@ -457,10 +458,22 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
synchronized (mStats) {
mStats.resetAllStatsLocked();
pw.println("Battery stats reset.");
+ noOutput = true;
}
+ } else if ("--write".equals(arg)) {
+ synchronized (mStats) {
+ mStats.writeSyncLocked();
+ pw.println("Battery stats written.");
+ noOutput = true;
+ }
+ } else {
+ pw.println("Unknown option: " + arg);
}
}
}
+ if (noOutput) {
+ return;
+ }
if (isCheckin) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
synchronized (mStats) {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 5465e37..3968f66 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -64,8 +64,10 @@ class ProcessRecord {
boolean keeping; // Actively running code so don't kill due to that?
boolean setIsForeground; // Running foreground UI when last set?
boolean foregroundServices; // Running any services that are foreground?
+ boolean foregroundActivities; // Running any activities that are foreground?
boolean bad; // True if disabled in the bad process list
boolean killedBackground; // True when proc has been killed due to too many bg
+ String waitingToKill; // Process is waiting to be killed when in the bg; reason
IBinder forcingToForeground;// Token that is forcing this process to be foreground
int adjSeq; // Sequence id for identifying oom_adj assignment cycles
int lruSeq; // Sequence id for identifying LRU update cycles
@@ -205,8 +207,9 @@ class ProcessRecord {
pw.print(" lastLowMemory=");
TimeUtils.formatDuration(lastLowMemory, now, pw);
pw.print(" reportLowMemory="); pw.println(reportLowMemory);
- if (killedBackground) {
- pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
+ if (killedBackground || waitingToKill != null) {
+ pw.print(prefix); pw.print("killedBackground="); pw.print(killedBackground);
+ pw.print(" waitingToKill="); pw.println(waitingToKill);
}
if (debugging || crashing || crashDialog != null || notResponding
|| anrDialog != null || bad) {
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 1a617dd..004e963 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -84,7 +84,6 @@ class ServiceRecord extends Binder {
boolean startRequested; // someone explicitly called start?
boolean stopIfKilled; // last onStart() said to stop if service killed?
boolean callStart; // last onStart() has asked to alway be called on restart.
- int lastStartId; // identifier of most recent start request.
int executeNesting; // number of outstanding operations keeping foreground.
long executingStart; // start time of last execute request.
int crashCount; // number of times proc has crashed with service running
@@ -96,8 +95,11 @@ class ServiceRecord extends Binder {
String stringName; // caching of toString
+ private int lastStartId; // identifier of most recent start request.
+
static class StartItem {
final ServiceRecord sr;
+ final boolean taskRemoved;
final int id;
final Intent intent;
final int targetPermissionUid;
@@ -108,8 +110,10 @@ class ServiceRecord extends Binder {
String stringName; // caching of toString
- StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) {
+ StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent,
+ int _targetPermissionUid) {
sr = _sr;
+ taskRemoved = _taskRemoved;
id = _id;
intent = _intent;
targetPermissionUid = _targetPermissionUid;
@@ -198,7 +202,9 @@ class ServiceRecord extends Binder {
long now = SystemClock.uptimeMillis();
long nowReal = SystemClock.elapsedRealtime();
pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
- if (!resDir.equals(baseDir)) pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ if (!resDir.equals(baseDir)) {
+ pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ }
pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("app="); pw.println(app);
if (isForeground || foregroundId != 0) {
@@ -321,6 +327,18 @@ class ServiceRecord extends Binder {
return null;
}
+ public int getLastStartId() {
+ return lastStartId;
+ }
+
+ public int makeNextStartId() {
+ lastStartId++;
+ if (lastStartId < 1) {
+ lastStartId = 1;
+ }
+ return lastStartId;
+ }
+
public void postNotification() {
final int appUid = appInfo.uid;
final int appPid = app.pid;
diff --git a/services/java/com/android/server/am/TaskAccessInfo.java b/services/java/com/android/server/am/TaskAccessInfo.java
new file mode 100644
index 0000000..5618c1a
--- /dev/null
+++ b/services/java/com/android/server/am/TaskAccessInfo.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 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.am;
+
+import java.util.ArrayList;
+
+import android.app.ActivityManager.TaskThumbnails;
+import android.graphics.Bitmap;
+
+final class TaskAccessInfo extends TaskThumbnails {
+ final static class SubTask {
+ Bitmap thumbnail;
+ ActivityRecord activity;
+ int index;
+ }
+
+ public ActivityRecord root;
+ public int rootIndex;
+
+ public ArrayList<SubTask> subtasks;
+}
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 76b4914..e61a7f4 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -23,7 +23,7 @@ import android.graphics.Bitmap;
import java.io.PrintWriter;
-class TaskRecord {
+class TaskRecord extends ThumbnailHolder {
final int taskId; // Unique identifier for this task.
final String affinity; // The affinity name for this task, or null.
Intent intent; // The original intent that started the task.
@@ -35,8 +35,6 @@ class TaskRecord {
boolean rootWasReset; // True if the intent at the root of the task had
// the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
boolean askedCompatMode;// Have asked the user about compat mode for this task.
- Bitmap lastThumbnail; // Last thumbnail captured for this task.
- CharSequence lastDescription; // Last description captured for this task.
String stringName; // caching of toString() result.
diff --git a/services/java/com/android/server/am/ThumbnailHolder.java b/services/java/com/android/server/am/ThumbnailHolder.java
new file mode 100644
index 0000000..02f4fcb
--- /dev/null
+++ b/services/java/com/android/server/am/ThumbnailHolder.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2011 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.am;
+
+import android.graphics.Bitmap;
+
+public class ThumbnailHolder {
+ Bitmap lastThumbnail; // Last thumbnail captured for this item.
+ CharSequence lastDescription; // Last description captured for this item.
+}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index ffadc65..e8155b4 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -84,6 +84,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private String[] mTetherableBluetoothRegexs;
private String[] mUpstreamIfaceRegexs;
+ private INetworkManagementService mNMService;
private Looper mLooper;
private HandlerThread mThread;
@@ -100,21 +101,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// with 255.255.255.0
private String[] mDhcpRange;
- private static final String DHCP_DEFAULT_RANGE1_START = "192.168.42.2";
- private static final String DHCP_DEFAULT_RANGE1_STOP = "192.168.42.254";
- private static final String DHCP_DEFAULT_RANGE2_START = "192.168.43.2";
- private static final String DHCP_DEFAULT_RANGE2_STOP = "192.168.43.254";
- private static final String DHCP_DEFAULT_RANGE3_START = "192.168.44.2";
- private static final String DHCP_DEFAULT_RANGE3_STOP = "192.168.44.254";
- private static final String DHCP_DEFAULT_RANGE4_START = "192.168.45.2";
- private static final String DHCP_DEFAULT_RANGE4_STOP = "192.168.45.254";
- private static final String DHCP_DEFAULT_RANGE5_START = "192.168.46.2";
- private static final String DHCP_DEFAULT_RANGE5_STOP = "192.168.46.254";
- private static final String DHCP_DEFAULT_RANGE6_START = "192.168.47.2";
- private static final String DHCP_DEFAULT_RANGE6_STOP = "192.168.47.254";
- private static final String DHCP_DEFAULT_RANGE7_START = "192.168.48.2";
- private static final String DHCP_DEFAULT_RANGE7_STOP = "192.168.48.254";
-
+ private static final String[] DHCP_DEFAULT_RANGE = {
+ "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
+ "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
+ "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
+ "192.168.48.2", "192.168.48.254",
+ };
private String[] mDnsServers;
private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
@@ -132,15 +124,14 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private boolean mUsbMassStorageOff; // track the status of USB Mass Storage
private boolean mUsbConnected; // track the status of USB connection
- public Tethering(Context context, Looper looper) {
+ public Tethering(Context context, INetworkManagementService nmService, Looper looper) {
mContext = context;
+ mNMService = nmService;
mLooper = looper;
// register for notifications from NetworkManagement Service
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
try {
- service.registerObserver(this);
+ mNMService.registerObserver(this);
} catch (RemoteException e) {
Log.e(TAG, "Error registering observer :" + e);
}
@@ -173,21 +164,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mDhcpRange = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_dhcp_range);
if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
- mDhcpRange = new String[14];
- mDhcpRange[0] = DHCP_DEFAULT_RANGE1_START;
- mDhcpRange[1] = DHCP_DEFAULT_RANGE1_STOP;
- mDhcpRange[2] = DHCP_DEFAULT_RANGE2_START;
- mDhcpRange[3] = DHCP_DEFAULT_RANGE2_STOP;
- mDhcpRange[4] = DHCP_DEFAULT_RANGE3_START;
- mDhcpRange[5] = DHCP_DEFAULT_RANGE3_STOP;
- mDhcpRange[6] = DHCP_DEFAULT_RANGE4_START;
- mDhcpRange[7] = DHCP_DEFAULT_RANGE4_STOP;
- mDhcpRange[8] = DHCP_DEFAULT_RANGE5_START;
- mDhcpRange[9] = DHCP_DEFAULT_RANGE5_STOP;
- mDhcpRange[10] = DHCP_DEFAULT_RANGE6_START;
- mDhcpRange[11] = DHCP_DEFAULT_RANGE6_STOP;
- mDhcpRange[12] = DHCP_DEFAULT_RANGE7_START;
- mDhcpRange[13] = DHCP_DEFAULT_RANGE7_STOP;
+ mDhcpRange = DHCP_DEFAULT_RANGE;
}
mDunRequired = false; // resample when we turn on
@@ -258,8 +235,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return false;
}
public void interfaceAdded(String iface) {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
boolean found = false;
boolean usb = false;
if (isWifi(iface)) {
@@ -354,9 +329,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private void sendTetherStateChangedBroadcast() {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
try {
- if (!service.isTetheringSupported()) return;
+ if (!cm.isTetheringSupported()) return;
} catch (RemoteException e) {
return;
}
@@ -503,11 +478,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// used on cable insert/remove
private void enableUsbIfaces(boolean enable) {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
String[] ifaces = new String[0];
try {
- ifaces = service.listInterfaces();
+ ifaces = mNMService.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
return;
@@ -526,19 +499,17 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// toggled when we enter/leave the fully tethered state
private boolean enableUsbRndis(boolean enabled) {
if (DEBUG) Log.d(TAG, "enableUsbRndis(" + enabled + ")");
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
try {
if (enabled) {
synchronized (this) {
- if (!service.isUsbRNDISStarted()) {
- service.startUsbRNDIS();
+ if (!mNMService.isUsbRNDISStarted()) {
+ mNMService.startUsbRNDIS();
}
}
} else {
- if (service.isUsbRNDISStarted()) {
- service.stopUsbRNDIS();
+ if (mNMService.isUsbRNDISStarted()) {
+ mNMService.stopUsbRNDIS();
}
}
} catch (Exception e) {
@@ -552,13 +523,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private boolean configureUsbIface(boolean enabled) {
if (DEBUG) Log.d(TAG, "configureUsbIface(" + enabled + ")");
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-
// bring toggle the interfaces
String[] ifaces = new String[0];
try {
- ifaces = service.listInterfaces();
+ ifaces = mNMService.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
return false;
@@ -567,7 +535,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
if (isUsb(iface)) {
InterfaceConfiguration ifcg = null;
try {
- ifcg = service.getInterfaceConfig(iface);
+ ifcg = mNMService.getInterfaceConfig(iface);
if (ifcg != null) {
InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH);
@@ -578,7 +546,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
- service.setInterfaceConfig(iface, ifcg);
+ mNMService.setInterfaceConfig(iface, ifcg);
}
} catch (Exception e) {
Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
@@ -884,11 +852,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
class TetheredState extends State {
@Override
public void enter() {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.tetherInterface(mIfaceName);
+ mNMService.tetherInterface(mIfaceName);
} catch (Exception e) {
setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
@@ -913,16 +878,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
switch (message.what) {
case CMD_TETHER_UNREQUESTED:
case CMD_INTERFACE_DOWN:
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
if (mMyUpstreamIfaceName != null) {
try {
- service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+ mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
mMyUpstreamIfaceName = null;
} catch (Exception e) {
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
setLastErrorAndTransitionToInitialState(
@@ -931,7 +893,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception e) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
@@ -954,8 +916,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
break;
case CMD_TETHER_CONNECTION_CHANGED:
String newUpstreamIfaceName = (String)(message.obj);
- b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- service = INetworkManagementService.Stub.asInterface(b);
if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
(mMyUpstreamIfaceName != null &&
mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
@@ -964,11 +924,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
if (mMyUpstreamIfaceName != null) {
try {
- service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+ mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
mMyUpstreamIfaceName = null;
} catch (Exception e) {
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
setLastErrorAndTransitionToInitialState(
@@ -978,10 +938,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
if (newUpstreamIfaceName != null) {
try {
- service.enableNat(mIfaceName, newUpstreamIfaceName);
+ mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
} catch (Exception e) {
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
@@ -1000,15 +960,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
error = true;
// fall through
case CMD_TETHER_MODE_DEAD:
- b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- service = INetworkManagementService.Stub.asInterface(b);
if (mMyUpstreamIfaceName != null) {
try {
- service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+ mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
mMyUpstreamIfaceName = null;
} catch (Exception e) {
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception ee) {}
setLastErrorAndTransitionToInitialState(
@@ -1017,7 +975,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
try {
- service.untetherInterface(mIfaceName);
+ mNMService.untetherInterface(mIfaceName);
} catch (Exception e) {
setLastErrorAndTransitionToInitialState(
ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
@@ -1150,10 +1108,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
boolean retValue = true;
if (mMobileReserved) return retValue;
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
int result = Phone.APN_REQUEST_FAILED;
try {
- result = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+ result = cm.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
(mDunRequired ? Phone.FEATURE_ENABLE_DUN_ALWAYS :
Phone.FEATURE_ENABLE_HIPRI), new Binder());
} catch (Exception e) {
@@ -1177,10 +1135,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
protected boolean turnOffMobileConnection() {
if (mMobileReserved) {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service =
- IConnectivityManager.Stub.asInterface(b);
+ IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
try {
- service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+ cm.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
(mDunRequired? Phone.FEATURE_ENABLE_DUN_ALWAYS :
Phone.FEATURE_ENABLE_HIPRI));
} catch (Exception e) {
@@ -1191,28 +1148,25 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return true;
}
protected boolean turnOnMasterTetherSettings() {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.setIpForwardingEnabled(true);
+ mNMService.setIpForwardingEnabled(true);
} catch (Exception e) {
transitionTo(mSetIpForwardingEnabledErrorState);
return false;
}
try {
- service.startTethering(mDhcpRange);
+ mNMService.startTethering(mDhcpRange);
} catch (Exception e) {
try {
- service.stopTethering();
- service.startTethering(mDhcpRange);
+ mNMService.stopTethering();
+ mNMService.startTethering(mDhcpRange);
} catch (Exception ee) {
transitionTo(mStartTetheringErrorState);
return false;
}
}
try {
- service.setDnsForwarders(mDnsServers);
+ mNMService.setDnsForwarders(mDnsServers);
} catch (Exception e) {
transitionTo(mSetDnsForwardersErrorState);
return false;
@@ -1220,17 +1174,14 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return true;
}
protected boolean turnOffMasterTetherSettings() {
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.stopTethering();
+ mNMService.stopTethering();
} catch (Exception e) {
transitionTo(mStopTetheringErrorState);
return false;
}
try {
- service.setIpForwardingEnabled(false);
+ mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {
transitionTo(mSetIpForwardingDisabledErrorState);
return false;
@@ -1253,12 +1204,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
} catch (RemoteException e) { }
- b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-
String[] ifaces = new String[0];
try {
- ifaces = service.listInterfaces();
+ ifaces = mNMService.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
return null;
@@ -1270,7 +1218,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// verify it is active
InterfaceConfiguration ifcg = null;
try {
- ifcg = service.getInterfaceConfig(iface);
+ ifcg = mNMService.getInterfaceConfig(iface);
if (ifcg.isActive()) {
return iface;
}
@@ -1503,11 +1451,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
public void enter() {
Log.e(TAG, "Error in startTethering");
notify(TetherInterfaceSM.CMD_START_TETHERING_ERROR);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.setIpForwardingEnabled(false);
+ mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
}
}
@@ -1517,11 +1462,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
public void enter() {
Log.e(TAG, "Error in stopTethering");
notify(TetherInterfaceSM.CMD_STOP_TETHERING_ERROR);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.setIpForwardingEnabled(false);
+ mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
}
}
@@ -1531,14 +1473,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
public void enter() {
Log.e(TAG, "Error in setDnsForwarders");
notify(TetherInterfaceSM.CMD_SET_DNS_FORWARDERS_ERROR);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService service =
- INetworkManagementService.Stub.asInterface(b);
try {
- service.stopTethering();
+ mNMService.stopTethering();
} catch (Exception e) {}
try {
- service.setIpForwardingEnabled(false);
+ mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {}
}
}
diff --git a/services/java/com/android/server/location/ComprehensiveCountryDetector.java b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
index e9ce3ce..bb9e60f 100755
--- a/services/java/com/android/server/location/ComprehensiveCountryDetector.java
+++ b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
@@ -56,6 +56,7 @@ import java.util.TimerTask;
public class ComprehensiveCountryDetector extends CountryDetectorBase {
private final static String TAG = "ComprehensiveCountryDetector";
+ /* package */ static final boolean DEBUG = false;
/**
* The refresh interval when the location based country was used
@@ -90,7 +91,9 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
* The listener for receiving the notification from LocationBasedCountryDetector.
*/
private CountryListener mLocationBasedCountryDetectionListener = new CountryListener() {
+ @Override
public void onCountryDetected(Country country) {
+ if (DEBUG) Slog.d(TAG, "Country detected via LocationBasedCountryDetector");
mCountryFromLocation = country;
// Don't start the LocationBasedCountryDetector.
detectCountry(true, false);
@@ -206,6 +209,7 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
protected void runAfterDetectionAsync(final Country country, final Country detectedCountry,
final boolean notifyChange, final boolean startLocationBasedDetection) {
mHandler.post(new Runnable() {
+ @Override
public void run() {
runAfterDetection(
country, detectedCountry, notifyChange, startLocationBasedDetection);
@@ -233,9 +237,20 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
if (notifyChange) {
notifyIfCountryChanged(country, detectedCountry);
}
+ if (DEBUG) {
+ Slog.d(TAG, "startLocationBasedDetection=" + startLocationBasedDetection
+ + " detectCountry=" + (detectedCountry == null ? null :
+ "(source: " + detectedCountry.getSource()
+ + ", countryISO: " + detectedCountry.getCountryIso() + ")")
+ + " isAirplaneModeOff()=" + isAirplaneModeOff()
+ + " mListener=" + mListener
+ + " isGeoCoderImplemnted()=" + isGeoCoderImplemented());
+ }
+
if (startLocationBasedDetection && (detectedCountry == null
|| detectedCountry.getSource() > Country.COUNTRY_SOURCE_LOCATION)
&& isAirplaneModeOff() && mListener != null && isGeoCoderImplemented()) {
+ if (DEBUG) Slog.d(TAG, "run startLocationBasedDetector()");
// Start finding location when the source is less reliable than the
// location and the airplane mode is off (as geocoder will not
// work).
@@ -266,12 +281,20 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
if (mLocationBasedCountryDetector != null) {
return;
}
+ if (DEBUG) {
+ Slog.d(TAG, "starts LocationBasedDetector to detect Country code via Location info "
+ + "(e.g. GPS)");
+ }
mLocationBasedCountryDetector = createLocationBasedCountryDetector();
mLocationBasedCountryDetector.setCountryListener(listener);
mLocationBasedCountryDetector.detectCountry();
}
private synchronized void stopLocationBasedDetector() {
+ if (DEBUG) {
+ Slog.d(TAG, "tries to stop LocationBasedDetector "
+ + "(current detector: " + mLocationBasedCountryDetector + ")");
+ }
if (mLocationBasedCountryDetector != null) {
mLocationBasedCountryDetector.stop();
mLocationBasedCountryDetector = null;
@@ -305,10 +328,17 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
*/
private synchronized void scheduleLocationRefresh() {
if (mLocationRefreshTimer != null) return;
+ if (DEBUG) {
+ Slog.d(TAG, "start periodic location refresh timer. Interval: "
+ + LOCATION_REFRESH_INTERVAL);
+ }
mLocationRefreshTimer = new Timer();
mLocationRefreshTimer.schedule(new TimerTask() {
@Override
public void run() {
+ if (DEBUG) {
+ Slog.d(TAG, "periodic location refresh event. Starts detecting Country code");
+ }
mLocationRefreshTimer = null;
detectCountry(false, true);
}
diff --git a/services/java/com/android/server/location/GpsXtraDownloader.java b/services/java/com/android/server/location/GpsXtraDownloader.java
index 813d255..e420073 100644
--- a/services/java/com/android/server/location/GpsXtraDownloader.java
+++ b/services/java/com/android/server/location/GpsXtraDownloader.java
@@ -19,7 +19,6 @@ package com.android.server.location;
import android.content.Context;
import android.net.Proxy;
import android.net.http.AndroidHttpClient;
-import android.util.Config;
import android.util.Log;
import org.apache.http.HttpEntity;
diff --git a/services/java/com/android/server/location/LocationBasedCountryDetector.java b/services/java/com/android/server/location/LocationBasedCountryDetector.java
index 139f05d..d4fb8ee 100755
--- a/services/java/com/android/server/location/LocationBasedCountryDetector.java
+++ b/services/java/com/android/server/location/LocationBasedCountryDetector.java
@@ -16,12 +16,6 @@
package com.android.server.location;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
-
import android.content.Context;
import android.location.Address;
import android.location.Country;
@@ -32,6 +26,12 @@ import android.location.LocationManager;
import android.os.Bundle;
import android.util.Slog;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
/**
* This class detects which country the user currently is in through the enabled
* location providers and the GeoCoder
@@ -86,24 +86,23 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
return country;
}
+ protected boolean isAcceptableProvider(String provider) {
+ // We don't want to actively initiate a location fix here (with gps or network providers).
+ return LocationManager.PASSIVE_PROVIDER.equals(provider);
+ }
+
/**
- * Register the listeners with the location providers
+ * Register a listener with a provider name
*/
- protected void registerEnabledProviders(List<LocationListener> listeners) {
- int total = listeners.size();
- for (int i = 0; i< total; i++) {
- mLocationManager.requestLocationUpdates(
- mEnabledProviders.get(i), 0, 0, listeners.get(i));
- }
+ protected void registerListener(String provider, LocationListener listener) {
+ mLocationManager.requestLocationUpdates(provider, 0, 0, listener);
}
/**
- * Unregister the listeners with the location providers
+ * Unregister an already registered listener
*/
- protected void unregisterProviders(List<LocationListener> listeners) {
- for (LocationListener listener : listeners) {
- mLocationManager.removeUpdates(listener);
- }
+ protected void unregisterListener(LocationListener listener) {
+ mLocationManager.removeUpdates(listener);
}
/**
@@ -130,14 +129,11 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
return QUERY_LOCATION_TIMEOUT;
}
- /**
- * @return the total number of enabled location providers
- */
- protected int getTotalEnabledProviders() {
+ protected List<String> getEnabledProviders() {
if (mEnabledProviders == null) {
mEnabledProviders = mLocationManager.getProviders(true);
}
- return mEnabledProviders.size();
+ return mEnabledProviders;
}
/**
@@ -152,27 +148,36 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
throw new IllegalStateException();
}
// Request the location from all enabled providers.
- int totalProviders = getTotalEnabledProviders();
+ List<String> enabledProviders = getEnabledProviders();
+ int totalProviders = enabledProviders.size();
if (totalProviders > 0) {
mLocationListeners = new ArrayList<LocationListener>(totalProviders);
for (int i = 0; i < totalProviders; i++) {
- LocationListener listener = new LocationListener () {
- public void onLocationChanged(Location location) {
- if (location != null) {
- LocationBasedCountryDetector.this.stop();
- queryCountryCode(location);
+ String provider = enabledProviders.get(i);
+ if (isAcceptableProvider(provider)) {
+ LocationListener listener = new LocationListener () {
+ @Override
+ public void onLocationChanged(Location location) {
+ if (location != null) {
+ LocationBasedCountryDetector.this.stop();
+ queryCountryCode(location);
+ }
}
- }
- public void onProviderDisabled(String provider) {
- }
- public void onProviderEnabled(String provider) {
- }
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
- };
- mLocationListeners.add(listener);
+ @Override
+ public void onProviderDisabled(String provider) {
+ }
+ @Override
+ public void onProviderEnabled(String provider) {
+ }
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+ mLocationListeners.add(listener);
+ registerListener(provider, listener);
+ }
}
- registerEnabledProviders(mLocationListeners);
+
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
@@ -197,7 +202,9 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
@Override
public synchronized void stop() {
if (mLocationListeners != null) {
- unregisterProviders(mLocationListeners);
+ for (LocationListener listener : mLocationListeners) {
+ unregisterListener(listener);
+ }
mLocationListeners = null;
}
if (mTimer != null) {
@@ -216,6 +223,7 @@ public class LocationBasedCountryDetector extends CountryDetectorBase {
}
if (mQueryThread != null) return;
mQueryThread = new Thread(new Runnable() {
+ @Override
public void run() {
String countryIso = null;
if (location != null) {
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
new file mode 100644
index 0000000..17c7161
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.MANAGE_APP_TOKENS;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+import static android.net.NetworkPolicyManager.dumpPolicy;
+import static android.net.NetworkPolicyManager.dumpRules;
+
+import android.app.IActivityManager;
+import android.app.IProcessObserver;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.INetworkPolicyListener;
+import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
+import android.os.IPowerManager;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Service that maintains low-level network policy rules and collects usage
+ * statistics to drive those rules.
+ * <p>
+ * Derives active rules by combining a given policy with other system status,
+ * and delivers to listeners, such as {@link ConnectivityManager}, for
+ * enforcement.
+ */
+public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
+ private static final String TAG = "NetworkPolicy";
+ private static final boolean LOGD = true;
+
+ private final Context mContext;
+ private final IActivityManager mActivityManager;
+ private final IPowerManager mPowerManager;
+ private final INetworkStatsService mNetworkStats;
+
+ private final Object mRulesLock = new Object();
+
+ private boolean mScreenOn;
+
+ /** Current network policy for each UID. */
+ private SparseIntArray mUidPolicy = new SparseIntArray();
+ /** Current derived network rules for each UID. */
+ private SparseIntArray mUidRules = new SparseIntArray();
+
+ /** Foreground at both UID and PID granularity. */
+ private SparseBooleanArray mUidForeground = new SparseBooleanArray();
+ private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
+ SparseBooleanArray>();
+
+ private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
+ INetworkPolicyListener>();
+
+ // TODO: save/restore policy information from disk
+
+ // TODO: keep whitelist of system-critical services that should never have
+ // rules enforced, such as system, phone, and radio UIDs.
+
+ // TODO: keep record of billing cycle details, and limit rules
+ // TODO: keep map of interfaces-to-billing-relationship
+
+ public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
+ IPowerManager powerManager, INetworkStatsService networkStats) {
+ mContext = checkNotNull(context, "missing context");
+ mActivityManager = checkNotNull(activityManager, "missing activityManager");
+ mPowerManager = checkNotNull(powerManager, "missing powerManager");
+ mNetworkStats = checkNotNull(networkStats, "missing networkStats");
+ }
+
+ public void systemReady() {
+ // TODO: read current policy from disk
+
+ updateScreenOn();
+
+ try {
+ mActivityManager.registerProcessObserver(mProcessObserver);
+ } catch (RemoteException e) {
+ // ouch, no foregroundActivities updates means some processes may
+ // never get network access.
+ Slog.e(TAG, "unable to register IProcessObserver", e);
+ }
+
+ // TODO: traverse existing processes to know foreground state, or have
+ // activitymanager dispatch current state when new observer attached.
+
+ final IntentFilter screenFilter = new IntentFilter();
+ screenFilter.addAction(Intent.ACTION_SCREEN_ON);
+ screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ mContext.registerReceiver(mScreenReceiver, screenFilter);
+
+ }
+
+ private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+ // only someone like AMS should only be calling us
+ mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);
+
+ synchronized (mRulesLock) {
+ // because a uid can have multiple pids running inside, we need to
+ // remember all pid states and summarize foreground at uid level.
+
+ // record foreground for this specific pid
+ SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+ if (pidForeground == null) {
+ pidForeground = new SparseBooleanArray(2);
+ mUidPidForeground.put(uid, pidForeground);
+ }
+ pidForeground.put(pid, foregroundActivities);
+ computeUidForegroundL(uid);
+ }
+ }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {
+ // only someone like AMS should only be calling us
+ mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);
+
+ synchronized (mRulesLock) {
+ // clear records and recompute, when they exist
+ final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+ if (pidForeground != null) {
+ pidForeground.delete(pid);
+ computeUidForegroundL(uid);
+ }
+ }
+ }
+ };
+
+ private BroadcastReceiver mScreenReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mRulesLock) {
+ // screen-related broadcasts are protected by system, no need
+ // for permissions check.
+ updateScreenOn();
+ }
+ }
+ };
+
+ @Override
+ public void setUidPolicy(int uid, int policy) {
+ // TODO: create permission for modifying data policy
+ mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+ final int oldPolicy;
+ synchronized (mRulesLock) {
+ oldPolicy = getUidPolicy(uid);
+ mUidPolicy.put(uid, policy);
+ updateRulesForUidL(uid);
+ }
+
+ // TODO: consider dispatching BACKGROUND_DATA_SETTING broadcast
+ }
+
+ @Override
+ public int getUidPolicy(int uid) {
+ synchronized (mRulesLock) {
+ return mUidPolicy.get(uid, POLICY_NONE);
+ }
+ }
+
+ @Override
+ public void registerListener(INetworkPolicyListener listener) {
+ mListeners.register(listener);
+
+ synchronized (mRulesLock) {
+ // dispatch any existing rules to new listeners
+ final int size = mUidRules.size();
+ for (int i = 0; i < size; i++) {
+ final int uid = mUidRules.keyAt(i);
+ final int uidRules = mUidRules.valueAt(i);
+ if (uidRules != RULE_ALLOW_ALL) {
+ try {
+ listener.onRulesChanged(uid, uidRules);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void unregisterListener(INetworkPolicyListener listener) {
+ mListeners.unregister(listener);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ mContext.enforceCallingOrSelfPermission(DUMP, TAG);
+
+ synchronized (mRulesLock) {
+ fout.println("Policy status for known UIDs:");
+
+ final SparseBooleanArray knownUids = new SparseBooleanArray();
+ collectKeys(mUidPolicy, knownUids);
+ collectKeys(mUidForeground, knownUids);
+ collectKeys(mUidRules, knownUids);
+
+ final int size = knownUids.size();
+ for (int i = 0; i < size; i++) {
+ final int uid = knownUids.keyAt(i);
+ fout.print(" UID=");
+ fout.print(uid);
+
+ fout.print(" policy=");
+ final int policyIndex = mUidPolicy.indexOfKey(uid);
+ if (policyIndex < 0) {
+ fout.print("UNKNOWN");
+ } else {
+ dumpPolicy(fout, mUidPolicy.valueAt(policyIndex));
+ }
+
+ fout.print(" foreground=");
+ final int foregroundIndex = mUidPidForeground.indexOfKey(uid);
+ if (foregroundIndex < 0) {
+ fout.print("UNKNOWN");
+ } else {
+ dumpSparseBooleanArray(fout, mUidPidForeground.valueAt(foregroundIndex));
+ }
+
+ fout.print(" rules=");
+ final int rulesIndex = mUidRules.indexOfKey(uid);
+ if (rulesIndex < 0) {
+ fout.print("UNKNOWN");
+ } else {
+ dumpRules(fout, mUidRules.valueAt(rulesIndex));
+ }
+
+ fout.println();
+ }
+ }
+ }
+
+ @Override
+ public boolean isUidForeground(int uid) {
+ synchronized (mRulesLock) {
+ // only really in foreground when screen is also on
+ return mUidForeground.get(uid, false) && mScreenOn;
+ }
+ }
+
+ /**
+ * Foreground for PID changed; recompute foreground at UID level. If
+ * changed, will trigger {@link #updateRulesForUidL(int)}.
+ */
+ private void computeUidForegroundL(int uid) {
+ final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+
+ // current pid is dropping foreground; examine other pids
+ boolean uidForeground = false;
+ final int size = pidForeground.size();
+ for (int i = 0; i < size; i++) {
+ if (pidForeground.valueAt(i)) {
+ uidForeground = true;
+ break;
+ }
+ }
+
+ final boolean oldUidForeground = mUidForeground.get(uid, false);
+ if (oldUidForeground != uidForeground) {
+ // foreground changed, push updated rules
+ mUidForeground.put(uid, uidForeground);
+ updateRulesForUidL(uid);
+ }
+ }
+
+ private void updateScreenOn() {
+ synchronized (mRulesLock) {
+ try {
+ mScreenOn = mPowerManager.isScreenOn();
+ } catch (RemoteException e) {
+ }
+ updateRulesForScreenL();
+ }
+ }
+
+ /**
+ * Update rules that might be changed by {@link #mScreenOn} value.
+ */
+ private void updateRulesForScreenL() {
+ // only update rules for anyone with foreground activities
+ final int size = mUidForeground.size();
+ for (int i = 0; i < size; i++) {
+ if (mUidForeground.valueAt(i)) {
+ final int uid = mUidForeground.keyAt(i);
+ updateRulesForUidL(uid);
+ }
+ }
+ }
+
+ private void updateRulesForUidL(int uid) {
+ final int uidPolicy = getUidPolicy(uid);
+ final boolean uidForeground = isUidForeground(uid);
+
+ // derive active rules based on policy and active state
+ int uidRules = RULE_ALLOW_ALL;
+ if (!uidForeground && (uidPolicy & POLICY_REJECT_PAID_BACKGROUND) != 0) {
+ // uid in background, and policy says to block paid data
+ uidRules = RULE_REJECT_PAID;
+ }
+
+ // TODO: only dispatch when rules actually change
+
+ // record rule locally to dispatch to new listeners
+ mUidRules.put(uid, uidRules);
+
+ // dispatch changed rule to existing listeners
+ final int length = mListeners.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+ if (listener != null) {
+ try {
+ listener.onRulesChanged(uid, uidRules);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mListeners.finishBroadcast();
+ }
+
+ private static <T> T checkNotNull(T value, String message) {
+ if (value == null) {
+ throw new NullPointerException(message);
+ }
+ return value;
+ }
+
+ private static void collectKeys(SparseIntArray source, SparseBooleanArray target) {
+ final int size = source.size();
+ for (int i = 0; i < size; i++) {
+ target.put(source.keyAt(i), true);
+ }
+ }
+
+ private static void collectKeys(SparseBooleanArray source, SparseBooleanArray target) {
+ final int size = source.size();
+ for (int i = 0; i < size; i++) {
+ target.put(source.keyAt(i), true);
+ }
+ }
+
+ private static void dumpSparseBooleanArray(PrintWriter fout, SparseBooleanArray value) {
+ fout.print("[");
+ final int size = value.size();
+ for (int i = 0; i < size; i++) {
+ fout.print(value.keyAt(i) + "=" + value.valueAt(i));
+ if (i < size - 1) fout.print(",");
+ }
+ fout.print("]");
+ }
+}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
new file mode 100644
index 0000000..d9c1f25
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2008 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.net;
+
+import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.SHUTDOWN;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkStats.UID_ALL;
+
+import android.app.AlarmManager;
+import android.app.IAlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.INetworkStatsService;
+import android.net.LinkProperties;
+import android.net.NetworkStats;
+import android.net.NetworkStatsHistory;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.telephony.TelephonyManager;
+import android.text.format.DateUtils;
+import android.util.NtpTrustedTime;
+import android.util.Slog;
+import android.util.TrustedTime;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyIntents;
+import com.google.android.collect.Maps;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+/**
+ * Collect and persist detailed network statistics, and provide this data to
+ * other system services.
+ */
+public class NetworkStatsService extends INetworkStatsService.Stub {
+ private static final String TAG = "NetworkStatsService";
+ private static final boolean LOGD = true;
+
+ private final Context mContext;
+ private final INetworkManagementService mNetworkManager;
+ private final IAlarmManager mAlarmManager;
+ private final TrustedTime mTime;
+
+ private static final String ACTION_NETWORK_STATS_POLL =
+ "com.android.server.action.NETWORK_STATS_POLL";
+
+ private PendingIntent mPollIntent;
+
+ // TODO: move tweakable params to Settings.Secure
+ // TODO: listen for kernel push events through netd instead of polling
+
+ private static final long KB_IN_BYTES = 1024;
+
+ private static final long POLL_INTERVAL = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
+ private static final long SUMMARY_BUCKET_DURATION = 6 * DateUtils.HOUR_IN_MILLIS;
+ private static final long SUMMARY_MAX_HISTORY = 90 * DateUtils.DAY_IN_MILLIS;
+
+ // TODO: remove these high-frequency testing values
+// private static final long POLL_INTERVAL = 5 * DateUtils.SECOND_IN_MILLIS;
+// private static final long SUMMARY_BUCKET_DURATION = 10 * DateUtils.SECOND_IN_MILLIS;
+// private static final long SUMMARY_MAX_HISTORY = 2 * DateUtils.MINUTE_IN_MILLIS;
+
+ /** Minimum delta required to persist to disk. */
+ private static final long SUMMARY_PERSIST_THRESHOLD = 64 * KB_IN_BYTES;
+
+ private static final long TIME_CACHE_MAX_AGE = DateUtils.DAY_IN_MILLIS;
+
+ private final Object mStatsLock = new Object();
+
+ /** Set of active ifaces during this boot. */
+ private HashMap<String, InterfaceInfo> mActiveIface = Maps.newHashMap();
+ /** Set of historical stats for known ifaces. */
+ private HashMap<InterfaceInfo, NetworkStatsHistory> mIfaceStats = Maps.newHashMap();
+
+ private NetworkStats mLastPollStats;
+ private NetworkStats mLastPersistStats;
+
+ private final HandlerThread mHandlerThread;
+ private final Handler mHandler;
+
+ // TODO: collect detailed uid stats, storing tag-granularity data until next
+ // dropbox, and uid summary for a specific bucket count.
+
+ // TODO: periodically compile statistics and send to dropbox.
+
+ public NetworkStatsService(
+ Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) {
+ // TODO: move to using cached NtpTrustedTime
+ this(context, networkManager, alarmManager, new NtpTrustedTime());
+ }
+
+ public NetworkStatsService(Context context, INetworkManagementService networkManager,
+ IAlarmManager alarmManager, TrustedTime time) {
+ mContext = checkNotNull(context, "missing Context");
+ mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService");
+ mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager");
+ mTime = checkNotNull(time, "missing TrustedTime");
+
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ }
+
+ public void systemReady() {
+ // read historical stats from disk
+ readStatsLocked();
+
+ // watch other system services that claim interfaces
+ // TODO: protect incoming broadcast with permissions check.
+ // TODO: consider migrating this to ConnectivityService, but it might
+ // cause a circular dependency.
+ final IntentFilter interfaceFilter = new IntentFilter();
+ interfaceFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+ interfaceFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(mInterfaceReceiver, interfaceFilter);
+
+ // listen for periodic polling events
+ final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
+ mContext.registerReceiver(mPollReceiver, pollFilter, UPDATE_DEVICE_STATS, mHandler);
+
+ // persist stats during clean shutdown
+ final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
+ mContext.registerReceiver(mShutdownReceiver, shutdownFilter, SHUTDOWN, null);
+
+ try {
+ registerPollAlarmLocked();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "unable to register poll alarm");
+ }
+ }
+
+ /**
+ * Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and
+ * reschedule based on current {@link #POLL_INTERVAL} value.
+ */
+ private void registerPollAlarmLocked() throws RemoteException {
+ if (mPollIntent != null) {
+ mAlarmManager.remove(mPollIntent);
+ }
+
+ mPollIntent = PendingIntent.getBroadcast(
+ mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
+
+ final long currentRealtime = SystemClock.elapsedRealtime();
+ mAlarmManager.setInexactRepeating(
+ AlarmManager.ELAPSED_REALTIME, currentRealtime, POLL_INTERVAL, mPollIntent);
+ }
+
+ @Override
+ public NetworkStatsHistory[] getNetworkStatsSummary(int networkType) {
+ // TODO: return history for requested types
+ return null;
+ }
+
+ @Override
+ public NetworkStatsHistory getNetworkStatsUid(int uid) {
+ // TODO: return history for requested uid
+ return null;
+ }
+
+ /**
+ * Receiver that watches for other system components that claim network
+ * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
+ * with mobile interfaces.
+ */
+ private BroadcastReceiver mInterfaceReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED.equals(action)) {
+ final LinkProperties prop = intent.getParcelableExtra(
+ Phone.DATA_LINK_PROPERTIES_KEY);
+ final String iface = prop != null ? prop.getInterfaceName() : null;
+ if (iface != null) {
+ final TelephonyManager teleManager = (TelephonyManager) context
+ .getSystemService(Context.TELEPHONY_SERVICE);
+ final InterfaceInfo info = new InterfaceInfo(
+ iface, TYPE_MOBILE, teleManager.getSubscriberId());
+ reportActiveInterface(info);
+ }
+ } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+ final LinkProperties prop = intent.getParcelableExtra(
+ WifiManager.EXTRA_LINK_PROPERTIES);
+ final String iface = prop != null ? prop.getInterfaceName() : null;
+ if (iface != null) {
+ final InterfaceInfo info = new InterfaceInfo(iface, TYPE_WIFI, null);
+ reportActiveInterface(info);
+ }
+ }
+ }
+ };
+
+ private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // already running on background handler, network/io is safe, and
+ // caller verified to have UPDATE_DEVICE_STATS permission above.
+ synchronized (mStatsLock) {
+ // TODO: acquire wakelock while performing poll
+ performPollLocked();
+ }
+ }
+ };
+
+ private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // persist stats during clean shutdown
+ synchronized (mStatsLock) {
+ writeStatsLocked();
+ }
+ }
+ };
+
+ private void performPollLocked() {
+ if (LOGD) Slog.v(TAG, "performPollLocked()");
+
+ // try refreshing time source when stale
+ if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
+ mTime.forceRefresh();
+ }
+
+ // TODO: consider marking "untrusted" times in historical stats
+ final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
+ : System.currentTimeMillis();
+
+ final NetworkStats current;
+ try {
+ current = mNetworkManager.getNetworkStatsSummary();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "problem reading network stats");
+ return;
+ }
+
+ // update historical usage with delta since last poll
+ final NetworkStats pollDelta = computeStatsDelta(mLastPollStats, current);
+ final long timeStart = currentTime - pollDelta.elapsedRealtime;
+ for (String iface : pollDelta.getKnownIfaces()) {
+ final InterfaceInfo info = mActiveIface.get(iface);
+ if (info == null) {
+ if (LOGD) Slog.w(TAG, "unknown interface " + iface + ", ignoring stats");
+ continue;
+ }
+
+ final int index = pollDelta.findIndex(iface, UID_ALL);
+ final long rx = pollDelta.rx[index];
+ final long tx = pollDelta.tx[index];
+
+ final NetworkStatsHistory history = findOrCreateHistoryLocked(info);
+ history.recordData(timeStart, currentTime, rx, tx);
+ history.removeBucketsBefore(currentTime - SUMMARY_MAX_HISTORY);
+ }
+
+ mLastPollStats = current;
+
+ // decide if enough has changed to trigger persist
+ final NetworkStats persistDelta = computeStatsDelta(mLastPersistStats, current);
+ for (String iface : persistDelta.getKnownIfaces()) {
+ final int index = persistDelta.findIndex(iface, UID_ALL);
+ if (persistDelta.rx[index] > SUMMARY_PERSIST_THRESHOLD
+ || persistDelta.tx[index] > SUMMARY_PERSIST_THRESHOLD) {
+ writeStatsLocked();
+ mLastPersistStats = current;
+ break;
+ }
+ }
+ }
+
+ private NetworkStatsHistory findOrCreateHistoryLocked(InterfaceInfo info) {
+ NetworkStatsHistory stats = mIfaceStats.get(info);
+ if (stats == null) {
+ stats = new NetworkStatsHistory(
+ info.networkType, info.identity, UID_ALL, SUMMARY_BUCKET_DURATION);
+ mIfaceStats.put(info, stats);
+ }
+ return stats;
+ }
+
+ private void readStatsLocked() {
+ if (LOGD) Slog.v(TAG, "readStatsLocked()");
+ // TODO: read historical stats from disk using AtomicFile
+ }
+
+ private void writeStatsLocked() {
+ if (LOGD) Slog.v(TAG, "writeStatsLocked()");
+ // TODO: persist historical stats to disk using AtomicFile
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mContext.enforceCallingOrSelfPermission(DUMP, TAG);
+
+ pw.println("Active interfaces:");
+ for (InterfaceInfo info : mActiveIface.values()) {
+ info.dump(" ", pw);
+ }
+
+ pw.println("Known historical stats:");
+ for (NetworkStatsHistory stats : mIfaceStats.values()) {
+ stats.dump(" ", pw);
+ }
+ }
+
+ /**
+ * Details for a well-known network interface, including its name, network
+ * type, and billing relationship identity (such as IMSI).
+ */
+ private static class InterfaceInfo {
+ public final String iface;
+ public final int networkType;
+ public final String identity;
+
+ public InterfaceInfo(String iface, int networkType, String identity) {
+ this.iface = iface;
+ this.networkType = networkType;
+ this.identity = identity;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((identity == null) ? 0 : identity.hashCode());
+ result = prime * result + ((iface == null) ? 0 : iface.hashCode());
+ result = prime * result + networkType;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof InterfaceInfo) {
+ final InterfaceInfo info = (InterfaceInfo) obj;
+ return equal(iface, info.iface) && networkType == info.networkType
+ && equal(identity, info.identity);
+ }
+ return false;
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix);
+ pw.print("InterfaceInfo: iface="); pw.print(iface);
+ pw.print(" networkType="); pw.print(networkType);
+ pw.print(" identity="); pw.println(identity);
+ }
+ }
+
+ private void reportActiveInterface(InterfaceInfo info) {
+ synchronized (mStatsLock) {
+ // TODO: when interface redefined, port over historical stats
+ mActiveIface.put(info.iface, info);
+ }
+ }
+
+ /**
+ * Return the delta between two {@link NetworkStats} snapshots, where {@code
+ * before} can be {@code null}.
+ */
+ private static NetworkStats computeStatsDelta(NetworkStats before, NetworkStats current) {
+ if (before != null) {
+ return current.subtract(before, false);
+ } else {
+ return current;
+ }
+ }
+
+ private static boolean equal(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
+ }
+
+ private static <T> T checkNotNull(T value, String message) {
+ if (value == null) {
+ throw new NullPointerException(message);
+ }
+ return value;
+ }
+
+}
diff --git a/services/java/com/android/server/pm/BasePermission.java b/services/java/com/android/server/pm/BasePermission.java
new file mode 100644
index 0000000..4f27408
--- /dev/null
+++ b/services/java/com/android/server/pm/BasePermission.java
@@ -0,0 +1,59 @@
+/*
+ * 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.pm;
+
+import android.content.pm.PackageParser;
+import android.content.pm.PermissionInfo;
+
+final class BasePermission {
+ final static int TYPE_NORMAL = 0;
+
+ final static int TYPE_BUILTIN = 1;
+
+ final static int TYPE_DYNAMIC = 2;
+
+ final String name;
+
+ String sourcePackage;
+
+ PackageSettingBase packageSetting;
+
+ final int type;
+
+ int protectionLevel;
+
+ PackageParser.Permission perm;
+
+ PermissionInfo pendingInfo;
+
+ int uid;
+
+ int[] gids;
+
+ BasePermission(String _name, String _sourcePackage, int _type) {
+ name = _name;
+ sourcePackage = _sourcePackage;
+ type = _type;
+ // Default to most conservative protection level.
+ protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+ }
+
+ public String toString() {
+ return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
+ + "}";
+ }
+}
diff --git a/services/java/com/android/server/pm/GrantedPermissions.java b/services/java/com/android/server/pm/GrantedPermissions.java
new file mode 100644
index 0000000..c7629b9
--- /dev/null
+++ b/services/java/com/android/server/pm/GrantedPermissions.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 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.pm;
+
+import android.content.pm.ApplicationInfo;
+
+import java.util.HashSet;
+
+class GrantedPermissions {
+ int pkgFlags;
+
+ HashSet<String> grantedPermissions = new HashSet<String>();
+
+ int[] gids;
+
+ GrantedPermissions(int pkgFlags) {
+ setFlags(pkgFlags);
+ }
+
+ @SuppressWarnings("unchecked")
+ GrantedPermissions(GrantedPermissions base) {
+ pkgFlags = base.pkgFlags;
+ grantedPermissions = (HashSet<String>) base.grantedPermissions.clone();
+
+ if (base.gids != null) {
+ gids = base.gids.clone();
+ }
+ }
+
+ void setFlags(int pkgFlags) {
+ this.pkgFlags = pkgFlags
+ & (ApplicationInfo.FLAG_SYSTEM
+ | ApplicationInfo.FLAG_FORWARD_LOCK
+ | ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+ }
+}
diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/pm/Installer.java
index 08d1b82..d10aa97 100644
--- a/services/java/com/android/server/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -14,28 +14,31 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.pm;
import android.content.pm.PackageStats;
-import android.net.LocalSocketAddress;
import android.net.LocalSocket;
-import android.util.Config;
+import android.net.LocalSocketAddress;
import android.util.Slog;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.Socket;
-
class Installer {
private static final String TAG = "Installer";
- InputStream mIn;
- OutputStream mOut;
- LocalSocket mSocket;
- byte buf[] = new byte[1024];
- int buflen = 0;
+ private static final boolean LOCAL_DEBUG = false;
+
+ InputStream mIn;
+
+ OutputStream mOut;
+
+ LocalSocket mSocket;
+
+ byte buf[] = new byte[1024];
+
+ int buflen = 0;
private boolean connect() {
if (mSocket != null) {
@@ -45,8 +48,8 @@ class Installer {
try {
mSocket = new LocalSocket();
- LocalSocketAddress address = new LocalSocketAddress(
- "installd", LocalSocketAddress.Namespace.RESERVED);
+ LocalSocketAddress address = new LocalSocketAddress("installd",
+ LocalSocketAddress.Namespace.RESERVED);
mSocket.connect(address);
@@ -59,112 +62,131 @@ class Installer {
return true;
}
- private void disconnect() {
- Slog.i(TAG,"disconnecting...");
- try {
- if (mSocket != null) mSocket.close();
- } catch (IOException ex) { }
- try {
- if (mIn != null) mIn.close();
- } catch (IOException ex) { }
- try {
- if (mOut != null) mOut.close();
- } catch (IOException ex) { }
- mSocket = null;
- mIn = null;
- mOut = null;
- }
-
- private boolean readBytes(byte buffer[], int len) {
- int off = 0, count;
- if (len < 0) return false;
- while (off != len) {
- try {
- count = mIn.read(buffer, off, len - off);
- if (count <= 0) {
+ private void disconnect() {
+ Slog.i(TAG, "disconnecting...");
+ try {
+ if (mSocket != null)
+ mSocket.close();
+ } catch (IOException ex) {
+ }
+ try {
+ if (mIn != null)
+ mIn.close();
+ } catch (IOException ex) {
+ }
+ try {
+ if (mOut != null)
+ mOut.close();
+ } catch (IOException ex) {
+ }
+ mSocket = null;
+ mIn = null;
+ mOut = null;
+ }
+
+ private boolean readBytes(byte buffer[], int len) {
+ int off = 0, count;
+ if (len < 0)
+ return false;
+ while (off != len) {
+ try {
+ count = mIn.read(buffer, off, len - off);
+ if (count <= 0) {
Slog.e(TAG, "read error " + count);
break;
}
- off += count;
- } catch (IOException ex) {
- Slog.e(TAG,"read exception");
- break;
- }
- }
-// Slog.i(TAG, "read "+len+" bytes");
- if (off == len) return true;
- disconnect();
- return false;
- }
-
- private boolean readReply() {
- int len;
- buflen = 0;
- if (!readBytes(buf, 2)) return false;
- len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
- if ((len < 1) || (len > 1024)) {
- Slog.e(TAG,"invalid reply length ("+len+")");
- disconnect();
- return false;
- }
- if (!readBytes(buf, len)) return false;
- buflen = len;
- return true;
- }
-
- private boolean writeCommand(String _cmd) {
- byte[] cmd = _cmd.getBytes();
- int len = cmd.length;
- if ((len < 1) || (len > 1024)) return false;
- buf[0] = (byte) (len & 0xff);
- buf[1] = (byte) ((len >> 8) & 0xff);
- try {
- mOut.write(buf, 0, 2);
- mOut.write(cmd, 0, len);
- } catch (IOException ex) {
- Slog.e(TAG,"write error");
- disconnect();
- return false;
- }
- return true;
- }
-
- private synchronized String transaction(String cmd) {
- if (!connect()) {
+ off += count;
+ } catch (IOException ex) {
+ Slog.e(TAG, "read exception");
+ break;
+ }
+ }
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "read " + len + " bytes");
+ }
+ if (off == len)
+ return true;
+ disconnect();
+ return false;
+ }
+
+ private boolean readReply() {
+ int len;
+ buflen = 0;
+ if (!readBytes(buf, 2))
+ return false;
+ len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
+ if ((len < 1) || (len > 1024)) {
+ Slog.e(TAG, "invalid reply length (" + len + ")");
+ disconnect();
+ return false;
+ }
+ if (!readBytes(buf, len))
+ return false;
+ buflen = len;
+ return true;
+ }
+
+ private boolean writeCommand(String _cmd) {
+ byte[] cmd = _cmd.getBytes();
+ int len = cmd.length;
+ if ((len < 1) || (len > 1024))
+ return false;
+ buf[0] = (byte) (len & 0xff);
+ buf[1] = (byte) ((len >> 8) & 0xff);
+ try {
+ mOut.write(buf, 0, 2);
+ mOut.write(cmd, 0, len);
+ } catch (IOException ex) {
+ Slog.e(TAG, "write error");
+ disconnect();
+ return false;
+ }
+ return true;
+ }
+
+ private synchronized String transaction(String cmd) {
+ if (!connect()) {
Slog.e(TAG, "connection failed");
return "-1";
}
if (!writeCommand(cmd)) {
- /* If installd died and restarted in the background
- * (unlikely but possible) we'll fail on the next
- * write (this one). Try to reconnect and write
- * the command one more time before giving up.
- */
+ /*
+ * If installd died and restarted in the background (unlikely but
+ * possible) we'll fail on the next write (this one). Try to
+ * reconnect and write the command one more time before giving up.
+ */
Slog.e(TAG, "write command failed? reconnect!");
if (!connect() || !writeCommand(cmd)) {
return "-1";
}
}
-// Slog.i(TAG,"send: '"+cmd+"'");
- if (readReply()) {
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "send: '" + cmd + "'");
+ }
+ if (readReply()) {
String s = new String(buf, 0, buflen);
-// Slog.i(TAG,"recv: '"+s+"'");
- return s;
- } else {
-// Slog.i(TAG,"fail");
- return "-1";
- }
- }
-
- private int execute(String cmd) {
- String res = transaction(cmd);
- try {
- return Integer.parseInt(res);
- } catch (NumberFormatException ex) {
- return -1;
- }
- }
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "recv: '" + s + "'");
+ }
+ return s;
+ } else {
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "fail");
+ }
+ return "-1";
+ }
+ }
+
+ private int execute(String cmd) {
+ String res = transaction(cmd);
+ try {
+ return Integer.parseInt(res);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
public int install(String name, int uid, int gid) {
StringBuilder builder = new StringBuilder("install");
@@ -203,10 +225,12 @@ class Installer {
return execute(builder.toString());
}
- public int remove(String name) {
+ public int remove(String name, int userId) {
StringBuilder builder = new StringBuilder("remove");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ builder.append(userId);
return execute(builder.toString());
}
@@ -225,14 +249,34 @@ class Installer {
builder.append(name);
return execute(builder.toString());
}
-
- public int clearUserData(String name) {
+
+ public int createUserData(String name, int uid, int userId) {
+ StringBuilder builder = new StringBuilder("mkuserdata");
+ builder.append(' ');
+ builder.append(name);
+ builder.append(' ');
+ builder.append(uid);
+ builder.append(' ');
+ builder.append(userId);
+ return execute(builder.toString());
+ }
+
+ public int removeUserDataDirs(int userId) {
+ StringBuilder builder = new StringBuilder("rmuser");
+ builder.append(' ');
+ builder.append(userId);
+ return execute(builder.toString());
+ }
+
+ public int clearUserData(String name, int userId) {
StringBuilder builder = new StringBuilder("rmuserdata");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ builder.append(userId);
return execute(builder.toString());
}
-
+
public boolean ping() {
if (execute("ping") < 0) {
return false;
@@ -240,7 +284,7 @@ class Installer {
return true;
}
}
-
+
public int freeCache(long freeStorageSize) {
StringBuilder builder = new StringBuilder("freecache");
builder.append(' ');
@@ -250,8 +294,8 @@ class Installer {
/*
* @param packagePathSuffix The name of the path relative to install
- * directory. Say if the path name is /data/app/com.test-1.apk,
- * the package suffix path will be com.test-1
+ * directory. Say if the path name is /data/app/com.test-1.apk, the package
+ * suffix path will be com.test-1
*/
public int setForwardLockPerm(String packagePathSuffix, int gid) {
StringBuilder builder = new StringBuilder("protect");
@@ -261,7 +305,7 @@ class Installer {
builder.append(gid);
return execute(builder.toString());
}
-
+
public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath,
PackageStats pStats) {
StringBuilder builder = new StringBuilder("getsize");
@@ -275,7 +319,7 @@ class Installer {
String s = transaction(builder.toString());
String res[] = s.split(" ");
- if((res == null) || (res.length != 4)) {
+ if ((res == null) || (res.length != 4)) {
return -1;
}
try {
@@ -286,7 +330,7 @@ class Installer {
} catch (NumberFormatException e) {
return -1;
}
- }
+ }
public int moveFiles() {
return execute("movefiles");
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index dd5c2a9..e1fbe1c 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -14,26 +14,30 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.pm;
+
+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 com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
+import com.android.server.DeviceStorageMonitorService;
+import com.android.server.EventLogTags;
+import com.android.server.IntentResolver;
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.admin.IDevicePolicyManager;
import android.app.backup.IBackupManager;
-import android.content.Context;
import android.content.ComponentName;
+import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
@@ -42,7 +46,6 @@ import android.content.ServiceConnection;
import android.content.IntentSender.SendIntentException;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
@@ -54,46 +57,46 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
import android.content.pm.PackageStats;
import android.content.pm.ParceledListSlice;
-
-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 android.content.pm.PackageParser;
-import android.content.pm.PermissionInfo;
import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Debug;
+import android.os.Environment;
+import android.os.FileObserver;
+import android.os.FileUtils;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
-import android.os.Parcelable;
-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.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.security.SystemKeyStore;
-import android.util.*;
+import android.util.DisplayMetrics;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.LogPrinter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
import android.view.Display;
import android.view.WindowManager;
-import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -132,25 +135,30 @@ import java.util.zip.ZipOutputStream;
mmm frameworks/base/tests/AndroidTests
adb install -r -f out/target/product/passion/data/app/AndroidTests.apk
adb shell am instrument -w -e class com.android.unit_tests.PackageManagerTests com.android.unit_tests/android.test.InstrumentationTestRunner
- *
+ *
+ * {@hide}
*/
-class PackageManagerService extends IPackageManager.Stub {
- private static final String TAG = "PackageManager";
- private static final boolean DEBUG_SETTINGS = false;
+public class PackageManagerService extends IPackageManager.Stub {
+ static final String TAG = "PackageManager";
+ static final boolean DEBUG_SETTINGS = false;
private static final boolean DEBUG_PREFERRED = false;
- private static final boolean DEBUG_UPGRADE = false;
+ static final boolean DEBUG_UPGRADE = false;
private static final boolean DEBUG_INSTALL = false;
- private static final boolean DEBUG_STOPPED = false;
-
- private static final boolean MULTIPLE_APPLICATION_UIDS = true;
+ private static final boolean DEBUG_REMOVE = false;
+ private static final boolean DEBUG_SHOW_INFO = false;
+ private static final boolean DEBUG_PACKAGE_INFO = false;
+ private static final boolean DEBUG_INTENT_MATCHING = false;
+ private static final boolean DEBUG_PACKAGE_SCANNING = false;
+ private static final boolean DEBUG_APP_DIR_OBSERVER = false;
+
+ static final boolean MULTIPLE_APPLICATION_UIDS = true;
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
- private static final int FIRST_APPLICATION_UID =
+ private static final int KEYCHAIN_UID = Process.KEYCHAIN_UID;
+ 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;
+ static final int MAX_APPLICATION_UIDS = 1000;
private static final boolean GET_CERTIFICATES = true;
@@ -164,19 +172,6 @@ class PackageManagerService extends IPackageManager.Stub {
// package apks to install directory.
private static final String INSTALL_PACKAGE_SUFFIX = "-";
- /**
- * Indicates the state of installation. Used by PackageManager to
- * figure out incomplete installations. Say a package is being installed
- * (the state is set to PKG_INSTALL_INCOMPLETE) and remains so till
- * the package installation is successful or unsuccesful lin which case
- * the PackageManager will no longer maintain state information associated
- * with the package. If some exception(like device freeze or battery being
- * pulled out) occurs during installation of a package, the PackageManager
- * needs this information to clean up the previously failed installation.
- */
- private static final int PKG_INSTALL_INCOMPLETE = 0;
- private static final int PKG_INSTALL_COMPLETE = 1;
-
static final int SCAN_MONITOR = 1<<0;
static final int SCAN_NO_DEX = 1<<1;
static final int SCAN_FORCE_DEX = 1<<2;
@@ -215,6 +210,9 @@ class PackageManagerService extends IPackageManager.Stub {
// This is where all application persistent data goes.
final File mAppDataDir;
+ // This is where all application persistent data goes for secondary users.
+ final File mUserAppDataDir;
+
// This is the object monitoring the framework dir.
final FileObserver mFrameworkInstallObserver;
@@ -365,6 +363,9 @@ class PackageManagerService extends IPackageManager.Stub {
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
+
+ final UserManager mUserManager;
+
final private DefaultContainerConnection mDefContainerConn =
new DefaultContainerConnection();
class DefaultContainerConnection implements ServiceConnection {
@@ -529,12 +530,12 @@ class PackageManagerService extends IPackageManager.Stub {
}
case MCS_GIVE_UP: {
if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_giveup too many retries");
- HandlerParams params = mPendingInstalls.remove(0);
+ mPendingInstalls.remove(0);
break;
}
case SEND_PENDING_BROADCAST : {
String packages[];
- ArrayList components[];
+ ArrayList<String> components[];
int size = 0;
int uids[];
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
@@ -566,8 +567,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
// Send broadcasts
for (int i = 0; i < size; i++) {
- sendPackageChangedBroadcast(packages[i], true,
- (ArrayList<String>)components[i], uids[i]);
+ sendPackageChangedBroadcast(packages[i], true, components[i], uids[i]);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
break;
@@ -665,7 +665,7 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
removeMessages(WRITE_SETTINGS);
removeMessages(WRITE_STOPPED_PACKAGES);
- mSettings.writeLP();
+ mSettings.writeLPr();
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
@@ -673,7 +673,7 @@ class PackageManagerService extends IPackageManager.Stub {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mPackages) {
removeMessages(WRITE_STOPPED_PACKAGES);
- mSettings.writeStoppedLP();
+ mSettings.writeStoppedLPr();
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
@@ -745,20 +745,24 @@ class PackageManagerService extends IPackageManager.Stub {
mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings();
- mSettings.addSharedUserLP("android.uid.system",
+ mSettings.addSharedUserLPw("android.uid.system",
Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLP("android.uid.phone",
+ mSettings.addSharedUserLPw("android.uid.phone",
MULTIPLE_APPLICATION_UIDS
? RADIO_UID : FIRST_APPLICATION_UID,
ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLP("android.uid.log",
+ mSettings.addSharedUserLPw("android.uid.log",
MULTIPLE_APPLICATION_UIDS
? LOG_UID : FIRST_APPLICATION_UID,
ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLP("android.uid.nfc",
+ mSettings.addSharedUserLPw("android.uid.nfc",
MULTIPLE_APPLICATION_UIDS
? NFC_UID : FIRST_APPLICATION_UID,
ApplicationInfo.FLAG_SYSTEM);
+ mSettings.addSharedUserLPw("android.uid.keychain",
+ MULTIPLE_APPLICATION_UIDS
+ ? KEYCHAIN_UID : FIRST_APPLICATION_UID,
+ ApplicationInfo.FLAG_SYSTEM);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
@@ -793,14 +797,18 @@ class PackageManagerService extends IPackageManager.Stub {
d.getMetrics(mMetrics);
synchronized (mInstallLock) {
+ // writer
synchronized (mPackages) {
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
+ mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
+ mUserManager = new UserManager(mInstaller, mUserAppDataDir);
+
if (mInstaller == null) {
// Make sure these dirs exist, when we are running in
// the simulator.
@@ -808,12 +816,13 @@ class PackageManagerService extends IPackageManager.Stub {
File miscDir = new File(dataDir, "misc");
miscDir.mkdirs();
mAppDataDir.mkdirs();
+ mUserAppDataDir.mkdirs();
mDrmAppPrivateInstallDir.mkdirs();
}
readPermissions();
- mRestoredSettings = mSettings.readLP();
+ mRestoredSettings = mSettings.readLPw();
long startTime = SystemClock.uptimeMillis();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
@@ -976,7 +985,8 @@ class PackageManagerService extends IPackageManager.Stub {
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
if (mInstaller != null) {
- mInstaller.remove(ps.name);
+ mInstaller.remove(ps.name, 0);
+ mUserManager.removePackageForAllUsers(ps.name);
}
}
}
@@ -988,7 +998,7 @@ class PackageManagerService extends IPackageManager.Stub {
mAppInstallDir.mkdirs(); // scanDirLI() assumes this dir exists
}
//look for any incomplete package installations
- ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackages();
+ ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
//clean up list
for(int i = 0; i < deletePkgsList.size(); i++) {
//clean up here
@@ -1029,9 +1039,10 @@ class PackageManagerService extends IPackageManager.Stub {
+ "; regranting permissions for internal storage");
mSettings.mInternalSdkPlatform = mSdkVersion;
- updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions);
+ updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions);
- mSettings.writeLP();
+ // can downgrade to reader
+ mSettings.writeLPr();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
@@ -1060,10 +1071,12 @@ class PackageManagerService extends IPackageManager.Stub {
void cleanupInstallFailedPackage(PackageSetting ps) {
Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
if (mInstaller != null) {
- int retCode = mInstaller.remove(ps.name);
+ int retCode = mInstaller.remove(ps.name, 0);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data directory for package: "
+ ps.name + ", retcode=" + retCode);
+ } else {
+ mUserManager.removePackageForAllUsers(ps.name);
}
} else {
//for emulator
@@ -1081,7 +1094,7 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Unable to remove old code file: " + ps.resourcePath);
}
}
- mSettings.removePackageLP(ps.name);
+ mSettings.removePackageLPw(ps.name);
}
void readPermissions() {
@@ -1348,16 +1361,16 @@ class PackageManagerService extends IPackageManager.Stub {
}
public PackageInfo getPackageInfo(String packageName, int flags) {
+ // reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
- if (Config.LOGV) Log.v(
- TAG, "getPackageInfo " + packageName
- + ": " + p);
+ if (DEBUG_PACKAGE_INFO)
+ Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
return generatePackageInfo(p, flags);
}
if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
- return generatePackageInfoFromSettingsLP(packageName, flags);
+ return generatePackageInfoFromSettingsLPw(packageName, flags);
}
}
return null;
@@ -1365,6 +1378,7 @@ class PackageManagerService extends IPackageManager.Stub {
public String[] currentToCanonicalPackageNames(String[] names) {
String[] out = new String[names.length];
+ // reader
synchronized (mPackages) {
for (int i=names.length-1; i>=0; i--) {
PackageSetting ps = mSettings.mPackages.get(names[i]);
@@ -1376,6 +1390,7 @@ class PackageManagerService extends IPackageManager.Stub {
public String[] canonicalToCurrentPackageNames(String[] names) {
String[] out = new String[names.length];
+ // reader
synchronized (mPackages) {
for (int i=names.length-1; i>=0; i--) {
String cur = mSettings.mRenamedPackages.get(names[i]);
@@ -1386,6 +1401,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public int getPackageUid(String packageName) {
+ // reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if(p != null) {
@@ -1401,11 +1417,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
public int[] getPackageGids(String packageName) {
+ // reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
- if (Config.LOGV) Log.v(
- TAG, "getPackageGids" + packageName
- + ": " + p);
+ if (DEBUG_PACKAGE_INFO)
+ Log.v(TAG, "getPackageGids" + packageName + ": " + p);
if (p != null) {
final PackageSetting ps = (PackageSetting)p.mExtras;
final SharedUserSetting suid = ps.sharedUser;
@@ -1430,6 +1446,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public PermissionInfo getPermissionInfo(String name, int flags) {
+ // reader
synchronized (mPackages) {
final BasePermission p = mSettings.mPermissions.get(name);
if (p != null) {
@@ -1440,6 +1457,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) {
+ // reader
synchronized (mPackages) {
ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
for (BasePermission p : mSettings.mPermissions.values()) {
@@ -1462,6 +1480,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) {
+ // reader
synchronized (mPackages) {
return PackageParser.generatePermissionGroupInfo(
mPermissionGroups.get(name), flags);
@@ -1469,6 +1488,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
+ // reader
synchronized (mPackages) {
final int N = mPermissionGroups.size();
ArrayList<PermissionGroupInfo> out
@@ -1480,12 +1500,12 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- private ApplicationInfo generateApplicationInfoFromSettingsLP(String packageName, int flags) {
+ private ApplicationInfo generateApplicationInfoFromSettingsLPw(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) {
+ if (ps != null) {
+ if (ps.pkg == null) {
+ PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, flags);
+ if (pInfo != null) {
return pInfo.applicationInfo;
}
return null;
@@ -1495,16 +1515,17 @@ class PackageManagerService extends IPackageManager.Stub {
return null;
}
- private PackageInfo generatePackageInfoFromSettingsLP(String packageName, int flags) {
+ private PackageInfo generatePackageInfoFromSettingsLPw(String packageName, int flags) {
PackageSetting ps = mSettings.mPackages.get(packageName);
- if(ps != null) {
- if(ps.pkg == null) {
+ if (ps != null) {
+ if (ps.pkg == null) {
ps.pkg = new PackageParser.Package(packageName);
ps.pkg.applicationInfo.packageName = packageName;
ps.pkg.applicationInfo.flags = ps.pkgFlags;
ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
ps.pkg.applicationInfo.sourceDir = ps.codePathString;
- ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath();
+ ps.pkg.applicationInfo.dataDir =
+ getDataPathForPackage(ps.pkg.packageName, 0).getPath();
ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
ps.pkg.mSetEnabled = ps.enabled;
ps.pkg.mSetStopped = ps.stopped;
@@ -1515,9 +1536,10 @@ class PackageManagerService extends IPackageManager.Stub {
}
public ApplicationInfo getApplicationInfo(String packageName, int flags) {
+ // writer
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
- if (Config.LOGV) Log.v(
+ if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getApplicationInfo " + packageName
+ ": " + p);
if (p != null) {
@@ -1528,7 +1550,7 @@ class PackageManagerService extends IPackageManager.Stub {
return mAndroidApplication;
}
if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
- return generateApplicationInfoFromSettingsLP(packageName, flags);
+ return generateApplicationInfoFromSettingsLPw(packageName, flags);
}
}
return null;
@@ -1592,8 +1614,8 @@ class PackageManagerService extends IPackageManager.Stub {
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)) {
+ if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
+ if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
return PackageParser.generateActivityInfo(a, flags);
}
if (mResolveComponentName.equals(component)) {
@@ -1606,9 +1628,9 @@ class PackageManagerService extends IPackageManager.Stub {
public ActivityInfo getReceiverInfo(ComponentName component, int flags) {
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
- if (Config.LOGV) Log.v(
+ if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
- if (a != null && mSettings.isEnabledLP(a.info, flags)) {
+ if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
return PackageParser.generateActivityInfo(a, flags);
}
}
@@ -1618,9 +1640,9 @@ class PackageManagerService extends IPackageManager.Stub {
public ServiceInfo getServiceInfo(ComponentName component, int flags) {
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
- if (Config.LOGV) Log.v(
+ if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
- if (s != null && mSettings.isEnabledLP(s.info, flags)) {
+ if (s != null && mSettings.isEnabledLPr(s.info, flags)) {
return PackageParser.generateServiceInfo(s, flags);
}
}
@@ -1630,9 +1652,9 @@ class PackageManagerService extends IPackageManager.Stub {
public ProviderInfo getProviderInfo(ComponentName component, int flags) {
synchronized (mPackages) {
PackageParser.Provider p = mProvidersByComponent.get(component);
- if (Config.LOGV) Log.v(
+ if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
- if (p != null && mSettings.isEnabledLP(p.info, flags)) {
+ if (p != null && mSettings.isEnabledLPr(p.info, flags)) {
return PackageParser.generateProviderInfo(p, flags);
}
}
@@ -1696,7 +1718,7 @@ class PackageManagerService extends IPackageManager.Stub {
public int checkUidPermission(String permName, int uid) {
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLP(uid);
+ Object obj = mSettings.getUserIdLPr(uid);
if (obj != null) {
GrantedPermissions gp = (GrantedPermissions)obj;
if (gp.grantedPermissions.contains(permName)) {
@@ -1801,7 +1823,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (changed) {
if (!async) {
- mSettings.writeLP();
+ mSettings.writeLPr();
} else {
scheduleWriteSettingsLocked();
}
@@ -1832,7 +1854,7 @@ class PackageManagerService extends IPackageManager.Stub {
+ name);
}
mSettings.mPermissions.remove(name);
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
}
@@ -1845,21 +1867,22 @@ class PackageManagerService extends IPackageManager.Stub {
public int checkSignatures(String pkg1, String pkg2) {
synchronized (mPackages) {
- PackageParser.Package p1 = mPackages.get(pkg1);
- PackageParser.Package p2 = mPackages.get(pkg2);
+ final PackageParser.Package p1 = mPackages.get(pkg1);
+ final 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.mSignatures, p2.mSignatures);
+ return compareSignatures(p1.mSignatures, p2.mSignatures);
}
}
public int checkUidSignatures(int uid1, int uid2) {
+ // reader
synchronized (mPackages) {
Signature[] s1;
Signature[] s2;
- Object obj = mSettings.getUserIdLP(uid1);
+ Object obj = mSettings.getUserIdLPr(uid1);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
s1 = ((SharedUserSetting)obj).signatures.mSignatures;
@@ -1871,7 +1894,7 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- obj = mSettings.getUserIdLP(uid2);
+ obj = mSettings.getUserIdLPr(uid2);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
s2 = ((SharedUserSetting)obj).signatures.mSignatures;
@@ -1883,11 +1906,11 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- return checkSignaturesLP(s1, s2);
+ return compareSignatures(s1, s2);
}
}
- int checkSignaturesLP(Signature[] s1, Signature[] s2) {
+ static int compareSignatures(Signature[] s1, Signature[] s2) {
if (s1 == null) {
return s2 == null
? PackageManager.SIGNATURE_NEITHER_SIGNED
@@ -1912,20 +1935,21 @@ class PackageManagerService extends IPackageManager.Stub {
}
public String[] getPackagesForUid(int uid) {
+ // reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLP(uid);
+ Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
- SharedUserSetting sus = (SharedUserSetting)obj;
+ final SharedUserSetting sus = (SharedUserSetting) obj;
final int N = sus.packages.size();
- String[] res = new String[N];
- Iterator<PackageSetting> it = sus.packages.iterator();
- int i=0;
+ final String[] res = new String[N];
+ final 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;
+ final PackageSetting ps = (PackageSetting) obj;
return new String[] { ps.name };
}
}
@@ -1933,13 +1957,14 @@ class PackageManagerService extends IPackageManager.Stub {
}
public String getNameForUid(int uid) {
+ // reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLP(uid);
+ Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
- SharedUserSetting sus = (SharedUserSetting)obj;
+ final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
} else if (obj instanceof PackageSetting) {
- PackageSetting ps = (PackageSetting)obj;
+ final PackageSetting ps = (PackageSetting) obj;
return ps.name;
}
}
@@ -1950,8 +1975,9 @@ class PackageManagerService extends IPackageManager.Stub {
if(sharedUserName == null) {
return -1;
}
+ // reader
synchronized (mPackages) {
- SharedUserSetting suid = mSettings.getSharedUserLP(sharedUserName, 0, false);
+ final SharedUserSetting suid = mSettings.getSharedUserLPw(sharedUserName, 0, false);
if(suid == null) {
return -1;
}
@@ -1976,11 +2002,9 @@ class PackageManagerService extends IPackageManager.Stub {
// 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 (DEBUG_INTENT_MATCHING) {
+ Log.d(TAG, 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.
@@ -2004,6 +2028,7 @@ class PackageManagerService extends IPackageManager.Stub {
ResolveInfo findPreferredActivity(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, int priority) {
+ // writer
synchronized (mPackages) {
if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
List<PreferredActivity> prefs =
@@ -2014,24 +2039,35 @@ class PackageManagerService extends IPackageManager.Stub {
// We will only allow preferred activities that came
// from the same match quality.
int match = 0;
+
+ if (DEBUG_PREFERRED) {
+ Log.v(TAG, "Figuring out best match...");
+ }
+
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;
+ final 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));
}
- 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);
+ final PreferredActivity pa = prefs.get(i);
if (pa.mPref.mMatch != match) {
continue;
}
- ActivityInfo ai = getActivityInfo(pa.mPref.mComponent, flags);
+ final ActivityInfo ai = getActivityInfo(pa.mPref.mComponent, flags);
if (DEBUG_PREFERRED) {
Log.v(TAG, "Got preferred activity:");
if (ai != null) {
@@ -2042,7 +2078,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (ai != null) {
for (int j=0; j<N; j++) {
- ResolveInfo ri = query.get(j);
+ final ResolveInfo ri = query.get(j);
if (!ri.activityInfo.applicationInfo.packageName
.equals(ai.applicationInfo.packageName)) {
continue;
@@ -2074,28 +2110,28 @@ class PackageManagerService extends IPackageManager.Stub {
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags) {
- ComponentName comp = intent.getComponent();
+ final ComponentName comp = intent.getComponent();
if (comp != null) {
- List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
- ActivityInfo ai = getActivityInfo(comp, flags);
+ final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final ActivityInfo ai = getActivityInfo(comp, flags);
if (ai != null) {
- ResolveInfo ri = new ResolveInfo();
+ final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
+ // reader
synchronized (mPackages) {
- String pkgName = intent.getPackage();
+ final String pkgName = intent.getPackage();
if (pkgName == null) {
- return (List<ResolveInfo>)mActivities.queryIntent(intent,
- resolvedType, flags);
+ return mActivities.queryIntent(intent, resolvedType, flags);
}
- PackageParser.Package pkg = mPackages.get(pkgName);
+ final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent,
- resolvedType, flags, pkg.activities);
+ return mActivities.queryIntentForPackage(intent, resolvedType, flags,
+ pkg.activities);
}
return new ArrayList<ResolveInfo>();
}
@@ -2106,9 +2142,12 @@ class PackageManagerService extends IPackageManager.Stub {
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);
+ List<ResolveInfo> results = queryIntentActivities(intent, resolvedType, flags
+ | PackageManager.GET_RESOLVED_FILTER);
+
+ if (DEBUG_INTENT_MATCHING) {
+ Log.v(TAG, "Query " + intent + ": " + results);
+ }
int specificsPos = 0;
int N;
@@ -2128,16 +2167,21 @@ class PackageManagerService extends IPackageManager.Stub {
continue;
}
- if (Config.LOGV) Log.v(TAG, "Specific #" + i + ": " + sintent);
+ if (DEBUG_INTENT_MATCHING) {
+ 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;
+
+ ComponentName comp = sintent.getComponent();
if (comp == null) {
ri = resolveIntent(
sintent,
@@ -2161,7 +2205,7 @@ class PackageManagerService extends IPackageManager.Stub {
// 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);
+ if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Specific #" + i + ": " + ai);
N = results.size();
int j;
for (j=specificsPos; j<N; j++) {
@@ -2171,7 +2215,7 @@ class PackageManagerService extends IPackageManager.Stub {
comp.getPackageName()))
|| (action != null && sri.filter.matchAction(action))) {
results.remove(j);
- if (Config.LOGV) Log.v(
+ if (DEBUG_INTENT_MATCHING) Log.v(
TAG, "Removing duplicate item from " + j
+ " due to specific " + specificsPos);
if (ri == null) {
@@ -2219,7 +2263,7 @@ class PackageManagerService extends IPackageManager.Stub {
final ResolveInfo rij = results.get(j);
if (rij.filter != null && rij.filter.hasAction(action)) {
results.remove(j);
- if (Config.LOGV) Log.v(
+ if (DEBUG_INTENT_MATCHING) Log.v(
TAG, "Removing duplicate item from " + j
+ " due to action " + action + " at " + i);
j--;
@@ -2258,12 +2302,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if (Config.LOGV) Log.v(TAG, "Result: " + results);
+ if (DEBUG_INTENT_MATCHING) Log.v(TAG, "Result: " + results);
return results;
}
- public List<ResolveInfo> queryIntentReceivers(Intent intent,
- String resolvedType, int flags) {
+ public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) {
ComponentName comp = intent.getComponent();
if (comp != null) {
List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
@@ -2276,25 +2319,22 @@ class PackageManagerService extends IPackageManager.Stub {
return list;
}
+ // reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
- return (List<ResolveInfo>)mReceivers.queryIntent(intent,
- resolvedType, flags);
+ return mReceivers.queryIntent(intent, resolvedType, flags);
}
- PackageParser.Package pkg = mPackages.get(pkgName);
+ final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return (List<ResolveInfo>) mReceivers.queryIntentForPackage(intent,
- resolvedType, flags, pkg.receivers);
+ return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers);
}
return null;
}
}
- public ResolveInfo resolveService(Intent intent, String resolvedType,
- int flags) {
- List<ResolveInfo> query = queryIntentServices(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,
@@ -2305,30 +2345,28 @@ class PackageManagerService extends IPackageManager.Stub {
return null;
}
- public List<ResolveInfo> queryIntentServices(Intent intent,
- String resolvedType, int flags) {
- ComponentName comp = intent.getComponent();
+ public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags) {
+ final ComponentName comp = intent.getComponent();
if (comp != null) {
- List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
- ServiceInfo si = getServiceInfo(comp, flags);
+ final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final ServiceInfo si = getServiceInfo(comp, flags);
if (si != null) {
- ResolveInfo ri = new ResolveInfo();
+ final ResolveInfo ri = new ResolveInfo();
ri.serviceInfo = si;
list.add(ri);
}
return list;
}
+ // reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
- return (List<ResolveInfo>)mServices.queryIntent(intent,
- resolvedType, flags);
+ return mServices.queryIntent(intent, resolvedType, flags);
}
- PackageParser.Package pkg = mPackages.get(pkgName);
+ final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return (List<ResolveInfo>)mServices.queryIntentForPackage(intent,
- resolvedType, flags, pkg.services);
+ return mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services);
}
return null;
}
@@ -2354,6 +2392,7 @@ class PackageManagerService extends IPackageManager.Stub {
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
final String[] keys;
+ // writer
synchronized (mPackages) {
if (listUninstalled) {
keys = mSettings.mPackages.keySet().toArray(new String[mSettings.mPackages.size()]);
@@ -2372,7 +2411,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (listUninstalled) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
- pi = generatePackageInfoFromSettingsLP(ps.name, flags);
+ pi = generatePackageInfoFromSettingsLPw(ps.name, flags);
}
} else {
final PackageParser.Package p = mPackages.get(packageName);
@@ -2400,6 +2439,7 @@ class PackageManagerService extends IPackageManager.Stub {
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
final String[] keys;
+ // writer
synchronized (mPackages) {
if (listUninstalled) {
keys = mSettings.mPackages.keySet().toArray(new String[mSettings.mPackages.size()]);
@@ -2418,7 +2458,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (listUninstalled) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
- ai = generateApplicationInfoFromSettingsLP(ps.name, flags);
+ ai = generateApplicationInfoFromSettingsLPw(ps.name, flags);
}
} else {
final PackageParser.Package p = mPackages.get(packageName);
@@ -2441,12 +2481,13 @@ class PackageManagerService extends IPackageManager.Stub {
}
public List<ApplicationInfo> getPersistentApplications(int flags) {
- ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
+ final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
+ // reader
synchronized (mPackages) {
- Iterator<PackageParser.Package> i = mPackages.values().iterator();
+ final Iterator<PackageParser.Package> i = mPackages.values().iterator();
while (i.hasNext()) {
- PackageParser.Package p = i.next();
+ final PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))) {
@@ -2459,10 +2500,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
public ProviderInfo resolveContentProvider(String name, int flags) {
+ // reader
synchronized (mPackages) {
final PackageParser.Provider provider = mProviders.get(name);
return provider != null
- && mSettings.isEnabledLP(provider.info, flags)
+ && mSettings.isEnabledLPr(provider.info, flags)
&& (!mSafeMode || (provider.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)
? PackageParser.generateProviderInfo(provider, flags)
@@ -2473,10 +2515,12 @@ class PackageManagerService extends IPackageManager.Stub {
/**
* @deprecated
*/
- public void querySyncProviders(List outNames, List outInfo) {
+ @Deprecated
+ public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) {
+ // reader
synchronized (mPackages) {
- Iterator<Map.Entry<String, PackageParser.Provider>> i
- = mProviders.entrySet().iterator();
+ final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
+ .iterator();
while (i.hasNext()) {
Map.Entry<String, PackageParser.Provider> entry = i.next();
@@ -2496,22 +2540,22 @@ class PackageManagerService extends IPackageManager.Stub {
int uid, int flags) {
ArrayList<ProviderInfo> finalList = null;
+ // reader
synchronized (mPackages) {
- Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
+ final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
while (i.hasNext()) {
- PackageParser.Provider p = i.next();
+ final 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)) {
+ && (processName == null
+ || (p.info.processName.equals(processName)
+ && p.info.applicationInfo.uid == uid))
+ && mSettings.isEnabledLPr(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));
+ finalList.add(PackageParser.generateProviderInfo(p, flags));
}
}
}
@@ -2525,6 +2569,7 @@ class PackageManagerService extends IPackageManager.Stub {
public InstrumentationInfo getInstrumentationInfo(ComponentName name,
int flags) {
+ // reader
synchronized (mPackages) {
final PackageParser.Instrumentation i = mInstrumentation.get(name);
return PackageParser.generateInstrumentationInfo(i, flags);
@@ -2536,10 +2581,11 @@ class PackageManagerService extends IPackageManager.Stub {
ArrayList<InstrumentationInfo> finalList =
new ArrayList<InstrumentationInfo>();
+ // reader
synchronized (mPackages) {
- Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator();
+ final Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator();
while (i.hasNext()) {
- PackageParser.Instrumentation p = i.next();
+ final PackageParser.Instrumentation p = i.next();
if (targetPackage == null
|| targetPackage.equals(p.info.targetPackage)) {
finalList.add(PackageParser.generateInstrumentationInfo(p,
@@ -2558,7 +2604,7 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
- if (false) {
+ if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + dir);
}
@@ -2588,7 +2634,7 @@ class PackageManagerService extends IPackageManager.Stub {
return fname;
}
- private static void reportSettingsProblem(int priority, String msg) {
+ static void reportSettingsProblem(int priority, String msg) {
try {
File fname = getSettingsProblemFile();
FileOutputStream out = new FileOutputStream(fname, true);
@@ -2652,17 +2698,18 @@ class PackageManagerService extends IPackageManager.Stub {
}
PackageSetting ps = null;
PackageSetting updatedPkg;
+ // reader
synchronized (mPackages) {
// Look to see if we already know about this package.
String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
// This package has been renamed to its original name. Let's
// use that.
- ps = mSettings.peekPackageLP(oldName);
+ ps = mSettings.peekPackageLPr(oldName);
}
// If there was no original package, see one for the real package name.
if (ps == null) {
- ps = mSettings.peekPackageLP(pkg.packageName);
+ ps = mSettings.peekPackageLPr(pkg.packageName);
}
// Check to see if this package could be hiding/updating a system
// package. Must look for it either under the original or real
@@ -2691,6 +2738,7 @@ class PackageManagerService extends IPackageManager.Stub {
// At this point, its safely assumed that package installation for
// apps in system partition will go through. If not there won't be a working
// version of the app
+ // writer
synchronized (mPackages) {
// Just remove the loaded entries from package lists.
mPackages.remove(ps.name);
@@ -2702,7 +2750,7 @@ class PackageManagerService extends IPackageManager.Stub {
InstallArgs args = new FileInstallArgs(ps.codePathString,
ps.resourcePathString, ps.nativeLibraryPathString);
args.cleanUpResourcesLI();
- mSettings.enableSystemPackageLP(ps.name);
+ mSettings.enableSystemPackageLPw(ps.name);
}
}
}
@@ -2760,7 +2808,7 @@ class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package pkg) {
if (pkgSetting.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
- if (checkSignaturesLP(pkgSetting.signatures.mSignatures, pkg.mSignatures) !=
+ if (compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures) !=
PackageManager.SIGNATURE_MATCH) {
Slog.e(TAG, "Package " + pkg.packageName
+ " signatures do not match the previously installed version; ignoring!");
@@ -2770,7 +2818,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
// Check for shared user signatures
if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
- if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures,
+ if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
Slog.e(TAG, "Package " + pkg.packageName
+ " has no signatures that match those in shared user "
@@ -2837,7 +2885,7 @@ class PackageManagerService extends IPackageManager.Stub {
return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
}
- private boolean verifyPackageUpdate(PackageSetting oldPkg, PackageParser.Package newPkg) {
+ private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) {
if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
Slog.w(TAG, "Unable to update from " + oldPkg.name
+ " to " + newPkg.packageName
@@ -2852,11 +2900,15 @@ class PackageManagerService extends IPackageManager.Stub {
return true;
}
- private File getDataPathForPackage(PackageParser.Package pkg) {
- final File dataPath = new File(mAppDataDir, pkg.packageName);
- return dataPath;
+ File getDataPathForUser(int userId) {
+ return new File(mUserAppDataDir.getAbsolutePath() + File.separator + userId);
}
-
+
+ private File getDataPathForPackage(String packageName, int userId) {
+ return new File(mUserAppDataDir.getAbsolutePath() + File.separator + userId
+ + File.separator + packageName);
+ }
+
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime) {
File scanFile = new File(pkg.mScanPath);
@@ -2911,8 +2963,11 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD) Log.d(
- TAG, "Scanning package " + pkg.packageName);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ Log.d(TAG, "Scanning package " + pkg.packageName);
+ }
+
if (mPackages.containsKey(pkg.packageName)
|| mSharedLibraries.containsKey(pkg.packageName)) {
Slog.w(TAG, "Application package " + pkg.packageName
@@ -2935,6 +2990,7 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.mAdoptPermissions = null;
}
+ // writer
synchronized (mPackages) {
// Check all shared libraries and map to their actual file path.
if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
@@ -2945,7 +3001,7 @@ class PackageManagerService extends IPackageManager.Stub {
int num = 0;
int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
for (int i=0; i<N; i++) {
- String file = mSharedLibraries.get(pkg.usesLibraries.get(i));
+ final String file = mSharedLibraries.get(pkg.usesLibraries.get(i));
if (file == null) {
Slog.e(TAG, "Package " + pkg.packageName
+ " requires unavailable shared library "
@@ -2958,7 +3014,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
for (int i=0; i<N; i++) {
- String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
+ final String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
if (file == null) {
Slog.w(TAG, "Package " + pkg.packageName
+ " desires unavailable shared library "
@@ -2976,7 +3032,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (pkg.mSharedUserId != null) {
- suid = mSettings.getSharedUserLP(pkg.mSharedUserId,
+ suid = mSettings.getSharedUserLPw(pkg.mSharedUserId,
pkg.applicationInfo.flags, true);
if (suid == null) {
Slog.w(TAG, "Creating application package " + pkg.packageName
@@ -2984,18 +3040,10 @@ class PackageManagerService extends IPackageManager.Stub {
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);
- }
- }
-
- if (false) {
- if (pkg.mOriginalPackages != null) {
- Log.w(TAG, "WAITING FOR DEBUGGER");
- Debug.waitForDebugger();
- Log.i(TAG, "Package " + pkg.packageName + " from original packages"
- + pkg.mOriginalPackages);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId
+ + "): packages=" + suid.packages);
}
}
@@ -3005,7 +3053,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (pkg.mOriginalPackages != null) {
// This package may need to be renamed to a previously
// installed name. Let's check on that...
- String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);
+ final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);
if (pkg.mOriginalPackages.contains(renamed)) {
// This package had originally been installed as the
// original name, and we have already taken care of
@@ -3021,11 +3069,11 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {
- if ((origPackage=mSettings.peekPackageLP(
+ if ((origPackage = mSettings.peekPackageLPr(
pkg.mOriginalPackages.get(i))) != null) {
// We do have the package already installed under its
// original name... should we use it?
- if (!verifyPackageUpdate(origPackage, pkg)) {
+ if (!verifyPackageUpdateLPr(origPackage, pkg)) {
// New package is not compatible with original.
origPackage = null;
continue;
@@ -3056,7 +3104,7 @@ class PackageManagerService extends IPackageManager.Stub {
// Just create the setting, don't add it yet. For already existing packages
// the PkgSetting exists already and doesn't have to be created.
- pkgSetting = mSettings.getPackageLP(pkg, origPackage, realName, suid, destCodeFile,
+ pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryDir,
pkg.applicationInfo.flags, true, false);
if (pkgSetting == null) {
@@ -3109,7 +3157,7 @@ class PackageManagerService extends IPackageManager.Stub {
// associated with an overall shared user, which doesn't seem all
// that unreasonable.
if (pkgSetting.sharedUser != null) {
- if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures,
+ if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
Log.w(TAG, "Signature mismatch for shared user : " + pkgSetting.sharedUser);
mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
@@ -3127,7 +3175,7 @@ class PackageManagerService extends IPackageManager.Stub {
// package isn't already installed, since we don't want to break
// things that are installed.
if ((scanMode&SCAN_NEW_INSTALL) != 0) {
- int N = pkg.providers.size();
+ final int N = pkg.providers.size();
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
@@ -3148,29 +3196,28 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
}
- }
- final String pkgName = pkg.packageName;
-
- if (pkg.mAdoptPermissions != null) {
- // This package wants to adopt ownership of permissions from
- // another package.
- for (int i=pkg.mAdoptPermissions.size()-1; i>=0; i--) {
- String origName = pkg.mAdoptPermissions.get(i);
- PackageSetting orig = mSettings.peekPackageLP(origName);
- if (orig != null) {
- if (verifyPackageUpdate(orig, pkg)) {
- Slog.i(TAG, "Adopting permissions from "
- + origName + " to " + pkg.packageName);
- mSettings.transferPermissions(origName, pkg.packageName);
+ if (pkg.mAdoptPermissions != null) {
+ // This package wants to adopt ownership of permissions from
+ // another package.
+ for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
+ final String origName = pkg.mAdoptPermissions.get(i);
+ final PackageSetting orig = mSettings.peekPackageLPr(origName);
+ if (orig != null) {
+ if (verifyPackageUpdateLPr(orig, pkg)) {
+ Slog.i(TAG, "Adopting permissions from " + origName + " to "
+ + pkg.packageName);
+ mSettings.transferPermissionsLPw(origName, pkg.packageName);
+ }
}
}
}
}
+
+ final String pkgName = pkg.packageName;
final long scanFileTime = scanFile.lastModified();
final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0;
- final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.timeStamp;
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
pkg.applicationInfo.processName,
@@ -3183,7 +3230,7 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
// This is a normal package, need to make its data directory.
- dataPath = getDataPathForPackage(pkg);
+ dataPath = getDataPathForPackage(pkg.packageName, 0);
boolean uidError = false;
@@ -3199,8 +3246,11 @@ class PackageManagerService extends IPackageManager.Stub {
// 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);
+ int ret = mInstaller.remove(pkgName, 0);
if (ret >= 0) {
+ // TODO: Kill the processes first
+ // Remove the data directories for all users
+ mUserManager.removePackageForAllUsers(pkgName);
// Old data gone!
String msg = "System package " + pkg.packageName
+ " has changed from uid: "
@@ -3220,6 +3270,9 @@ class PackageManagerService extends IPackageManager.Stub {
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
+ // Create data directories for all users
+ mUserManager.installPackageForAllUsers(pkgName,
+ pkg.applicationInfo.uid);
}
}
if (!recovered) {
@@ -3235,6 +3288,7 @@ class PackageManagerService extends IPackageManager.Stub {
+ " has mismatched uid: "
+ mOutPermissions[1] + " on disk, "
+ pkg.applicationInfo.uid + " in settings";
+ // writer
synchronized (mPackages) {
mSettings.mReadMessages.append(msg);
mSettings.mReadMessages.append('\n');
@@ -3247,17 +3301,21 @@ class PackageManagerService extends IPackageManager.Stub {
}
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
- if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGV)
- Log.v(TAG, "Want this data dir: " + dataPath);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ 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) {
+ if (ret < 0) {
// Error from installer
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
+ // Create data directories for all users
+ mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
} else {
dataPath.mkdirs();
if (dataPath.exists()) {
@@ -3311,36 +3369,42 @@ class PackageManagerService extends IPackageManager.Stub {
* only for non-system apps and system app upgrades.
*/
if (pkg.applicationInfo.nativeLibraryDir != null) {
- final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
- final String dataPathString = dataPath.getPath();
+ try {
+ final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
+ final String dataPathString = dataPath.getCanonicalFile().getPath();
- if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
- /*
- * Upgrading from a previous version of the OS sometimes
- * leaves native libraries in the /data/data/<app>/lib
- * directory for system apps even when they shouldn't be.
- * Recent changes in the JNI library search path
- * necessitates we remove those to match previous behavior.
- */
- if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) {
- Log.i(TAG, "removed obsolete native libraries for system package " + path);
+ if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
+ /*
+ * Upgrading from a previous version of the OS sometimes
+ * leaves native libraries in the /data/data/<app>/lib
+ * directory for system apps even when they shouldn't be.
+ * Recent changes in the JNI library search path
+ * necessitates we remove those to match previous behavior.
+ */
+ if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) {
+ Log.i(TAG, "removed obsolete native libraries for system package "
+ + path);
+ }
+ } else if (nativeLibraryDir.getCanonicalFile().getParent()
+ .equals(dataPathString)) {
+ /*
+ * If this is an internal application or our
+ * nativeLibraryPath points to our data directory, unpack
+ * the libraries. The native library path pointing to the
+ * data directory for an application in an ASEC container
+ * can happen for older apps that existed before an OTA to
+ * Gingerbread.
+ */
+ Slog.i(TAG, "Unpacking native libraries for " + path);
+ mInstaller.unlinkNativeLibraryDirectory(dataPathString);
+ NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir);
+ } else {
+ Slog.i(TAG, "Linking native library dir for " + path);
+ mInstaller.linkNativeLibraryDirectory(dataPathString,
+ pkg.applicationInfo.nativeLibraryDir);
}
- } else if (nativeLibraryDir.getParent().equals(dataPathString)) {
- /*
- * If this is an internal application or our
- * nativeLibraryPath points to our data directory, unpack
- * the libraries. The native library path pointing to the
- * data directory for an application in an ASEC container
- * can happen for older apps that existed before an OTA to
- * Gingerbread.
- */
- Slog.i(TAG, "Unpacking native libraries for " + path);
- mInstaller.unlinkNativeLibraryDirectory(dataPathString);
- NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir);
- } else {
- Slog.i(TAG, "Linking native library dir for " + path);
- mInstaller.linkNativeLibraryDirectory(dataPathString,
- pkg.applicationInfo.nativeLibraryDir);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Unable to get canonical file " + ioe.toString());
}
}
pkg.mScanPath = path;
@@ -3366,13 +3430,14 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.uid);
}
+ // writer
synchronized (mPackages) {
// We don't expect installation to fail beyond this point,
if ((scanMode&SCAN_MONITOR) != 0) {
mAppDirs.put(pkg.mPath, pkg);
}
// Add the new setting to mSettings
- mSettings.insertPackageSettingLP(pkgSetting, pkg);
+ mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
// Make sure we don't accidentally delete its data.
@@ -3428,10 +3493,12 @@ class PackageManagerService extends IPackageManager.Stub {
} 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);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ Log.d(TAG, "Registered content provider: " + names[j]
+ + ", className = " + p.info.name + ", isSyncable = "
+ + p.info.isSyncable);
+ }
} else {
PackageParser.Provider other = mProviders.get(names[j]);
Slog.w(TAG, "Skipping provider name " + names[j] +
@@ -3452,7 +3519,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Providers: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Providers: " + r);
}
N = pkg.services.size();
@@ -3472,7 +3539,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Services: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Services: " + r);
}
N = pkg.receivers.size();
@@ -3492,7 +3559,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Receivers: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Receivers: " + r);
}
N = pkg.activities.size();
@@ -3512,7 +3579,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Activities: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r);
}
N = pkg.permissionGroups.size();
@@ -3546,7 +3613,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Permission Groups: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permission Groups: " + r);
}
N = pkg.permissions.size();
@@ -3611,7 +3678,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Permissions: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permissions: " + r);
}
N = pkg.instrumentation.size();
@@ -3634,7 +3701,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Instrumentation: " + r);
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Instrumentation: " + r);
}
if (pkg.protectedBroadcasts != null) {
@@ -3663,25 +3730,15 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- // Return the path of the directory that will contain the native binaries
- // of a given installed package. This is relative to the data path.
- //
- private File getNativeBinaryDirForPackage(PackageParser.Package pkg) {
- final String nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
- if (nativeLibraryDir != null) {
- return new File(nativeLibraryDir);
- } else {
- // Fall back for old packages
- return new File(pkg.applicationInfo.dataDir, LIB_DIR_NAME);
- }
- }
-
void removePackageLI(PackageParser.Package pkg, boolean chatty) {
- if (chatty && Config.LOGD) Log.d(
- TAG, "Removing package " + pkg.applicationInfo.packageName );
+ if (DEBUG_INSTALL) {
+ if (chatty)
+ Log.d(TAG, "Removing package " + pkg.applicationInfo.packageName);
+ }
+ // writer
synchronized (mPackages) {
- clearPackagePreferredActivitiesLP(pkg.packageName);
+ clearPackagePreferredActivitiesLPw(pkg.packageName);
mPackages.remove(pkg.applicationInfo.packageName);
if (pkg.mPath != null) {
@@ -3707,10 +3764,12 @@ class PackageManagerService extends IPackageManager.Stub {
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 (DEBUG_REMOVE) {
+ if (chatty)
+ Log.d(TAG, "Unregistered content provider: " + names[j]
+ + ", className = " + p.info.name + ", isSyncable = "
+ + p.info.isSyncable);
+ }
}
}
if (chatty) {
@@ -3723,7 +3782,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Providers: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Providers: " + r);
}
N = pkg.services.size();
@@ -3741,7 +3800,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Services: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Services: " + r);
}
N = pkg.receivers.size();
@@ -3759,7 +3818,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Receivers: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Receivers: " + r);
}
N = pkg.activities.size();
@@ -3777,17 +3836,15 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Activities: " + r);
+ if (DEBUG_REMOVE) 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) {
@@ -3803,7 +3860,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Permissions: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
}
N = pkg.instrumentation.size();
@@ -3821,7 +3878,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (r != null) {
- if (Config.LOGD) Log.d(TAG, " Instrumentation: " + r);
+ if (DEBUG_REMOVE) Log.d(TAG, " Instrumentation: " + r);
}
}
}
@@ -3839,14 +3896,13 @@ class PackageManagerService extends IPackageManager.Stub {
return false;
}
- private void updatePermissionsLP(String changingPkg,
+ private void updatePermissionsLPw(String changingPkg,
PackageParser.Package pkgInfo, boolean grantPermissions,
boolean replace, boolean replaceAll) {
// Make sure there are no dangling permission trees.
- Iterator<BasePermission> it = mSettings.mPermissionTrees
- .values().iterator();
+ Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
while (it.hasNext()) {
- BasePermission bp = it.next();
+ final BasePermission bp = it.next();
if (bp.packageSetting == null) {
// We may not yet have parsed the package, so just see if
// we still know about its settings.
@@ -3870,13 +3926,13 @@ class PackageManagerService extends IPackageManager.Stub {
// and make sure there are no dangling permissions.
it = mSettings.mPermissions.values().iterator();
while (it.hasNext()) {
- BasePermission bp = it.next();
+ final 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.packageSetting == null && bp.pendingInfo != null) {
- BasePermission tree = findPermissionTreeLP(bp.name);
+ final BasePermission tree = findPermissionTreeLP(bp.name);
if (tree != null) {
bp.packageSetting = tree.packageSetting;
bp.perm = new PackageParser.Permission(tree.perm.owner,
@@ -3911,18 +3967,18 @@ class PackageManagerService extends IPackageManager.Stub {
if (grantPermissions) {
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg != pkgInfo) {
- grantPermissionsLP(pkg, replaceAll);
+ grantPermissionsLPw(pkg, replaceAll);
}
}
}
if (pkgInfo != null) {
- grantPermissionsLP(pkgInfo, replace);
+ grantPermissionsLPw(pkgInfo, replace);
}
}
- private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) {
- final PackageSetting ps = (PackageSetting)pkg.mExtras;
+ private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace) {
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
return;
}
@@ -3943,12 +3999,11 @@ class PackageManagerService extends IPackageManager.Stub {
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);
- if (false) {
+ final String name = pkg.requestedPermissions.get(i);
+ final BasePermission bp = mSettings.mPermissions.get(name);
+ if (DEBUG_INSTALL) {
if (gp != ps) {
- Log.i(TAG, "Package " + pkg.packageName + " checking " + name
- + ": " + bp);
+ Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
}
}
if (bp != null && bp.packageSetting != null) {
@@ -3963,10 +4018,10 @@ class PackageManagerService extends IPackageManager.Stub {
allowed = false;
} else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
|| bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
- allowed = (checkSignaturesLP(
+ allowed = (compareSignatures(
bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
- || (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures)
+ || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
if (!allowed && bp.protectionLevel
== PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
@@ -3974,8 +4029,8 @@ class PackageManagerService extends IPackageManager.Stub {
// For updated system applications, the signatureOrSystem permission
// is granted only if it had been defined by the original application.
if (isUpdatedSystemApp(pkg)) {
- PackageSetting sysPs = mSettings.getDisabledSystemPkg(
- pkg.packageName);
+ final PackageSetting sysPs = mSettings
+ .getDisabledSystemPkgLPr(pkg.packageName);
final GrantedPermissions origGp = sysPs.sharedUser != null
? sysPs.sharedUser : sysPs;
if (origGp.grantedPermissions.contains(perm)) {
@@ -3994,7 +4049,7 @@ class PackageManagerService extends IPackageManager.Stub {
} else {
allowed = false;
}
- if (false) {
+ if (DEBUG_INSTALL) {
if (gp != ps) {
Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
}
@@ -4072,25 +4127,26 @@ class PackageManagerService extends IPackageManager.Stub {
private final class ActivityIntentResolver
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
- public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
+ public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
+ boolean defaultOnly) {
mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
return super.queryIntent(intent, resolvedType, defaultOnly);
}
- public List queryIntent(Intent intent, String resolvedType, int flags) {
+ public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags) {
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
}
- public List queryIntentForPackage(Intent intent, String resolvedType, int flags,
- ArrayList<PackageParser.Activity> packageActivities) {
+ public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
+ int flags, ArrayList<PackageParser.Activity> packageActivities) {
if (packageActivities == null) {
return null;
}
mFlags = flags;
final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
- int N = packageActivities.size();
+ final int N = packageActivities.size();
ArrayList<ArrayList<PackageParser.ActivityIntentInfo>> listCut =
new ArrayList<ArrayList<PackageParser.ActivityIntentInfo>>(N);
@@ -4107,11 +4163,13 @@ class PackageManagerService extends IPackageManager.Stub {
public final void addActivity(PackageParser.Activity a, String type) {
final boolean systemApp = isSystemApp(a.info.applicationInfo);
mActivities.put(a.getComponentName(), a);
- if (SHOW_INFO || Config.LOGV) Log.v(
+ if (DEBUG_SHOW_INFO)
+ 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();
+ if (DEBUG_SHOW_INFO)
+ Log.v(TAG, " Class=" + a.info.name);
+ final int NI = a.intents.size();
for (int j=0; j<NI; j++) {
PackageParser.ActivityIntentInfo intent = a.intents.get(j);
if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) {
@@ -4119,7 +4177,7 @@ class PackageManagerService extends IPackageManager.Stub {
Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity "
+ a.className + " with priority > 0, forcing to 0");
}
- if (SHOW_INFO || Config.LOGV) {
+ if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
@@ -4132,14 +4190,16 @@ class PackageManagerService extends IPackageManager.Stub {
public final void removeActivity(PackageParser.Activity a, String type) {
mActivities.remove(a.getComponentName());
- 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();
+ if (DEBUG_SHOW_INFO) {
+ Log.v(TAG, " " + type + " "
+ + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel
+ : a.info.name) + ":");
+ Log.v(TAG, " Class=" + a.info.name);
+ }
+ final int NI = a.intents.size();
for (int j=0; j<NI; j++) {
PackageParser.ActivityIntentInfo intent = a.intents.get(j);
- if (SHOW_INFO || Config.LOGV) {
+ if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
@@ -4181,7 +4241,7 @@ class PackageManagerService extends IPackageManager.Stub {
@Override
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match) {
- if (!mSettings.isEnabledLP(info.activity.info, mFlags)) {
+ if (!mSettings.isEnabledLPr(info.activity.info, mFlags)) {
return null;
}
final PackageParser.Activity activity = info.activity;
@@ -4243,25 +4303,26 @@ class PackageManagerService extends IPackageManager.Stub {
private final class ServiceIntentResolver
extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> {
- public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {
+ public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
+ boolean defaultOnly) {
mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
return super.queryIntent(intent, resolvedType, defaultOnly);
}
- public List queryIntent(Intent intent, String resolvedType, int flags) {
+ public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags) {
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
}
- public List queryIntentForPackage(Intent intent, String resolvedType, int flags,
- ArrayList<PackageParser.Service> packageServices) {
+ public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
+ int flags, ArrayList<PackageParser.Service> packageServices) {
if (packageServices == null) {
return null;
}
mFlags = flags;
final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
- int N = packageServices.size();
+ final int N = packageServices.size();
ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut =
new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N);
@@ -4277,16 +4338,17 @@ class PackageManagerService extends IPackageManager.Stub {
public final void addService(PackageParser.Service s) {
mServices.put(s.getComponentName(), s);
- if (SHOW_INFO || Config.LOGV) Log.v(
- TAG, " " + (s.info.nonLocalizedLabel != null
+ if (DEBUG_SHOW_INFO) {
+ 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();
+ Log.v(TAG, " Class=" + s.info.name);
+ }
+ final 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) {
+ if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
@@ -4299,16 +4361,16 @@ class PackageManagerService extends IPackageManager.Stub {
public final void removeService(PackageParser.Service s) {
mServices.remove(s.getComponentName());
- if (SHOW_INFO || Config.LOGV) Log.v(
- TAG, " " + (s.info.nonLocalizedLabel != null
+ if (DEBUG_SHOW_INFO) {
+ 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();
+ Log.v(TAG, " Class=" + s.info.name);
+ }
+ final 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) {
+ if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
@@ -4351,7 +4413,7 @@ class PackageManagerService extends IPackageManager.Stub {
protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter,
int match) {
final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo)filter;
- if (!mSettings.isEnabledLP(info.service.info, mFlags)) {
+ if (!mSettings.isEnabledLPr(info.service.info, mFlags)) {
return null;
}
final PackageParser.Service service = info.service;
@@ -4444,7 +4506,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
};
- private static final void sendPackageBroadcast(String action, String pkg,
+ static final void sendPackageBroadcast(String action, String pkg,
Bundle extras, String targetPkg, IIntentReceiver finishedReceiver) {
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
@@ -4475,6 +4537,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
public String nextPackageToClean(String lastPackage) {
+ // writer
synchronized (mPackages) {
if (!isExternalMediaAvailable()) {
// If the external storage is no longer mounted at this point,
@@ -4495,6 +4558,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
void startCleaningPackages() {
+ // reader
synchronized (mPackages) {
if (!isExternalMediaAvailable()) {
return;
@@ -4527,6 +4591,7 @@ class PackageManagerService extends IPackageManager.Stub {
String addedPackage = null;
int addedUid = -1;
+ // TODO post a message to the handler to obtain serial ordering
synchronized (mInstallLock) {
String fullPathStr = null;
File fullPath = null;
@@ -4535,13 +4600,12 @@ class PackageManagerService extends IPackageManager.Stub {
fullPathStr = fullPath.getPath();
}
- if (Config.LOGV) Log.v(
- TAG, "File " + fullPathStr + " changed: "
- + Integer.toHexString(event));
+ if (DEBUG_APP_DIR_OBSERVER)
+ 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);
+ if (DEBUG_APP_DIR_OBSERVER)
+ Log.v(TAG, "Ignoring change of non-package file: " + fullPathStr);
return;
}
@@ -4551,6 +4615,7 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
PackageParser.Package p = null;
+ // reader
synchronized (mPackages) {
p = mAppDirs.get(fullPathStr);
}
@@ -4572,8 +4637,14 @@ class PackageManagerService extends IPackageManager.Stub {
SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
System.currentTimeMillis());
if (p != null) {
+ /*
+ * TODO this seems dangerous as the package may have
+ * changed since we last acquired the mPackages
+ * lock.
+ */
+ // writer
synchronized (mPackages) {
- updatePermissionsLP(p.packageName, p,
+ updatePermissionsLPw(p.packageName, p,
p.permissions.size() > 0, false, false);
}
addedPackage = p.applicationInfo.packageName;
@@ -4582,8 +4653,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ // reader
synchronized (mPackages) {
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
@@ -4631,13 +4703,9 @@ class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessage(msg);
}
- public void setInstallerPackageName(String targetPackage,
- String installerPackageName) {
- PackageSetting pkgSetting;
+ public void setInstallerPackageName(String targetPackage, String installerPackageName) {
final int uid = Binder.getCallingUid();
- final int permission = mContext.checkCallingPermission(
- android.Manifest.permission.INSTALL_PACKAGES);
- final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ // writer
synchronized (mPackages) {
PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage);
if (targetPackageSetting == null) {
@@ -4656,7 +4724,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
Signature[] callerSignature;
- Object obj = mSettings.getUserIdLP(uid);
+ Object obj = mSettings.getUserIdLPr(uid);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
callerSignature = ((SharedUserSetting)obj).signatures.mSignatures;
@@ -4672,7 +4740,7 @@ class PackageManagerService extends IPackageManager.Stub {
// Verify: can't set installerPackageName to a package that is
// not signed with the same cert as the caller.
if (installerPackageSetting != null) {
- if (checkSignaturesLP(callerSignature,
+ if (compareSignatures(callerSignature,
installerPackageSetting.signatures.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
@@ -4689,7 +4757,7 @@ class PackageManagerService extends IPackageManager.Stub {
// If the currently set package isn't valid, then it's always
// okay to change it.
if (setting != null) {
- if (checkSignaturesLP(callerSignature,
+ if (compareSignatures(callerSignature,
setting.signatures.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
@@ -4905,6 +4973,7 @@ class PackageManagerService extends IPackageManager.Stub {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
+ // reader
synchronized (mPackages) {
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null) {
@@ -4969,12 +5038,24 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Cannot install fwd locked apps on sdcard");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
+ final long lowThreshold;
+
+ final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager
+ .getService(DeviceStorageMonitorService.SERVICE);
+ if (dsm == null) {
+ Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
+ lowThreshold = 0L;
+ } else {
+ lowThreshold = dsm.getMemoryLowThreshold();
+ }
+
// Remote call to find out default install location
final PackageInfoLite pkgLite;
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags);
+ pkgLite = mContainerService.getMinimalPackageInfo(packageURI, flags,
+ lowThreshold);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -5194,10 +5275,26 @@ class PackageManagerService extends IPackageManager.Stub {
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
+ final long lowThreshold;
+
+ final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager
+ .getService(DeviceStorageMonitorService.SERVICE);
+ if (dsm == null) {
+ Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
+ lowThreshold = 0L;
+ } else {
+ if (dsm.isMemoryLow()) {
+ Log.w(TAG, "Memory is reported as being too low; aborting package install");
+ return false;
+ }
+
+ lowThreshold = dsm.getMemoryLowThreshold();
+ }
+
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- return imcs.checkFreeStorage(false, packageURI);
+ return imcs.checkInternalFreeStorage(packageURI, lowThreshold);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -5423,7 +5520,7 @@ class PackageManagerService extends IPackageManager.Stub {
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
- return imcs.checkFreeStorage(true, packageURI);
+ return imcs.checkExternalFreeStorage(packageURI);
} finally {
mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
@@ -5688,7 +5785,7 @@ class PackageManagerService extends IPackageManager.Stub {
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
- boolean dataDirExists = getDataPathForPackage(pkg).exists();
+ boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
res.name = pkgName;
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
@@ -5747,7 +5844,7 @@ class PackageManagerService extends IPackageManager.Stub {
// First find the old package info and check signatures
synchronized(mPackages) {
oldPackage = mPackages.get(pkgName);
- if (checkSignaturesLP(oldPackage.mSignatures, pkg.mSignatures)
+ if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
return;
@@ -5770,11 +5867,6 @@ class PackageManagerService extends IPackageManager.Stub {
boolean deletedPkg = true;
boolean updatedSettings = false;
- String oldInstallerPackageName = null;
- synchronized (mPackages) {
- oldInstallerPackageName = mSettings.getInstallerPackageName(pkgName);
- }
-
long origUpdateTime;
if (pkg.mExtras != null) {
origUpdateTime = ((PackageSetting)pkg.mExtras).lastUpdateTime;
@@ -5838,10 +5930,12 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
// Restore of old package succeeded. Update permissions.
+ // writer
synchronized (mPackages) {
- updatePermissionsLP(deletedPackage.packageName, deletedPackage,
+ updatePermissionsLPw(deletedPackage.packageName, deletedPackage,
true, false, false);
- mSettings.writeLP();
+ // can downgrade to reader
+ mSettings.writeLPr();
}
Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");
}
@@ -5864,6 +5958,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
PackageParser.Package oldPkg;
PackageSetting oldPkgSetting;
+ // reader
synchronized (mPackages) {
oldPkg = mPackages.get(packageName);
oldPkgSetting = mSettings.mPackages.get(packageName);
@@ -5880,8 +5975,9 @@ class PackageManagerService extends IPackageManager.Stub {
res.removedInfo.removedPackage = packageName;
// Remove existing system package
removePackageLI(oldPkg, true);
+ // writer
synchronized (mPackages) {
- if (!mSettings.disableSystemPackageLP(packageName) && deletedPackage != null) {
+ if (!mSettings.disableSystemPackageLPw(packageName) && deletedPackage != null) {
// We didn't need to disable the .apk as a current system package,
// which means we are replacing another update that is already
// installed. We need to make sure to delete the older one's .apk.
@@ -5925,11 +6021,11 @@ class PackageManagerService extends IPackageManager.Stub {
// Restore the old system information in Settings
synchronized(mPackages) {
if (updatedSettings) {
- mSettings.enableSystemPackageLP(packageName);
+ mSettings.enableSystemPackageLPw(packageName);
mSettings.setInstallerPackageName(packageName,
oldPkgSetting.installerPackageName);
}
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
}
@@ -5963,8 +6059,8 @@ class PackageManagerService extends IPackageManager.Stub {
//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();
+ mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
+ mSettings.writeLPr();
}
if ((res.returnCode = moveDexFilesLI(newPackage))
@@ -5982,16 +6078,16 @@ class PackageManagerService extends IPackageManager.Stub {
Log.d(TAG, "New package installed in " + newPackage.mPath);
}
synchronized (mPackages) {
- updatePermissionsLP(newPackage.packageName, newPackage,
+ updatePermissionsLPw(newPackage.packageName, newPackage,
newPackage.permissions.size() > 0, true, false);
res.name = pkgName;
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
- mSettings.setInstallStatus(pkgName, PKG_INSTALL_COMPLETE);
+ mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
mSettings.setInstallerPackageName(pkgName, installerPackageName);
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
//to update install status
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
@@ -6090,7 +6186,6 @@ class PackageManagerService extends IPackageManager.Stub {
}
private int setPermissionsLI(PackageParser.Package newPackage) {
- String pkgName = newPackage.packageName;
int retCode = 0;
// TODO Gross hack but fix later. Ideally move this to be a post installation
// check after alloting uid.
@@ -6120,9 +6215,8 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (retCode != 0) {
- Slog.e(TAG, "Couldn't set new package file permissions for " +
- newPackage.mPath
- + ". The return code was: " + retCode);
+ Slog.e(TAG, "Couldn't set new package file permissions for " + newPackage.mPath
+ + ". The return code was: " + retCode);
// TODO Define new internal error
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
@@ -6371,37 +6465,46 @@ class PackageManagerService extends IPackageManager.Stub {
}
removePackageLI(p, (flags&REMOVE_CHATTY) != 0);
// Retrieve object to delete permissions for shared user later on
- PackageSetting deletedPs;
+ final PackageSetting deletedPs;
+ // reader
synchronized (mPackages) {
deletedPs = mSettings.mPackages.get(packageName);
}
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
if (mInstaller != null) {
- int retCode = mInstaller.remove(packageName);
+ int retCode = mInstaller.remove(packageName, 0);
if (retCode < 0) {
Slog.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 {
+ // TODO: Kill the processes first
+ mUserManager.removePackageForAllUsers(packageName);
}
} else {
// for simulator
- PackageParser.Package pkg = mPackages.get(packageName);
- File dataDir = new File(pkg.applicationInfo.dataDir);
+ File dataDir;
+ // reader
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(packageName);
+ dataDir = new File(pkg.applicationInfo.dataDir);
+ }
dataDir.delete();
}
schedulePackageCleaning(packageName);
}
+ // writer
synchronized (mPackages) {
if (deletedPs != null) {
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
if (outInfo != null) {
- outInfo.removedUid = mSettings.removePackageLP(packageName);
+ outInfo.removedUid = mSettings.removePackageLPw(packageName);
}
if (deletedPs != null) {
- updatePermissionsLP(deletedPs.name, null, false, false, false);
+ updatePermissionsLPw(deletedPs.name, null, false, false, false);
if (deletedPs.sharedUser != null) {
// remove permissions associated with package
- mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
+ mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);
}
}
}
@@ -6416,9 +6519,10 @@ class PackageManagerService extends IPackageManager.Stub {
mSettings.mPreferredActivities.removeFilter(pa);
}
}
+ // can downgrade to reader
if (writeSettings) {
// Save settings now
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
}
@@ -6438,8 +6542,9 @@ class PackageManagerService extends IPackageManager.Stub {
// 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
+ // reader
synchronized (mPackages) {
- ps = mSettings.getDisabledSystemPkg(p.packageName);
+ ps = mSettings.getDisabledSystemPkgLPr(p.packageName);
}
if (ps == null) {
Slog.w(TAG, "Attempt to delete unknown system package "+ p.packageName);
@@ -6461,9 +6566,10 @@ class PackageManagerService extends IPackageManager.Stub {
if (!ret) {
return false;
}
+ // writer
synchronized (mPackages) {
// Reinstate the old system package
- mSettings.enableSystemPackageLP(p.packageName);
+ mSettings.enableSystemPackageLPw(p.packageName);
// Remove any native libraries from the upgraded package.
NativeLibraryHelper.removeNativeBinariesLI(p.applicationInfo.nativeLibraryDir);
}
@@ -6476,10 +6582,12 @@ class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError);
return false;
}
+ // writer
synchronized (mPackages) {
- updatePermissionsLP(newPkg.packageName, newPkg, true, true, false);
+ updatePermissionsLPw(newPkg.packageName, newPkg, true, true, false);
+ // can downgrade to reader here
if (writeSettings) {
- mSettings.writeLP();
+ mSettings.writeLPr();
}
}
return true;
@@ -6631,7 +6739,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
if (mInstaller != null) {
- int retCode = mInstaller.clearUserData(packageName);
+ int retCode = mInstaller.clearUserData(packageName, 0); // TODO - correct userId
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove cache files for package: "
+ packageName);
@@ -6767,16 +6875,14 @@ class PackageManagerService extends IPackageManager.Stub {
return new ArrayList<PackageInfo>();
}
- int getUidTargetSdkVersionLockedLP(int uid) {
- Object obj = mSettings.getUserIdLP(uid);
+ private int getUidTargetSdkVersionLockedLPr(int uid) {
+ Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
- SharedUserSetting sus = (SharedUserSetting)obj;
- final int N = sus.packages.size();
+ final SharedUserSetting sus = (SharedUserSetting) obj;
int vers = Build.VERSION_CODES.CUR_DEVELOPMENT;
- Iterator<PackageSetting> it = sus.packages.iterator();
- int i=0;
+ final Iterator<PackageSetting> it = sus.packages.iterator();
while (it.hasNext()) {
- PackageSetting ps = it.next();
+ final PackageSetting ps = it.next();
if (ps.pkg != null) {
int v = ps.pkg.applicationInfo.targetSdkVersion;
if (v < vers) vers = v;
@@ -6784,7 +6890,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
return vers;
} else if (obj instanceof PackageSetting) {
- PackageSetting ps = (PackageSetting)obj;
+ final PackageSetting ps = (PackageSetting) obj;
if (ps.pkg != null) {
return ps.pkg.applicationInfo.targetSdkVersion;
}
@@ -6794,11 +6900,12 @@ class PackageManagerService extends IPackageManager.Stub {
public void addPreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity) {
+ // writer
synchronized (mPackages) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
- if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid())
+ if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid())
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
+ Binder.getCallingUid());
@@ -6838,7 +6945,7 @@ class PackageManagerService extends IPackageManager.Stub {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
- if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid())
+ if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid())
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
+ Binder.getCallingUid());
@@ -6864,14 +6971,15 @@ class PackageManagerService extends IPackageManager.Stub {
}
public void clearPackagePreferredActivities(String packageName) {
+ final int uid = Binder.getCallingUid();
+ // writer
synchronized (mPackages) {
- int uid = Binder.getCallingUid();
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null || pkg.applicationInfo.uid != uid) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
- if (getUidTargetSdkVersionLockedLP(Binder.getCallingUid())
+ if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid())
< Build.VERSION_CODES.FROYO) {
Slog.w(TAG, "Ignoring clearPackagePreferredActivities() from uid "
+ Binder.getCallingUid());
@@ -6882,13 +6990,13 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if (clearPackagePreferredActivitiesLP(packageName)) {
+ if (clearPackagePreferredActivitiesLPw(packageName)) {
scheduleWriteSettingsLocked();
}
}
}
- boolean clearPackagePreferredActivitiesLP(String packageName) {
+ boolean clearPackagePreferredActivitiesLPw(String packageName) {
boolean changed = false;
Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
while (it.hasNext()) {
@@ -6905,10 +7013,11 @@ class PackageManagerService extends IPackageManager.Stub {
List<ComponentName> outActivities, String packageName) {
int num = 0;
+ // reader
synchronized (mPackages) {
- Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
+ final Iterator<PreferredActivity> it = mSettings.mPreferredActivities.filterIterator();
while (it.hasNext()) {
- PreferredActivity pa = it.next();
+ final PreferredActivity pa = it.next();
if (packageName == null
|| pa.mPref.mComponent.getPackageName().equals(packageName)) {
if (outFilters != null) {
@@ -6953,6 +7062,8 @@ class PackageManagerService extends IPackageManager.Stub {
String componentName = isApp ? packageName : className;
int packageUid = -1;
ArrayList<String> components;
+
+ // writer
synchronized (mPackages) {
pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) {
@@ -6982,17 +7093,17 @@ class PackageManagerService extends IPackageManager.Stub {
// We're dealing with a component level state change
switch (newState) {
case COMPONENT_ENABLED_STATE_ENABLED:
- if (!pkgSetting.enableComponentLP(className)) {
+ if (!pkgSetting.enableComponentLPw(className)) {
return;
}
break;
case COMPONENT_ENABLED_STATE_DISABLED:
- if (!pkgSetting.disableComponentLP(className)) {
+ if (!pkgSetting.disableComponentLPw(className)) {
return;
}
break;
case COMPONENT_ENABLED_STATE_DEFAULT:
- if (!pkgSetting.restoreComponentLP(className)) {
+ if (!pkgSetting.restoreComponentLPw(className)) {
return;
}
break;
@@ -7001,10 +7112,10 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
}
- mSettings.writeLP();
+ mSettings.writeLPr();
packageUid = pkgSetting.userId;
components = mPendingBroadcasts.get(packageName);
- boolean newPackage = components == null;
+ final boolean newPackage = components == null;
if (newPackage) {
components = new ArrayList<String>();
}
@@ -7040,8 +7151,9 @@ class PackageManagerService extends IPackageManager.Stub {
private void sendPackageChangedBroadcast(String packageName,
boolean killFlag, ArrayList<String> componentNames, int packageUid) {
- if (false) Log.v(TAG, "Sending package changed: package=" + packageName
- + " components=" + componentNames);
+ if (DEBUG_INSTALL)
+ Log.v(TAG, "Sending package changed: package=" + packageName + " components="
+ + componentNames);
Bundle extras = new Bundle(4);
extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
String nameList[] = new String[componentNames.size()];
@@ -7053,72 +7165,37 @@ class PackageManagerService extends IPackageManager.Stub {
}
public void setPackageStoppedState(String packageName, boolean stopped) {
- PackageSetting pkgSetting;
final int uid = Binder.getCallingUid();
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ // writer
synchronized (mPackages) {
- pkgSetting = mSettings.mPackages.get(packageName);
- if (pkgSetting == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- if (!allowedByPermission && (uid != pkgSetting.userId)) {
- throw new SecurityException(
- "Permission Denial: attempt to change stopped state from pid="
- + Binder.getCallingPid()
- + ", uid=" + uid + ", package uid=" + pkgSetting.userId);
- }
- if (DEBUG_STOPPED && stopped) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Slog.i(TAG, "Stopping package " + packageName, e);
- }
- if (pkgSetting.stopped != stopped) {
- pkgSetting.stopped = stopped;
- pkgSetting.pkg.mSetStopped = stopped;
- if (pkgSetting.notLaunched) {
- if (pkgSetting.installerPackageName != null) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
- pkgSetting.name, null,
- pkgSetting.installerPackageName, null);
- }
- pkgSetting.notLaunched = false;
- }
+ if (mSettings.setPackageStoppedStateLPw(packageName, stopped, allowedByPermission,
+ uid)) {
scheduleWriteStoppedPackagesLocked();
}
}
}
public String getInstallerPackageName(String packageName) {
+ // reader
synchronized (mPackages) {
- PackageSetting pkg = mSettings.mPackages.get(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- return pkg.installerPackageName;
+ return mSettings.getInstallerPackageNameLPr(packageName);
}
}
- public int getApplicationEnabledSetting(String appPackageName) {
+ public int getApplicationEnabledSetting(String packageName) {
+ // reader
synchronized (mPackages) {
- PackageSetting pkg = mSettings.mPackages.get(appPackageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + appPackageName);
- }
- return pkg.enabled;
+ return mSettings.getApplicationEnabledSettingLPr(packageName);
}
}
public int getComponentEnabledSetting(ComponentName componentName) {
+ // reader
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);
+ return mSettings.getComponentEnabledSettingLPr(componentName);
}
}
@@ -7162,6 +7239,76 @@ class PackageManagerService extends IPackageManager.Stub {
return buf.toString();
}
+ static class DumpState {
+ public static final int DUMP_LIBS = 1 << 0;
+
+ public static final int DUMP_FEATURES = 1 << 1;
+
+ public static final int DUMP_RESOLVERS = 1 << 2;
+
+ public static final int DUMP_PERMISSIONS = 1 << 3;
+
+ public static final int DUMP_PACKAGES = 1 << 4;
+
+ public static final int DUMP_SHARED_USERS = 1 << 5;
+
+ public static final int DUMP_MESSAGES = 1 << 6;
+
+ public static final int DUMP_PROVIDERS = 1 << 7;
+
+ public static final int OPTION_SHOW_FILTERS = 1 << 0;
+
+ private int mTypes;
+
+ private int mOptions;
+
+ private boolean mTitlePrinted;
+
+ private SharedUserSetting mSharedUser;
+
+ public boolean isDumping(int type) {
+ if (mTypes == 0) {
+ return true;
+ }
+
+ return (mTypes & type) != 0;
+ }
+
+ public void setDump(int type) {
+ mTypes |= type;
+ }
+
+ public boolean isOptionEnabled(int option) {
+ return (mOptions & option) != 0;
+ }
+
+ public void setOptionEnabled(int option) {
+ mOptions |= option;
+ }
+
+ public boolean onTitlePrinted() {
+ final boolean printed = mTitlePrinted;
+ mTitlePrinted = true;
+ return printed;
+ }
+
+ public boolean getTitlePrinted() {
+ return mTitlePrinted;
+ }
+
+ public void setTitlePrinted(boolean enabled) {
+ mTitlePrinted = enabled;
+ }
+
+ public SharedUserSetting getSharedUser() {
+ return mSharedUser;
+ }
+
+ public void setSharedUser(SharedUserSetting user) {
+ mSharedUser = user;
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -7174,18 +7321,9 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
- boolean dumpStar = true;
- boolean dumpLibs = false;
- boolean dumpFeatures = false;
- boolean dumpResolvers = false;
- boolean dumpPermissions = false;
- boolean dumpPackages = false;
- boolean dumpSharedUsers = false;
- boolean dumpMessages = false;
- boolean dumpProviders = false;
+ DumpState dumpState = new DumpState();
String packageName = null;
- boolean showFilters = false;
int opti = 0;
while (opti < args.length) {
@@ -7213,7 +7351,7 @@ class PackageManagerService extends IPackageManager.Stub {
pw.println(" <package.name>: info about given package");
return;
} else if ("-f".equals(opt)) {
- showFilters = true;
+ dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
}
@@ -7227,40 +7365,31 @@ class PackageManagerService extends IPackageManager.Stub {
if ("android".equals(cmd) || cmd.contains(".")) {
packageName = cmd;
} else if ("l".equals(cmd) || "libraries".equals(cmd)) {
- dumpStar = false;
- dumpLibs = true;
+ dumpState.setDump(DumpState.DUMP_LIBS);
} else if ("f".equals(cmd) || "features".equals(cmd)) {
- dumpStar = false;
- dumpFeatures = true;
+ dumpState.setDump(DumpState.DUMP_FEATURES);
} else if ("r".equals(cmd) || "resolvers".equals(cmd)) {
- dumpStar = false;
- dumpResolvers = true;
+ dumpState.setDump(DumpState.DUMP_RESOLVERS);
} else if ("perm".equals(cmd) || "permissions".equals(cmd)) {
- dumpStar = false;
- dumpPermissions = true;
+ dumpState.setDump(DumpState.DUMP_PERMISSIONS);
} else if ("p".equals(cmd) || "packages".equals(cmd)) {
- dumpStar = false;
- dumpPackages = true;
+ dumpState.setDump(DumpState.DUMP_PACKAGES);
} else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
- dumpStar = false;
- dumpSharedUsers = true;
+ dumpState.setDump(DumpState.DUMP_SHARED_USERS);
} else if ("prov".equals(cmd) || "providers".equals(cmd)) {
- dumpStar = false;
- dumpProviders = true;
+ dumpState.setDump(DumpState.DUMP_PROVIDERS);
} else if ("m".equals(cmd) || "messages".equals(cmd)) {
- dumpStar = false;
- dumpMessages = true;
+ dumpState.setDump(DumpState.DUMP_MESSAGES);
}
}
-
- boolean printedTitle = false;
-
+
+ // reader
synchronized (mPackages) {
- if ((dumpStar || dumpLibs) && packageName == null) {
- if (printedTitle) pw.println(" ");
- printedTitle = true;
+ if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
pw.println("Libraries:");
- Iterator<String> it = mSharedLibraries.keySet().iterator();
+ final Iterator<String> it = mSharedLibraries.keySet().iterator();
while (it.hasNext()) {
String name = it.next();
pw.print(" ");
@@ -7270,9 +7399,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if ((dumpStar || dumpFeatures) && packageName == null) {
- if (printedTitle) pw.println(" ");
- printedTitle = true;
+ if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
pw.println("Features:");
Iterator<String> it = mAvailableFeatures.keySet().iterator();
while (it.hasNext()) {
@@ -7282,276 +7411,72 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- if (dumpStar || dumpResolvers) {
- if (mActivities.dump(pw, printedTitle
- ? "\nActivity Resolver Table:" : "Activity Resolver Table:",
- " ", packageName, showFilters)) {
- printedTitle = true;
+ if (dumpState.isDumping(DumpState.DUMP_RESOLVERS)) {
+ if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
+ : "Activity Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.setTitlePrinted(true);
}
- if (mReceivers.dump(pw, printedTitle
- ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:",
- " ", packageName, showFilters)) {
- printedTitle = true;
+ if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
+ : "Receiver Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.setTitlePrinted(true);
}
- if (mServices.dump(pw, printedTitle
- ? "\nService Resolver Table:" : "Service Resolver Table:",
- " ", packageName, showFilters)) {
- printedTitle = true;
+ if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
+ : "Service Resolver Table:", " ", packageName,
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.setTitlePrinted(true);
}
- if (mSettings.mPreferredActivities.dump(pw, printedTitle
- ? "\nPreferred Activities:" : "Preferred Activities:",
- " ", packageName, showFilters)) {
- printedTitle = true;
+ if (mSettings.mPreferredActivities.dump(pw,
+ dumpState.getTitlePrinted() ? "\nPreferred Activities:"
+ : "Preferred Activities:", " ",
+ packageName, dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.setTitlePrinted(true);
}
}
- boolean printedSomething = false;
- if (dumpStar || dumpPermissions) {
- for (BasePermission p : mSettings.mPermissions.values()) {
- if (packageName != null && !packageName.equals(p.sourcePackage)) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Permissions:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" Permission ["); pw.print(p.name); pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(p)));
- pw.println("):");
- pw.print(" sourcePackage="); pw.println(p.sourcePackage);
- pw.print(" uid="); pw.print(p.uid);
- pw.print(" gids="); pw.print(arrayToString(p.gids));
- pw.print(" type="); pw.print(p.type);
- pw.print(" prot="); pw.println(p.protectionLevel);
- if (p.packageSetting != null) {
- pw.print(" packageSetting="); pw.println(p.packageSetting);
- }
- if (p.perm != null) {
- pw.print(" perm="); pw.println(p.perm);
- }
- }
+ if (dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+ mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
}
- if (dumpStar || dumpProviders) {
- printedSomething = false;
+ if (dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ boolean printedSomething = false;
for (PackageParser.Provider p : mProviders.values()) {
if (packageName != null && !packageName.equals(p.info.packageName)) {
continue;
}
if (!printedSomething) {
- if (printedTitle) pw.println(" ");
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
pw.println("Registered ContentProviders:");
printedSomething = true;
- printedTitle = true;
}
pw.print(" ["); pw.print(p.info.authority); pw.print("]: ");
pw.println(p.toString());
}
}
- printedSomething = false;
- SharedUserSetting packageSharedUser = null;
- if (dumpStar || dumpPackages) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- Date date = new Date();
- for (PackageSetting ps : mSettings.mPackages.values()) {
- if (packageName != null && !packageName.equals(ps.realName)
- && !packageName.equals(ps.name)) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Packages:");
- printedSomething = true;
- printedTitle = true;
- }
- packageSharedUser = ps.sharedUser;
- pw.print(" Package [");
- pw.print(ps.realName != null ? ps.realName : ps.name);
- pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(ps)));
- pw.println("):");
- if (ps.realName != null) {
- pw.print(" compat name="); pw.println(ps.name);
- }
- pw.print(" userId="); pw.print(ps.userId);
- pw.print(" gids="); pw.println(arrayToString(ps.gids));
- pw.print(" sharedUser="); pw.println(ps.sharedUser);
- pw.print(" pkg="); pw.println(ps.pkg);
- pw.print(" codePath="); pw.println(ps.codePathString);
- pw.print(" resourcePath="); pw.println(ps.resourcePathString);
- pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString);
- pw.print(" versionCode="); pw.println(ps.versionCode);
- if (ps.pkg != null) {
- pw.print(" versionName="); pw.println(ps.pkg.mVersionName);
- pw.print(" dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
- pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion);
- if (ps.pkg.mOperationPending) {
- pw.println(" mOperationPending=true");
- }
- pw.print(" supportsScreens=[");
- boolean first = true;
- if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
- if (!first) pw.print(", ");
- first = false;
- pw.print("small");
- }
- if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
- if (!first) pw.print(", ");
- first = false;
- pw.print("medium");
- }
- if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
- if (!first) pw.print(", ");
- first = false;
- pw.print("large");
- }
- if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
- if (!first) pw.print(", ");
- first = false;
- pw.print("xlarge");
- }
- if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
- if (!first) pw.print(", ");
- first = false;
- pw.print("resizeable");
- }
- if ((ps.pkg.applicationInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
- if (!first) pw.print(", ");
- first = false;
- pw.print("anyDensity");
- }
- }
- pw.println("]");
- pw.print(" timeStamp=");
- date.setTime(ps.timeStamp); pw.println(sdf.format(date));
- pw.print(" firstInstallTime=");
- date.setTime(ps.firstInstallTime); pw.println(sdf.format(date));
- pw.print(" lastUpdateTime=");
- date.setTime(ps.lastUpdateTime); pw.println(sdf.format(date));
- if (ps.installerPackageName != null) {
- pw.print(" installerPackageName="); pw.println(ps.installerPackageName);
- }
- pw.print(" signatures="); pw.println(ps.signatures);
- pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed);
- pw.print(" haveGids="); pw.println(ps.haveGids);
- pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
- pw.print(" installStatus="); pw.print(ps.installStatus);
- pw.print(" stopped="); pw.print(ps.stopped);
- pw.print(" enabled="); pw.println(ps.enabled);
- if (ps.disabledComponents.size() > 0) {
- pw.println(" disabledComponents:");
- for (String s : ps.disabledComponents) {
- pw.print(" "); pw.println(s);
- }
- }
- if (ps.enabledComponents.size() > 0) {
- pw.println(" enabledComponents:");
- for (String s : ps.enabledComponents) {
- pw.print(" "); pw.println(s);
- }
- }
- if (ps.grantedPermissions.size() > 0) {
- pw.println(" grantedPermissions:");
- for (String s : ps.grantedPermissions) {
- pw.print(" "); pw.println(s);
- }
- }
- }
- }
- printedSomething = false;
- if (dumpStar || dumpPackages) {
- if (mSettings.mRenamedPackages.size() > 0) {
- for (HashMap.Entry<String, String> e
- : mSettings.mRenamedPackages.entrySet()) {
- if (packageName != null && !packageName.equals(e.getKey())
- && !packageName.equals(e.getValue())) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Renamed packages:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" "); pw.print(e.getKey()); pw.print(" -> ");
- pw.println(e.getValue());
- }
- }
- printedSomething = false;
- if (mSettings.mDisabledSysPackages.size() > 0) {
- for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) {
- if (packageName != null && !packageName.equals(ps.realName)
- && !packageName.equals(ps.name)) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Hidden system packages:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" Package [");
- pw.print(ps.realName != null ? ps.realName : ps.name);
- pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(ps)));
- pw.println("):");
- if (ps.realName != null) {
- pw.print(" compat name="); pw.println(ps.name);
- }
- pw.print(" userId="); pw.println(ps.userId);
- pw.print(" sharedUser="); pw.println(ps.sharedUser);
- pw.print(" codePath="); pw.println(ps.codePathString);
- pw.print(" resourcePath="); pw.println(ps.resourcePathString);
- }
- }
+ if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
+ mSettings.dumpPackagesLPr(pw, packageName, dumpState);
}
- printedSomething = false;
- if (dumpStar || dumpSharedUsers) {
- for (SharedUserSetting su : mSettings.mSharedUsers.values()) {
- if (packageName != null && su != packageSharedUser) {
- continue;
- }
- if (!printedSomething) {
- if (printedTitle) pw.println(" ");
- pw.println("Shared users:");
- printedSomething = true;
- printedTitle = true;
- }
- pw.print(" SharedUser ["); pw.print(su.name); pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(su)));
- pw.println("):");
- pw.print(" userId="); pw.print(su.userId);
- pw.print(" gids="); pw.println(arrayToString(su.gids));
- pw.println(" grantedPermissions:");
- for (String s : su.grantedPermissions) {
- pw.print(" "); pw.println(s);
- }
- }
+
+ if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+ mSettings.dumpSharedUsersLPr(pw, packageName, dumpState);
}
-
- if ((dumpStar || dumpMessages) && packageName == null) {
- if (printedTitle) pw.println(" ");
- printedTitle = true;
- pw.println("Settings parse messages:");
- pw.print(mSettings.mReadMessages.toString());
-
+
+ if (dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ mSettings.dumpReadMessagesLPr(pw, dumpState);
+
pw.println(" ");
pw.println("Package warning messages:");
- File fname = getSettingsProblemFile();
+ final File fname = getSettingsProblemFile();
FileInputStream in = null;
try {
in = new FileInputStream(fname);
- int avail = in.available();
- byte[] data = new byte[avail];
+ final int avail = in.available();
+ final byte[] data = new byte[avail];
in.read(data);
pw.print(new String(data));
} catch (FileNotFoundException e) {
@@ -7560,2918 +7485,513 @@ class PackageManagerService extends IPackageManager.Stub {
if (in != null) {
try {
in.close();
- } catch (IOException e) {
- }
- }
- }
- }
- }
- }
-
- 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;
- String sourcePackage;
- PackageSettingBase packageSetting;
- final int type;
- int protectionLevel;
- PackageParser.Permission perm;
- PermissionInfo pendingInfo;
- int uid;
- int[] gids;
-
- BasePermission(String _name, String _sourcePackage, int _type) {
- name = _name;
- sourcePackage = _sourcePackage;
- type = _type;
- // Default to most conservative protection level.
- protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
- }
-
- public String toString() {
- return "BasePermission{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + name + "}";
- }
- }
-
- static class PackageSignatures {
- private Signature[] mSignatures;
-
- PackageSignatures(PackageSignatures orig) {
- if (orig != null && orig.mSignatures != null) {
- mSignatures = orig.mSignatures.clone();
- }
- }
-
- 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());
+ } catch (IOException e) {
}
- } 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;
- }
- }
-
- 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 implements PreferredComponent.Callbacks {
- final PreferredComponent mPref;
-
- PreferredActivity(IntentFilter filter, int match, ComponentName[] set,
- ComponentName activity) {
- super(filter);
- mPref = new PreferredComponent(this, match, set, activity);
- }
-
- PreferredActivity(XmlPullParser parser) throws XmlPullParserException,
- IOException {
- mPref = new PreferredComponent(this, parser);
- }
-
- public void writeToXml(XmlSerializer serializer) throws IOException {
- mPref.writeToXml(serializer);
- serializer.startTag(null, "filter");
- super.writeToXml(serializer);
- serializer.endTag(null, "filter");
- }
-
- public boolean onReadTag(String tagName, XmlPullParser parser)
- throws XmlPullParserException, IOException {
- 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);
- }
- return true;
}
}
- static class GrantedPermissions {
- int pkgFlags;
-
- HashSet<String> grantedPermissions = new HashSet<String>();
- int[] gids;
-
- GrantedPermissions(int pkgFlags) {
- setFlags(pkgFlags);
- }
-
- GrantedPermissions(GrantedPermissions base) {
- pkgFlags = base.pkgFlags;
- grantedPermissions = (HashSet<String>) base.grantedPermissions.clone();
-
- if (base.gids != null) {
- gids = base.gids.clone();
- }
- }
-
- void setFlags(int pkgFlags) {
- this.pkgFlags = pkgFlags & (
- ApplicationInfo.FLAG_SYSTEM |
- ApplicationInfo.FLAG_FORWARD_LOCK |
- ApplicationInfo.FLAG_EXTERNAL_STORAGE);
- }
- }
-
- /**
- * Settings base class for pending and resolved classes.
- */
- static class PackageSettingBase extends GrantedPermissions {
- final String name;
- final String realName;
- File codePath;
- String codePathString;
- File resourcePath;
- String resourcePathString;
- String nativeLibraryPathString;
- long timeStamp;
- long firstInstallTime;
- long lastUpdateTime;
- int versionCode;
-
- boolean uidError;
-
- PackageSignatures signatures = new PackageSignatures();
-
- boolean permissionsFixed;
- boolean haveGids;
-
- // Whether this package is currently stopped, thus can not be
- // started until explicitly launched by the user.
- public boolean stopped;
-
- // Set to true if we have never launched this app.
- public boolean notLaunched;
-
- /* 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 origPackage;
-
- /* package name of the app that installed this package */
- String installerPackageName;
-
- PackageSettingBase(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
- super(pkgFlags);
- this.name = name;
- this.realName = realName;
- init(codePath, resourcePath, nativeLibraryPathString, pVersionCode);
- }
-
- /**
- * New instance of PackageSetting with one-level-deep cloning.
- */
- PackageSettingBase(PackageSettingBase base) {
- super(base);
-
- name = base.name;
- realName = base.realName;
- codePath = base.codePath;
- codePathString = base.codePathString;
- resourcePath = base.resourcePath;
- resourcePathString = base.resourcePathString;
- nativeLibraryPathString = base.nativeLibraryPathString;
- timeStamp = base.timeStamp;
- firstInstallTime = base.firstInstallTime;
- lastUpdateTime = base.lastUpdateTime;
- versionCode = base.versionCode;
-
- uidError = base.uidError;
-
- signatures = new PackageSignatures(base.signatures);
-
- permissionsFixed = base.permissionsFixed;
- haveGids = base.haveGids;
- stopped = base.stopped;
- notLaunched = base.notLaunched;
-
- disabledComponents = (HashSet<String>) base.disabledComponents.clone();
-
- enabledComponents = (HashSet<String>) base.enabledComponents.clone();
-
- enabled = base.enabled;
- installStatus = base.installStatus;
-
- origPackage = base.origPackage;
-
- installerPackageName = base.installerPackageName;
- }
-
- void init(File codePath, File resourcePath, String nativeLibraryPathString,
- int pVersionCode) {
- this.codePath = codePath;
- this.codePathString = codePath.toString();
- this.resourcePath = resourcePath;
- this.resourcePathString = resourcePath.toString();
- this.nativeLibraryPathString = nativeLibraryPathString;
- this.versionCode = pVersionCode;
- }
-
- public void setInstallerPackageName(String packageName) {
- installerPackageName = packageName;
- }
-
- String getInstallerPackageName() {
- return installerPackageName;
- }
-
- public void setInstallStatus(int newStatus) {
- installStatus = newStatus;
- }
-
- public int getInstallStatus() {
- return installStatus;
- }
-
- public void setTimeStamp(long newStamp) {
- timeStamp = newStamp;
- }
-
- /**
- * Make a shallow copy of this package settings.
- */
- public void copyFrom(PackageSettingBase base) {
- grantedPermissions = base.grantedPermissions;
- gids = base.gids;
-
- timeStamp = base.timeStamp;
- firstInstallTime = base.firstInstallTime;
- lastUpdateTime = base.lastUpdateTime;
- signatures = base.signatures;
- permissionsFixed = base.permissionsFixed;
- haveGids = base.haveGids;
- stopped = base.stopped;
- notLaunched = base.notLaunched;
- disabledComponents = base.disabledComponents;
- enabledComponents = base.enabledComponents;
- enabled = base.enabled;
- installStatus = base.installStatus;
- }
-
- boolean enableComponentLP(String componentClassName) {
- boolean changed = disabledComponents.remove(componentClassName);
- changed |= enabledComponents.add(componentClassName);
- return changed;
- }
-
- boolean disableComponentLP(String componentClassName) {
- boolean changed = enabledComponents.remove(componentClassName);
- changed |= disabledComponents.add(componentClassName);
- return changed;
- }
-
- boolean restoreComponentLP(String componentClassName) {
- boolean changed = enabledComponents.remove(componentClassName);
- changed |= disabledComponents.remove(componentClassName);
- return changed;
- }
-
- 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, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
- super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
- pkgFlags);
- }
-
- /**
- * New instance of PackageSetting replicating the original settings.
- * Note that it keeps the same PackageParser.Package instance.
- */
- PackageSetting(PackageSetting orig) {
- super(orig);
-
- userId = orig.userId;
- pkg = orig.pkg;
- sharedUser = orig.sharedUser;
- }
-
- @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 File mPackageListFilename;
- private final File mStoppedPackagesFilename;
- private final File mBackupStoppedPackagesFilename;
- private final HashMap<String, PackageSetting> mPackages =
- new HashMap<String, PackageSetting>();
- // List of replaced system applications
- final HashMap<String, PackageSetting> mDisabledSysPackages =
- new HashMap<String, PackageSetting>();
-
- // These are the last platform API version we were using for
- // the apps installed on internal and external storage. It is
- // used to grant newer permissions one time during a system upgrade.
- int mInternalSdkPlatform;
- int mExternalSdkPlatform;
-
- // The user's preferred activities associated with particular intent
- // filters.
- private final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities =
- new IntentResolver<PreferredActivity, PreferredActivity>() {
- @Override
- protected String packageForFilter(PreferredActivity filter) {
- return filter.mPref.mComponent.getPackageName();
- }
- @Override
- protected void dumpFilter(PrintWriter out, String prefix,
- PreferredActivity filter) {
- filter.mPref.dump(out, prefix, filter);
- }
- };
- 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>();
-
- // Packages that have been uninstalled and still need their external
- // storage data deleted.
- final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
-
- // Packages that have been renamed since they were first installed.
- // Keys are the new names of the packages, values are the original
- // names. The packages appear everwhere else under their original
- // names.
- final HashMap<String, String> mRenamedPackages = new HashMap<String, String>();
-
- private final StringBuilder mReadMessages = new StringBuilder();
-
- private static final class PendingPackage extends PackageSettingBase {
- final int sharedId;
-
- PendingPackage(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) {
- super(name, realName, codePath, resourcePath, nativeLibraryPathString,
- pVersionCode, 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");
- mPackageListFilename = new File(systemDir, "packages.list");
- mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml");
- mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml");
- }
-
- PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage,
- String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
- final String name = pkg.packageName;
- PackageSetting p = getPackageLP(name, origPackage, realName, sharedUser, codePath,
- resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
- return p;
- }
-
- PackageSetting peekPackageLP(String name) {
- return mPackages.get(name);
- /*
- 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);
- }
- }
- }
+ // ------- apps on sdcard specific code -------
+ static final boolean DEBUG_SD_INSTALL = false;
- void setInstallerPackageName(String pkgName,
- String installerPkgName) {
- PackageSetting p = mPackages.get(pkgName);
- if(p != null) {
- p.setInstallerPackageName(installerPkgName);
- }
- }
+ private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD";
- String getInstallerPackageName(String pkgName) {
- PackageSetting p = mPackages.get(pkgName);
- return (p == null) ? null : p.getInstallerPackageName();
- }
+ private static final String SD_ENCRYPTION_ALGORITHM = "AES";
- int getInstallStatus(String pkgName) {
- PackageSetting p = mPackages.get(pkgName);
- if(p != null) {
- return p.getInstallStatus();
- }
- return -1;
- }
+ private boolean mMediaMounted = false;
- SharedUserSetting getSharedUserLP(String name,
- int pkgFlags, boolean create) {
- SharedUserSetting s = mSharedUsers.get(name);
- if (s == null) {
- if (!create) {
+ private String getEncryptKey() {
+ try {
+ String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
+ SD_ENCRYPTION_KEYSTORE_NAME);
+ if (sdEncKey == null) {
+ sdEncKey = SystemKeyStore.getInstance().generateNewKeyHexString(128,
+ SD_ENCRYPTION_ALGORITHM, SD_ENCRYPTION_KEYSTORE_NAME);
+ if (sdEncKey == null) {
+ Slog.e(TAG, "Failed to create encryption keys");
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;
- }
-
- boolean disableSystemPackageLP(String name) {
- PackageSetting p = mPackages.get(name);
- if(p == null) {
- Log.w(TAG, "Package:"+name+" is not an installed package");
- return false;
- }
- 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);
-
- // a little trick... when we install the new package, we don't
- // want to modify the existing PackageSetting for the built-in
- // version. so at this point we need a new PackageSetting that
- // is okay to muck with.
- PackageSetting newp = new PackageSetting(p);
- replacePackageLP(name, newp);
- return true;
- }
- return false;
- }
-
- 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.realName, p.codePath, p.resourcePath,
- p.nativeLibraryPathString, p.userId, p.versionCode, p.pkgFlags);
- mDisabledSysPackages.remove(name);
- return ret;
- }
-
- PackageSetting addPackageLP(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int uid, int vc, 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, realName, codePath, resourcePath, nativeLibraryPathString,
- vc, pkgFlags);
- p.userId = uid;
- if (addUserIdLP(uid, p, name)) {
- mPackages.put(name, p);
- return p;
}
+ return sdEncKey;
+ } catch (NoSuchAlgorithmException nsae) {
+ Slog.e(TAG, "Failed to create encryption keys with exception: " + nsae);
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;
- }
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Failed to retrieve encryption keys with exception: " + ioe);
return null;
}
- // Transfer ownership of permissions from one package to another.
- private void transferPermissions(String origPkg, String newPkg) {
- // Transfer ownership of permissions to the new package.
- for (int i=0; i<2; i++) {
- HashMap<String, BasePermission> permissions =
- i == 0 ? mPermissionTrees : mPermissions;
- for (BasePermission bp : permissions.values()) {
- if (origPkg.equals(bp.sourcePackage)) {
- if (DEBUG_UPGRADE) Log.v(TAG,
- "Moving permission " + bp.name
- + " from pkg " + bp.sourcePackage
- + " to " + newPkg);
- bp.sourcePackage = newPkg;
- bp.packageSetting = null;
- bp.perm = null;
- if (bp.pendingInfo != null) {
- bp.pendingInfo.packageName = newPkg;
- }
- bp.uid = 0;
- bp.gids = null;
- }
- }
- }
- }
-
- private PackageSetting getPackageLP(String name, PackageSetting origPackage,
- String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String nativeLibraryPathString, int vc, 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
- if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- // This is an updated system app with versions in both system
- // and data partition. Just let the most recent version
- // take precedence.
- Slog.w(TAG, "Trying to update system app code path from " +
- p.codePathString + " to " + codePath.toString());
- } else {
- // Just a change in the code path is not an issue, but
- // let's log a message about it.
- Slog.i(TAG, "Package " + name + " codePath changed from " + p.codePath
- + " to " + codePath + "; Retaining data and using new");
- /*
- * Since we've changed paths, we need to prefer the new
- * native library path over the one stored in the
- * package settings since we might have moved from
- * internal to external storage or vice versa.
- */
- p.nativeLibraryPathString = nativeLibraryPathString;
- }
- }
- 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;
- } else {
- if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
- // If what we are scanning is a system package, then
- // make it so, regardless of whether it was previously
- // installed only in the data partition.
- p.pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- }
- }
- }
- 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;
- }
- if (origPackage != null) {
- // We are consuming the data from an existing package.
- p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
- nativeLibraryPathString, vc, pkgFlags);
- if (DEBUG_UPGRADE) Log.v(TAG, "Package " + name
- + " is adopting original package " + origPackage.name);
- // Note that we will retain the new package's signature so
- // that we can keep its data.
- PackageSignatures s = p.signatures;
- p.copyFrom(origPackage);
- p.signatures = s;
- p.sharedUser = origPackage.sharedUser;
- p.userId = origPackage.userId;
- p.origPackage = origPackage;
- mRenamedPackages.put(name, origPackage.name);
- name = origPackage.name;
- // Update new package state.
- p.setTimeStamp(codePath.lastModified());
- } else {
- p = new PackageSetting(name, realName, codePath, resourcePath,
- nativeLibraryPathString, vc, pkgFlags);
- p.setTimeStamp(codePath.lastModified());
- p.sharedUser = sharedUser;
- // If this is not a system app, it starts out stopped.
- if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
- if (DEBUG_STOPPED) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Slog.i(TAG, "Stopping package " + name, e);
- }
- p.stopped = true;
- p.notLaunched = true;
- }
- if (sharedUser != null) {
- p.userId = sharedUser.userId;
- } else if (MULTIPLE_APPLICATION_UIDS) {
- // Clone the setting here for disabled system packages
- PackageSetting dis = mDisabledSysPackages.get(name);
- if (dis != null) {
- // For disabled packages a new setting is created
- // from the existing user id. This still has to be
- // added to list of user id's
- // Copy signatures from previous setting
- if (dis.signatures.mSignatures != null) {
- p.signatures.mSignatures = dis.signatures.mSignatures.clone();
- }
- p.userId = dis.userId;
- // Clone permissions
- p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
- // Clone component info
- p.disabledComponents = new HashSet<String>(dis.disabledComponents);
- p.enabledComponents = new HashSet<String>(dis.enabledComponents);
- // Add new setting to list of user ids
- addUserIdLP(p.userId, p, name);
- } else {
- // Assign new user id
- 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
- addPackageSettingLP(p, name, sharedUser);
- }
- }
- return p;
- }
-
- private void insertPackageSettingLP(PackageSetting p, PackageParser.Package pkg) {
- p.pkg = pkg;
- pkg.mSetEnabled = p.enabled;
- pkg.mSetStopped = p.stopped;
- final String codePath = pkg.applicationInfo.sourceDir;
- final String resourcePath = pkg.applicationInfo.publicSourceDir;
- // Update code path if needed
- if (!codePath.equalsIgnoreCase(p.codePathString)) {
- Slog.w(TAG, "Code path for pkg : " + p.pkg.packageName +
- " changing from " + p.codePathString + " to " + codePath);
- p.codePath = new File(codePath);
- p.codePathString = codePath;
- }
- //Update resource path if needed
- if (!resourcePath.equalsIgnoreCase(p.resourcePathString)) {
- Slog.w(TAG, "Resource path for pkg : " + p.pkg.packageName +
- " changing from " + p.resourcePathString + " to " + resourcePath);
- p.resourcePath = new File(resourcePath);
- p.resourcePathString = resourcePath;
- }
- // Update the native library path if needed
- final String nativeLibraryPath = pkg.applicationInfo.nativeLibraryDir;
- if (nativeLibraryPath != null
- && !nativeLibraryPath.equalsIgnoreCase(p.nativeLibraryPathString)) {
- p.nativeLibraryPathString = nativeLibraryPath;
- }
- // Update version code if needed
- if (pkg.mVersionCode != p.versionCode) {
- p.versionCode = pkg.mVersionCode;
- }
- // Update signatures if needed.
- if (p.signatures.mSignatures == null) {
- p.signatures.assignSignatures(pkg.mSignatures);
- }
- // If this app defines a shared user id initialize
- // the shared user signatures as well.
- if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
- p.sharedUser.signatures.assignSignatures(pkg.mSignatures);
- }
- addPackageSettingLP(p, pkg.packageName, p.sharedUser);
- }
-
- // Utility method that adds a PackageSetting to mPackages and
- // completes updating the shared user attributes
- private void addPackageSettingLP(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;
- }
- }
+ }
- /*
- * Update the shared user setting when a package using
- * specifying the shared user id is removed. The gids
- * associated with each permission of the deleted package
- * are removed from the shared user's gid list only if its
- * not in use by other permissions of packages in the
- * shared user setting.
- */
- private void updateSharedUserPermsLP(PackageSetting deletedPs, int[] globalGids) {
- if ( (deletedPs == null) || (deletedPs.pkg == null)) {
- Slog.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)) {
+ /* package */static String getTempContainerId() {
+ int tmpIdx = 1;
+ String list[] = PackageHelper.getSecureContainerList();
+ if (list != null) {
+ for (final String name : list) {
+ // Ignore null and non-temporary container entries
+ if (name == null || !name.startsWith(mTempContainerPrefix)) {
continue;
}
- for (PackageSetting pkg:sus.packages) {
- if (pkg.pkg != null &&
- !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&
- pkg.pkg.requestedPermissions.contains(eachPerm)) {
- used = true;
- break;
- }
- }
- if (!used) {
- // can safely delete this permission from list
- sus.grantedPermissions.remove(eachPerm);
- }
- }
- // Update gids
- int newGids[] = globalGids;
- for (String eachPerm : sus.grantedPermissions) {
- BasePermission bp = mPermissions.get(eachPerm);
- if (bp != null) {
- newGids = appendInts(newGids, bp.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;
+ String subStr = name.substring(mTempContainerPrefix.length());
+ try {
+ int cid = Integer.parseInt(subStr);
+ if (cid >= tmpIdx) {
+ tmpIdx = cid + 1;
}
- } else {
- removeUserIdLP(p.userId);
- return p.userId;
- }
- }
- return -1;
- }
-
- private void replacePackageLP(String name, PackageSetting newp) {
- PackageSetting p = mPackages.get(name);
- if (p != null) {
- if (p.sharedUser != null) {
- p.sharedUser.packages.remove(p);
- p.sharedUser.packages.add(newp);
- } else {
- replaceUserIdLP(p.userId, newp);
- }
- }
- mPackages.put(name, newp);
- }
-
- 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 user 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 Set<String> findPackagesWithFlag(int flag) {
- Set<String> ret = new HashSet<String>();
- for (PackageSetting ps : mPackages.values()) {
- // Has to match atleast all the flag bits set on flag
- if ((ps.pkgFlags & flag) == flag) {
- ret.add(ps.name);
+ } catch (NumberFormatException e) {
}
}
- return ret;
}
+ return mTempContainerPrefix + tmpIdx;
+ }
- 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);
- }
+ /*
+ * Update media status on PackageManager.
+ */
+ public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("Media status can only be updated by the system");
}
-
- private void replaceUserIdLP(int uid, Object obj) {
- if (uid >= FIRST_APPLICATION_UID) {
- int N = mUserIds.size();
- final int index = uid - FIRST_APPLICATION_UID;
- if (index < N) mUserIds.set(index, obj);
- } else {
- mOtherUserIds.put(uid, obj);
- }
- }
-
- void writeStoppedLP() {
- // Keep the old stopped packages around until we know the new ones have
- // been successfully written.
- if (mStoppedPackagesFilename.exists()) {
- // Presence of backup settings file indicates that we failed
- // to persist packages earlier. So preserve the older
- // backup for future reference since the current packages
- // might have been corrupted.
- if (!mBackupStoppedPackagesFilename.exists()) {
- if (!mStoppedPackagesFilename.renameTo(mBackupStoppedPackagesFilename)) {
- Log.wtf(TAG, "Unable to backup package manager stopped packages, "
- + "current changes will be lost at reboot");
- return;
- }
- } else {
- mStoppedPackagesFilename.delete();
- Slog.w(TAG, "Preserving older stopped packages backup");
- }
- }
-
- try {
- FileOutputStream fstr = new FileOutputStream(mStoppedPackagesFilename);
- BufferedOutputStream str = new BufferedOutputStream(fstr);
-
- //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, "stopped-packages");
-
- for (PackageSetting pkg : mPackages.values()) {
- if (pkg.stopped) {
- serializer.startTag(null, "pkg");
- serializer.attribute(null, "name", pkg.name);
- if (pkg.notLaunched) {
- serializer.attribute(null, "nl", "1");
- }
- serializer.endTag(null, "pkg");
- }
- }
-
- serializer.endTag(null, "stopped-packages");
-
- serializer.endDocument();
-
- str.flush();
- FileUtils.sync(fstr);
- str.close();
-
- // New settings successfully written, old ones are no longer
- // needed.
- mBackupStoppedPackagesFilename.delete();
- FileUtils.setPermissions(mStoppedPackagesFilename.toString(),
- FileUtils.S_IRUSR|FileUtils.S_IWUSR
- |FileUtils.S_IRGRP|FileUtils.S_IWGRP
- |FileUtils.S_IROTH,
- -1, -1);
-
- // Done, all is good!
+ // reader; this apparently protects mMediaMounted, but should probably
+ // be a different lock in that case.
+ synchronized (mPackages) {
+ Log.i(TAG, "Updating external media status from "
+ + (mMediaMounted ? "mounted" : "unmounted") + " to "
+ + (mediaStatus ? "mounted" : "unmounted"));
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus
+ + ", mMediaMounted=" + mMediaMounted);
+ if (mediaStatus == mMediaMounted) {
+ final Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1
+ : 0, -1);
+ mHandler.sendMessage(msg);
return;
-
- } catch(java.io.IOException e) {
- Log.wtf(TAG, "Unable to write package manager stopped packages, "
- + " current changes will be lost at reboot", e);
- }
-
- // Clean up partially written files
- if (mStoppedPackagesFilename.exists()) {
- if (!mStoppedPackagesFilename.delete()) {
- Log.i(TAG, "Failed to clean up mangled file: " + mStoppedPackagesFilename);
- }
}
+ mMediaMounted = mediaStatus;
}
-
- // Note: assumed "stopped" field is already cleared in all packages.
- void readStoppedLP() {
- FileInputStream str = null;
- if (mBackupStoppedPackagesFilename.exists()) {
- try {
- str = new FileInputStream(mBackupStoppedPackagesFilename);
- mReadMessages.append("Reading from backup stopped packages file\n");
- reportSettingsProblem(Log.INFO, "Need to read from backup stopped packages file");
- if (mSettingsFilename.exists()) {
- // If both the backup and normal file exist, we
- // ignore the normal one since it might have been
- // corrupted.
- Slog.w(TAG, "Cleaning up stopped packages file "
- + mStoppedPackagesFilename);
- mStoppedPackagesFilename.delete();
- }
- } catch (java.io.IOException e) {
- // We'll try for the normal settings file.
- }
+ // Queue up an async operation since the package installation may take a
+ // little while.
+ mHandler.post(new Runnable() {
+ public void run() {
+ // TODO fix this; this does nothing.
+ mHandler.removeCallbacks(this);
+ updateExternalMediaStatusInner(mediaStatus, reportStatus);
}
+ });
+ }
- try {
- if (str == null) {
- if (!mStoppedPackagesFilename.exists()) {
- mReadMessages.append("No stopped packages file found\n");
- reportSettingsProblem(Log.INFO, "No stopped packages file file; "
- + "assuming all started");
- // At first boot, make sure no packages are stopped.
- // We usually want to have third party apps initialize
- // in the stopped state, but not at first boot.
- for (PackageSetting pkg : mPackages.values()) {
- pkg.stopped = false;
- pkg.notLaunched = false;
- }
- return;
- }
- str = new FileInputStream(mStoppedPackagesFilename);
- }
- 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 stopped packages file\n");
- reportSettingsProblem(Log.WARN,
- "No start tag found in package manager stopped packages");
- return;
- }
-
- 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) {
+ /*
+ * Collect information of applications on external media, map them against
+ * existing containers and update information based on current mount status.
+ * Please note that we always have to report status if reportStatus has been
+ * set to true especially when unloading packages.
+ */
+ private void updateExternalMediaStatusInner(boolean mediaStatus, boolean reportStatus) {
+ // Collection of uids
+ int uidArr[] = null;
+ // Collection of stale containers
+ HashSet<String> removeCids = new HashSet<String>();
+ // Collection of packages on external media with valid containers.
+ HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
+ // Get list of secure containers.
+ final String list[] = PackageHelper.getSecureContainerList();
+ if (list == null || list.length == 0) {
+ Log.i(TAG, "No secure containers on sdcard");
+ } else {
+ // Process list of secure containers and categorize them
+ // as active or stale based on their package internal state.
+ int uidList[] = new int[list.length];
+ int num = 0;
+ // reader
+ synchronized (mPackages) {
+ for (String cid : list) {
+ SdInstallArgs args = new SdInstallArgs(cid);
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Processing container " + cid);
+ String pkgName = args.getPackageName();
+ if (pkgName == null) {
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Container : " + cid + " stale");
+ removeCids.add(cid);
continue;
}
-
- String tagName = parser.getName();
- if (tagName.equals("pkg")) {
- String name = parser.getAttributeValue(null, "name");
- PackageSetting ps = mPackages.get(name);
- if (ps != null) {
- ps.stopped = true;
- if ("1".equals(parser.getAttributeValue(null, "nl"))) {
- ps.notLaunched = true;
- }
- } else {
- Slog.w(TAG, "No package known for stopped package: " + name);
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Looking for pkg : " + pkgName);
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ // The package status is changed only if the code path
+ // matches between settings and the container id.
+ if (ps != null && ps.codePathString != null
+ && ps.codePathString.equals(args.getCodePath())) {
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Container : " + cid + " corresponds to pkg : " + pkgName
+ + " at code path: " + ps.codePathString);
+ // We do have a valid package installed on sdcard
+ processCids.put(args, ps.codePathString);
+ int uid = ps.userId;
+ if (uid != -1) {
+ uidList[num++] = uid;
}
- XmlUtils.skipCurrentTag(parser);
} else {
- Slog.w(TAG, "Unknown element under <stopped-packages>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
+ // Stale container on sdcard. Just delete
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Container : " + cid + " stale");
+ removeCids.add(cid);
}
}
-
- str.close();
-
- } catch(XmlPullParserException e) {
- mReadMessages.append("Error reading: " + e.toString());
- reportSettingsProblem(Log.ERROR, "Error reading stopped packages: " + e);
- Log.wtf(TAG, "Error reading package manager stopped packages", e);
-
- } catch(java.io.IOException e) {
- mReadMessages.append("Error reading: " + e.toString());
- reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
- Log.wtf(TAG, "Error reading package manager stopped packages", e);
-
}
- }
-
- 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()) {
- // Presence of backup settings file indicates that we failed
- // to persist settings earlier. So preserve the older
- // backup for future reference since the current settings
- // might have been corrupted.
- if (!mBackupSettingsFilename.exists()) {
- if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) {
- Log.wtf(TAG, "Unable to backup package manager settings, "
- + " current changes will be lost at reboot");
- return;
+ if (num > 0) {
+ // Sort uid list
+ Arrays.sort(uidList, 0, num);
+ // Throw away duplicates
+ uidArr = new int[num];
+ uidArr[0] = uidList[0];
+ int di = 0;
+ for (int i = 1; i < num; i++) {
+ if (uidList[i - 1] != uidList[i]) {
+ uidArr[di++] = uidList[i];
}
- } else {
- mSettingsFilename.delete();
- Slog.w(TAG, "Preserving older settings backup");
}
}
-
- mPastSignatures.clear();
-
- try {
- FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
- BufferedOutputStream str = new BufferedOutputStream(fstr);
-
- //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, "last-platform-version");
- serializer.attribute(null, "internal", Integer.toString(mInternalSdkPlatform));
- serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform));
- serializer.endTag(null, "last-platform-version");
-
- 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-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");
- }
-
- if (mPackagesToBeCleaned.size() > 0) {
- for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
- serializer.startTag(null, "cleaning-package");
- serializer.attribute(null, "name", mPackagesToBeCleaned.get(i));
- serializer.endTag(null, "cleaning-package");
- }
- }
-
- if (mRenamedPackages.size() > 0) {
- for (HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) {
- serializer.startTag(null, "renamed-package");
- serializer.attribute(null, "new", e.getKey());
- serializer.attribute(null, "old", e.getValue());
- serializer.endTag(null, "renamed-package");
- }
- }
-
- serializer.endTag(null, "packages");
-
- serializer.endDocument();
-
- str.flush();
- FileUtils.sync(fstr);
- 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);
-
- // Write package list file now, use a JournaledFile.
- //
- File tempFile = new File(mPackageListFilename.toString() + ".tmp");
- JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile);
-
- fstr = new FileOutputStream(journal.chooseForWrite());
- str = new BufferedOutputStream(fstr);
- try {
- StringBuilder sb = new StringBuilder();
- for (PackageSetting pkg : mPackages.values()) {
- ApplicationInfo ai = pkg.pkg.applicationInfo;
- String dataPath = ai.dataDir;
- boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-
- // Avoid any application that has a space in its path
- // or that is handled by the system.
- if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID)
- continue;
-
- // we store on each line the following information for now:
- //
- // pkgName - package name
- // userId - application-specific user id
- // debugFlag - 0 or 1 if the package is debuggable.
- // dataPath - path to package's data path
- //
- // NOTE: We prefer not to expose all ApplicationInfo flags for now.
- //
- // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS
- // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
- // system/core/run-as/run-as.c
- //
- sb.setLength(0);
- sb.append(ai.packageName);
- sb.append(" ");
- sb.append((int)ai.uid);
- sb.append(isDebug ? " 1 " : " 0 ");
- sb.append(dataPath);
- sb.append("\n");
- str.write(sb.toString().getBytes());
- }
- str.flush();
- FileUtils.sync(fstr);
- str.close();
- journal.commit();
- }
- catch (Exception e) {
- journal.rollback();
- }
-
- FileUtils.setPermissions(mPackageListFilename.toString(),
- FileUtils.S_IRUSR|FileUtils.S_IWUSR
- |FileUtils.S_IRGRP|FileUtils.S_IWGRP
- |FileUtils.S_IROTH,
- -1, -1);
-
- writeStoppedLP();
-
- return;
-
- } catch(XmlPullParserException e) {
- Log.wtf(TAG, "Unable to write package manager settings, "
- + "current changes will be lost at reboot", e);
- } catch(java.io.IOException e) {
- Log.wtf(TAG, "Unable to write package manager settings, "
- + "current changes will be lost at reboot", e);
- }
- // Clean up partially written files
- if (mSettingsFilename.exists()) {
- if (!mSettingsFilename.delete()) {
- Log.wtf(TAG, "Failed to clean up mangled file: " + mSettingsFilename);
- }
- }
- //Debug.stopMethodTracing();
- }
-
- void writeDisabledSysPackage(XmlSerializer serializer, final PackageSetting pkg)
- throws java.io.IOException {
- serializer.startTag(null, "updated-package");
- serializer.attribute(null, "name", pkg.name);
- if (pkg.realName != null) {
- serializer.attribute(null, "realName", pkg.realName);
- }
- serializer.attribute(null, "codePath", pkg.codePathString);
- serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
- serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
- serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
- serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
- if (!pkg.resourcePathString.equals(pkg.codePathString)) {
- serializer.attribute(null, "resourcePath", pkg.resourcePathString);
- }
- if (pkg.nativeLibraryPathString != null) {
- serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
- }
- 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) {
- // 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);
- if (pkg.realName != null) {
- serializer.attribute(null, "realName", pkg.realName);
- }
- serializer.attribute(null, "codePath", pkg.codePathString);
- if (!pkg.resourcePathString.equals(pkg.codePathString)) {
- serializer.attribute(null, "resourcePath", pkg.resourcePathString);
- }
- if (pkg.nativeLibraryPathString != null) {
- serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
- }
- serializer.attribute(null, "flags",
- Integer.toString(pkg.pkgFlags));
- serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
- serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
- serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
- serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
- if (pkg.sharedUser == null) {
- serializer.attribute(null, "userId",
- Integer.toString(pkg.userId));
- } else {
- serializer.attribute(null, "sharedUserId",
- Integer.toString(pkg.userId));
- }
- if (pkg.uidError) {
- serializer.attribute(null, "uidError", "true");
- }
- 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");
- }
- if (pkg.installerPackageName != null) {
- serializer.attribute(null, "installer", pkg.installerPackageName);
- }
- 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 (bp.protectionLevel !=
- PermissionInfo.PROTECTION_NORMAL) {
- serializer.attribute(null, "protection",
- Integer.toString(bp.protectionLevel));
- }
- 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());
- }
- }
- }
- serializer.endTag(null, "item");
- }
- }
-
- String getReadMessagesLP() {
- return mReadMessages.toString();
- }
-
- ArrayList<PackageSetting> getListOfIncompleteInstallPackages() {
- HashSet<String> kList = new HashSet<String>(mPackages.keySet());
- Iterator<String> its = kList.iterator();
- ArrayList<PackageSetting> ret = new ArrayList<PackageSetting>();
- while(its.hasNext()) {
- String key = its.next();
- PackageSetting ps = mPackages.get(key);
- if(ps.getInstallStatus() == PKG_INSTALL_INCOMPLETE) {
- ret.add(ps);
- }
- }
- return ret;
+ // Process packages with valid entries.
+ if (mediaStatus) {
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Loading packages");
+ loadMediaPackages(processCids, uidArr, removeCids);
+ startCleaningPackages();
+ } else {
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Unloading packages");
+ unloadMediaPackages(processCids, uidArr, reportStatus);
}
+ }
- boolean readLP() {
- FileInputStream str = null;
- if (mBackupSettingsFilename.exists()) {
- try {
- str = new FileInputStream(mBackupSettingsFilename);
- mReadMessages.append("Reading from backup settings file\n");
- reportSettingsProblem(Log.INFO, "Need to read from backup settings file");
- if (mSettingsFilename.exists()) {
- // If both the backup and settings file exist, we
- // ignore the settings since it might have been
- // corrupted.
- Slog.w(TAG, "Cleaning up settings file " + mSettingsFilename);
- mSettingsFilename.delete();
- }
- } 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");
- reportSettingsProblem(Log.INFO, "No settings file; creating initial state");
- 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");
- reportSettingsProblem(Log.WARN, "No start tag found in package manager settings");
- Log.wtf(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")) {
- // no longer used.
- } else if (tagName.equals("preferred-activities")) {
- readPreferredActivitiesLP(parser);
- } else if(tagName.equals("updated-package")) {
- readDisabledSysPackageLP(parser);
- } else if (tagName.equals("cleaning-package")) {
- String name = parser.getAttributeValue(null, "name");
- if (name != null) {
- mPackagesToBeCleaned.add(name);
- }
- } else if (tagName.equals("renamed-package")) {
- String nname = parser.getAttributeValue(null, "new");
- String oname = parser.getAttributeValue(null, "old");
- if (nname != null && oname != null) {
- mRenamedPackages.put(nname, oname);
- }
- } else if (tagName.equals("last-platform-version")) {
- mInternalSdkPlatform = mExternalSdkPlatform = 0;
- try {
- String internal = parser.getAttributeValue(null, "internal");
- if (internal != null) {
- mInternalSdkPlatform = Integer.parseInt(internal);
- }
- String external = parser.getAttributeValue(null, "external");
- if (external != null) {
- mExternalSdkPlatform = Integer.parseInt(external);
- }
- } catch (NumberFormatException e) {
- }
- } else {
- Slog.w(TAG, "Unknown element under <packages>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
- str.close();
-
- } catch(XmlPullParserException e) {
- mReadMessages.append("Error reading: " + e.toString());
- reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
- Log.wtf(TAG, "Error reading package manager settings", e);
-
- } catch(java.io.IOException e) {
- mReadMessages.append("Error reading: " + e.toString());
- reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
- Log.wtf(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, null, pp.realName,
- (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
- pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true);
- if (p == null) {
- reportSettingsProblem(Log.WARN, "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);
- reportSettingsProblem(Log.ERROR, msg);
- } else {
- String msg = "Bad package setting: package " + pp.name
- + " has shared uid " + pp.sharedId
- + " that is not defined\n";
- mReadMessages.append(msg);
- reportSettingsProblem(Log.ERROR, msg);
- }
+ private void sendResourcesChangedBroadcast(boolean mediaStatus, ArrayList<String> pkgList,
+ int uidArr[], IIntentReceiver finishedReceiver) {
+ int size = pkgList.size();
+ if (size > 0) {
+ // Send broadcasts here
+ Bundle extras = new Bundle();
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList
+ .toArray(new String[size]));
+ if (uidArr != null) {
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
}
- mPendingPackages.clear();
-
- /*
- * Make sure all the updated system packages have their shared users
- * associated with them.
- */
- final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
- while (disabledIt.hasNext()) {
- final PackageSetting disabledPs = disabledIt.next();
- final Object id = getUserIdLP(disabledPs.userId);
- if (id != null && id instanceof SharedUserSetting) {
- disabledPs.sharedUser = (SharedUserSetting) id;
- }
- }
-
- readStoppedLP();
-
- mReadMessages.append("Read completed successfully: "
- + mPackages.size() + " packages, "
- + mSharedUsers.size() + " shared uids\n");
-
- return true;
+ String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
+ : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
+ sendPackageBroadcast(action, null, extras, null, finishedReceiver);
}
+ }
- private int readInt(XmlPullParser parser, String ns, String name,
- int defValue) {
- String v = parser.getAttributeValue(ns, name);
+ /*
+ * Look at potentially valid container ids from processCids If package
+ * information doesn't match the one on record or package scanning fails,
+ * the cid is added to list of removeCids. We currently don't delete stale
+ * containers.
+ */
+ private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[],
+ HashSet<String> removeCids) {
+ ArrayList<String> pkgList = new ArrayList<String>();
+ Set<SdInstallArgs> keys = processCids.keySet();
+ boolean doGc = false;
+ for (SdInstallArgs args : keys) {
+ String codePath = processCids.get(args);
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Loading container : " + args.cid);
+ int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
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) {
+ // Make sure there are no container errors first.
+ if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) {
+ Slog.e(TAG, "Failed to mount cid : " + args.cid
+ + " when installing from sdcard");
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);
- bp.protectionLevel = readInt(parser, null, "protection",
- PermissionInfo.PROTECTION_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 = bp.protectionLevel;
- 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 realName = parser.getAttributeValue(null, "realName");
- String codePathStr = parser.getAttributeValue(null, "codePath");
- String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
- String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
- if (resourcePathStr == null) {
- resourcePathStr = codePathStr;
- }
- String version = parser.getAttributeValue(null, "version");
- int versionCode = 0;
- if (version != null) {
- try {
- versionCode = Integer.parseInt(version);
- } catch (NumberFormatException e) {
- }
- }
-
- int pkgFlags = 0;
- pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
- new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags);
- String timeStampStr = parser.getAttributeValue(null, "ft");
- if (timeStampStr != null) {
- try {
- long timeStamp = Long.parseLong(timeStampStr, 16);
- ps.setTimeStamp(timeStamp);
- } catch (NumberFormatException e) {
- }
- } else {
- timeStampStr = parser.getAttributeValue(null, "ts");
- if (timeStampStr != null) {
- try {
- long timeStamp = Long.parseLong(timeStampStr);
- ps.setTimeStamp(timeStamp);
- } catch (NumberFormatException e) {
- }
- }
- }
- timeStampStr = parser.getAttributeValue(null, "it");
- if (timeStampStr != null) {
- try {
- ps.firstInstallTime = Long.parseLong(timeStampStr, 16);
- } catch (NumberFormatException e) {
- }
- }
- timeStampStr = parser.getAttributeValue(null, "ut");
- if (timeStampStr != null) {
- try {
- ps.lastUpdateTime = Long.parseLong(timeStampStr, 16);
- } 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) {
+ // Check code path here.
+ if (codePath == null || !codePath.equals(args.getCodePath())) {
+ Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()
+ + " does not match one in settings " + codePath);
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 realName = null;
- String idStr = null;
- String sharedIdStr = null;
- String codePathStr = null;
- String resourcePathStr = null;
- String nativeLibraryPathStr = null;
- String systemStr = null;
- String installerPackageName = null;
- String uidError = null;
- int pkgFlags = 0;
- long timeStamp = 0;
- long firstInstallTime = 0;
- long lastUpdateTime = 0;
- PackageSettingBase packageSetting = null;
- String version = null;
- int versionCode = 0;
- try {
- name = parser.getAttributeValue(null, "name");
- realName = parser.getAttributeValue(null, "realName");
- idStr = parser.getAttributeValue(null, "userId");
- uidError = parser.getAttributeValue(null, "uidError");
- sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
- codePathStr = parser.getAttributeValue(null, "codePath");
- resourcePathStr = parser.getAttributeValue(null, "resourcePath");
- nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
- version = parser.getAttributeValue(null, "version");
- if (version != null) {
- try {
- versionCode = Integer.parseInt(version);
- } catch (NumberFormatException e) {
- }
- }
- installerPackageName = parser.getAttributeValue(null, "installer");
-
- systemStr = parser.getAttributeValue(null, "flags");
- if (systemStr != null) {
- try {
- pkgFlags = Integer.parseInt(systemStr);
- } catch (NumberFormatException e) {
- }
- } else {
- // For backward compatibility
- systemStr = parser.getAttributeValue(null, "system");
- if (systemStr != null) {
- pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM : 0;
- } else {
- // Old settings that don't specify system... just treat
- // them as system, good enough.
- pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- }
- }
- String timeStampStr = parser.getAttributeValue(null, "ft");
- if (timeStampStr != null) {
- try {
- timeStamp = Long.parseLong(timeStampStr, 16);
- } catch (NumberFormatException e) {
- }
- } else {
- timeStampStr = parser.getAttributeValue(null, "ts");
- if (timeStampStr != null) {
- try {
- timeStamp = Long.parseLong(timeStampStr);
- } catch (NumberFormatException e) {
+ // Parse package
+ int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+ doGc = true;
+ synchronized (mInstallLock) {
+ final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
+ 0, 0);
+ // Scan the package
+ if (pkg != null) {
+ /*
+ * TODO why is the lock being held? doPostInstall is
+ * called in other places without the lock. This needs
+ * to be straightened out.
+ */
+ // writer
+ synchronized (mPackages) {
+ retCode = PackageManager.INSTALL_SUCCEEDED;
+ pkgList.add(pkg.packageName);
+ // Post process args
+ args.doPostInstall(PackageManager.INSTALL_SUCCEEDED);
}
- }
- }
- timeStampStr = parser.getAttributeValue(null, "it");
- if (timeStampStr != null) {
- try {
- firstInstallTime = Long.parseLong(timeStampStr, 16);
- } catch (NumberFormatException e) {
- }
- }
- timeStampStr = parser.getAttributeValue(null, "ut");
- if (timeStampStr != null) {
- try {
- lastUpdateTime = Long.parseLong(timeStampStr, 16);
- } 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 (realName != null) {
- realName = realName.intern();
- }
- 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(), realName, new File(codePathStr),
- new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode,
- 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);
- packageSetting.firstInstallTime = firstInstallTime;
- packageSetting.lastUpdateTime = lastUpdateTime;
+ Slog.i(TAG, "Failed to install pkg from " + codePath + " from sdcard");
}
- } else if (sharedIdStr != null) {
- userId = sharedIdStr != null
- ? Integer.parseInt(sharedIdStr) : 0;
- if (userId > 0) {
- packageSetting = new PendingPackage(name.intern(), realName,
- new File(codePathStr), new File(resourcePathStr),
- nativeLibraryPathStr, userId, versionCode, pkgFlags);
- packageSetting.setTimeStamp(timeStamp);
- packageSetting.firstInstallTime = firstInstallTime;
- packageSetting.lastUpdateTime = lastUpdateTime;
- 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) {
- packageSetting.uidError = "true".equals(uidError);
- packageSetting.installerPackageName = installerPackageName;
- packageSetting.nativeLibraryPathString = nativeLibraryPathStr;
- 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.grantedPermissions);
- packageSetting.permissionsFixed = true;
- } else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <package>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
+ } finally {
+ if (retCode != PackageManager.INSTALL_SUCCEEDED) {
+ // Don't destroy container here. Wait till gc clears things
+ // up.
+ removeCids.add(args.cid);
}
- } 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());
- }
+ // writer
+ synchronized (mPackages) {
+ // If the platform SDK has changed since the last time we booted,
+ // we need to re-grant app permission to catch any new ones that
+ // appear. This is really a hack, and means that apps can in some
+ // cases get permissions that the user didn't initially explicitly
+ // allow... it would be nice to have some better way to handle
+ // this situation.
+ final boolean regrantPermissions = mSettings.mExternalSdkPlatform != mSdkVersion;
+ if (regrantPermissions)
+ Slog.i(TAG, "Platform changed from " + mSettings.mExternalSdkPlatform + " to "
+ + mSdkVersion + "; regranting permissions for external storage");
+ mSettings.mExternalSdkPlatform = mSdkVersion;
+
+ // Make sure group IDs have been assigned, and any permission
+ // changes in other apps are accounted for
+ updatePermissionsLPw(null, null, true, regrantPermissions, regrantPermissions);
+ // can downgrade to reader
+ // Persist settings
+ mSettings.writeLPr();
+ }
+ // Send a broadcast to let everyone know we are done processing
+ if (pkgList.size() > 0) {
+ sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
+ }
+ // Force gc to avoid any stale parser references that we might have.
+ if (doGc) {
+ Runtime.getRuntime().gc();
+ }
+ // List stale containers and destroy stale temporary containers.
+ if (removeCids != null) {
+ for (String cid : removeCids) {
+ if (cid.startsWith(mTempContainerPrefix)) {
+ Log.i(TAG, "Destroying stale temporary container " + cid);
+ PackageHelper.destroySdDir(cid);
} else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <disabled-components>: "
- + parser.getName());
- }
- XmlUtils.skipCurrentTag(parser);
- }
+ Log.w(TAG, "Container " + cid + " is stale");
+ }
+ }
}
+ }
- 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;
- }
+ /*
+ * Utility method to unload a list of specified containers
+ */
+ private void unloadAllContainers(Set<SdInstallArgs> cidArgs) {
+ // Just unmount all valid containers.
+ for (SdInstallArgs arg : cidArgs) {
+ synchronized (mInstallLock) {
+ arg.doPostDeleteLI(false);
+ }
+ }
+ }
- 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());
- }
+ /*
+ * Unload packages mounted on external media. This involves deleting package
+ * data from internal structures, sending broadcasts about diabled packages,
+ * gc'ing to free up references, unmounting all secure containers
+ * corresponding to packages on external media, and posting a
+ * UPDATED_MEDIA_STATUS message if status has been requested. Please note
+ * that we always have to post this message if status has been requested no
+ * matter what.
+ */
+ private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[],
+ final boolean reportStatus) {
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "unloading media packages");
+ ArrayList<String> pkgList = new ArrayList<String>();
+ ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
+ final Set<SdInstallArgs> keys = processCids.keySet();
+ for (SdInstallArgs args : keys) {
+ String pkgName = args.getPackageName();
+ if (DEBUG_SD_INSTALL)
+ Log.i(TAG, "Trying to unload pkg : " + pkgName);
+ // Delete package internally
+ PackageRemovedInfo outInfo = new PackageRemovedInfo();
+ synchronized (mInstallLock) {
+ boolean res = deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA,
+ outInfo, false);
+ if (res) {
+ pkgList.add(pkgName);
} else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <enabled-components>: "
- + parser.getName());
+ Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName);
+ failedList.add(args);
}
- 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.grantedPermissions);
- } else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <shared-user>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
+ // reader
+ synchronized (mPackages) {
+ // We didn't update the settings after removing each package;
+ // write them now for all packages.
+ mSettings.writeLPr();
+ }
+
+ // We have to absolutely send UPDATED_MEDIA_STATUS only
+ // after confirming that all the receivers processed the ordered
+ // broadcast when packages get disabled, force a gc to clean things up.
+ // and unload all the containers.
+ if (pkgList.size() > 0) {
+ sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky) throws RemoteException {
+ Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
+ reportStatus ? 1 : 0, 1, keys);
+ mHandler.sendMessage(msg);
}
-
- } else {
- XmlUtils.skipCurrentTag(parser);
- }
+ });
+ } else {
+ Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1 : 0, -1,
+ keys);
+ mHandler.sendMessage(msg);
}
+ }
- 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());
- }
+ public void movePackage(final String packageName, final IPackageMoveObserver observer,
+ final int flags) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
+ int returnCode = PackageManager.MOVE_SUCCEEDED;
+ int currFlags = 0;
+ int newFlags = 0;
+ // reader
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
+ } else {
+ // Disable moving fwd locked apps and system packages
+ if (pkg.applicationInfo != null && isSystemApp(pkg)) {
+ Slog.w(TAG, "Cannot move system application");
+ returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
+ } else if (pkg.applicationInfo != null && isForwardLocked(pkg)) {
+ Slog.w(TAG, "Cannot move forward locked app.");
+ returnCode = PackageManager.MOVE_FAILED_FORWARD_LOCKED;
+ } else if (pkg.mOperationPending) {
+ Slog.w(TAG, "Attempt to move package which has pending operations");
+ returnCode = PackageManager.MOVE_FAILED_OPERATION_PENDING;
} else {
- reportSettingsProblem(Log.WARN,
- "Unknown element under <perms>: "
- + 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.mPref.getParseError() == null) {
- mPreferredActivities.addFilter(pa);
+ // Find install location first
+ if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0
+ && (flags & PackageManager.MOVE_INTERNAL) != 0) {
+ Slog.w(TAG, "Ambigous flags specified for move location.");
+ returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
} else {
- reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <preferred-activity> "
- + pa.mPref.getParseError() + " at "
- + parser.getPositionDescription());
+ newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 ? PackageManager.INSTALL_EXTERNAL
+ : PackageManager.INSTALL_INTERNAL;
+ currFlags = isExternal(pkg) ? PackageManager.INSTALL_EXTERNAL
+ : PackageManager.INSTALL_INTERNAL;
+ if (newFlags == currFlags) {
+ Slog.w(TAG, "No move required. Trying to move to same location");
+ returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
+ }
}
- } 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) {
- if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- return true;
- }
- 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()));
- }
- if (packageSettings == null) {
- if (false) {
- Log.w(TAG, "WAITING FOR DEBUGGER");
- Debug.waitForDebugger();
- Log.i(TAG, "We will crash!");
- }
- return false;
- }
- if (packageSettings.enabled == COMPONENT_ENABLED_STATE_DISABLED
- || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
- && packageSettings.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
- return false;
- }
- if (packageSettings.enabledComponents.contains(componentInfo.name)) {
- return true;
- }
- if (packageSettings.disabledComponents.contains(componentInfo.name)) {
- return false;
- }
- return componentInfo.enabled;
- }
- }
-
- // ------- apps on sdcard specific code -------
- static final boolean DEBUG_SD_INSTALL = false;
- private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD";
- private static final String SD_ENCRYPTION_ALGORITHM = "AES";
- static final int MAX_CONTAINERS = 250;
- private boolean mMediaMounted = false;
-
- private String getEncryptKey() {
- try {
- String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
- SD_ENCRYPTION_KEYSTORE_NAME);
- if (sdEncKey == null) {
- sdEncKey = SystemKeyStore.getInstance().generateNewKeyHexString(128,
- SD_ENCRYPTION_ALGORITHM, SD_ENCRYPTION_KEYSTORE_NAME);
- if (sdEncKey == null) {
- Slog.e(TAG, "Failed to create encryption keys");
- return null;
- }
- }
- return sdEncKey;
- } catch (NoSuchAlgorithmException nsae) {
- Slog.e(TAG, "Failed to create encryption keys with exception: " + nsae);
- return null;
- } catch (IOException ioe) {
- Slog.e(TAG, "Failed to retrieve encryption keys with exception: "
- + ioe);
- return null;
- }
-
- }
-
- /* package */ static String getTempContainerId() {
- int tmpIdx = 1;
- String list[] = PackageHelper.getSecureContainerList();
- if (list != null) {
- for (final String name : list) {
- // Ignore null and non-temporary container entries
- if (name == null || !name.startsWith(mTempContainerPrefix)) {
- continue;
- }
-
- String subStr = name.substring(mTempContainerPrefix.length());
- try {
- int cid = Integer.parseInt(subStr);
- if (cid >= tmpIdx) {
- tmpIdx = cid + 1;
+ if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+ pkg.mOperationPending = true;
}
- } catch (NumberFormatException e) {
}
}
- }
- return mTempContainerPrefix + tmpIdx;
- }
-
- /*
- * Update media status on PackageManager.
- */
- public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
- int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
- throw new SecurityException("Media status can only be updated by the system");
- }
- synchronized (mPackages) {
- Log.i(TAG, "Updating external media status from " +
- (mMediaMounted ? "mounted" : "unmounted") + " to " +
- (mediaStatus ? "mounted" : "unmounted"));
- if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
- mediaStatus+", mMediaMounted=" + mMediaMounted);
- if (mediaStatus == mMediaMounted) {
- Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
- reportStatus ? 1 : 0, -1);
- mHandler.sendMessage(msg);
- return;
- }
- mMediaMounted = mediaStatus;
- }
- // Queue up an async operation since the package installation may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- mHandler.removeCallbacks(this);
- updateExternalMediaStatusInner(mediaStatus, reportStatus);
- }
- });
- }
-
- /*
- * Collect information of applications on external media, map them
- * against existing containers and update information based on current
- * mount status. Please note that we always have to report status
- * if reportStatus has been set to true especially when unloading packages.
- */
- private void updateExternalMediaStatusInner(boolean mediaStatus,
- boolean reportStatus) {
- // Collection of uids
- int uidArr[] = null;
- // Collection of stale containers
- HashSet<String> removeCids = new HashSet<String>();
- // Collection of packages on external media with valid containers.
- HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
- // Get list of secure containers.
- final String list[] = PackageHelper.getSecureContainerList();
- if (list == null || list.length == 0) {
- Log.i(TAG, "No secure containers on sdcard");
- } else {
- // Process list of secure containers and categorize them
- // as active or stale based on their package internal state.
- int uidList[] = new int[list.length];
- int num = 0;
- synchronized (mPackages) {
- for (String cid : list) {
- SdInstallArgs args = new SdInstallArgs(cid);
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Processing container " + cid);
- String pkgName = args.getPackageName();
- if (pkgName == null) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale");
- removeCids.add(cid);
- continue;
- }
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Looking for pkg : " + pkgName);
- PackageSetting ps = mSettings.mPackages.get(pkgName);
- // The package status is changed only if the code path
- // matches between settings and the container id.
- if (ps != null && ps.codePathString != null &&
- ps.codePathString.equals(args.getCodePath())) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid +
- " corresponds to pkg : " + pkgName +
- " at code path: " + ps.codePathString);
- // We do have a valid package installed on sdcard
- processCids.put(args, ps.codePathString);
- int uid = ps.userId;
- if (uid != -1) {
- uidList[num++] = uid;
- }
- } else {
- // Stale container on sdcard. Just delete
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale");
- removeCids.add(cid);
- }
- }
- }
-
- if (num > 0) {
- // Sort uid list
- Arrays.sort(uidList, 0, num);
- // Throw away duplicates
- uidArr = new int[num];
- uidArr[0] = uidList[0];
- int di = 0;
- for (int i = 1; i < num; i++) {
- if (uidList[i-1] != uidList[i]) {
- uidArr[di++] = uidList[i];
- }
- }
- }
- }
- // Process packages with valid entries.
- if (mediaStatus) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr, removeCids);
- startCleaningPackages();
- } else {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
- unloadMediaPackages(processCids, uidArr, reportStatus);
- }
- }
-
- private void sendResourcesChangedBroadcast(boolean mediaStatus,
- ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
- int size = pkgList.size();
- if (size > 0) {
- // Send broadcasts here
- Bundle extras = new Bundle();
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
- pkgList.toArray(new String[size]));
- if (uidArr != null) {
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
- }
- String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
- : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver);
- }
- }
-
- /*
- * Look at potentially valid container ids from processCids
- * If package information doesn't match the one on record
- * or package scanning fails, the cid is added to list of
- * removeCids. We currently don't delete stale containers.
- */
- private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids,
- int uidArr[], HashSet<String> removeCids) {
- ArrayList<String> pkgList = new ArrayList<String>();
- Set<SdInstallArgs> keys = processCids.keySet();
- boolean doGc = false;
- for (SdInstallArgs args : keys) {
- String codePath = processCids.get(args);
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading container : "
- + args.cid);
- int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
- try {
- // Make sure there are no container errors first.
- if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED)
- != PackageManager.INSTALL_SUCCEEDED) {
- Slog.e(TAG, "Failed to mount cid : " + args.cid +
- " when installing from sdcard");
- continue;
- }
- // Check code path here.
- if (codePath == null || !codePath.equals(args.getCodePath())) {
- Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()+
- " does not match one in settings " + codePath);
- continue;
- }
- // Parse package
- int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
- doGc = true;
- synchronized (mInstallLock) {
- final PackageParser.Package pkg = scanPackageLI(new File(codePath),
- parseFlags, 0, 0);
- // Scan the package
- if (pkg != null) {
- synchronized (mPackages) {
- retCode = PackageManager.INSTALL_SUCCEEDED;
- pkgList.add(pkg.packageName);
- // Post process args
- args.doPostInstall(PackageManager.INSTALL_SUCCEEDED);
- }
- } else {
- Slog.i(TAG, "Failed to install pkg from " +
- codePath + " from sdcard");
- }
- }
- } finally {
- if (retCode != PackageManager.INSTALL_SUCCEEDED) {
- // Don't destroy container here. Wait till gc clears things up.
- removeCids.add(args.cid);
- }
- }
- }
- synchronized (mPackages) {
- // If the platform SDK has changed since the last time we booted,
- // we need to re-grant app permission to catch any new ones that
- // appear. This is really a hack, and means that apps can in some
- // cases get permissions that the user didn't initially explicitly
- // allow... it would be nice to have some better way to handle
- // this situation.
- final boolean regrantPermissions = mSettings.mExternalSdkPlatform
- != mSdkVersion;
- if (regrantPermissions) Slog.i(TAG, "Platform changed from "
- + mSettings.mExternalSdkPlatform + " to " + mSdkVersion
- + "; regranting permissions for external storage");
- mSettings.mExternalSdkPlatform = mSdkVersion;
-
- // Make sure group IDs have been assigned, and any permission
- // changes in other apps are accounted for
- updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions);
- // Persist settings
- mSettings.writeLP();
- }
- // Send a broadcast to let everyone know we are done processing
- if (pkgList.size() > 0) {
- sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
- }
- // Force gc to avoid any stale parser references that we might have.
- if (doGc) {
- Runtime.getRuntime().gc();
- }
- // List stale containers and destroy stale temporary containers.
- if (removeCids != null) {
- for (String cid : removeCids) {
- if (cid.startsWith(mTempContainerPrefix)) {
- Log.i(TAG, "Destroying stale temporary container " + cid);
- PackageHelper.destroySdDir(cid);
- } else {
- Log.w(TAG, "Container " + cid + " is stale");
- }
- }
- }
- }
-
- /*
- * Utility method to unload a list of specified containers
- */
- private void unloadAllContainers(Set<SdInstallArgs> cidArgs) {
- // Just unmount all valid containers.
- for (SdInstallArgs arg : cidArgs) {
- synchronized (mInstallLock) {
- arg.doPostDeleteLI(false);
- }
- }
- }
-
- /*
- * Unload packages mounted on external media. This involves deleting
- * package data from internal structures, sending broadcasts about
- * diabled packages, gc'ing to free up references, unmounting all
- * secure containers corresponding to packages on external media, and
- * posting a UPDATED_MEDIA_STATUS message if status has been requested.
- * Please note that we always have to post this message if status has
- * been requested no matter what.
- */
- private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids,
- int uidArr[], final boolean reportStatus) {
- if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
- ArrayList<String> pkgList = new ArrayList<String>();
- ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
- final Set<SdInstallArgs> keys = processCids.keySet();
- for (SdInstallArgs args : keys) {
- String cid = args.cid;
- String pkgName = args.getPackageName();
- if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to unload pkg : " + pkgName);
- // Delete package internally
- PackageRemovedInfo outInfo = new PackageRemovedInfo();
- synchronized (mInstallLock) {
- boolean res = deletePackageLI(pkgName, false,
- PackageManager.DONT_DELETE_DATA, outInfo, false);
- if (res) {
- pkgList.add(pkgName);
- } else {
- Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName);
- failedList.add(args);
- }
- }
- }
-
- synchronized (mPackages) {
- // We didn't update the settings after removing each package;
- // write them now for all packages.
- mSettings.writeLP();
- }
-
- // We have to absolutely send UPDATED_MEDIA_STATUS only
- // after confirming that all the receivers processed the ordered
- // broadcast when packages get disabled, force a gc to clean things up.
- // and unload all the containers.
- if (pkgList.size() > 0) {
- sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
- public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky) throws RemoteException {
- Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
- reportStatus ? 1 : 0, 1, keys);
- mHandler.sendMessage(msg);
- }
- });
- } else {
- Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
- reportStatus ? 1 : 0, -1, keys);
- mHandler.sendMessage(msg);
- }
- }
-
- public void movePackage(final String packageName,
- final IPackageMoveObserver observer, final int flags) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MOVE_PACKAGE, null);
- int returnCode = PackageManager.MOVE_SUCCEEDED;
- int currFlags = 0;
- int newFlags = 0;
- synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg == null) {
- returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
- } else {
- // Disable moving fwd locked apps and system packages
- if (pkg.applicationInfo != null && isSystemApp(pkg)) {
- Slog.w(TAG, "Cannot move system application");
- returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
- } else if (pkg.applicationInfo != null && isForwardLocked(pkg)) {
- Slog.w(TAG, "Cannot move forward locked app.");
- returnCode = PackageManager.MOVE_FAILED_FORWARD_LOCKED;
- } else if (pkg.mOperationPending) {
- Slog.w(TAG, "Attempt to move package which has pending operations");
- returnCode = PackageManager.MOVE_FAILED_OPERATION_PENDING;
- } else {
- // Find install location first
- if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 &&
- (flags & PackageManager.MOVE_INTERNAL) != 0) {
- Slog.w(TAG, "Ambigous flags specified for move location.");
- returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
- } else {
- newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 ?
- PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL;
- currFlags = isExternal(pkg) ? PackageManager.INSTALL_EXTERNAL
- : PackageManager.INSTALL_INTERNAL;
- if (newFlags == currFlags) {
- Slog.w(TAG, "No move required. Trying to move to same location");
- returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
- }
- }
- if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- pkg.mOperationPending = true;
- }
- }
- }
- if (returnCode != PackageManager.MOVE_SUCCEEDED) {
+ /*
+ * TODO this next block probably shouldn't be inside the lock. We
+ * can't guarantee these won't change after this is fired off
+ * anyway.
+ */
+ if (returnCode != PackageManager.MOVE_SUCCEEDED) {
processPendingMove(new MoveParams(null, observer, 0, packageName, null), returnCode);
- } else {
- Message msg = mHandler.obtainMessage(INIT_COPY);
- InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
+ } else {
+ Message msg = mHandler.obtainMessage(INIT_COPY);
+ InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir);
- MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
+ MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
pkg.applicationInfo.dataDir);
- msg.obj = mp;
- mHandler.sendMessage(msg);
- }
- }
- }
+ msg.obj = mp;
+ mHandler.sendMessage(msg);
+ }
+ }
+ }
- private void processPendingMove(final MoveParams mp, final int currentStatus) {
- // Queue up an async operation since the package deletion may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- mHandler.removeCallbacks(this);
- int returnCode = currentStatus;
- if (currentStatus == PackageManager.MOVE_SUCCEEDED) {
- int uidArr[] = null;
- ArrayList<String> pkgList = null;
- synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(mp.packageName);
- if (pkg == null) {
- Slog.w(TAG, " Package " + mp.packageName +
- " doesn't exist. Aborting move");
- returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
- } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) {
- Slog.w(TAG, "Package " + mp.packageName + " code path changed from " +
- mp.srcArgs.getCodePath() + " to " + pkg.applicationInfo.sourceDir +
- " Aborting move and returning error");
- returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
- } else {
- uidArr = new int[] { pkg.applicationInfo.uid };
- pkgList = new ArrayList<String>();
- pkgList.add(mp.packageName);
- }
- }
- if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- // Send resources unavailable broadcast
- sendResourcesChangedBroadcast(false, pkgList, uidArr, null);
- // Update package code and resource paths
- synchronized (mInstallLock) {
- synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(mp.packageName);
- // Recheck for package again.
+ private void processPendingMove(final MoveParams mp, final int currentStatus) {
+ // Queue up an async operation since the package deletion may take a
+ // little while.
+ mHandler.post(new Runnable() {
+ public void run() {
+ // TODO fix this; this does nothing.
+ mHandler.removeCallbacks(this);
+ int returnCode = currentStatus;
+ if (currentStatus == PackageManager.MOVE_SUCCEEDED) {
+ int uidArr[] = null;
+ ArrayList<String> pkgList = null;
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(mp.packageName);
+ if (pkg == null) {
+ Slog.w(TAG, " Package " + mp.packageName
+ + " doesn't exist. Aborting move");
+ returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
+ } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) {
+ Slog.w(TAG, "Package " + mp.packageName + " code path changed from "
+ + mp.srcArgs.getCodePath() + " to "
+ + pkg.applicationInfo.sourceDir
+ + " Aborting move and returning error");
+ returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
+ } else {
+ uidArr = new int[] {
+ pkg.applicationInfo.uid
+ };
+ pkgList = new ArrayList<String>();
+ pkgList.add(mp.packageName);
+ }
+ }
+ if (returnCode == PackageManager.MOVE_SUCCEEDED) {
+ // Send resources unavailable broadcast
+ sendResourcesChangedBroadcast(false, pkgList, uidArr, null);
+ // Update package code and resource paths
+ synchronized (mInstallLock) {
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(mp.packageName);
+ // Recheck for package again.
if (pkg == null) {
Slog.w(TAG, " Package " + mp.packageName
+ " doesn't exist. Aborting move");
returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
- } else if (!mp.srcArgs.getCodePath().equals(pkg.applicationInfo.sourceDir)) {
- Slog.w(TAG, "Package " + mp.packageName + " code path changed from " +
- mp.srcArgs.getCodePath() + " to " + pkg.applicationInfo.sourceDir +
- " Aborting move and returning error");
- returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
- } else {
- final String oldCodePath = pkg.mPath;
- final String newCodePath = mp.targetArgs.getCodePath();
- final String newResPath = mp.targetArgs.getResourcePath();
- final String newNativePath = mp.targetArgs.getNativeLibraryPath();
+ } else if (!mp.srcArgs.getCodePath().equals(
+ pkg.applicationInfo.sourceDir)) {
+ Slog.w(TAG, "Package " + mp.packageName
+ + " code path changed from " + mp.srcArgs.getCodePath()
+ + " to " + pkg.applicationInfo.sourceDir
+ + " Aborting move and returning error");
+ returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
+ } else {
+ final String oldCodePath = pkg.mPath;
+ final String newCodePath = mp.targetArgs.getCodePath();
+ final String newResPath = mp.targetArgs.getResourcePath();
+ final String newNativePath = mp.targetArgs
+ .getNativeLibraryPath();
if ((mp.flags & PackageManager.INSTALL_EXTERNAL) == 0) {
if (mInstaller
.unlinkNativeLibraryDirectory(pkg.applicationInfo.dataDir) < 0) {
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
} else {
- NativeLibraryHelper.copyNativeBinariesLI(
- new File(newCodePath), new File(newNativePath));
+ NativeLibraryHelper.copyNativeBinariesLI(new File(
+ newCodePath), new File(newNativePath));
}
} else {
if (mInstaller.linkNativeLibraryDirectory(
@@ -10492,90 +8012,106 @@ class PackageManagerService extends IPackageManager.Stub {
}
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- pkg.mScanPath = newCodePath;
- pkg.applicationInfo.sourceDir = newCodePath;
- pkg.applicationInfo.publicSourceDir = newResPath;
- pkg.applicationInfo.nativeLibraryDir = newNativePath;
- PackageSetting ps = (PackageSetting) pkg.mExtras;
- ps.codePath = new File(pkg.applicationInfo.sourceDir);
- ps.codePathString = ps.codePath.getPath();
- ps.resourcePath = new File(pkg.applicationInfo.publicSourceDir);
- ps.resourcePathString = ps.resourcePath.getPath();
- ps.nativeLibraryPathString = newNativePath;
- // Set the application info flag correctly.
- if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
- } else {
- pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
- }
- ps.setFlags(pkg.applicationInfo.flags);
- mAppDirs.remove(oldCodePath);
- mAppDirs.put(newCodePath, pkg);
- // Persist settings
- mSettings.writeLP();
- }
- }
- }
- }
- // Send resources available broadcast
- sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
- }
- }
- if (returnCode != PackageManager.MOVE_SUCCEEDED){
- // Clean up failed installation
- if (mp.targetArgs != null) {
- mp.targetArgs.doPostInstall(PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
- }
- } else {
- // Force a gc to clear things up.
- Runtime.getRuntime().gc();
- // Delete older code
- synchronized (mInstallLock) {
- mp.srcArgs.doPostDeleteLI(true);
- }
- }
+ pkg.mScanPath = newCodePath;
+ pkg.applicationInfo.sourceDir = newCodePath;
+ pkg.applicationInfo.publicSourceDir = newResPath;
+ pkg.applicationInfo.nativeLibraryDir = newNativePath;
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ ps.codePath = new File(pkg.applicationInfo.sourceDir);
+ ps.codePathString = ps.codePath.getPath();
+ ps.resourcePath = new File(
+ pkg.applicationInfo.publicSourceDir);
+ ps.resourcePathString = ps.resourcePath.getPath();
+ ps.nativeLibraryPathString = newNativePath;
+ // Set the application info flag
+ // correctly.
+ if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+ } else {
+ pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+ }
+ ps.setFlags(pkg.applicationInfo.flags);
+ mAppDirs.remove(oldCodePath);
+ mAppDirs.put(newCodePath, pkg);
+ // Persist settings
+ mSettings.writeLPr();
+ }
+ }
+ }
+ }
+ // Send resources available broadcast
+ sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
+ }
+ }
+ if (returnCode != PackageManager.MOVE_SUCCEEDED) {
+ // Clean up failed installation
+ if (mp.targetArgs != null) {
+ mp.targetArgs.doPostInstall(PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+ }
+ } else {
+ // Force a gc to clear things up.
+ Runtime.getRuntime().gc();
+ // Delete older code
+ synchronized (mInstallLock) {
+ mp.srcArgs.doPostDeleteLI(true);
+ }
+ }
- // Allow more operations on this file if we didn't fail because
- // an operation was already pending for this package.
- if (returnCode != PackageManager.MOVE_FAILED_OPERATION_PENDING) {
- synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(mp.packageName);
- if (pkg != null) {
- pkg.mOperationPending = false;
+ // Allow more operations on this file if we didn't fail because
+ // an operation was already pending for this package.
+ if (returnCode != PackageManager.MOVE_FAILED_OPERATION_PENDING) {
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(mp.packageName);
+ if (pkg != null) {
+ pkg.mOperationPending = false;
}
}
- }
+ }
- IPackageMoveObserver observer = mp.observer;
- if (observer != null) {
- try {
- observer.packageMoved(mp.packageName, returnCode);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- }
- }
- }
- });
- }
+ IPackageMoveObserver observer = mp.observer;
+ if (observer != null) {
+ try {
+ observer.packageMoved(mp.packageName, returnCode);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
+ }
+ }
+ }
+ });
+ }
- public boolean setInstallLocation(int loc) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS, null);
- if (getInstallLocation() == loc) {
- return true;
- }
- if (loc == PackageHelper.APP_INSTALL_AUTO ||
- loc == PackageHelper.APP_INSTALL_INTERNAL ||
- loc == PackageHelper.APP_INSTALL_EXTERNAL) {
- android.provider.Settings.System.putInt(mContext.getContentResolver(),
- android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, loc);
- return true;
- }
- return false;
+ public boolean setInstallLocation(int loc) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
+ null);
+ if (getInstallLocation() == loc) {
+ return true;
+ }
+ if (loc == PackageHelper.APP_INSTALL_AUTO || loc == PackageHelper.APP_INSTALL_INTERNAL
+ || loc == PackageHelper.APP_INSTALL_EXTERNAL) {
+ android.provider.Settings.System.putInt(mContext.getContentResolver(),
+ android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, loc);
+ return true;
+ }
+ return false;
}
- public int getInstallLocation() {
- return android.provider.Settings.System.getInt(mContext.getContentResolver(),
- android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION, PackageHelper.APP_INSTALL_AUTO);
- }
+ public int getInstallLocation() {
+ return android.provider.Settings.System.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION,
+ PackageHelper.APP_INSTALL_AUTO);
+ }
+
+ public UserInfo createUser(String name, int flags) {
+ // TODO(kroot): fix this API
+ UserInfo userInfo = mUserManager.createUser(name, flags, new ArrayList<ApplicationInfo>());
+ return userInfo;
+ }
+
+ public boolean removeUser(int userId) {
+ if (userId == 0) {
+ return false;
+ }
+ mUserManager.removeUser(userId);
+ return true;
+ }
}
diff --git a/services/java/com/android/server/pm/PackageSetting.java b/services/java/com/android/server/pm/PackageSetting.java
new file mode 100644
index 0000000..efdc2b3
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageSetting.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 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.pm;
+
+import android.content.pm.PackageParser;
+
+import java.io.File;
+
+/**
+ * Settings data for a particular package we know about.
+ */
+final class PackageSetting extends PackageSettingBase {
+ int userId;
+ PackageParser.Package pkg;
+ SharedUserSetting sharedUser;
+
+ PackageSetting(String name, String realName, File codePath, File resourcePath,
+ String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
+ super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
+ pkgFlags);
+ }
+
+ /**
+ * New instance of PackageSetting replicating the original settings.
+ * Note that it keeps the same PackageParser.Package instance.
+ */
+ PackageSetting(PackageSetting orig) {
+ super(orig);
+
+ userId = orig.userId;
+ pkg = orig.pkg;
+ sharedUser = orig.sharedUser;
+ }
+
+ @Override
+ public String toString() {
+ return "PackageSetting{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + name + "/" + userId + "}";
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
new file mode 100644
index 0000000..e2f83ad
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2011 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.pm;
+
+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 java.io.File;
+import java.util.HashSet;
+
+/**
+ * Settings base class for pending and resolved classes.
+ */
+class PackageSettingBase extends GrantedPermissions {
+ /**
+ * Indicates the state of installation. Used by PackageManager to figure out
+ * incomplete installations. Say a package is being installed (the state is
+ * set to PKG_INSTALL_INCOMPLETE) and remains so till the package
+ * installation is successful or unsuccessful in which case the
+ * PackageManager will no longer maintain state information associated with
+ * the package. If some exception(like device freeze or battery being pulled
+ * out) occurs during installation of a package, the PackageManager needs
+ * this information to clean up the previously failed installation.
+ */
+ static final int PKG_INSTALL_COMPLETE = 1;
+ static final int PKG_INSTALL_INCOMPLETE = 0;
+
+ final String name;
+ final String realName;
+ File codePath;
+ String codePathString;
+ File resourcePath;
+ String resourcePathString;
+ String nativeLibraryPathString;
+ long timeStamp;
+ long firstInstallTime;
+ long lastUpdateTime;
+ int versionCode;
+
+ boolean uidError;
+
+ PackageSignatures signatures = new PackageSignatures();
+
+ boolean permissionsFixed;
+ boolean haveGids;
+
+ // Whether this package is currently stopped, thus can not be
+ // started until explicitly launched by the user.
+ public boolean stopped;
+
+ // Set to true if we have never launched this app.
+ public boolean notLaunched;
+
+ /* 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 origPackage;
+
+ /* package name of the app that installed this package */
+ String installerPackageName;
+ PackageSettingBase(String name, String realName, File codePath, File resourcePath,
+ String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
+ super(pkgFlags);
+ this.name = name;
+ this.realName = realName;
+ init(codePath, resourcePath, nativeLibraryPathString, pVersionCode);
+ }
+
+ /**
+ * New instance of PackageSetting with one-level-deep cloning.
+ */
+ @SuppressWarnings("unchecked")
+ PackageSettingBase(PackageSettingBase base) {
+ super(base);
+
+ name = base.name;
+ realName = base.realName;
+ codePath = base.codePath;
+ codePathString = base.codePathString;
+ resourcePath = base.resourcePath;
+ resourcePathString = base.resourcePathString;
+ nativeLibraryPathString = base.nativeLibraryPathString;
+ timeStamp = base.timeStamp;
+ firstInstallTime = base.firstInstallTime;
+ lastUpdateTime = base.lastUpdateTime;
+ versionCode = base.versionCode;
+
+ uidError = base.uidError;
+
+ signatures = new PackageSignatures(base.signatures);
+
+ permissionsFixed = base.permissionsFixed;
+ haveGids = base.haveGids;
+ stopped = base.stopped;
+ notLaunched = base.notLaunched;
+
+ disabledComponents = (HashSet<String>) base.disabledComponents.clone();
+
+ enabledComponents = (HashSet<String>) base.enabledComponents.clone();
+
+ enabled = base.enabled;
+ installStatus = base.installStatus;
+
+ origPackage = base.origPackage;
+
+ installerPackageName = base.installerPackageName;
+ }
+
+ void init(File codePath, File resourcePath, String nativeLibraryPathString,
+ int pVersionCode) {
+ this.codePath = codePath;
+ this.codePathString = codePath.toString();
+ this.resourcePath = resourcePath;
+ this.resourcePathString = resourcePath.toString();
+ this.nativeLibraryPathString = nativeLibraryPathString;
+ this.versionCode = pVersionCode;
+ }
+
+ public void setInstallerPackageName(String packageName) {
+ installerPackageName = packageName;
+ }
+
+ String getInstallerPackageName() {
+ return installerPackageName;
+ }
+
+ public void setInstallStatus(int newStatus) {
+ installStatus = newStatus;
+ }
+
+ public int getInstallStatus() {
+ return installStatus;
+ }
+
+ public void setTimeStamp(long newStamp) {
+ timeStamp = newStamp;
+ }
+
+ /**
+ * Make a shallow copy of this package settings.
+ */
+ public void copyFrom(PackageSettingBase base) {
+ grantedPermissions = base.grantedPermissions;
+ gids = base.gids;
+
+ timeStamp = base.timeStamp;
+ firstInstallTime = base.firstInstallTime;
+ lastUpdateTime = base.lastUpdateTime;
+ signatures = base.signatures;
+ permissionsFixed = base.permissionsFixed;
+ haveGids = base.haveGids;
+ stopped = base.stopped;
+ notLaunched = base.notLaunched;
+ disabledComponents = base.disabledComponents;
+ enabledComponents = base.enabledComponents;
+ enabled = base.enabled;
+ installStatus = base.installStatus;
+ }
+
+ boolean enableComponentLPw(String componentClassName) {
+ boolean changed = disabledComponents.remove(componentClassName);
+ changed |= enabledComponents.add(componentClassName);
+ return changed;
+ }
+
+ boolean disableComponentLPw(String componentClassName) {
+ boolean changed = enabledComponents.remove(componentClassName);
+ changed |= disabledComponents.add(componentClassName);
+ return changed;
+ }
+
+ boolean restoreComponentLPw(String componentClassName) {
+ boolean changed = enabledComponents.remove(componentClassName);
+ changed |= disabledComponents.remove(componentClassName);
+ return changed;
+ }
+
+ int getCurrentEnabledStateLPr(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;
+ }
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/pm/PackageSignatures.java b/services/java/com/android/server/pm/PackageSignatures.java
new file mode 100644
index 0000000..a25ec6c
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageSignatures.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2011 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.pm;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.content.pm.Signature;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+class PackageSignatures {
+ Signature[] mSignatures;
+
+ PackageSignatures(PackageSignatures orig) {
+ if (orig != null && orig.mSignatures != null) {
+ mSignatures = orig.mSignatures.clone();
+ }
+ }
+
+ 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) {
+ PackageManagerService.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 {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <cert> "
+ + "index " + index + " is not defined at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.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) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <cert> "
+ + "index " + index + " is not a number at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <cert> has"
+ + " no index at " + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: too "
+ + "many <cert> tags, expected " + count
+ + " at " + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.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;
+ }
+ }
+
+ 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();
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/pm/PendingPackage.java b/services/java/com/android/server/pm/PendingPackage.java
new file mode 100644
index 0000000..c17cc46
--- /dev/null
+++ b/services/java/com/android/server/pm/PendingPackage.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 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.pm;
+
+import java.io.File;
+
+final class PendingPackage extends PackageSettingBase {
+ final int sharedId;
+
+ PendingPackage(String name, String realName, File codePath, File resourcePath,
+ String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) {
+ super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
+ pkgFlags);
+ this.sharedId = sharedId;
+ }
+}
diff --git a/services/java/com/android/server/pm/PreferredActivity.java b/services/java/com/android/server/pm/PreferredActivity.java
new file mode 100644
index 0000000..b100eb1
--- /dev/null
+++ b/services/java/com/android/server/pm/PreferredActivity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 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.pm;
+
+import com.android.internal.util.XmlUtils;
+import com.android.server.PreferredComponent;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.content.ComponentName;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import java.io.IOException;
+
+class PreferredActivity extends IntentFilter implements PreferredComponent.Callbacks {
+ private static final String TAG = "PreferredActivity";
+
+ private static final boolean DEBUG_FILTERS = false;
+
+ final PreferredComponent mPref;
+
+ PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) {
+ super(filter);
+ mPref = new PreferredComponent(this, match, set, activity);
+ }
+
+ PreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException {
+ mPref = new PreferredComponent(this, parser);
+ }
+
+ public void writeToXml(XmlSerializer serializer) throws IOException {
+ mPref.writeToXml(serializer);
+ serializer.startTag(null, "filter");
+ super.writeToXml(serializer);
+ serializer.endTag(null, "filter");
+ }
+
+ public boolean onReadTag(String tagName, XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ if (tagName.equals("filter")) {
+ if (DEBUG_FILTERS) {
+ Log.i(TAG, "Starting to parse filter...");
+ }
+ readFromXml(parser);
+ if (DEBUG_FILTERS) {
+ Log.i(TAG, "Finished filter: depth=" + parser.getDepth() + " tag="
+ + parser.getName());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <preferred-activities>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ return true;
+ }
+}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
new file mode 100644
index 0000000..2720bf8
--- /dev/null
+++ b/services/java/com/android/server/pm/Settings.java
@@ -0,0 +1,2230 @@
+/*
+ * Copyright (C) 2011 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.pm;
+
+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 com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.JournaledFile;
+import com.android.internal.util.XmlUtils;
+import com.android.server.IntentResolver;
+import com.android.server.pm.PackageManagerService.DumpState;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PermissionInfo;
+import android.content.pm.Signature;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Process;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * Holds information about dynamic settings.
+ */
+final class Settings {
+ private static final String TAG = "PackageSettings";
+
+ private static final boolean DEBUG_STOPPED = false;
+
+ private final File mSettingsFilename;
+ private final File mBackupSettingsFilename;
+ private final File mPackageListFilename;
+ private final File mStoppedPackagesFilename;
+ private final File mBackupStoppedPackagesFilename;
+ final HashMap<String, PackageSetting> mPackages =
+ new HashMap<String, PackageSetting>();
+ // List of replaced system applications
+ final HashMap<String, PackageSetting> mDisabledSysPackages =
+ new HashMap<String, PackageSetting>();
+
+ // These are the last platform API version we were using for
+ // the apps installed on internal and external storage. It is
+ // used to grant newer permissions one time during a system upgrade.
+ int mInternalSdkPlatform;
+ int mExternalSdkPlatform;
+
+ // The user's preferred activities associated with particular intent
+ // filters.
+ final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities =
+ new IntentResolver<PreferredActivity, PreferredActivity>() {
+ @Override
+ protected String packageForFilter(PreferredActivity filter) {
+ return filter.mPref.mComponent.getPackageName();
+ }
+ @Override
+ protected void dumpFilter(PrintWriter out, String prefix,
+ PreferredActivity filter) {
+ filter.mPref.dump(out, prefix, filter);
+ }
+ };
+ 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>();
+
+ // Packages that have been uninstalled and still need their external
+ // storage data deleted.
+ final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
+
+ // Packages that have been renamed since they were first installed.
+ // Keys are the new names of the packages, values are the original
+ // names. The packages appear everwhere else under their original
+ // names.
+ final HashMap<String, String> mRenamedPackages = new HashMap<String, String>();
+
+ final StringBuilder mReadMessages = new StringBuilder();
+
+ /**
+ * Used to track packages that have a shared user ID that hasn't been read
+ * in yet.
+ * <p>
+ * TODO: make this just a local variable that is passed in during package
+ * scanning to make it less confusing.
+ */
+ 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");
+ mPackageListFilename = new File(systemDir, "packages.list");
+ mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml");
+ mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml");
+ }
+
+ PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
+ String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
+ String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
+ final String name = pkg.packageName;
+ PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
+ resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
+ return p;
+ }
+
+ PackageSetting peekPackageLPr(String name) {
+ return mPackages.get(name);
+ }
+
+ void setInstallStatus(String pkgName, int status) {
+ PackageSetting p = mPackages.get(pkgName);
+ if(p != null) {
+ if(p.getInstallStatus() != status) {
+ p.setInstallStatus(status);
+ }
+ }
+ }
+
+ void setInstallerPackageName(String pkgName,
+ String installerPkgName) {
+ PackageSetting p = mPackages.get(pkgName);
+ if(p != null) {
+ p.setInstallerPackageName(installerPkgName);
+ }
+ }
+
+ SharedUserSetting getSharedUserLPw(String name,
+ int pkgFlags, boolean create) {
+ SharedUserSetting s = mSharedUsers.get(name);
+ if (s == null) {
+ if (!create) {
+ return null;
+ }
+ s = new SharedUserSetting(name, pkgFlags);
+ if (PackageManagerService.MULTIPLE_APPLICATION_UIDS) {
+ s.userId = newUserIdLPw(s);
+ } else {
+ s.userId = PackageManagerService.FIRST_APPLICATION_UID;
+ }
+ Log.i(PackageManagerService.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;
+ }
+
+ boolean disableSystemPackageLPw(String name) {
+ final PackageSetting p = mPackages.get(name);
+ if(p == null) {
+ Log.w(PackageManagerService.TAG, "Package:"+name+" is not an installed package");
+ return false;
+ }
+ final 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);
+
+ // a little trick... when we install the new package, we don't
+ // want to modify the existing PackageSetting for the built-in
+ // version. so at this point we need a new PackageSetting that
+ // is okay to muck with.
+ PackageSetting newp = new PackageSetting(p);
+ replacePackageLPw(name, newp);
+ return true;
+ }
+ return false;
+ }
+
+ PackageSetting enableSystemPackageLPw(String name) {
+ PackageSetting p = mDisabledSysPackages.get(name);
+ if(p == null) {
+ Log.w(PackageManagerService.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 = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
+ p.nativeLibraryPathString, p.userId, p.versionCode, p.pkgFlags);
+ mDisabledSysPackages.remove(name);
+ return ret;
+ }
+
+ PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
+ String nativeLibraryPathString, int uid, int vc, int pkgFlags) {
+ PackageSetting p = mPackages.get(name);
+ if (p != null) {
+ if (p.userId == uid) {
+ return p;
+ }
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Adding duplicate package, keeping first: " + name);
+ return null;
+ }
+ p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
+ vc, pkgFlags);
+ p.userId = uid;
+ if (addUserIdLPw(uid, p, name)) {
+ mPackages.put(name, p);
+ return p;
+ }
+ return null;
+ }
+
+ SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {
+ SharedUserSetting s = mSharedUsers.get(name);
+ if (s != null) {
+ if (s.userId == uid) {
+ return s;
+ }
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Adding duplicate shared user, keeping first: " + name);
+ return null;
+ }
+ s = new SharedUserSetting(name, pkgFlags);
+ s.userId = uid;
+ if (addUserIdLPw(uid, s, name)) {
+ mSharedUsers.put(name, s);
+ return s;
+ }
+ return null;
+ }
+
+ // Transfer ownership of permissions from one package to another.
+ void transferPermissionsLPw(String origPkg, String newPkg) {
+ // Transfer ownership of permissions to the new package.
+ for (int i=0; i<2; i++) {
+ HashMap<String, BasePermission> permissions =
+ i == 0 ? mPermissionTrees : mPermissions;
+ for (BasePermission bp : permissions.values()) {
+ if (origPkg.equals(bp.sourcePackage)) {
+ if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG,
+ "Moving permission " + bp.name
+ + " from pkg " + bp.sourcePackage
+ + " to " + newPkg);
+ bp.sourcePackage = newPkg;
+ bp.packageSetting = null;
+ bp.perm = null;
+ if (bp.pendingInfo != null) {
+ bp.pendingInfo.packageName = newPkg;
+ }
+ bp.uid = 0;
+ bp.gids = null;
+ }
+ }
+ }
+ }
+
+ private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
+ String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
+ String nativeLibraryPathString, int vc, 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
+ if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // This is an updated system app with versions in both system
+ // and data partition. Just let the most recent version
+ // take precedence.
+ Slog.w(PackageManagerService.TAG, "Trying to update system app code path from " +
+ p.codePathString + " to " + codePath.toString());
+ } else {
+ // Just a change in the code path is not an issue, but
+ // let's log a message about it.
+ Slog.i(PackageManagerService.TAG, "Package " + name + " codePath changed from " + p.codePath
+ + " to " + codePath + "; Retaining data and using new");
+ /*
+ * Since we've changed paths, we need to prefer the new
+ * native library path over the one stored in the
+ * package settings since we might have moved from
+ * internal to external storage or vice versa.
+ */
+ p.nativeLibraryPathString = nativeLibraryPathString;
+ }
+ }
+ if (p.sharedUser != sharedUser) {
+ PackageManagerService.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;
+ } else {
+ if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // If what we are scanning is a system package, then
+ // make it so, regardless of whether it was previously
+ // installed only in the data partition.
+ p.pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ }
+ }
+ }
+ 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;
+ }
+ if (origPackage != null) {
+ // We are consuming the data from an existing package.
+ p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
+ nativeLibraryPathString, vc, pkgFlags);
+ if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + name
+ + " is adopting original package " + origPackage.name);
+ // Note that we will retain the new package's signature so
+ // that we can keep its data.
+ PackageSignatures s = p.signatures;
+ p.copyFrom(origPackage);
+ p.signatures = s;
+ p.sharedUser = origPackage.sharedUser;
+ p.userId = origPackage.userId;
+ p.origPackage = origPackage;
+ mRenamedPackages.put(name, origPackage.name);
+ name = origPackage.name;
+ // Update new package state.
+ p.setTimeStamp(codePath.lastModified());
+ } else {
+ p = new PackageSetting(name, realName, codePath, resourcePath,
+ nativeLibraryPathString, vc, pkgFlags);
+ p.setTimeStamp(codePath.lastModified());
+ p.sharedUser = sharedUser;
+ // If this is not a system app, it starts out stopped.
+ if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
+ if (DEBUG_STOPPED) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Slog.i(PackageManagerService.TAG, "Stopping package " + name, e);
+ }
+ p.stopped = true;
+ p.notLaunched = true;
+ }
+ if (sharedUser != null) {
+ p.userId = sharedUser.userId;
+ } else if (PackageManagerService.MULTIPLE_APPLICATION_UIDS) {
+ // Clone the setting here for disabled system packages
+ PackageSetting dis = mDisabledSysPackages.get(name);
+ if (dis != null) {
+ // For disabled packages a new setting is created
+ // from the existing user id. This still has to be
+ // added to list of user id's
+ // Copy signatures from previous setting
+ if (dis.signatures.mSignatures != null) {
+ p.signatures.mSignatures = dis.signatures.mSignatures.clone();
+ }
+ p.userId = dis.userId;
+ // Clone permissions
+ p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
+ // Clone component info
+ p.disabledComponents = new HashSet<String>(dis.disabledComponents);
+ p.enabledComponents = new HashSet<String>(dis.enabledComponents);
+ // Add new setting to list of user ids
+ addUserIdLPw(p.userId, p, name);
+ } else {
+ // Assign new user id
+ p.userId = newUserIdLPw(p);
+ }
+ } else {
+ p.userId = PackageManagerService.FIRST_APPLICATION_UID;
+ }
+ }
+ if (p.userId < 0) {
+ PackageManagerService.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
+ addPackageSettingLPw(p, name, sharedUser);
+ }
+ }
+ return p;
+ }
+
+ void insertPackageSettingLPw(PackageSetting p, PackageParser.Package pkg) {
+ p.pkg = pkg;
+ pkg.mSetEnabled = p.enabled;
+ pkg.mSetStopped = p.stopped;
+ final String codePath = pkg.applicationInfo.sourceDir;
+ final String resourcePath = pkg.applicationInfo.publicSourceDir;
+ // Update code path if needed
+ if (!codePath.equalsIgnoreCase(p.codePathString)) {
+ Slog.w(PackageManagerService.TAG, "Code path for pkg : " + p.pkg.packageName +
+ " changing from " + p.codePathString + " to " + codePath);
+ p.codePath = new File(codePath);
+ p.codePathString = codePath;
+ }
+ //Update resource path if needed
+ if (!resourcePath.equalsIgnoreCase(p.resourcePathString)) {
+ Slog.w(PackageManagerService.TAG, "Resource path for pkg : " + p.pkg.packageName +
+ " changing from " + p.resourcePathString + " to " + resourcePath);
+ p.resourcePath = new File(resourcePath);
+ p.resourcePathString = resourcePath;
+ }
+ // Update the native library path if needed
+ final String nativeLibraryPath = pkg.applicationInfo.nativeLibraryDir;
+ if (nativeLibraryPath != null
+ && !nativeLibraryPath.equalsIgnoreCase(p.nativeLibraryPathString)) {
+ p.nativeLibraryPathString = nativeLibraryPath;
+ }
+ // Update version code if needed
+ if (pkg.mVersionCode != p.versionCode) {
+ p.versionCode = pkg.mVersionCode;
+ }
+ // Update signatures if needed.
+ if (p.signatures.mSignatures == null) {
+ p.signatures.assignSignatures(pkg.mSignatures);
+ }
+ // If this app defines a shared user id initialize
+ // the shared user signatures as well.
+ if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
+ p.sharedUser.signatures.assignSignatures(pkg.mSignatures);
+ }
+ addPackageSettingLPw(p, pkg.packageName, p.sharedUser);
+ }
+
+ // Utility method that adds a PackageSetting to mPackages and
+ // completes updating the shared user attributes
+ private void addPackageSettingLPw(PackageSetting p, String name,
+ SharedUserSetting sharedUser) {
+ mPackages.put(name, p);
+ if (sharedUser != null) {
+ if (p.sharedUser != null && p.sharedUser != sharedUser) {
+ PackageManagerService.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) {
+ PackageManagerService.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;
+ }
+ }
+
+ /*
+ * Update the shared user setting when a package using
+ * specifying the shared user id is removed. The gids
+ * associated with each permission of the deleted package
+ * are removed from the shared user's gid list only if its
+ * not in use by other permissions of packages in the
+ * shared user setting.
+ */
+ void updateSharedUserPermsLPw(PackageSetting deletedPs, int[] globalGids) {
+ if ((deletedPs == null) || (deletedPs.pkg == null)) {
+ Slog.i(PackageManagerService.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.pkg != null &&
+ !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&
+ pkg.pkg.requestedPermissions.contains(eachPerm)) {
+ used = true;
+ break;
+ }
+ }
+ if (!used) {
+ // can safely delete this permission from list
+ sus.grantedPermissions.remove(eachPerm);
+ }
+ }
+ // Update gids
+ int newGids[] = globalGids;
+ for (String eachPerm : sus.grantedPermissions) {
+ BasePermission bp = mPermissions.get(eachPerm);
+ if (bp != null) {
+ newGids = PackageManagerService.appendInts(newGids, bp.gids);
+ }
+ }
+ sus.gids = newGids;
+ }
+
+ int removePackageLPw(String name) {
+ final 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);
+ removeUserIdLPw(p.sharedUser.userId);
+ return p.sharedUser.userId;
+ }
+ } else {
+ removeUserIdLPw(p.userId);
+ return p.userId;
+ }
+ }
+ return -1;
+ }
+
+ private void replacePackageLPw(String name, PackageSetting newp) {
+ final PackageSetting p = mPackages.get(name);
+ if (p != null) {
+ if (p.sharedUser != null) {
+ p.sharedUser.packages.remove(p);
+ p.sharedUser.packages.add(newp);
+ } else {
+ replaceUserIdLPw(p.userId, newp);
+ }
+ }
+ mPackages.put(name, newp);
+ }
+
+ private boolean addUserIdLPw(int uid, Object obj, Object name) {
+ if (uid >= PackageManagerService.FIRST_APPLICATION_UID + PackageManagerService.MAX_APPLICATION_UIDS) {
+ return false;
+ }
+
+ if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+ int N = mUserIds.size();
+ final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+ while (index >= N) {
+ mUserIds.add(null);
+ N++;
+ }
+ if (mUserIds.get(index) != null) {
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Adding duplicate user id: " + uid
+ + " name=" + name);
+ return false;
+ }
+ mUserIds.set(index, obj);
+ } else {
+ if (mOtherUserIds.get(uid) != null) {
+ PackageManagerService.reportSettingsProblem(Log.ERROR,
+ "Adding duplicate shared id: " + uid
+ + " name=" + name);
+ return false;
+ }
+ mOtherUserIds.put(uid, obj);
+ }
+ return true;
+ }
+
+ public Object getUserIdLPr(int uid) {
+ if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+ final int N = mUserIds.size();
+ final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+ return index < N ? mUserIds.get(index) : null;
+ } else {
+ return mOtherUserIds.get(uid);
+ }
+ }
+
+ private void removeUserIdLPw(int uid) {
+ if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+ final int N = mUserIds.size();
+ final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+ if (index < N) mUserIds.set(index, null);
+ } else {
+ mOtherUserIds.remove(uid);
+ }
+ }
+
+ private void replaceUserIdLPw(int uid, Object obj) {
+ if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
+ final int N = mUserIds.size();
+ final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
+ if (index < N) mUserIds.set(index, obj);
+ } else {
+ mOtherUserIds.put(uid, obj);
+ }
+ }
+
+ void writeStoppedLPr() {
+ // Keep the old stopped packages around until we know the new ones have
+ // been successfully written.
+ if (mStoppedPackagesFilename.exists()) {
+ // Presence of backup settings file indicates that we failed
+ // to persist packages earlier. So preserve the older
+ // backup for future reference since the current packages
+ // might have been corrupted.
+ if (!mBackupStoppedPackagesFilename.exists()) {
+ if (!mStoppedPackagesFilename.renameTo(mBackupStoppedPackagesFilename)) {
+ Log.wtf(PackageManagerService.TAG, "Unable to backup package manager stopped packages, "
+ + "current changes will be lost at reboot");
+ return;
+ }
+ } else {
+ mStoppedPackagesFilename.delete();
+ Slog.w(PackageManagerService.TAG, "Preserving older stopped packages backup");
+ }
+ }
+
+ try {
+ final FileOutputStream fstr = new FileOutputStream(mStoppedPackagesFilename);
+ final BufferedOutputStream str = new BufferedOutputStream(fstr);
+
+ //XmlSerializer serializer = XmlUtils.serializerInstance();
+ final 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, "stopped-packages");
+
+ for (final PackageSetting pkg : mPackages.values()) {
+ if (pkg.stopped) {
+ serializer.startTag(null, "pkg");
+ serializer.attribute(null, "name", pkg.name);
+ if (pkg.notLaunched) {
+ serializer.attribute(null, "nl", "1");
+ }
+ serializer.endTag(null, "pkg");
+ }
+ }
+
+ serializer.endTag(null, "stopped-packages");
+
+ serializer.endDocument();
+
+ str.flush();
+ FileUtils.sync(fstr);
+ str.close();
+
+ // New settings successfully written, old ones are no longer
+ // needed.
+ mBackupStoppedPackagesFilename.delete();
+ FileUtils.setPermissions(mStoppedPackagesFilename.toString(),
+ FileUtils.S_IRUSR|FileUtils.S_IWUSR
+ |FileUtils.S_IRGRP|FileUtils.S_IWGRP
+ |FileUtils.S_IROTH,
+ -1, -1);
+
+ // Done, all is good!
+ return;
+ } catch(java.io.IOException e) {
+ Log.wtf(PackageManagerService.TAG, "Unable to write package manager stopped packages, "
+ + " current changes will be lost at reboot", e);
+ }
+
+ // Clean up partially written files
+ if (mStoppedPackagesFilename.exists()) {
+ if (!mStoppedPackagesFilename.delete()) {
+ Log.i(PackageManagerService.TAG, "Failed to clean up mangled file: " + mStoppedPackagesFilename);
+ }
+ }
+ }
+
+ // Note: assumed "stopped" field is already cleared in all packages.
+ void readStoppedLPw() {
+ FileInputStream str = null;
+ if (mBackupStoppedPackagesFilename.exists()) {
+ try {
+ str = new FileInputStream(mBackupStoppedPackagesFilename);
+ mReadMessages.append("Reading from backup stopped packages file\n");
+ PackageManagerService.reportSettingsProblem(Log.INFO, "Need to read from backup stopped packages file");
+ if (mSettingsFilename.exists()) {
+ // If both the backup and normal file exist, we
+ // ignore the normal one since it might have been
+ // corrupted.
+ Slog.w(PackageManagerService.TAG, "Cleaning up stopped packages file "
+ + mStoppedPackagesFilename);
+ mStoppedPackagesFilename.delete();
+ }
+ } catch (java.io.IOException e) {
+ // We'll try for the normal settings file.
+ }
+ }
+
+ try {
+ if (str == null) {
+ if (!mStoppedPackagesFilename.exists()) {
+ mReadMessages.append("No stopped packages file found\n");
+ PackageManagerService.reportSettingsProblem(Log.INFO, "No stopped packages file file; "
+ + "assuming all started");
+ // At first boot, make sure no packages are stopped.
+ // We usually want to have third party apps initialize
+ // in the stopped state, but not at first boot.
+ for (PackageSetting pkg : mPackages.values()) {
+ pkg.stopped = false;
+ pkg.notLaunched = false;
+ }
+ return;
+ }
+ str = new FileInputStream(mStoppedPackagesFilename);
+ }
+ final 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 stopped packages file\n");
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "No start tag found in package manager stopped packages");
+ return;
+ }
+
+ 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("pkg")) {
+ String name = parser.getAttributeValue(null, "name");
+ PackageSetting ps = mPackages.get(name);
+ if (ps != null) {
+ ps.stopped = true;
+ if ("1".equals(parser.getAttributeValue(null, "nl"))) {
+ ps.notLaunched = true;
+ }
+ } else {
+ Slog.w(PackageManagerService.TAG, "No package known for stopped package: " + name);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } else {
+ Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ str.close();
+
+ } catch(XmlPullParserException e) {
+ mReadMessages.append("Error reading: " + e.toString());
+ PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading stopped packages: " + e);
+ Log.wtf(PackageManagerService.TAG, "Error reading package manager stopped packages", e);
+
+ } catch(java.io.IOException e) {
+ mReadMessages.append("Error reading: " + e.toString());
+ PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
+ Log.wtf(PackageManagerService.TAG, "Error reading package manager stopped packages", e);
+
+ }
+ }
+
+ void writeLPr() {
+ //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()) {
+ // Presence of backup settings file indicates that we failed
+ // to persist settings earlier. So preserve the older
+ // backup for future reference since the current settings
+ // might have been corrupted.
+ if (!mBackupSettingsFilename.exists()) {
+ if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) {
+ Log.wtf(PackageManagerService.TAG, "Unable to backup package manager settings, "
+ + " current changes will be lost at reboot");
+ return;
+ }
+ } else {
+ mSettingsFilename.delete();
+ Slog.w(PackageManagerService.TAG, "Preserving older settings backup");
+ }
+ }
+
+ mPastSignatures.clear();
+
+ try {
+ FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
+ BufferedOutputStream str = new BufferedOutputStream(fstr);
+
+ //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, "last-platform-version");
+ serializer.attribute(null, "internal", Integer.toString(mInternalSdkPlatform));
+ serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform));
+ serializer.endTag(null, "last-platform-version");
+
+ serializer.startTag(null, "permission-trees");
+ for (BasePermission bp : mPermissionTrees.values()) {
+ writePermissionLPr(serializer, bp);
+ }
+ serializer.endTag(null, "permission-trees");
+
+ serializer.startTag(null, "permissions");
+ for (BasePermission bp : mPermissions.values()) {
+ writePermissionLPr(serializer, bp);
+ }
+ serializer.endTag(null, "permissions");
+
+ for (final PackageSetting pkg : mPackages.values()) {
+ writePackageLPr(serializer, pkg);
+ }
+
+ for (final PackageSetting pkg : mDisabledSysPackages.values()) {
+ writeDisabledSysPackageLPr(serializer, pkg);
+ }
+
+ serializer.startTag(null, "preferred-activities");
+ for (final PreferredActivity pa : mPreferredActivities.filterSet()) {
+ serializer.startTag(null, "item");
+ pa.writeToXml(serializer);
+ serializer.endTag(null, "item");
+ }
+ serializer.endTag(null, "preferred-activities");
+
+ for (final 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");
+ }
+
+ if (mPackagesToBeCleaned.size() > 0) {
+ for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
+ serializer.startTag(null, "cleaning-package");
+ serializer.attribute(null, "name", mPackagesToBeCleaned.get(i));
+ serializer.endTag(null, "cleaning-package");
+ }
+ }
+
+ if (mRenamedPackages.size() > 0) {
+ for (HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) {
+ serializer.startTag(null, "renamed-package");
+ serializer.attribute(null, "new", e.getKey());
+ serializer.attribute(null, "old", e.getValue());
+ serializer.endTag(null, "renamed-package");
+ }
+ }
+
+ serializer.endTag(null, "packages");
+
+ serializer.endDocument();
+
+ str.flush();
+ FileUtils.sync(fstr);
+ 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);
+
+ // Write package list file now, use a JournaledFile.
+ //
+ File tempFile = new File(mPackageListFilename.toString() + ".tmp");
+ JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile);
+
+ fstr = new FileOutputStream(journal.chooseForWrite());
+ str = new BufferedOutputStream(fstr);
+ try {
+ StringBuilder sb = new StringBuilder();
+ for (final PackageSetting pkg : mPackages.values()) {
+ ApplicationInfo ai = pkg.pkg.applicationInfo;
+ String dataPath = ai.dataDir;
+ boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+
+ // Avoid any application that has a space in its path
+ // or that is handled by the system.
+ if (dataPath.indexOf(" ") >= 0 || ai.uid <= Process.FIRST_APPLICATION_UID)
+ continue;
+
+ // we store on each line the following information for now:
+ //
+ // pkgName - package name
+ // userId - application-specific user id
+ // debugFlag - 0 or 1 if the package is debuggable.
+ // dataPath - path to package's data path
+ //
+ // NOTE: We prefer not to expose all ApplicationInfo flags for now.
+ //
+ // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS
+ // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
+ // system/core/run-as/run-as.c
+ //
+ sb.setLength(0);
+ sb.append(ai.packageName);
+ sb.append(" ");
+ sb.append((int)ai.uid);
+ sb.append(isDebug ? " 1 " : " 0 ");
+ sb.append(dataPath);
+ sb.append("\n");
+ str.write(sb.toString().getBytes());
+ }
+ str.flush();
+ FileUtils.sync(fstr);
+ str.close();
+ journal.commit();
+ }
+ catch (Exception e) {
+ journal.rollback();
+ }
+
+ FileUtils.setPermissions(mPackageListFilename.toString(),
+ FileUtils.S_IRUSR|FileUtils.S_IWUSR
+ |FileUtils.S_IRGRP|FileUtils.S_IWGRP
+ |FileUtils.S_IROTH,
+ -1, -1);
+
+ writeStoppedLPr();
+
+ return;
+
+ } catch(XmlPullParserException e) {
+ Log.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
+ + "current changes will be lost at reboot", e);
+ } catch(java.io.IOException e) {
+ Log.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
+ + "current changes will be lost at reboot", e);
+ }
+ // Clean up partially written files
+ if (mSettingsFilename.exists()) {
+ if (!mSettingsFilename.delete()) {
+ Log.wtf(PackageManagerService.TAG, "Failed to clean up mangled file: " + mSettingsFilename);
+ }
+ }
+ //Debug.stopMethodTracing();
+ }
+
+ void writeDisabledSysPackageLPr(XmlSerializer serializer, final PackageSetting pkg)
+ throws java.io.IOException {
+ serializer.startTag(null, "updated-package");
+ serializer.attribute(null, "name", pkg.name);
+ if (pkg.realName != null) {
+ serializer.attribute(null, "realName", pkg.realName);
+ }
+ serializer.attribute(null, "codePath", pkg.codePathString);
+ serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
+ serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
+ serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
+ serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
+ if (!pkg.resourcePathString.equals(pkg.codePathString)) {
+ serializer.attribute(null, "resourcePath", pkg.resourcePathString);
+ }
+ if (pkg.nativeLibraryPathString != null) {
+ serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
+ }
+ 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) {
+ // 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 writePackageLPr(XmlSerializer serializer, final PackageSetting pkg)
+ throws java.io.IOException {
+ serializer.startTag(null, "package");
+ serializer.attribute(null, "name", pkg.name);
+ if (pkg.realName != null) {
+ serializer.attribute(null, "realName", pkg.realName);
+ }
+ serializer.attribute(null, "codePath", pkg.codePathString);
+ if (!pkg.resourcePathString.equals(pkg.codePathString)) {
+ serializer.attribute(null, "resourcePath", pkg.resourcePathString);
+ }
+ if (pkg.nativeLibraryPathString != null) {
+ serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
+ }
+ serializer.attribute(null, "flags", Integer.toString(pkg.pkgFlags));
+ serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
+ serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
+ serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
+ serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
+ if (pkg.sharedUser == null) {
+ serializer.attribute(null, "userId", Integer.toString(pkg.userId));
+ } else {
+ serializer.attribute(null, "sharedUserId", Integer.toString(pkg.userId));
+ }
+ if (pkg.uidError) {
+ serializer.attribute(null, "uidError", "true");
+ }
+ if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
+ serializer.attribute(null, "enabled",
+ pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED ? "true" : "false");
+ }
+ if (pkg.installStatus == PackageSettingBase.PKG_INSTALL_INCOMPLETE) {
+ serializer.attribute(null, "installStatus", "false");
+ }
+ if (pkg.installerPackageName != null) {
+ serializer.attribute(null, "installer", pkg.installerPackageName);
+ }
+ 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 writePermissionLPr(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 (bp.protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
+ serializer.attribute(null, "protection", Integer.toString(bp.protectionLevel));
+ }
+ if (PackageManagerService.DEBUG_SETTINGS)
+ Log.v(PackageManagerService.TAG, "Writing perm: name=" + bp.name + " type="
+ + bp.type);
+ if (bp.type == BasePermission.TYPE_DYNAMIC) {
+ final 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());
+ }
+ }
+ }
+ serializer.endTag(null, "item");
+ }
+ }
+
+ ArrayList<PackageSetting> getListOfIncompleteInstallPackagesLPr() {
+ final HashSet<String> kList = new HashSet<String>(mPackages.keySet());
+ final Iterator<String> its = kList.iterator();
+ final ArrayList<PackageSetting> ret = new ArrayList<PackageSetting>();
+ while (its.hasNext()) {
+ final String key = its.next();
+ final PackageSetting ps = mPackages.get(key);
+ if (ps.getInstallStatus() == PackageSettingBase.PKG_INSTALL_INCOMPLETE) {
+ ret.add(ps);
+ }
+ }
+ return ret;
+ }
+
+ boolean readLPw() {
+ FileInputStream str = null;
+ if (mBackupSettingsFilename.exists()) {
+ try {
+ str = new FileInputStream(mBackupSettingsFilename);
+ mReadMessages.append("Reading from backup settings file\n");
+ PackageManagerService.reportSettingsProblem(Log.INFO,
+ "Need to read from backup settings file");
+ if (mSettingsFilename.exists()) {
+ // If both the backup and settings file exist, we
+ // ignore the settings since it might have been
+ // corrupted.
+ Slog.w(PackageManagerService.TAG, "Cleaning up settings file "
+ + mSettingsFilename);
+ mSettingsFilename.delete();
+ }
+ } catch (java.io.IOException e) {
+ // We'll try for the normal settings file.
+ }
+ }
+
+ mPendingPackages.clear();
+ mPastSignatures.clear();
+
+ try {
+ if (str == null) {
+ if (!mSettingsFilename.exists()) {
+ mReadMessages.append("No settings file found\n");
+ PackageManagerService.reportSettingsProblem(Log.INFO,
+ "No settings file; creating initial state");
+ 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");
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "No start tag found in package manager settings");
+ Log
+ .wtf(PackageManagerService.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")) {
+ readPackageLPw(parser);
+ } else if (tagName.equals("permissions")) {
+ readPermissionsLPw(mPermissions, parser);
+ } else if (tagName.equals("permission-trees")) {
+ readPermissionsLPw(mPermissionTrees, parser);
+ } else if (tagName.equals("shared-user")) {
+ readSharedUserLPw(parser);
+ } else if (tagName.equals("preferred-packages")) {
+ // no longer used.
+ } else if (tagName.equals("preferred-activities")) {
+ readPreferredActivitiesLPw(parser);
+ } else if (tagName.equals("updated-package")) {
+ readDisabledSysPackageLPw(parser);
+ } else if (tagName.equals("cleaning-package")) {
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ mPackagesToBeCleaned.add(name);
+ }
+ } else if (tagName.equals("renamed-package")) {
+ String nname = parser.getAttributeValue(null, "new");
+ String oname = parser.getAttributeValue(null, "old");
+ if (nname != null && oname != null) {
+ mRenamedPackages.put(nname, oname);
+ }
+ } else if (tagName.equals("last-platform-version")) {
+ mInternalSdkPlatform = mExternalSdkPlatform = 0;
+ try {
+ String internal = parser.getAttributeValue(null, "internal");
+ if (internal != null) {
+ mInternalSdkPlatform = Integer.parseInt(internal);
+ }
+ String external = parser.getAttributeValue(null, "external");
+ if (external != null) {
+ mExternalSdkPlatform = Integer.parseInt(external);
+ }
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ str.close();
+
+ } catch (XmlPullParserException e) {
+ mReadMessages.append("Error reading: " + e.toString());
+ PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
+ Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+
+ } catch (java.io.IOException e) {
+ mReadMessages.append("Error reading: " + e.toString());
+ PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
+ Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+
+ }
+
+ final int N = mPendingPackages.size();
+ for (int i = 0; i < N; i++) {
+ final PendingPackage pp = mPendingPackages.get(i);
+ Object idObj = getUserIdLPr(pp.sharedId);
+ if (idObj != null && idObj instanceof SharedUserSetting) {
+ PackageSetting p = getPackageLPw(pp.name, null, pp.realName,
+ (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
+ pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true);
+ if (p == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "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);
+ PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
+ } else {
+ String msg = "Bad package setting: package " + pp.name + " has shared uid "
+ + pp.sharedId + " that is not defined\n";
+ mReadMessages.append(msg);
+ PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
+ }
+ }
+ mPendingPackages.clear();
+
+ /*
+ * Make sure all the updated system packages have their shared users
+ * associated with them.
+ */
+ final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
+ while (disabledIt.hasNext()) {
+ final PackageSetting disabledPs = disabledIt.next();
+ final Object id = getUserIdLPr(disabledPs.userId);
+ if (id != null && id instanceof SharedUserSetting) {
+ disabledPs.sharedUser = (SharedUserSetting) id;
+ }
+ }
+
+ readStoppedLPw();
+
+ 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) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: attribute " + name
+ + " has bad integer value " + v + " at "
+ + parser.getPositionDescription());
+ }
+ return defValue;
+ }
+
+ private void readPermissionsLPw(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;
+ }
+
+ final String tagName = parser.getName();
+ if (tagName.equals("item")) {
+ final String name = parser.getAttributeValue(null, "name");
+ final String sourcePackage = parser.getAttributeValue(null, "package");
+ final String ptype = parser.getAttributeValue(null, "type");
+ if (name != null && sourcePackage != null) {
+ final boolean dynamic = "dynamic".equals(ptype);
+ final BasePermission bp = new BasePermission(name, sourcePackage,
+ dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
+ bp.protectionLevel = readInt(parser, null, "protection",
+ PermissionInfo.PROTECTION_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 = bp.protectionLevel;
+ bp.pendingInfo = pi;
+ }
+ out.put(bp.name, bp);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: permissions has" + " no name at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element reading permissions: " + parser.getName() + " at "
+ + parser.getPositionDescription());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readDisabledSysPackageLPw(XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ String name = parser.getAttributeValue(null, "name");
+ String realName = parser.getAttributeValue(null, "realName");
+ String codePathStr = parser.getAttributeValue(null, "codePath");
+ String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+ String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
+ if (resourcePathStr == null) {
+ resourcePathStr = codePathStr;
+ }
+ String version = parser.getAttributeValue(null, "version");
+ int versionCode = 0;
+ if (version != null) {
+ try {
+ versionCode = Integer.parseInt(version);
+ } catch (NumberFormatException e) {
+ }
+ }
+
+ int pkgFlags = 0;
+ pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
+ new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags);
+ String timeStampStr = parser.getAttributeValue(null, "ft");
+ if (timeStampStr != null) {
+ try {
+ long timeStamp = Long.parseLong(timeStampStr, 16);
+ ps.setTimeStamp(timeStamp);
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ timeStampStr = parser.getAttributeValue(null, "ts");
+ if (timeStampStr != null) {
+ try {
+ long timeStamp = Long.parseLong(timeStampStr);
+ ps.setTimeStamp(timeStamp);
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "it");
+ if (timeStampStr != null) {
+ try {
+ ps.firstInstallTime = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "ut");
+ if (timeStampStr != null) {
+ try {
+ ps.lastUpdateTime = Long.parseLong(timeStampStr, 16);
+ } 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")) {
+ readGrantedPermissionsLPw(parser, ps.grantedPermissions);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <updated-package>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ mDisabledSysPackages.put(name, ps);
+ }
+
+ private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
+ String name = null;
+ String realName = null;
+ String idStr = null;
+ String sharedIdStr = null;
+ String codePathStr = null;
+ String resourcePathStr = null;
+ String nativeLibraryPathStr = null;
+ String systemStr = null;
+ String installerPackageName = null;
+ String uidError = null;
+ int pkgFlags = 0;
+ long timeStamp = 0;
+ long firstInstallTime = 0;
+ long lastUpdateTime = 0;
+ PackageSettingBase packageSetting = null;
+ String version = null;
+ int versionCode = 0;
+ try {
+ name = parser.getAttributeValue(null, "name");
+ realName = parser.getAttributeValue(null, "realName");
+ idStr = parser.getAttributeValue(null, "userId");
+ uidError = parser.getAttributeValue(null, "uidError");
+ sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
+ codePathStr = parser.getAttributeValue(null, "codePath");
+ resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+ nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
+ version = parser.getAttributeValue(null, "version");
+ if (version != null) {
+ try {
+ versionCode = Integer.parseInt(version);
+ } catch (NumberFormatException e) {
+ }
+ }
+ installerPackageName = parser.getAttributeValue(null, "installer");
+
+ systemStr = parser.getAttributeValue(null, "flags");
+ if (systemStr != null) {
+ try {
+ pkgFlags = Integer.parseInt(systemStr);
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ // For backward compatibility
+ systemStr = parser.getAttributeValue(null, "system");
+ if (systemStr != null) {
+ pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM
+ : 0;
+ } else {
+ // Old settings that don't specify system... just treat
+ // them as system, good enough.
+ pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+ }
+ }
+ String timeStampStr = parser.getAttributeValue(null, "ft");
+ if (timeStampStr != null) {
+ try {
+ timeStamp = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ timeStampStr = parser.getAttributeValue(null, "ts");
+ if (timeStampStr != null) {
+ try {
+ timeStamp = Long.parseLong(timeStampStr);
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "it");
+ if (timeStampStr != null) {
+ try {
+ firstInstallTime = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ }
+ timeStampStr = parser.getAttributeValue(null, "ut");
+ if (timeStampStr != null) {
+ try {
+ lastUpdateTime = Long.parseLong(timeStampStr, 16);
+ } catch (NumberFormatException e) {
+ }
+ }
+ if (PackageManagerService.DEBUG_SETTINGS)
+ Log.v(PackageManagerService.TAG, "Reading package: " + name + " userId=" + idStr
+ + " sharedUserId=" + sharedIdStr);
+ int userId = idStr != null ? Integer.parseInt(idStr) : 0;
+ if (resourcePathStr == null) {
+ resourcePathStr = codePathStr;
+ }
+ if (realName != null) {
+ realName = realName.intern();
+ }
+ if (name == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <package> has no name at "
+ + parser.getPositionDescription());
+ } else if (codePathStr == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <package> has no codePath at "
+ + parser.getPositionDescription());
+ } else if (userId > 0) {
+ packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
+ new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode,
+ pkgFlags);
+ if (PackageManagerService.DEBUG_SETTINGS)
+ Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
+ + userId + " pkg=" + packageSetting);
+ if (packageSetting == null) {
+ PackageManagerService.reportSettingsProblem(Log.ERROR, "Failure adding uid "
+ + userId + " while parsing settings at "
+ + parser.getPositionDescription());
+ } else {
+ packageSetting.setTimeStamp(timeStamp);
+ packageSetting.firstInstallTime = firstInstallTime;
+ packageSetting.lastUpdateTime = lastUpdateTime;
+ }
+ } else if (sharedIdStr != null) {
+ userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
+ if (userId > 0) {
+ packageSetting = new PendingPackage(name.intern(), realName, new File(
+ codePathStr), new File(resourcePathStr), nativeLibraryPathStr, userId,
+ versionCode, pkgFlags);
+ packageSetting.setTimeStamp(timeStamp);
+ packageSetting.firstInstallTime = firstInstallTime;
+ packageSetting.lastUpdateTime = lastUpdateTime;
+ mPendingPackages.add((PendingPackage) packageSetting);
+ if (PackageManagerService.DEBUG_SETTINGS)
+ Log.i(PackageManagerService.TAG, "Reading package " + name
+ + ": sharedUserId=" + userId + " pkg=" + packageSetting);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: package " + name
+ + " has bad sharedId " + sharedIdStr + " at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: package " + name + " has bad userId "
+ + idStr + " at " + parser.getPositionDescription());
+ }
+ } catch (NumberFormatException e) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: package " + name + " has bad userId "
+ + idStr + " at " + parser.getPositionDescription());
+ }
+ if (packageSetting != null) {
+ packageSetting.uidError = "true".equals(uidError);
+ packageSetting.installerPackageName = installerPackageName;
+ packageSetting.nativeLibraryPathString = nativeLibraryPathStr;
+ 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 {
+ PackageManagerService.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 = PackageSettingBase.PKG_INSTALL_INCOMPLETE;
+ } else {
+ packageSetting.installStatus = PackageSettingBase.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")) {
+ readDisabledComponentsLPw(packageSetting, parser);
+ } else if (tagName.equals("enabled-components")) {
+ readEnabledComponentsLPw(packageSetting, parser);
+ } else if (tagName.equals("sigs")) {
+ packageSetting.signatures.readXml(parser, mPastSignatures);
+ } else if (tagName.equals("perms")) {
+ readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions);
+ packageSetting.permissionsFixed = true;
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <package>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readDisabledComponentsLPw(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 {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <disabled-components> has"
+ + " no name at " + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <disabled-components>: " + parser.getName());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readEnabledComponentsLPw(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 {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <enabled-components> has"
+ + " no name at " + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <enabled-components>: " + parser.getName());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readSharedUserLPw(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) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <shared-user> has no name at "
+ + parser.getPositionDescription());
+ } else if (userId == 0) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: shared-user " + name
+ + " has bad userId " + idStr + " at "
+ + parser.getPositionDescription());
+ } else {
+ if ((su = addSharedUserLPw(name.intern(), userId, pkgFlags)) == null) {
+ PackageManagerService
+ .reportSettingsProblem(Log.ERROR, "Occurred while parsing settings at "
+ + parser.getPositionDescription());
+ }
+ }
+ } catch (NumberFormatException e) {
+ PackageManagerService.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")) {
+ readGrantedPermissionsLPw(parser, su.grantedPermissions);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <shared-user>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readGrantedPermissionsLPw(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 {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <perms> has" + " no name at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <perms>: " + parser.getName());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ private void readPreferredActivitiesLPw(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.mPref.getParseError() == null) {
+ mPreferredActivities.addFilter(pa);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <preferred-activity> "
+ + pa.mPref.getParseError() + " at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.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 newUserIdLPw(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 PackageManagerService.FIRST_APPLICATION_UID + i;
+ }
+ }
+
+ // None left?
+ if (N >= PackageManagerService.MAX_APPLICATION_UIDS) {
+ return -1;
+ }
+
+ mUserIds.add(obj);
+ return PackageManagerService.FIRST_APPLICATION_UID + N;
+ }
+
+ public PackageSetting getDisabledSystemPkgLPr(String name) {
+ PackageSetting ps = mDisabledSysPackages.get(name);
+ return ps;
+ }
+
+ boolean isEnabledLPr(ComponentInfo componentInfo, int flags) {
+ if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
+ return true;
+ }
+ final PackageSetting packageSettings = mPackages.get(componentInfo.packageName);
+ if (PackageManagerService.DEBUG_SETTINGS) {
+ Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = " + componentInfo.packageName
+ + " componentName = " + componentInfo.name);
+ Log.v(PackageManagerService.TAG, "enabledComponents: "
+ + Arrays.toString(packageSettings.enabledComponents.toArray()));
+ Log.v(PackageManagerService.TAG, "disabledComponents: "
+ + Arrays.toString(packageSettings.disabledComponents.toArray()));
+ }
+ if (packageSettings == null) {
+ return false;
+ }
+ if (packageSettings.enabled == COMPONENT_ENABLED_STATE_DISABLED
+ || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
+ && packageSettings.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
+ return false;
+ }
+ if (packageSettings.enabledComponents.contains(componentInfo.name)) {
+ return true;
+ }
+ if (packageSettings.disabledComponents.contains(componentInfo.name)) {
+ return false;
+ }
+ return componentInfo.enabled;
+ }
+
+ String getInstallerPackageNameLPr(String packageName) {
+ final PackageSetting pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ return pkg.installerPackageName;
+ }
+
+ int getApplicationEnabledSettingLPr(String packageName) {
+ final PackageSetting pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ return pkg.enabled;
+ }
+
+ int getComponentEnabledSettingLPr(ComponentName componentName) {
+ final String packageName = componentName.getPackageName();
+ final PackageSetting pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ throw new IllegalArgumentException("Unknown component: " + componentName);
+ }
+ final String classNameStr = componentName.getClassName();
+ return pkg.getCurrentEnabledStateLPr(classNameStr);
+ }
+
+ boolean setPackageStoppedStateLPw(String packageName, boolean stopped,
+ boolean allowedByPermission, int uid) {
+ final PackageSetting pkgSetting = mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ if (!allowedByPermission && (uid != pkgSetting.userId)) {
+ throw new SecurityException(
+ "Permission Denial: attempt to change stopped state from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + uid + ", package uid=" + pkgSetting.userId);
+ }
+ if (DEBUG_STOPPED) {
+ if (stopped) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Slog.i(TAG, "Stopping package " + packageName, e);
+ }
+ }
+ if (pkgSetting.stopped != stopped) {
+ pkgSetting.stopped = stopped;
+ pkgSetting.pkg.mSetStopped = stopped;
+ if (pkgSetting.notLaunched) {
+ if (pkgSetting.installerPackageName != null) {
+ PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
+ pkgSetting.name, null,
+ pkgSetting.installerPackageName, null);
+ }
+ pkgSetting.notLaunched = false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState) {
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ final Date date = new Date();
+ boolean printedSomething = false;
+ for (final PackageSetting ps : mPackages.values()) {
+ if (packageName != null && !packageName.equals(ps.realName)
+ && !packageName.equals(ps.name)) {
+ continue;
+ }
+
+ if (packageName != null) {
+ dumpState.setSharedUser(ps.sharedUser);
+ }
+
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Packages:");
+ printedSomething = true;
+ }
+ pw.print(" Package [");
+ pw.print(ps.realName != null ? ps.realName : ps.name);
+ pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(ps)));
+ pw.println("):");
+
+ if (ps.realName != null) {
+ pw.print(" compat name=");
+ pw.println(ps.name);
+ }
+
+ pw.print(" userId="); pw.print(ps.userId);
+ pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids));
+ pw.print(" sharedUser="); pw.println(ps.sharedUser);
+ pw.print(" pkg="); pw.println(ps.pkg);
+ pw.print(" codePath="); pw.println(ps.codePathString);
+ pw.print(" resourcePath="); pw.println(ps.resourcePathString);
+ pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString);
+ pw.print(" versionCode="); pw.println(ps.versionCode);
+ if (ps.pkg != null) {
+ pw.print(" versionName="); pw.println(ps.pkg.mVersionName);
+ pw.print(" dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
+ pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion);
+ if (ps.pkg.mOperationPending) {
+ pw.println(" mOperationPending=true");
+ }
+ pw.print(" supportsScreens=[");
+ boolean first = true;
+ if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+ if (!first)
+ pw.print(", ");
+ first = false;
+ pw.print("small");
+ }
+ if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
+ if (!first)
+ pw.print(", ");
+ first = false;
+ pw.print("medium");
+ }
+ if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+ if (!first)
+ pw.print(", ");
+ first = false;
+ pw.print("large");
+ }
+ if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
+ if (!first)
+ pw.print(", ");
+ first = false;
+ pw.print("xlarge");
+ }
+ if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
+ if (!first)
+ pw.print(", ");
+ first = false;
+ pw.print("resizeable");
+ }
+ if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
+ if (!first)
+ pw.print(", ");
+ first = false;
+ pw.print("anyDensity");
+ }
+ }
+ pw.println("]");
+ pw.print(" timeStamp=");
+ date.setTime(ps.timeStamp);
+ pw.println(sdf.format(date));
+ pw.print(" firstInstallTime=");
+ date.setTime(ps.firstInstallTime);
+ pw.println(sdf.format(date));
+ pw.print(" lastUpdateTime=");
+ date.setTime(ps.lastUpdateTime);
+ pw.println(sdf.format(date));
+ if (ps.installerPackageName != null) {
+ pw.print(" installerPackageName="); pw.println(ps.installerPackageName);
+ }
+ pw.print(" signatures="); pw.println(ps.signatures);
+ pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed);
+ pw.print(" haveGids="); pw.println(ps.haveGids);
+ pw.print(" pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
+ pw.print(" installStatus="); pw.print(ps.installStatus);
+ pw.print(" stopped="); pw.print(ps.stopped);
+ pw.print(" enabled="); pw.println(ps.enabled);
+ if (ps.disabledComponents.size() > 0) {
+ pw.println(" disabledComponents:");
+ for (String s : ps.disabledComponents) {
+ pw.print(" "); pw.println(s);
+ }
+ }
+ if (ps.enabledComponents.size() > 0) {
+ pw.println(" enabledComponents:");
+ for (String s : ps.enabledComponents) {
+ pw.print(" "); pw.println(s);
+ }
+ }
+ if (ps.grantedPermissions.size() > 0) {
+ pw.println(" grantedPermissions:");
+ for (String s : ps.grantedPermissions) {
+ pw.print(" "); pw.println(s);
+ }
+ }
+ }
+
+ printedSomething = false;
+ if (mRenamedPackages.size() > 0) {
+ for (final HashMap.Entry<String, String> e : mRenamedPackages.entrySet()) {
+ if (packageName != null && !packageName.equals(e.getKey())
+ && !packageName.equals(e.getValue())) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Renamed packages:");
+ printedSomething = true;
+ }
+ pw.print(" ");
+ pw.print(e.getKey());
+ pw.print(" -> ");
+ pw.println(e.getValue());
+ }
+ }
+
+ printedSomething = false;
+ if (mDisabledSysPackages.size() > 0) {
+ for (final PackageSetting ps : mDisabledSysPackages.values()) {
+ if (packageName != null && !packageName.equals(ps.realName)
+ && !packageName.equals(ps.name)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Hidden system packages:");
+ printedSomething = true;
+ }
+ pw.print(" Package [");
+ pw.print(ps.realName != null ? ps.realName : ps.name);
+ pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(ps)));
+ pw.println("):");
+ if (ps.realName != null) {
+ pw.print(" compat name=");
+ pw.println(ps.name);
+ }
+ pw.print(" userId=");
+ pw.println(ps.userId);
+ pw.print(" sharedUser=");
+ pw.println(ps.sharedUser);
+ pw.print(" codePath=");
+ pw.println(ps.codePathString);
+ pw.print(" resourcePath=");
+ pw.println(ps.resourcePathString);
+ }
+ }
+ }
+
+ void dumpPermissionsLPr(PrintWriter pw, String packageName, DumpState dumpState) {
+ boolean printedSomething = false;
+ for (BasePermission p : mPermissions.values()) {
+ if (packageName != null && !packageName.equals(p.sourcePackage)) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Permissions:");
+ printedSomething = true;
+ }
+ pw.print(" Permission ["); pw.print(p.name); pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(p)));
+ pw.println("):");
+ pw.print(" sourcePackage="); pw.println(p.sourcePackage);
+ pw.print(" uid="); pw.print(p.uid);
+ pw.print(" gids="); pw.print(PackageManagerService.arrayToString(p.gids));
+ pw.print(" type="); pw.print(p.type);
+ pw.print(" prot="); pw.println(p.protectionLevel);
+ if (p.packageSetting != null) {
+ pw.print(" packageSetting="); pw.println(p.packageSetting);
+ }
+ if (p.perm != null) {
+ pw.print(" perm="); pw.println(p.perm);
+ }
+ }
+ }
+
+ void dumpSharedUsersLPr(PrintWriter pw, String packageName, DumpState dumpState) {
+ boolean printedSomething = false;
+ for (SharedUserSetting su : mSharedUsers.values()) {
+ if (packageName != null && su != dumpState.getSharedUser()) {
+ continue;
+ }
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Shared users:");
+ printedSomething = true;
+ }
+ pw.print(" SharedUser [");
+ pw.print(su.name);
+ pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(su)));
+ pw.println("):");
+ pw.print(" userId=");
+ pw.print(su.userId);
+ pw.print(" gids=");
+ pw.println(PackageManagerService.arrayToString(su.gids));
+ pw.println(" grantedPermissions:");
+ for (String s : su.grantedPermissions) {
+ pw.print(" ");
+ pw.println(s);
+ }
+ }
+ }
+
+ void dumpReadMessagesLPr(PrintWriter pw, DumpState dumpState) {
+ pw.println("Settings parse messages:");
+ pw.print(mReadMessages.toString());
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/pm/SharedUserSetting.java b/services/java/com/android/server/pm/SharedUserSetting.java
new file mode 100644
index 0000000..76826ea
--- /dev/null
+++ b/services/java/com/android/server/pm/SharedUserSetting.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 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.pm;
+
+import java.util.HashSet;
+
+/**
+ * Settings data for a particular shared user ID we know about.
+ */
+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 + "}";
+ }
+}
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
new file mode 100644
index 0000000..76fa5ab
--- /dev/null
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2011 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.pm;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+public class UserManager {
+ private static final String TAG_NAME = "name";
+
+ private static final String ATTR_FLAGS = "flags";
+
+ private static final String ATTR_ID = "id";
+
+ private static final String TAG_USERS = "users";
+
+ private static final String TAG_USER = "user";
+
+ private static final String LOG_TAG = "UserManager";
+
+ private static final String USER_INFO_DIR = "system" + File.separator + "users";
+ private static final String USER_LIST_FILENAME = "userlist.xml";
+
+ private SparseArray<UserInfo> mUsers;
+
+ private final File mUsersDir;
+ private final File mUserListFile;
+ private int[] mUserIds;
+
+ private Installer mInstaller;
+ private File mBaseUserPath;
+
+ /**
+ * Available for testing purposes.
+ */
+ UserManager(File dataDir, File baseUserPath) {
+ mUsersDir = new File(dataDir, USER_INFO_DIR);
+ mUsersDir.mkdirs();
+ mBaseUserPath = baseUserPath;
+ FileUtils.setPermissions(mUsersDir.toString(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG
+ |FileUtils.S_IROTH|FileUtils.S_IXOTH,
+ -1, -1);
+ mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
+ readUserList();
+ }
+
+ public UserManager(Installer installer, File baseUserPath) {
+ this(Environment.getDataDirectory(), baseUserPath);
+ mInstaller = installer;
+ }
+
+ public List<UserInfo> getUsers() {
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ for (int i = 0; i < mUsers.size(); i++) {
+ users.add(mUsers.valueAt(i));
+ }
+ return users;
+ }
+
+ /**
+ * Returns an array of user ids. This array is cached here for quick access, so do not modify or
+ * cache it elsewhere.
+ * @return the array of user ids.
+ */
+ int[] getUserIds() {
+ return mUserIds;
+ }
+
+ private void readUserList() {
+ mUsers = new SparseArray<UserInfo>();
+ if (!mUserListFile.exists()) {
+ fallbackToSingleUser();
+ return;
+ }
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(mUserListFile);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ Slog.e(LOG_TAG, "Unable to read user list");
+ fallbackToSingleUser();
+ return;
+ }
+
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
+ String id = parser.getAttributeValue(null, ATTR_ID);
+ UserInfo user = readUser(Integer.parseInt(id));
+ if (user != null) {
+ mUsers.put(user.id, user);
+ }
+ }
+ }
+ updateUserIds();
+ } catch (IOException ioe) {
+ fallbackToSingleUser();
+ } catch (XmlPullParserException pe) {
+ fallbackToSingleUser();
+ }
+ }
+
+ private void fallbackToSingleUser() {
+ // Create the primary user
+ UserInfo primary = new UserInfo(0, "Primary",
+ UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+ mUsers.put(0, primary);
+ updateUserIds();
+
+ writeUserList();
+ writeUser(primary);
+ }
+
+ /*
+ * Writes the user file in this format:
+ *
+ * <user flags="20039023" id="0">
+ * <name>Primary</name>
+ * </user>
+ */
+ private void writeUser(UserInfo userInfo) {
+ try {
+ final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
+ final FileOutputStream fos = new FileOutputStream(mUserFile);
+ final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+ // XmlSerializer serializer = XmlUtils.serializerInstance();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(bos, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, TAG_USER);
+ serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
+ serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
+
+ serializer.startTag(null, TAG_NAME);
+ serializer.text(userInfo.name);
+ serializer.endTag(null, TAG_NAME);
+
+ serializer.endTag(null, TAG_USER);
+
+ serializer.endDocument();
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+ }
+ }
+
+ /*
+ * Writes the user list file in this format:
+ *
+ * <users>
+ * <user id="0"></user>
+ * <user id="2"></user>
+ * </users>
+ */
+ private void writeUserList() {
+ try {
+ final FileOutputStream fos = new FileOutputStream(mUserListFile);
+ final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+ // XmlSerializer serializer = XmlUtils.serializerInstance();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(bos, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, TAG_USERS);
+
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo user = mUsers.valueAt(i);
+ serializer.startTag(null, TAG_USER);
+ serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
+ serializer.endTag(null, TAG_USER);
+ }
+
+ serializer.endTag(null, TAG_USERS);
+
+ serializer.endDocument();
+ } catch (IOException ioe) {
+ Slog.e(LOG_TAG, "Error writing user list");
+ }
+ }
+
+ private UserInfo readUser(int id) {
+ int flags = 0;
+ String name = null;
+
+ FileInputStream fis = null;
+ try {
+ File userFile = new File(mUsersDir, Integer.toString(id) + ".xml");
+ fis = new FileInputStream(userFile);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ Slog.e(LOG_TAG, "Unable to read user " + id);
+ return null;
+ }
+
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
+ String storedId = parser.getAttributeValue(null, ATTR_ID);
+ if (Integer.parseInt(storedId) != id) {
+ Slog.e(LOG_TAG, "User id does not match the file name");
+ return null;
+ }
+ String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
+ flags = Integer.parseInt(flagString);
+
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ }
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ name = parser.getText();
+ }
+ }
+ }
+ fis.close();
+
+ UserInfo userInfo = new UserInfo(id, name, flags);
+ return userInfo;
+
+ } catch (IOException ioe) {
+ } catch (XmlPullParserException pe) {
+ }
+ return null;
+ }
+
+ public UserInfo createUser(String name, int flags, List<ApplicationInfo> apps) {
+ int userId = getNextAvailableId();
+ UserInfo userInfo = new UserInfo(userId, name, flags);
+ File userPath = new File(mBaseUserPath, Integer.toString(userId));
+ if (!createPackageFolders(userId, userPath, apps)) {
+ return null;
+ }
+ mUsers.put(userId, userInfo);
+ writeUserList();
+ writeUser(userInfo);
+ updateUserIds();
+ return userInfo;
+ }
+
+ /**
+ * Removes a user and all data directories created for that user. This method should be called
+ * after the user's processes have been terminated.
+ * @param id the user's id
+ */
+ public void removeUser(int id) {
+ // Remove from the list
+ UserInfo userInfo = mUsers.get(id);
+ if (userInfo != null) {
+ // Remove this user from the list
+ mUsers.remove(id);
+ // Remove user file
+ File userFile = new File(mUsersDir, id + ".xml");
+ userFile.delete();
+ // Update the user list
+ writeUserList();
+ // Remove the data directories for all packages for this user
+ removePackageFolders(id);
+ updateUserIds();
+ }
+ }
+
+ public void installPackageForAllUsers(String packageName, int uid) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.createUserData(packageName, PackageManager.getUid(userId, uid),
+ userId);
+ }
+ }
+
+ public void clearUserDataForAllUsers(String packageName) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.clearUserData(packageName, userId);
+ }
+ }
+
+ public void removePackageForAllUsers(String packageName) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.remove(packageName, userId);
+ }
+ }
+
+ /**
+ * Caches the list of user ids in an array, adjusting the array size when necessary.
+ */
+ private void updateUserIds() {
+ if (mUserIds == null || mUserIds.length != mUsers.size()) {
+ mUserIds = new int[mUsers.size()];
+ }
+ for (int i = 0; i < mUsers.size(); i++) {
+ mUserIds[i] = mUsers.keyAt(i);
+ }
+ }
+
+ /**
+ * Returns the next available user id, filling in any holes in the ids.
+ * @return
+ */
+ private int getNextAvailableId() {
+ int i = 0;
+ while (i < Integer.MAX_VALUE) {
+ if (mUsers.indexOfKey(i) < 0) {
+ break;
+ }
+ i++;
+ }
+ return i;
+ }
+
+ private boolean createPackageFolders(int id, File userPath, final List<ApplicationInfo> apps) {
+ // mInstaller may not be available for unit-tests.
+ if (mInstaller == null || apps == null) return true;
+
+ final long startTime = SystemClock.elapsedRealtime();
+ // Create the user path
+ userPath.mkdir();
+ FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+ | FileUtils.S_IXOTH, -1, -1);
+
+ // Create the individual data directories
+ for (ApplicationInfo app : apps) {
+ if (app.uid > android.os.Process.FIRST_APPLICATION_UID
+ && app.uid < PackageManager.PER_USER_RANGE) {
+ mInstaller.createUserData(app.packageName,
+ PackageManager.getUid(id, app.uid), id);
+ }
+ }
+ final long stopTime = SystemClock.elapsedRealtime();
+ Log.i(LOG_TAG,
+ "Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
+ return true;
+ }
+
+ private boolean removePackageFolders(int id) {
+ // mInstaller may not be available for unit-tests.
+ if (mInstaller == null) return true;
+
+ mInstaller.removeUserDataDirs(id);
+ return true;
+ }
+}
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
new file mode 100644
index 0000000..3791cc4
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2011 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 an
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.os.UEventObserver;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * UsbDeviceManager manages USB state in device mode.
+ */
+public class UsbDeviceManager {
+ private static final String TAG = UsbDeviceManager.class.getSimpleName();
+ private static final boolean LOG = false;
+
+ private static final String USB_CONNECTED_MATCH =
+ "DEVPATH=/devices/virtual/switch/usb_connected";
+ private static final String USB_CONFIGURATION_MATCH =
+ "DEVPATH=/devices/virtual/switch/usb_configuration";
+ private static final String USB_FUNCTIONS_MATCH =
+ "DEVPATH=/devices/virtual/usb_composite/";
+ private static final String USB_CONNECTED_PATH =
+ "/sys/class/switch/usb_connected/state";
+ private static final String USB_CONFIGURATION_PATH =
+ "/sys/class/switch/usb_configuration/state";
+ private static final String USB_COMPOSITE_CLASS_PATH =
+ "/sys/class/usb_composite";
+
+ private static final int MSG_UPDATE_STATE = 0;
+ private static final int MSG_FUNCTION_ENABLED = 1;
+ private static final int MSG_FUNCTION_DISABLED = 2;
+
+ // Delay for debouncing USB disconnects.
+ // We often get rapid connect/disconnect events when enabling USB functions,
+ // which need debouncing.
+ private static final int UPDATE_DELAY = 1000;
+
+ // current connected and configuration state
+ private int mConnected;
+ private int mConfiguration;
+
+ // last broadcasted connected and configuration state
+ private int mLastConnected = -1;
+ private int mLastConfiguration = -1;
+
+ // lists of enabled and disabled USB functions
+ private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
+ private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
+
+ private boolean mSystemReady;
+
+ private UsbAccessory mCurrentAccessory;
+ // USB functions that are enabled by default, to restore after exiting accessory mode
+ private final ArrayList<String> mDefaultFunctions = new ArrayList<String>();
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+ private final UsbSettingsManager mSettingsManager;
+ private final boolean mHasUsbAccessory;
+
+ private final void readCurrentAccessoryLocked() {
+ if (mHasUsbAccessory) {
+ String[] strings = nativeGetAccessoryStrings();
+ if (strings != null) {
+ mCurrentAccessory = new UsbAccessory(strings);
+ Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
+ if (mSystemReady) {
+ mSettingsManager.accessoryAttached(mCurrentAccessory);
+ }
+ } else {
+ Log.e(TAG, "nativeGetAccessoryStrings failed");
+ }
+ }
+ }
+
+ /*
+ * Handles USB function enable/disable events
+ */
+ private final void functionEnabledLocked(String function, boolean enabled) {
+ if (enabled) {
+ if (!mEnabledFunctions.contains(function)) {
+ mEnabledFunctions.add(function);
+ }
+ mDisabledFunctions.remove(function);
+
+ if (UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) {
+ readCurrentAccessoryLocked();
+ }
+ } else {
+ if (!mDisabledFunctions.contains(function)) {
+ mDisabledFunctions.add(function);
+ }
+ mEnabledFunctions.remove(function);
+ }
+ }
+
+ /*
+ * Listens for uevent messages from the kernel to monitor the USB state
+ */
+ private final UEventObserver mUEventObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "USB UEVENT: " + event.toString());
+ }
+
+ synchronized (mLock) {
+ String name = event.get("SWITCH_NAME");
+ String state = event.get("SWITCH_STATE");
+ if (name != null && state != null) {
+ try {
+ int intState = Integer.parseInt(state);
+ if ("usb_connected".equals(name)) {
+ mConnected = intState;
+ // trigger an Intent broadcast
+ if (mSystemReady) {
+ // debounce disconnects to avoid problems bringing up USB tethering
+ update(mConnected == 0);
+ }
+ } else if ("usb_configuration".equals(name)) {
+ mConfiguration = intState;
+ // trigger an Intent broadcast
+ if (mSystemReady) {
+ update(mConnected == 0);
+ }
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Could not parse switch state from event " + event);
+ }
+ } else {
+ String function = event.get("FUNCTION");
+ String enabledStr = event.get("ENABLED");
+ if (function != null && enabledStr != null) {
+ // Note: we do not broadcast a change when a function is enabled or disabled.
+ // We just record the state change for the next broadcast.
+ int what = ("1".equals(enabledStr) ?
+ MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED);
+ Message msg = Message.obtain(mHandler, what);
+ msg.obj = function;
+ mHandler.sendMessage(msg);
+ }
+ }
+ }
+ }
+ };
+
+ private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ // handle accessories attached at boot time
+ synchronized (mLock) {
+ if (mCurrentAccessory != null) {
+ mSettingsManager.accessoryAttached(mCurrentAccessory);
+ }
+ }
+ }
+ };
+
+ public UsbDeviceManager(Context context, UsbSettingsManager settingsManager) {
+ mContext = context;
+ mSettingsManager = settingsManager;
+ PackageManager pm = mContext.getPackageManager();
+ mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
+
+ synchronized (mLock) {
+ init(); // set initial status
+
+ // Watch for USB configuration changes
+ if (mConfiguration >= 0) {
+ mUEventObserver.startObserving(USB_CONNECTED_MATCH);
+ mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
+ mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
+ }
+ }
+ }
+
+ private final void init() {
+ char[] buffer = new char[1024];
+ boolean inAccessoryMode = false;
+
+ // Read initial USB state
+ mConfiguration = -1;
+ try {
+ FileReader file = new FileReader(USB_CONNECTED_PATH);
+ int len = file.read(buffer, 0, 1024);
+ file.close();
+ mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ file = new FileReader(USB_CONFIGURATION_PATH);
+ len = file.read(buffer, 0, 1024);
+ file.close();
+ mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ } catch (FileNotFoundException e) {
+ Slog.i(TAG, "This kernel does not have USB configuration switch support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
+ if (mConfiguration < 0) {
+ // This may happen in the emulator or devices without USB device mode support
+ return;
+ }
+
+ // Read initial list of enabled and disabled functions (device mode)
+ try {
+ File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
+ for (int i = 0; i < files.length; i++) {
+ File file = new File(files[i], "enable");
+ FileReader reader = new FileReader(file);
+ int len = reader.read(buffer, 0, 1024);
+ reader.close();
+ int value = Integer.valueOf((new String(buffer, 0, len)).trim());
+ String functionName = files[i].getName();
+ if (value == 1) {
+ mEnabledFunctions.add(functionName);
+ if (UsbManager.USB_FUNCTION_ACCESSORY.equals(functionName)) {
+ // The USB accessory driver is on by default, but it might have been
+ // enabled before the USB service has initialized.
+ inAccessoryMode = true;
+ } else if (!UsbManager.USB_FUNCTION_ADB.equals(functionName)) {
+ // adb is enabled/disabled automatically by the adbd daemon,
+ // so don't treat it as a default function.
+ mDefaultFunctions.add(functionName);
+ }
+ } else {
+ mDisabledFunctions.add(functionName);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "This kernel does not have USB composite class support");
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ }
+
+ // handle the case where an accessory switched the driver to accessory mode
+ // before the framework finished booting
+ if (inAccessoryMode) {
+ readCurrentAccessoryLocked();
+
+ // FIXME - if we booted in accessory mode, then we have no way to figure out
+ // which functions are enabled by default.
+ // For now, assume that MTP or mass storage are the only possibilities
+ if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) {
+ mDefaultFunctions.add(UsbManager.USB_FUNCTION_MTP);
+ } else if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
+ mDefaultFunctions.add(UsbManager.USB_FUNCTION_MASS_STORAGE);
+ }
+ }
+ }
+
+ public void systemReady() {
+ synchronized (mLock) {
+ update(false);
+ if (mCurrentAccessory != null) {
+ Log.d(TAG, "accessoryAttached at systemReady");
+ // its still too early to handle accessories, so add a BOOT_COMPLETED receiver
+ // to handle this later.
+ mContext.registerReceiver(mBootCompletedReceiver,
+ new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+ }
+ mSystemReady = true;
+ }
+ }
+
+ /*
+ * Sends a message to update the USB connected and configured state (device mode).
+ * If delayed is true, then we add a small delay in sending the message to debounce
+ * the USB connection when enabling USB tethering.
+ */
+ private final void update(boolean delayed) {
+ mHandler.removeMessages(MSG_UPDATE_STATE);
+ mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
+ }
+
+ /* returns the currently attached USB accessory (device mode) */
+ public UsbAccessory getCurrentAccessory() {
+ return mCurrentAccessory;
+ }
+
+ /* opens the currently attached USB accessory (device mode) */
+ public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
+ synchronized (mLock) {
+ if (mCurrentAccessory == null) {
+ throw new IllegalArgumentException("no accessory attached");
+ }
+ if (!mCurrentAccessory.equals(accessory)) {
+ Log.e(TAG, accessory.toString() + " does not match current accessory "
+ + mCurrentAccessory);
+ throw new IllegalArgumentException("accessory not attached");
+ }
+ mSettingsManager.checkPermission(mCurrentAccessory);
+ return nativeOpenAccessory();
+ }
+ }
+
+ /*
+ * This handler is for deferred handling of events related to device mode and accessories.
+ */
+ private final Handler mHandler = new Handler() {
+ private void addEnabledFunctionsLocked(Intent intent) {
+ // include state of all USB functions in our extras
+ for (int i = 0; i < mEnabledFunctions.size(); i++) {
+ intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
+ }
+ for (int i = 0; i < mDisabledFunctions.size(); i++) {
+ intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mLock) {
+ switch (msg.what) {
+ case MSG_UPDATE_STATE:
+ if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
+ if (mConnected == 0) {
+ if (UsbManager.isFunctionEnabled(
+ UsbManager.USB_FUNCTION_ACCESSORY)) {
+ // make sure accessory mode is off, and restore default functions
+ Log.d(TAG, "exited USB accessory mode");
+ if (!UsbManager.setFunctionEnabled
+ (UsbManager.USB_FUNCTION_ACCESSORY, false)) {
+ Log.e(TAG, "could not disable accessory function");
+ }
+ int count = mDefaultFunctions.size();
+ for (int i = 0; i < count; i++) {
+ String function = mDefaultFunctions.get(i);
+ if (!UsbManager.setFunctionEnabled(function, true)) {
+ Log.e(TAG, "could not reenable function " + function);
+ }
+ }
+
+ if (mCurrentAccessory != null) {
+ mSettingsManager.accessoryDetached(mCurrentAccessory);
+ mCurrentAccessory = null;
+ }
+ }
+ }
+
+ final ContentResolver cr = mContext.getContentResolver();
+ if (Settings.Secure.getInt(cr,
+ Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
+ Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
+ return;
+ }
+
+ mLastConnected = mConnected;
+ mLastConfiguration = mConfiguration;
+
+ // send a sticky broadcast containing current USB state
+ Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
+ intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
+ addEnabledFunctionsLocked(intent);
+ mContext.sendStickyBroadcast(intent);
+ }
+ break;
+ case MSG_FUNCTION_ENABLED:
+ case MSG_FUNCTION_DISABLED:
+ functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
+ break;
+ }
+ }
+ }
+ };
+
+ public void dump(FileDescriptor fd, PrintWriter pw) {
+ synchronized (mLock) {
+ pw.println(" USB Device State:");
+ pw.print(" Enabled Functions: ");
+ for (int i = 0; i < mEnabledFunctions.size(); i++) {
+ pw.print(mEnabledFunctions.get(i) + " ");
+ }
+ pw.println("");
+ pw.print(" Disabled Functions: ");
+ for (int i = 0; i < mDisabledFunctions.size(); i++) {
+ pw.print(mDisabledFunctions.get(i) + " ");
+ }
+ pw.println("");
+ pw.print(" Default Functions: ");
+ for (int i = 0; i < mDefaultFunctions.size(); i++) {
+ pw.print(mDefaultFunctions.get(i) + " ");
+ }
+ pw.println("");
+ pw.println(" mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
+ pw.println(" mCurrentAccessory: " + mCurrentAccessory);
+ }
+ }
+
+ private native String[] nativeGetAccessoryStrings();
+ private native ParcelFileDescriptor nativeOpenAccessory();
+}
diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java
new file mode 100644
index 0000000..923b049
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbHostManager.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2011 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 an
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.IUsbManager;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.os.UEventObserver;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileReader;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * UsbHostManager manages USB state in host mode.
+ */
+public class UsbHostManager {
+ private static final String TAG = UsbHostManager.class.getSimpleName();
+ private static final boolean LOG = false;
+
+ // contains all connected USB devices
+ private final HashMap<String,UsbDevice> mDevices = new HashMap<String,UsbDevice>();
+
+ // USB busses to exclude from USB host support
+ private final String[] mHostBlacklist;
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+ private final UsbSettingsManager mSettingsManager;
+
+ public UsbHostManager(Context context, UsbSettingsManager settingsManager) {
+ mContext = context;
+ mSettingsManager = settingsManager;
+ mHostBlacklist = context.getResources().getStringArray(
+ com.android.internal.R.array.config_usbHostBlacklist);
+ }
+
+ private boolean isBlackListed(String deviceName) {
+ int count = mHostBlacklist.length;
+ for (int i = 0; i < count; i++) {
+ if (deviceName.startsWith(mHostBlacklist[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* returns true if the USB device should not be accessible by applications */
+ private boolean isBlackListed(int clazz, int subClass, int protocol) {
+ // blacklist hubs
+ if (clazz == UsbConstants.USB_CLASS_HUB) return true;
+
+ // blacklist HID boot devices (mouse and keyboard)
+ if (clazz == UsbConstants.USB_CLASS_HID &&
+ subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /* Called from JNI in monitorUsbHostBus() to report new USB devices */
+ private void usbDeviceAdded(String deviceName, int vendorID, int productID,
+ int deviceClass, int deviceSubclass, int deviceProtocol,
+ /* array of quintuples containing id, class, subclass, protocol
+ and number of endpoints for each interface */
+ int[] interfaceValues,
+ /* array of quadruples containing address, attributes, max packet size
+ and interval for each endpoint */
+ int[] endpointValues) {
+
+ if (isBlackListed(deviceName) ||
+ isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
+ return;
+ }
+
+ synchronized (mLock) {
+ if (mDevices.get(deviceName) != null) {
+ Log.w(TAG, "device already on mDevices list: " + deviceName);
+ return;
+ }
+
+ int numInterfaces = interfaceValues.length / 5;
+ Parcelable[] interfaces = new UsbInterface[numInterfaces];
+ try {
+ // repackage interfaceValues as an array of UsbInterface
+ int intf, endp, ival = 0, eval = 0;
+ for (intf = 0; intf < numInterfaces; intf++) {
+ int interfaceId = interfaceValues[ival++];
+ int interfaceClass = interfaceValues[ival++];
+ int interfaceSubclass = interfaceValues[ival++];
+ int interfaceProtocol = interfaceValues[ival++];
+ int numEndpoints = interfaceValues[ival++];
+
+ Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
+ for (endp = 0; endp < numEndpoints; endp++) {
+ int address = endpointValues[eval++];
+ int attributes = endpointValues[eval++];
+ int maxPacketSize = endpointValues[eval++];
+ int interval = endpointValues[eval++];
+ endpoints[endp] = new UsbEndpoint(address, attributes,
+ maxPacketSize, interval);
+ }
+
+ // don't allow if any interfaces are blacklisted
+ if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) {
+ return;
+ }
+ interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
+ interfaceSubclass, interfaceProtocol, endpoints);
+ }
+ } catch (Exception e) {
+ // beware of index out of bound exceptions, which might happen if
+ // a device does not set bNumEndpoints correctly
+ Log.e(TAG, "error parsing USB descriptors", e);
+ return;
+ }
+
+ UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
+ deviceClass, deviceSubclass, deviceProtocol, interfaces);
+ mDevices.put(deviceName, device);
+ mSettingsManager.deviceAttached(device);
+ }
+ }
+
+ /* Called from JNI in monitorUsbHostBus to report USB device removal */
+ private void usbDeviceRemoved(String deviceName) {
+ synchronized (mLock) {
+ UsbDevice device = mDevices.remove(deviceName);
+ if (device != null) {
+ mSettingsManager.deviceDetached(device);
+ }
+ }
+ }
+
+ public void systemReady() {
+ synchronized (mLock) {
+ // Create a thread to call into native code to wait for USB host events.
+ // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
+ Runnable runnable = new Runnable() {
+ public void run() {
+ monitorUsbHostBus();
+ }
+ };
+ new Thread(null, runnable, "UsbService host thread").start();
+ }
+ }
+
+ /* Returns a list of all currently attached USB devices */
+ public void getDeviceList(Bundle devices) {
+ synchronized (mLock) {
+ for (String name : mDevices.keySet()) {
+ devices.putParcelable(name, mDevices.get(name));
+ }
+ }
+ }
+
+ /* Opens the specified USB device */
+ public ParcelFileDescriptor openDevice(String deviceName) {
+ synchronized (mLock) {
+ if (isBlackListed(deviceName)) {
+ throw new SecurityException("USB device is on a restricted bus");
+ }
+ UsbDevice device = mDevices.get(deviceName);
+ if (device == null) {
+ // if it is not in mDevices, it either does not exist or is blacklisted
+ throw new IllegalArgumentException(
+ "device " + deviceName + " does not exist or is restricted");
+ }
+ mSettingsManager.checkPermission(device);
+ return nativeOpenDevice(deviceName);
+ }
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw) {
+ synchronized (mLock) {
+ pw.println(" USB Host State:");
+ for (String name : mDevices.keySet()) {
+ pw.println(" " + name + ": " + mDevices.get(name));
+ }
+ }
+ }
+
+ private native void monitorUsbHostBus();
+ private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
+}
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index a151af0..21e5997 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -10,608 +10,142 @@
* 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
+ * See the License for the specific language governing permissions an
* limitations under the License.
*/
package com.android.server.usb;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbEndpoint;
-import android.hardware.usb.UsbInterface;
-import android.hardware.usb.UsbManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
-import android.os.UEventObserver;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.Slog;
import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
/**
- * UsbService monitors for changes to USB state.
- * This includes code for both USB host support (where the android device is the host)
- * as well as USB device support (android device is connected to a USB host).
- * Accessory mode is a special case of USB device mode, where the android device is
- * connected to a USB host that supports the android accessory protocol.
+ * UsbService manages all USB related state, including both host and device support.
+ * Host related events and calls are delegated to UsbHostManager, and device related
+ * support is delegated to UsbDeviceManager.
*/
public class UsbService extends IUsbManager.Stub {
- private static final String TAG = UsbService.class.getSimpleName();
- private static final boolean LOG = false;
-
- private static final String USB_CONNECTED_MATCH =
- "DEVPATH=/devices/virtual/switch/usb_connected";
- private static final String USB_CONFIGURATION_MATCH =
- "DEVPATH=/devices/virtual/switch/usb_configuration";
- private static final String USB_FUNCTIONS_MATCH =
- "DEVPATH=/devices/virtual/usb_composite/";
- private static final String USB_CONNECTED_PATH =
- "/sys/class/switch/usb_connected/state";
- private static final String USB_CONFIGURATION_PATH =
- "/sys/class/switch/usb_configuration/state";
- private static final String USB_COMPOSITE_CLASS_PATH =
- "/sys/class/usb_composite";
-
- private static final int MSG_UPDATE_STATE = 0;
- private static final int MSG_FUNCTION_ENABLED = 1;
- private static final int MSG_FUNCTION_DISABLED = 2;
-
- // Delay for debouncing USB disconnects.
- // We often get rapid connect/disconnect events when enabling USB functions,
- // which need debouncing.
- private static final int UPDATE_DELAY = 1000;
-
- // current connected and configuration state
- private int mConnected;
- private int mConfiguration;
-
- // last broadcasted connected and configuration state
- private int mLastConnected = -1;
- private int mLastConfiguration = -1;
-
- // lists of enabled and disabled USB functions (for USB device mode)
- private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
- private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
-
- // contains all connected USB devices (for USB host mode)
- private final HashMap<String,UsbDevice> mDevices = new HashMap<String,UsbDevice>();
-
- // USB busses to exclude from USB host support
- private final String[] mHostBlacklist;
-
- private boolean mSystemReady;
-
- private UsbAccessory mCurrentAccessory;
- // USB functions that are enabled by default, to restore after exiting accessory mode
- private final ArrayList<String> mDefaultFunctions = new ArrayList<String>();
-
private final Context mContext;
- private final Object mLock = new Object();
- private final UsbDeviceSettingsManager mDeviceManager;
- private final boolean mHasUsbHost;
- private final boolean mHasUsbAccessory;
+ private UsbDeviceManager mDeviceManager;
+ private UsbHostManager mHostManager;
+ private final UsbSettingsManager mSettingsManager;
- private final void readCurrentAccessoryLocked() {
- if (mHasUsbAccessory) {
- String[] strings = nativeGetAccessoryStrings();
- if (strings != null) {
- mCurrentAccessory = new UsbAccessory(strings);
- Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
- if (mSystemReady) {
- mDeviceManager.accessoryAttached(mCurrentAccessory);
- }
- } else {
- Log.e(TAG, "nativeGetAccessoryStrings failed");
- }
- }
- }
-
- /*
- * Handles USB function enable/disable events (device mode)
- */
- private final void functionEnabledLocked(String function, boolean enabled) {
- if (enabled) {
- if (!mEnabledFunctions.contains(function)) {
- mEnabledFunctions.add(function);
- }
- mDisabledFunctions.remove(function);
-
- if (UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) {
- readCurrentAccessoryLocked();
- }
- } else {
- if (!mDisabledFunctions.contains(function)) {
- mDisabledFunctions.add(function);
- }
- mEnabledFunctions.remove(function);
- }
- }
-
- /*
- * Listens for uevent messages from the kernel to monitor the USB state (device mode)
- */
- private final UEventObserver mUEventObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "USB UEVENT: " + event.toString());
- }
-
- synchronized (mLock) {
- String name = event.get("SWITCH_NAME");
- String state = event.get("SWITCH_STATE");
- if (name != null && state != null) {
- try {
- int intState = Integer.parseInt(state);
- if ("usb_connected".equals(name)) {
- mConnected = intState;
- // trigger an Intent broadcast
- if (mSystemReady) {
- // debounce disconnects to avoid problems bringing up USB tethering
- update(mConnected == 0);
- }
- } else if ("usb_configuration".equals(name)) {
- mConfiguration = intState;
- // trigger an Intent broadcast
- if (mSystemReady) {
- update(mConnected == 0);
- }
- }
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Could not parse switch state from event " + event);
- }
- } else {
- String function = event.get("FUNCTION");
- String enabledStr = event.get("ENABLED");
- if (function != null && enabledStr != null) {
- // Note: we do not broadcast a change when a function is enabled or disabled.
- // We just record the state change for the next broadcast.
- int what = ("1".equals(enabledStr) ?
- MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED);
- Message msg = Message.obtain(mHandler, what);
- msg.obj = function;
- mHandler.sendMessage(msg);
- }
- }
- }
- }
- };
-
- private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- // handle accessories attached at boot time
- synchronized (mLock) {
- if (mCurrentAccessory != null) {
- mDeviceManager.accessoryAttached(mCurrentAccessory);
- }
- }
- }
- };
public UsbService(Context context) {
mContext = context;
- mDeviceManager = new UsbDeviceSettingsManager(context);
+ mSettingsManager = new UsbSettingsManager(context);
PackageManager pm = mContext.getPackageManager();
- mHasUsbHost = pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST);
- mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
-
- mHostBlacklist = context.getResources().getStringArray(
- com.android.internal.R.array.config_usbHostBlacklist);
-
- synchronized (mLock) {
- init(); // set initial status
-
- // Watch for USB configuration changes
- if (mConfiguration >= 0) {
- mUEventObserver.startObserving(USB_CONNECTED_MATCH);
- mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
- mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
- }
- }
- }
-
- private final void init() {
- char[] buffer = new char[1024];
- boolean inAccessoryMode = false;
-
- // Read initial USB state (device mode)
- mConfiguration = -1;
- try {
- FileReader file = new FileReader(USB_CONNECTED_PATH);
- int len = file.read(buffer, 0, 1024);
- file.close();
- mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
-
- file = new FileReader(USB_CONFIGURATION_PATH);
- len = file.read(buffer, 0, 1024);
- file.close();
- mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
-
- } catch (FileNotFoundException e) {
- Slog.i(TAG, "This kernel does not have USB configuration switch support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
- if (mConfiguration < 0) {
- // This may happen in the emulator or devices without USB device mode support
- return;
- }
-
- // Read initial list of enabled and disabled functions (device mode)
- try {
- File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
- for (int i = 0; i < files.length; i++) {
- File file = new File(files[i], "enable");
- FileReader reader = new FileReader(file);
- int len = reader.read(buffer, 0, 1024);
- reader.close();
- int value = Integer.valueOf((new String(buffer, 0, len)).trim());
- String functionName = files[i].getName();
- if (value == 1) {
- mEnabledFunctions.add(functionName);
- if (UsbManager.USB_FUNCTION_ACCESSORY.equals(functionName)) {
- // The USB accessory driver is on by default, but it might have been
- // enabled before the USB service has initialized.
- inAccessoryMode = true;
- } else if (!UsbManager.USB_FUNCTION_ADB.equals(functionName)) {
- // adb is enabled/disabled automatically by the adbd daemon,
- // so don't treat it as a default function.
- mDefaultFunctions.add(functionName);
- }
- } else {
- mDisabledFunctions.add(functionName);
- }
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have USB composite class support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
+ if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
+ mHostManager = new UsbHostManager(context, mSettingsManager);
}
-
- // handle the case where an accessory switched the driver to accessory mode
- // before the framework finished booting
- if (inAccessoryMode) {
- readCurrentAccessoryLocked();
-
- // FIXME - if we booted in accessory mode, then we have no way to figure out
- // which functions are enabled by default.
- // For now, assume that MTP or mass storage are the only possibilities
- if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) {
- mDefaultFunctions.add(UsbManager.USB_FUNCTION_MTP);
- } else if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
- mDefaultFunctions.add(UsbManager.USB_FUNCTION_MASS_STORAGE);
- }
- }
- }
-
- private boolean isBlackListed(String deviceName) {
- int count = mHostBlacklist.length;
- for (int i = 0; i < count; i++) {
- if (deviceName.startsWith(mHostBlacklist[i])) {
- return true;
- }
+ if (new File("/sys/class/usb_composite").exists()) {
+ mDeviceManager = new UsbDeviceManager(context, mSettingsManager);
}
- return false;
- }
-
- /* returns true if the USB device should not be accessible by applications (host mode) */
- private boolean isBlackListed(int clazz, int subClass, int protocol) {
- // blacklist hubs
- if (clazz == UsbConstants.USB_CLASS_HUB) return true;
-
- // blacklist HID boot devices (mouse and keyboard)
- if (clazz == UsbConstants.USB_CLASS_HID &&
- subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
- return true;
- }
-
- return false;
- }
-
- /* Called from JNI in monitorUsbHostBus() to report new USB devices (host mode) */
- private void usbDeviceAdded(String deviceName, int vendorID, int productID,
- int deviceClass, int deviceSubclass, int deviceProtocol,
- /* array of quintuples containing id, class, subclass, protocol
- and number of endpoints for each interface */
- int[] interfaceValues,
- /* array of quadruples containing address, attributes, max packet size
- and interval for each endpoint */
- int[] endpointValues) {
-
- if (isBlackListed(deviceName) ||
- isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
- return;
- }
-
- synchronized (mLock) {
- if (mDevices.get(deviceName) != null) {
- Log.w(TAG, "device already on mDevices list: " + deviceName);
- return;
- }
-
- int numInterfaces = interfaceValues.length / 5;
- Parcelable[] interfaces = new UsbInterface[numInterfaces];
- try {
- // repackage interfaceValues as an array of UsbInterface
- int intf, endp, ival = 0, eval = 0;
- for (intf = 0; intf < numInterfaces; intf++) {
- int interfaceId = interfaceValues[ival++];
- int interfaceClass = interfaceValues[ival++];
- int interfaceSubclass = interfaceValues[ival++];
- int interfaceProtocol = interfaceValues[ival++];
- int numEndpoints = interfaceValues[ival++];
-
- Parcelable[] endpoints = new UsbEndpoint[numEndpoints];
- for (endp = 0; endp < numEndpoints; endp++) {
- int address = endpointValues[eval++];
- int attributes = endpointValues[eval++];
- int maxPacketSize = endpointValues[eval++];
- int interval = endpointValues[eval++];
- endpoints[endp] = new UsbEndpoint(address, attributes,
- maxPacketSize, interval);
- }
-
- // don't allow if any interfaces are blacklisted
- if (isBlackListed(interfaceClass, interfaceSubclass, interfaceProtocol)) {
- return;
- }
- interfaces[intf] = new UsbInterface(interfaceId, interfaceClass,
- interfaceSubclass, interfaceProtocol, endpoints);
- }
- } catch (Exception e) {
- // beware of index out of bound exceptions, which might happen if
- // a device does not set bNumEndpoints correctly
- Log.e(TAG, "error parsing USB descriptors", e);
- return;
- }
-
- UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
- deviceClass, deviceSubclass, deviceProtocol, interfaces);
- mDevices.put(deviceName, device);
- mDeviceManager.deviceAttached(device);
- }
- }
-
- /* Called from JNI in monitorUsbHostBus to report USB device removal (host mode) */
- private void usbDeviceRemoved(String deviceName) {
- synchronized (mLock) {
- UsbDevice device = mDevices.remove(deviceName);
- if (device != null) {
- mDeviceManager.deviceDetached(device);
- }
- }
- }
-
- private void initHostSupport() {
- // Create a thread to call into native code to wait for USB host events.
- // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
- Runnable runnable = new Runnable() {
- public void run() {
- monitorUsbHostBus();
- }
- };
- new Thread(null, runnable, "UsbService host thread").start();
}
public void systemReady() {
- synchronized (mLock) {
- if (mHasUsbHost) {
- // start monitoring for connected USB devices
- initHostSupport();
- }
-
- update(false);
- if (mCurrentAccessory != null) {
- Log.d(TAG, "accessoryAttached at systemReady");
- // its still too early to handle accessories, so add a BOOT_COMPLETED receiver
- // to handle this later.
- mContext.registerReceiver(mBootCompletedReceiver,
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
- }
- mSystemReady = true;
+ if (mDeviceManager != null) {
+ mDeviceManager.systemReady();
+ }
+ if (mHostManager != null) {
+ mHostManager.systemReady();
}
- }
-
- /*
- * Sends a message to update the USB connected and configured state (device mode).
- * If delayed is true, then we add a small delay in sending the message to debounce
- * the USB connection when enabling USB tethering.
- */
- private final void update(boolean delayed) {
- mHandler.removeMessages(MSG_UPDATE_STATE);
- mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
}
/* Returns a list of all currently attached USB devices (host mdoe) */
public void getDeviceList(Bundle devices) {
- synchronized (mLock) {
- for (String name : mDevices.keySet()) {
- devices.putParcelable(name, mDevices.get(name));
- }
+ if (mHostManager != null) {
+ mHostManager.getDeviceList(devices);
}
}
/* Opens the specified USB device (host mode) */
public ParcelFileDescriptor openDevice(String deviceName) {
- synchronized (mLock) {
- if (isBlackListed(deviceName)) {
- throw new SecurityException("USB device is on a restricted bus");
- }
- UsbDevice device = mDevices.get(deviceName);
- if (device == null) {
- // if it is not in mDevices, it either does not exist or is blacklisted
- throw new IllegalArgumentException(
- "device " + deviceName + " does not exist or is restricted");
- }
- mDeviceManager.checkPermission(device);
- return nativeOpenDevice(deviceName);
+ if (mHostManager != null) {
+ return mHostManager.openDevice(deviceName);
+ } else {
+ return null;
}
}
/* returns the currently attached USB accessory (device mode) */
public UsbAccessory getCurrentAccessory() {
- return mCurrentAccessory;
+ if (mDeviceManager != null) {
+ return mDeviceManager.getCurrentAccessory();
+ } else {
+ return null;
+ }
}
/* opens the currently attached USB accessory (device mode) */
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
- synchronized (mLock) {
- if (mCurrentAccessory == null) {
- throw new IllegalArgumentException("no accessory attached");
- }
- if (!mCurrentAccessory.equals(accessory)) {
- Log.e(TAG, accessory.toString() + " does not match current accessory "
- + mCurrentAccessory);
- throw new IllegalArgumentException("accessory not attached");
- }
- mDeviceManager.checkPermission(mCurrentAccessory);
- return nativeOpenAccessory();
+ if (mDeviceManager != null) {
+ return openAccessory(accessory);
+ } else {
+ return null;
}
}
public void setDevicePackage(UsbDevice device, String packageName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mDeviceManager.setDevicePackage(device, packageName);
+ mSettingsManager.setDevicePackage(device, packageName);
}
public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mDeviceManager.setAccessoryPackage(accessory, packageName);
+ mSettingsManager.setAccessoryPackage(accessory, packageName);
}
public boolean hasDevicePermission(UsbDevice device) {
- return mDeviceManager.hasPermission(device);
+ return mSettingsManager.hasPermission(device);
}
public boolean hasAccessoryPermission(UsbAccessory accessory) {
- return mDeviceManager.hasPermission(accessory);
+ return mSettingsManager.hasPermission(accessory);
}
public void requestDevicePermission(UsbDevice device, String packageName,
PendingIntent pi) {
- mDeviceManager.requestPermission(device, packageName, pi);
+ mSettingsManager.requestPermission(device, packageName, pi);
}
public void requestAccessoryPermission(UsbAccessory accessory, String packageName,
PendingIntent pi) {
- mDeviceManager.requestPermission(accessory, packageName, pi);
+ mSettingsManager.requestPermission(accessory, packageName, pi);
}
public void grantDevicePermission(UsbDevice device, int uid) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mDeviceManager.grantDevicePermission(device, uid);
+ mSettingsManager.grantDevicePermission(device, uid);
}
public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mDeviceManager.grantAccessoryPermission(accessory, uid);
+ mSettingsManager.grantAccessoryPermission(accessory, uid);
}
public boolean hasDefaults(String packageName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- return mDeviceManager.hasDefaults(packageName);
+ return mSettingsManager.hasDefaults(packageName);
}
public void clearDefaults(String packageName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mDeviceManager.clearDefaults(packageName);
+ mSettingsManager.clearDefaults(packageName);
}
- /*
- * This handler is for deferred handling of events related to device mode and accessories.
- */
- private final Handler mHandler = new Handler() {
- private void addEnabledFunctionsLocked(Intent intent) {
- // include state of all USB functions in our extras
- for (int i = 0; i < mEnabledFunctions.size(); i++) {
- intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
- }
- for (int i = 0; i < mDisabledFunctions.size(); i++) {
- intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
- }
- }
-
- @Override
- public void handleMessage(Message msg) {
- synchronized (mLock) {
- switch (msg.what) {
- case MSG_UPDATE_STATE:
- if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
- if (mConnected == 0) {
- if (UsbManager.isFunctionEnabled(
- UsbManager.USB_FUNCTION_ACCESSORY)) {
- // make sure accessory mode is off, and restore default functions
- Log.d(TAG, "exited USB accessory mode");
- if (!UsbManager.setFunctionEnabled
- (UsbManager.USB_FUNCTION_ACCESSORY, false)) {
- Log.e(TAG, "could not disable accessory function");
- }
- int count = mDefaultFunctions.size();
- for (int i = 0; i < count; i++) {
- String function = mDefaultFunctions.get(i);
- if (!UsbManager.setFunctionEnabled(function, true)) {
- Log.e(TAG, "could not reenable function " + function);
- }
- }
-
- if (mCurrentAccessory != null) {
- mDeviceManager.accessoryDetached(mCurrentAccessory);
- mCurrentAccessory = null;
- }
- }
- }
-
- final ContentResolver cr = mContext.getContentResolver();
- if (Settings.Secure.getInt(cr,
- Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
- Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
- return;
- }
-
- mLastConnected = mConnected;
- mLastConfiguration = mConfiguration;
-
- // send a sticky broadcast containing current USB state
- Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
- intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
- addEnabledFunctionsLocked(intent);
- mContext.sendStickyBroadcast(intent);
- }
- break;
- case MSG_FUNCTION_ENABLED:
- case MSG_FUNCTION_DISABLED:
- functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
- break;
- }
- }
- }
- };
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -622,40 +156,14 @@ public class UsbService extends IUsbManager.Stub {
return;
}
- synchronized (mLock) {
- pw.println("USB Manager State:");
-
- pw.println(" USB Device State:");
- pw.print(" Enabled Functions: ");
- for (int i = 0; i < mEnabledFunctions.size(); i++) {
- pw.print(mEnabledFunctions.get(i) + " ");
- }
- pw.println("");
- pw.print(" Disabled Functions: ");
- for (int i = 0; i < mDisabledFunctions.size(); i++) {
- pw.print(mDisabledFunctions.get(i) + " ");
- }
- pw.println("");
- pw.print(" Default Functions: ");
- for (int i = 0; i < mDefaultFunctions.size(); i++) {
- pw.print(mDefaultFunctions.get(i) + " ");
- }
- pw.println("");
- pw.println(" mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
- pw.println(" mCurrentAccessory: " + mCurrentAccessory);
+ pw.println("USB Manager State:");
- pw.println(" USB Host State:");
- for (String name : mDevices.keySet()) {
- pw.println(" " + name + ": " + mDevices.get(name));
- }
+ if (mDeviceManager != null) {
mDeviceManager.dump(fd, pw);
}
+ if (mHostManager != null) {
+ mHostManager.dump(fd, pw);
+ }
+ mSettingsManager.dump(fd, pw);
}
-
- // host support
- private native void monitorUsbHostBus();
- private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
- // accessory support
- private native String[] nativeGetAccessoryStrings();
- private native ParcelFileDescriptor nativeOpenAccessory();
}
diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index de0b114..9113677 100644
--- a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -59,9 +59,9 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-class UsbDeviceSettingsManager {
+class UsbSettingsManager {
- private static final String TAG = "UsbDeviceSettingsManager";
+ private static final String TAG = "UsbSettingsManager";
private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml");
private final Context mContext;
@@ -363,7 +363,7 @@ class UsbDeviceSettingsManager {
}
MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
- public UsbDeviceSettingsManager(Context context) {
+ public UsbSettingsManager(Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
synchronized (mLock) {
diff --git a/services/java/com/android/server/wm/InputFilter.java b/services/java/com/android/server/wm/InputFilter.java
new file mode 100644
index 0000000..8f0001a
--- /dev/null
+++ b/services/java/com/android/server/wm/InputFilter.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2011 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.wm;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.InputEvent;
+import android.view.InputEventConsistencyVerifier;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy;
+
+/**
+ * Filters input events before they are dispatched to the system.
+ * <p>
+ * At most one input filter can be installed by calling
+ * {@link WindowManagerService#setInputFilter}. When an input filter is installed, the
+ * system's behavior changes as follows:
+ * <ul>
+ * <li>Input events are first delivered to the {@link WindowManagerPolicy}
+ * interception methods before queueing as usual. This critical step takes care of managing
+ * the power state of the device and handling wake keys.</li>
+ * <li>Input events are then asynchronously delivered to the input filter's
+ * {@link #onInputEvent(InputEvent)} method instead of being enqueued for dispatch to
+ * applications as usual. The input filter only receives input events that were
+ * generated by input device; the input filter will not receive input events that were
+ * injected into the system by other means, such as by instrumentation.</li>
+ * <li>The input filter processes and optionally transforms the stream of events. For example,
+ * it may transform a sequence of motion events representing an accessibility gesture into
+ * a different sequence of motion events, key presses or other system-level interactions.
+ * The input filter can send events to be dispatched by calling
+ * {@link #sendInputEvent(InputEvent)} and passing appropriate policy flags for the
+ * input event.</li>
+ * </ul>
+ * </p>
+ * <h3>The importance of input event consistency</h3>
+ * <p>
+ * The input filter mechanism is very low-level. At a minimum, it needs to ensure that it
+ * sends an internally consistent stream of input events to the dispatcher. There are
+ * very important invariants to be maintained.
+ * </p><p>
+ * For example, if a key down is sent, a corresponding key up should also be sent eventually.
+ * Likewise, for touch events, each pointer must individually go down with
+ * {@link MotionEvent#ACTION_DOWN} or {@link MotionEvent#ACTION_POINTER_DOWN} and then
+ * individually go up with {@link MotionEvent#ACTION_POINTER_UP} or {@link MotionEvent#ACTION_UP}
+ * and the sequence of pointer ids used must be consistent throughout the gesture.
+ * </p><p>
+ * Sometimes a filter may wish to cancel a previously dispatched key or motion. It should
+ * use {@link KeyEvent#FLAG_CANCELED} or {@link MotionEvent#ACTION_CANCEL} accordingly.
+ * </p><p>
+ * The input filter must take into account the fact that the input events coming from different
+ * devices or even different sources all consist of distinct streams of input.
+ * Use {@link InputEvent#getDeviceId()} and {@link InputEvent#getSource()} to identify
+ * the source of the event and its semantics. There are be multiple sources of keys,
+ * touches and other input: they must be kept separate.
+ * </p>
+ * <h3>Policy flags</h3>
+ * <p>
+ * Input events received from the dispatcher and sent to the dispatcher have policy flags
+ * associated with them. Policy flags control some functions of the dispatcher.
+ * </p><p>
+ * The early policy interception decides whether an input event should be delivered
+ * to applications or dropped. The policy indicates its decision by setting the
+ * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} policy flag. The input filter may
+ * sometimes receive events that do not have this flag set. It should take note of
+ * the fact that the policy intends to drop the event, clean up its state, and
+ * then send appropriate cancelation events to the dispatcher if needed.
+ * </p><p>
+ * For example, suppose the input filter is processing a gesture and one of the touch events
+ * it receives does not have the {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag set.
+ * The input filter should clear its internal state about the gesture and then send key or
+ * motion events to the dispatcher to cancel any keys or pointers that are down.
+ * </p><p>
+ * Corollary: Events that set sent to the dispatcher should usually include the
+ * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag. Otherwise, they will be dropped!
+ * </p><p>
+ * It may be prudent to disable automatic key repeating for synthetically generated
+ * keys by setting the {@link WindowManagerPolicy#FLAG_DISABLE_KEY_REPEAT} policy flag.
+ * </p>
+ */
+public abstract class InputFilter {
+ private static final int MSG_INSTALL = 1;
+ private static final int MSG_UNINSTALL = 2;
+ private static final int MSG_INPUT_EVENT = 3;
+
+ private final H mH;
+ private Host mHost;
+
+ // Consistency verifiers for debugging purposes.
+ private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this,
+ InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT,
+ "InputFilter#InboundInputEventConsistencyVerifier") : null;
+ private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this,
+ InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT,
+ "InputFilter#OutboundInputEventConsistencyVerifier") : null;
+
+ /**
+ * Creates the input filter.
+ *
+ * @param looper The looper to run callbacks on.
+ */
+ public InputFilter(Looper looper) {
+ mH = new H(looper);
+ }
+
+ /**
+ * Called when the input filter is installed.
+ * This method is guaranteed to be non-reentrant.
+ *
+ * @param host The input filter host environment.
+ */
+ final void install(Host host) {
+ mH.obtainMessage(MSG_INSTALL, host).sendToTarget();
+ }
+
+ /**
+ * Called when the input filter is uninstalled.
+ * This method is guaranteed to be non-reentrant.
+ */
+ final void uninstall() {
+ mH.obtainMessage(MSG_UNINSTALL).sendToTarget();
+ }
+
+ /**
+ * Called to enqueue the input event for filtering.
+ * The event will be recycled after the input filter processes it.
+ * This method is guaranteed to be non-reentrant.
+ *
+ * @param event The input event to enqueue.
+ */
+ final void filterInputEvent(InputEvent event, int policyFlags) {
+ mH.obtainMessage(MSG_INPUT_EVENT, policyFlags, 0, event).sendToTarget();
+ }
+
+ /**
+ * Sends an input event to the dispatcher.
+ *
+ * @param event The input event to publish.
+ * @param policyFlags The input event policy flags.
+ */
+ public void sendInputEvent(InputEvent event, int policyFlags) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+ if (mHost == null) {
+ throw new IllegalStateException("Cannot send input event because the input filter " +
+ "is not installed.");
+ }
+ if (mOutboundInputEventConsistencyVerifier != null) {
+ mOutboundInputEventConsistencyVerifier.onInputEvent(event, 0);
+ }
+ mHost.sendInputEvent(event, policyFlags);
+ }
+
+ /**
+ * Called when an input event has been received from the dispatcher.
+ * <p>
+ * The default implementation sends the input event back to the dispatcher, unchanged.
+ * </p><p>
+ * The event will be recycled when this method returns. If you want to keep it around,
+ * make a copy!
+ * </p>
+ *
+ * @param event The input event that was received.
+ * @param policyFlags The input event policy flags.
+ */
+ public void onInputEvent(InputEvent event, int policyFlags) {
+ sendInputEvent(event, policyFlags);
+ }
+
+ /**
+ * Called when the filter is installed into the dispatch pipeline.
+ * <p>
+ * This method is called before the input filter receives any input events.
+ * The input filter should take this opportunity to prepare itself.
+ * </p>
+ */
+ public void onInstalled() {
+ }
+
+ /**
+ * Called when the filter is uninstalled from the dispatch pipeline.
+ * <p>
+ * This method is called after the input filter receives its last input event.
+ * The input filter should take this opportunity to clean up.
+ * </p>
+ */
+ public void onUninstalled() {
+ }
+
+ private final class H extends Handler {
+ public H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INSTALL:
+ mHost = (Host)msg.obj;
+ if (mInboundInputEventConsistencyVerifier != null) {
+ mInboundInputEventConsistencyVerifier.reset();
+ }
+ if (mOutboundInputEventConsistencyVerifier != null) {
+ mOutboundInputEventConsistencyVerifier.reset();
+ }
+ onInstalled();
+ break;
+
+ case MSG_UNINSTALL:
+ try {
+ onUninstalled();
+ } finally {
+ mHost = null;
+ }
+ break;
+
+ case MSG_INPUT_EVENT: {
+ final InputEvent event = (InputEvent)msg.obj;
+ try {
+ if (mInboundInputEventConsistencyVerifier != null) {
+ mInboundInputEventConsistencyVerifier.onInputEvent(event, 0);
+ }
+ onInputEvent(event, msg.arg1);
+ } finally {
+ event.recycle();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ interface Host {
+ public void sendInputEvent(InputEvent event, int policyFlags);
+ }
+}
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index 4eda684..3095c37 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -41,6 +41,7 @@ import android.view.PointerIcon;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
import java.io.File;
import java.io.FileNotFoundException;
@@ -78,8 +79,10 @@ public class InputManager {
private static native void nativeRegisterInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle, boolean monitor);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
+ private static native void nativeSetInputFilterEnabled(boolean enable);
private static native int nativeInjectInputEvent(InputEvent event,
- int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
+ int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
+ int policyFlags);
private static native void nativeSetInputWindows(InputWindow[] windows);
private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
private static native void nativeSetSystemUiVisibility(int visibility);
@@ -118,6 +121,11 @@ public class InputManager {
/** The key is down but is a virtual key press that is being emulated by the system. */
public static final int KEY_STATE_VIRTUAL = 2;
+ // State for the currently installed input filter.
+ final Object mInputFilterLock = new Object();
+ InputFilter mInputFilter;
+ InputFilterHost mInputFilterHost;
+
public InputManager(Context context, WindowManagerService windowManagerService) {
this.mContext = context;
this.mWindowManagerService = windowManagerService;
@@ -272,7 +280,42 @@ public class InputManager {
nativeUnregisterInputChannel(inputChannel);
}
-
+
+ /**
+ * Sets an input filter that will receive all input events before they are dispatched.
+ * The input filter may then reinterpret input events or inject new ones.
+ *
+ * To ensure consistency, the input dispatcher automatically drops all events
+ * in progress whenever an input filter is installed or uninstalled. After an input
+ * filter is uninstalled, it can no longer send input events unless it is reinstalled.
+ * Any events it attempts to send after it has been uninstalled will be dropped.
+ *
+ * @param filter The input filter, or null to remove the current filter.
+ */
+ public void setInputFilter(InputFilter filter) {
+ synchronized (mInputFilterLock) {
+ final InputFilter oldFilter = mInputFilter;
+ if (oldFilter == filter) {
+ return; // nothing to do
+ }
+
+ if (oldFilter != null) {
+ mInputFilter = null;
+ mInputFilterHost.disconnectLocked();
+ mInputFilterHost = null;
+ oldFilter.uninstall();
+ }
+
+ if (filter != null) {
+ mInputFilter = filter;
+ mInputFilterHost = new InputFilterHost();
+ filter.install(mInputFilterHost);
+ }
+
+ nativeSetInputFilterEnabled(filter != null);
+ }
+ }
+
/**
* Injects an input event into the event system on behalf of an application.
* The synchronization mode determines whether the method blocks while waiting for
@@ -308,9 +351,10 @@ public class InputManager {
throw new IllegalArgumentException("timeoutMillis must be positive");
}
- return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
+ return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis,
+ WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
}
-
+
/**
* Gets information about the input device with the specified id.
* @param id The device id.
@@ -410,10 +454,31 @@ public class InputManager {
}
}
+ private final class InputFilterHost implements InputFilter.Host {
+ private boolean mDisconnected;
+
+ public void disconnectLocked() {
+ mDisconnected = true;
+ }
+
+ public void sendInputEvent(InputEvent event, int policyFlags) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+
+ synchronized (mInputFilterLock) {
+ if (!mDisconnected) {
+ nativeInjectInputEvent(event, 0, 0, INPUT_EVENT_INJECTION_SYNC_NONE, 0,
+ policyFlags | WindowManagerPolicy.FLAG_FILTERED);
+ }
+ }
+ }
+ }
+
/*
* Callbacks from native.
*/
- private class Callbacks {
+ private final class Callbacks {
static final String TAG = "InputManager-Callbacks";
private static final boolean DEBUG_VIRTUAL_KEYS = false;
@@ -441,7 +506,19 @@ public class InputManager {
return mWindowManagerService.mInputMonitor.notifyANR(
inputApplicationHandle, inputWindowHandle);
}
-
+
+ @SuppressWarnings("unused")
+ final boolean filterInputEvent(InputEvent event, int policyFlags) {
+ synchronized (mInputFilterLock) {
+ if (mInputFilter != null) {
+ mInputFilter.filterInputEvent(event, policyFlags);
+ return false;
+ }
+ }
+ event.recycle();
+ return true;
+ }
+
@SuppressWarnings("unused")
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
diff --git a/services/java/com/android/server/wm/ViewServer.java b/services/java/com/android/server/wm/ViewServer.java
index cebd5e7..70cb26a 100644
--- a/services/java/com/android/server/wm/ViewServer.java
+++ b/services/java/com/android/server/wm/ViewServer.java
@@ -49,7 +49,7 @@ class ViewServer implements Runnable {
// Debug facility
private static final String LOG_TAG = "ViewServer";
- private static final String VALUE_PROTOCOL_VERSION = "3";
+ private static final String VALUE_PROTOCOL_VERSION = "4";
private static final String VALUE_SERVER_VERSION = "4";
// Protocol commands
@@ -73,19 +73,6 @@ class ViewServer implements Runnable {
private ExecutorService mThreadPool;
/**
- * Creates a new ViewServer associated with the specified window manager.
- * The server uses the default port {@link #VIEW_SERVER_DEFAULT_PORT}. The server
- * is not started by default.
- *
- * @param windowManager The window manager used to communicate with the views.
- *
- * @see #start()
- */
- ViewServer(WindowManagerService windowManager) {
- this(windowManager, VIEW_SERVER_DEFAULT_PORT);
- }
-
- /**
* Creates a new ViewServer associated with the specified window manager on the
* specified local port. The server is not started by default.
*
@@ -177,7 +164,7 @@ class ViewServer implements Runnable {
// Any uncaught exception will crash the system process
try {
Socket client = mServer.accept();
- if(mThreadPool != null) {
+ if (mThreadPool != null) {
mThreadPool.submit(new ViewServerWorker(client));
} else {
try {
@@ -220,6 +207,7 @@ class ViewServer implements Runnable {
private Socket mClient;
private boolean mNeedWindowListUpdate;
private boolean mNeedFocusedWindowUpdate;
+
public ViewServerWorker(Socket client) {
mClient = client;
mNeedWindowListUpdate = false;
@@ -255,7 +243,7 @@ class ViewServer implements Runnable {
result = mWindowManager.viewServerListWindows(mClient);
} else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
result = mWindowManager.viewServerGetFocusedWindow(mClient);
- } else if(COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
+ } else if (COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
result = windowManagerAutolistLoop();
} else {
result = mWindowManager.viewServerWindowCommand(mClient,
@@ -263,7 +251,7 @@ class ViewServer implements Runnable {
}
if (!result) {
- Slog.w(LOG_TAG, "An error occured with the command: " + command);
+ Slog.w(LOG_TAG, "An error occurred with the command: " + command);
}
} catch(IOException e) {
Slog.w(LOG_TAG, "Connection error: ", e);
@@ -321,11 +309,11 @@ class ViewServer implements Runnable {
needFocusedWindowUpdate = true;
}
}
- if(needWindowListUpdate) {
+ if (needWindowListUpdate) {
out.write("LIST UPDATE\n");
out.flush();
}
- if(needFocusedWindowUpdate) {
+ if (needFocusedWindowUpdate) {
out.write("FOCUS UPDATE\n");
out.flush();
}
@@ -337,6 +325,7 @@ class ViewServer implements Runnable {
try {
out.close();
} catch (IOException e) {
+ // Ignore
}
}
mWindowManager.removeWindowChangeListener(this);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 4ff6b06..d95d4c5 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4631,6 +4631,10 @@ public class WindowManagerService extends IWindowManager.Stub
return mInputManager.monitorInput(inputChannelName);
}
+ public void setInputFilter(InputFilter filter) {
+ mInputManager.setInputFilter(filter);
+ }
+
public InputDevice getInputDevice(int deviceId) {
return mInputManager.getInputDevice(deviceId);
}
@@ -8308,6 +8312,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean state = mHoldingScreenWakeLock.isHeld();
if (holding != state) {
if (holding) {
+ mPolicy.screenOnStartedLw();
mHoldingScreenWakeLock.acquire();
} else {
mPolicy.screenOnStoppedLw();