Merge "Remove unused IntentFilter in LocationManager" into jb-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index d053109..720681a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3012,6 +3012,7 @@
method public android.app.AlertDialog.Builder setNeutralButton(int, android.content.DialogInterface.OnClickListener);
method public android.app.AlertDialog.Builder setNeutralButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
method public android.app.AlertDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+ method public android.app.AlertDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
method public android.app.AlertDialog.Builder setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
method public android.app.AlertDialog.Builder setOnKeyListener(android.content.DialogInterface.OnKeyListener);
method public android.app.AlertDialog.Builder setPositiveButton(int, android.content.DialogInterface.OnClickListener);
@@ -6293,6 +6294,7 @@
field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000
field public static final int FLAG_FACTORY_TEST = 16; // 0x10
field public static final int FLAG_HAS_CODE = 4; // 0x4
+ field public static final int FLAG_INSTALLED = 8388608; // 0x800000
field public static final int FLAG_KILL_AFTER_RESTORE = 65536; // 0x10000
field public static final int FLAG_LARGE_HEAP = 1048576; // 0x100000
field public static final int FLAG_PERSISTENT = 8; // 0x8
@@ -6474,6 +6476,7 @@
method public abstract int checkSignatures(int, int);
method public abstract void clearPackagePreferredActivities(java.lang.String);
method public abstract java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]);
+ method public abstract void extendVerificationTimeout(int, int, long);
method public abstract android.graphics.drawable.Drawable getActivityIcon(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.graphics.drawable.Drawable getActivityIcon(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.ActivityInfo getActivityInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -6532,7 +6535,6 @@
method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
method public abstract void verifyPendingInstall(int, int);
- method public abstract void extendVerificationTimeout(int, int, long);
field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
@@ -6593,6 +6595,7 @@
field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
+ field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
field public static final int PERMISSION_DENIED = -1; // 0xffffffff
field public static final int PERMISSION_GRANTED = 0; // 0x0
field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
@@ -6603,7 +6606,6 @@
field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
field public static final int VERIFICATION_ALLOW = 1; // 0x1
field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
- field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80
}
public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
@@ -6629,6 +6631,17 @@
field public java.lang.String packageName;
}
+ public class PackageUserState {
+ ctor public PackageUserState();
+ ctor public PackageUserState(android.content.pm.PackageUserState);
+ field public java.util.HashSet disabledComponents;
+ field public int enabled;
+ field public java.util.HashSet enabledComponents;
+ field public boolean installed;
+ field public boolean notLaunched;
+ field public boolean stopped;
+ }
+
public class PathPermission extends android.os.PatternMatcher {
ctor public PathPermission(java.lang.String, int, java.lang.String, java.lang.String);
ctor public PathPermission(android.os.Parcel);
@@ -11477,9 +11490,13 @@
method public void setWakeMode(android.content.Context, int);
method public void start() throws java.lang.IllegalStateException;
method public void stop() throws java.lang.IllegalStateException;
+ field public static final int MEDIA_ERROR_IO = -1004; // 0xfffffc14
+ field public static final int MEDIA_ERROR_MALFORMED = -1007; // 0xfffffc11
field public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; // 0xc8
field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
+ field public static final int MEDIA_ERROR_TIMED_OUT = -110; // 0xffffff92
field public static final int MEDIA_ERROR_UNKNOWN = 1; // 0x1
+ field public static final int MEDIA_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be
field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
@@ -20057,8 +20074,8 @@
method public void setContentView(android.view.View);
method public void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public void setInteractive(boolean);
- field public static final java.lang.String SERVICE_INTERFACE = "android.service.dreams.Dream";
field public static final java.lang.String METADATA_NAME_CONFIG_ACTIVITY = "android.service.dreams.config_activity";
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.dreams.Dream";
}
}
@@ -21316,6 +21333,7 @@
method public int checkSignatures(int, int);
method public void clearPackagePreferredActivities(java.lang.String);
method public java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]);
+ method public void extendVerificationTimeout(int, int, long);
method public android.graphics.drawable.Drawable getActivityIcon(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.graphics.drawable.Drawable getActivityIcon(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.ActivityInfo getActivityInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -21373,7 +21391,6 @@
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
method public void verifyPendingInstall(int, int);
- method public void extendVerificationTimeout(int, int, long);
}
public class MockResources extends android.content.res.Resources {
@@ -26928,6 +26945,7 @@
method public synchronized int getDefaultFixedFontSize();
method public synchronized int getDefaultFontSize();
method public synchronized java.lang.String getDefaultTextEncodingName();
+ method public static java.lang.String getDefaultUserAgent(android.content.Context);
method public android.webkit.WebSettings.ZoomDensity getDefaultZoom();
method public boolean getDisplayZoomControls();
method public synchronized boolean getDomStorageEnabled();
@@ -26958,7 +26976,6 @@
method public synchronized boolean getUseWideViewPort();
method public deprecated synchronized int getUserAgent();
method public synchronized java.lang.String getUserAgentString();
- method public static java.lang.String getDefaultUserAgent(android.content.Context);
method public void setAllowContentAccess(boolean);
method public void setAllowFileAccess(boolean);
method public abstract void setAllowFileAccessFromFileURLs(boolean);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index a79eb14..47d6a02 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -393,7 +393,7 @@
private void runStartService() throws Exception {
Intent intent = makeIntent();
System.out.println("Starting service: " + intent);
- ComponentName cn = mAm.startService(null, intent, intent.getType());
+ ComponentName cn = mAm.startService(null, intent, intent.getType(), 0);
if (cn == null) {
System.err.println("Error: Not found; no service started.");
}
@@ -1344,6 +1344,7 @@
" am display-density [reset|DENSITY]\n" +
" am to-uri [INTENT]\n" +
" am to-intent-uri [INTENT]\n" +
+ " am switch-user <USER_ID>\n" +
"\n" +
"am start: start an Activity. Options are:\n" +
" -D: enable debugging\n" +
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index c16e6fb..a52f74a 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -194,6 +194,11 @@
ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
return -errno;
}
+ if (chmod(pkgdir, 0751) < 0) {
+ ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(pkgdir);
+ return -errno;
+ }
if (chown(pkgdir, uid, uid) < 0) {
ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
unlink(pkgdir);
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 7108d68..d51004a 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -384,6 +384,11 @@
// Introducing multi-user, so migrate /data/media contents into /data/media/0
ALOGD("Migrating /data/media for multi-user");
+ // Ensure /data/media
+ if (ensure_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ goto fail;
+ }
+
// /data/media.tmp
char media_tmp_dir[PATH_MAX];
snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index b34fd05..8cc4e69 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -776,7 +776,7 @@
}
private void runInstall() {
- int installFlags = 0;
+ int installFlags = PackageManager.INSTALL_ALL_USERS;
String installerPackageName = null;
String opt;
@@ -811,6 +811,8 @@
} else if (opt.equals("-f")) {
// Override if -s option is specified.
installFlags |= PackageManager.INSTALL_INTERNAL;
+ } else if (opt.equals("-d")) {
+ installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else if (opt.equals("--algo")) {
algo = nextOptionData();
if (algo == null) {
@@ -1105,7 +1107,7 @@
String opt = nextOption();
if (opt != null && opt.equals("-k")) {
- unInstallFlags = PackageManager.DONT_DELETE_DATA;
+ unInstallFlags = PackageManager.DELETE_KEEP_DATA;
}
String pkg = nextArg();
@@ -1473,21 +1475,24 @@
System.err.println(" pm list instrumentation [-f] [TARGET-PACKAGE]");
System.err.println(" pm list features");
System.err.println(" pm list libraries");
+ System.err.println(" pm list users");
System.err.println(" pm path PACKAGE");
System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]");
System.err.println(" [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>]");
System.err.println(" [--originating-uri <URI>] [--referrer <URI>] PATH");
System.err.println(" pm uninstall [-k] PACKAGE");
System.err.println(" pm clear PACKAGE");
- System.err.println(" pm enable PACKAGE_OR_COMPONENT");
- System.err.println(" pm disable PACKAGE_OR_COMPONENT");
- System.err.println(" pm disable-user PACKAGE_OR_COMPONENT");
+ System.err.println(" pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
+ System.err.println(" pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
+ System.err.println(" pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm grant PACKAGE PERMISSION");
System.err.println(" pm revoke PACKAGE PERMISSION");
System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]");
System.err.println(" pm get-install-location");
System.err.println(" pm set-permission-enforced PERMISSION [true|false]");
System.err.println(" pm trim-caches DESIRED_FREE_SPACE");
+ System.err.println(" pm create-user USER_NAME");
+ System.err.println(" pm remove-user USER_ID");
System.err.println("");
System.err.println("pm list packages: prints all packages, optionally only");
System.err.println(" those whose package name contains the text in FILTER. Options:");
@@ -1525,6 +1530,7 @@
System.err.println(" -i: specify the installer package name.");
System.err.println(" -s: install package on sdcard.");
System.err.println(" -f: install package on internal flash.");
+ System.err.println(" -d: allow version code downgrade.");
System.err.println("");
System.err.println("pm uninstall: removes a package from the system. Options:");
System.err.println(" -k: keep the data and cache directories around after package removal.");
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 3197a63..adc9434 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -697,7 +697,8 @@
IApplicationThread app = ApplicationThreadNative.asInterface(b);
Intent service = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
- ComponentName cn = startService(app, service, resolvedType);
+ int userId = data.readInt();
+ ComponentName cn = startService(app, service, resolvedType, userId);
reply.writeNoException();
ComponentName.writeToParcel(cn, reply);
return true;
@@ -709,7 +710,8 @@
IApplicationThread app = ApplicationThreadNative.asInterface(b);
Intent service = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
- int res = stopService(app, service, resolvedType);
+ int userId = data.readInt();
+ int res = stopService(app, service, resolvedType, userId);
reply.writeNoException();
reply.writeInt(res);
return true;
@@ -2523,7 +2525,7 @@
}
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType) throws RemoteException
+ String resolvedType, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2531,6 +2533,7 @@
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
service.writeToParcel(data, 0);
data.writeString(resolvedType);
+ data.writeInt(userId);
mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
ComponentName res = ComponentName.readFromParcel(reply);
@@ -2539,7 +2542,7 @@
return res;
}
public int stopService(IApplicationThread caller, Intent service,
- String resolvedType) throws RemoteException
+ String resolvedType, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2547,6 +2550,7 @@
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
service.writeToParcel(data, 0);
data.writeString(resolvedType);
+ data.writeInt(userId);
mRemote.transact(STOP_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
int res = reply.readInt();
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index e37b3fa..7809e73 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -566,7 +566,14 @@
/**
* Sets the callback that will be called if the dialog is canceled.
+ *
+ * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than
+ * being canceled or one of the supplied choices being selected.
+ * If you are interested in listening for all cases where the dialog is dismissed
+ * and not just when it is canceled, see
+ * {@link #setOnDismissListener(android.content.DialogInterface.OnDismissListener) setOnDismissListener}.</p>
* @see #setCancelable(boolean)
+ * @see #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
*
* @return This Builder object to allow for chaining of calls to set methods
*/
@@ -576,6 +583,16 @@
}
/**
+ * Sets the callback that will be called when the dialog is dismissed for any reason.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods
+ */
+ public Builder setOnDismissListener(OnDismissListener onDismissListener) {
+ P.mOnDismissListener = onDismissListener;
+ return this;
+ }
+
+ /**
* Sets the callback that will be called if a key is dispatched to the dialog.
*
* @return This Builder object to allow for chaining of calls to set methods
@@ -917,6 +934,7 @@
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
+ dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f3f75ce..0f10c4f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -45,6 +45,7 @@
import android.content.pm.UserInfo;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -997,6 +998,21 @@
}
@Override
+ public int installExistingPackage(String packageName)
+ throws NameNotFoundException {
+ try {
+ int res = mPM.installExistingPackage(packageName);
+ if (res == INSTALL_FAILED_INVALID_URI) {
+ throw new NameNotFoundException("Package " + packageName + " doesn't exist");
+ }
+ return res;
+ } catch (RemoteException e) {
+ // Should never happen!
+ throw new NameNotFoundException("Package " + packageName + " doesn't exist");
+ }
+ }
+
+ @Override
public void verifyPendingInstall(int id, int response) {
try {
mPM.verifyPendingInstall(id, response);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 0ae4d06..32086d7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1220,11 +1220,21 @@
@Override
public ComponentName startService(Intent service) {
+ return startServiceAsUser(service, Process.myUserHandle());
+ }
+
+ @Override
+ public boolean stopService(Intent service) {
+ return stopServiceAsUser(service, Process.myUserHandle());
+ }
+
+ @Override
+ public ComponentName startServiceAsUser(Intent service, UserHandle user) {
try {
service.setAllowFds(false);
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service,
- service.resolveTypeIfNeeded(getContentResolver()));
+ service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
if (cn != null && cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
@@ -1237,12 +1247,12 @@
}
@Override
- public boolean stopService(Intent service) {
+ public boolean stopServiceAsUser(Intent service, UserHandle user) {
try {
service.setAllowFds(false);
int res = ActivityManagerNative.getDefault().stopService(
mMainThread.getApplicationThread(), service,
- service.resolveTypeIfNeeded(getContentResolver()));
+ service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to stop service " + service);
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 2cc3b02..16112cb3 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -1106,10 +1106,12 @@
/**
* Set a listener to be invoked when the dialog is canceled.
- * <p>
- * This will only be invoked when the dialog is canceled, if the creator
- * needs to know when it is dismissed in general, use
- * {@link #setOnDismissListener}.
+ *
+ * <p>This will only be invoked when the dialog is canceled.
+ * Cancel events alone will not capture all ways that
+ * the dialog might be dismissed. If the creator needs
+ * to know when a dialog is dismissed in general, use
+ * {@link #setOnDismissListener}.</p>
*
* @param listener The {@link DialogInterface.OnCancelListener} to use.
*/
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index a6d1995..c3e911e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -133,9 +133,9 @@
public PendingIntent getRunningServiceControlPanel(ComponentName service)
throws RemoteException;
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType) throws RemoteException;
+ String resolvedType, int userId) throws RemoteException;
public int stopService(IApplicationThread caller, Intent service,
- String resolvedType) throws RemoteException;
+ String resolvedType, int userId) throws RemoteException;
public boolean stopServiceToken(ComponentName className, IBinder token,
int startId) throws RemoteException;
public void setServiceForeground(ComponentName className, IBinder token,
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index bf83f5e..69c20b0 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -17,10 +17,9 @@
package android.app;
import android.content.Context;
-import android.os.Binder;
-import android.os.RemoteException;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
@@ -88,6 +87,11 @@
mContext = context;
}
+ /** {@hide} */
+ public static NotificationManager from(Context context) {
+ return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
/**
* Post a notification to be shown in the status bar. If a notification with
* the same id has already been posted by your application and has not yet been canceled, it
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1460bf5..dc6d93f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1414,6 +1414,16 @@
public abstract boolean stopService(Intent service);
/**
+ * @hide like {@link #startService(Intent)} but for a specific user.
+ */
+ public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);
+
+ /**
+ * @hide like {@link #stopService(Intent)} but for a specific user.
+ */
+ public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
+
+ /**
* Connect to an application service, creating it if needed. This defines
* a dependency between your application and the service. The given
* <var>conn</var> will receive the service object when it is created and be
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3a13725..4bbe44e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -410,6 +410,18 @@
return mBase.stopService(name);
}
+ /** @hide */
+ @Override
+ public ComponentName startServiceAsUser(Intent service, UserHandle user) {
+ return mBase.startServiceAsUser(service, user);
+ }
+
+ /** @hide */
+ @Override
+ public boolean stopServiceAsUser(Intent name, UserHandle user) {
+ return mBase.stopServiceAsUser(name, user);
+ }
+
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index cbabc7c..1a82d58 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -302,6 +302,12 @@
public static final int FLAG_SUPPORTS_RTL = 1<<22;
/**
+ * Value for {@link #flags}: true if the application is currently
+ * installed for the calling user.
+ */
+ public static final int FLAG_INSTALLED = 1<<23;
+
+ /**
* Value for {@link #flags}: Set to true if the application has been
* installed using the forward lock option.
*
@@ -334,7 +340,8 @@
* {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
* {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_SUPPORTS_XLARGE_SCREENS},
* {@link #FLAG_RESIZEABLE_FOR_SCREENS},
- * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE}
+ * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE},
+ * {@link #FLAG_INSTALLED}.
*/
public int flags = 0;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0be8b83..0e1fe3e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -32,6 +32,7 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.ManifestDigest;
+import android.content.pm.PackageCleanItem;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.PermissionGroupInfo;
@@ -351,7 +352,7 @@
*/
void updateExternalMediaStatus(boolean mounted, boolean reportStatus);
- String nextPackageToClean(String lastPackage);
+ PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
void movePackage(String packageName, IPackageMoveObserver observer, int flags);
@@ -369,6 +370,8 @@
in VerificationParams verificationParams,
in ContainerEncryptionParams encryptionParams);
+ int installExistingPackage(String packageName);
+
void verifyPendingInstall(int id, int verificationCode);
void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
diff --git a/core/java/android/content/pm/ManifestDigest.java b/core/java/android/content/pm/ManifestDigest.java
index f5e72e0..75505bc 100644
--- a/core/java/android/content/pm/ManifestDigest.java
+++ b/core/java/android/content/pm/ManifestDigest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 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 android.content.pm;
import android.os.Parcel;
diff --git a/core/java/android/content/pm/PackageCleanItem.aidl b/core/java/android/content/pm/PackageCleanItem.aidl
new file mode 100644
index 0000000..9bb203e
--- /dev/null
+++ b/core/java/android/content/pm/PackageCleanItem.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2012, 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 android.content.pm;
+
+parcelable PackageCleanItem;
diff --git a/core/java/android/content/pm/PackageCleanItem.java b/core/java/android/content/pm/PackageCleanItem.java
new file mode 100644
index 0000000..eea3b9c
--- /dev/null
+++ b/core/java/android/content/pm/PackageCleanItem.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 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 android.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class PackageCleanItem {
+ public final String packageName;
+ public final boolean andCode;
+
+ public PackageCleanItem(String packageName, boolean andCode) {
+ this.packageName = packageName;
+ this.andCode = andCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ try {
+ if (obj != null) {
+ PackageCleanItem other = (PackageCleanItem)obj;
+ return packageName.equals(other.packageName) && andCode == other.andCode;
+ }
+ } catch (ClassCastException e) {
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + packageName.hashCode();
+ result = 31 * result + (andCode ? 1 : 0);
+ return result;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeString(packageName);
+ dest.writeInt(andCode ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<PackageCleanItem> CREATOR
+ = new Parcelable.Creator<PackageCleanItem>() {
+ public PackageCleanItem createFromParcel(Parcel source) {
+ return new PackageCleanItem(source);
+ }
+
+ public PackageCleanItem[] newArray(int size) {
+ return new PackageCleanItem[size];
+ }
+ };
+
+ private PackageCleanItem(Parcel source) {
+ packageName = source.readString();
+ andCode = source.readInt() != 0;
+ }
+}
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 9625944..a1566da 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -32,6 +32,11 @@
public String packageName;
/**
+ * The android:versionCode of the package.
+ */
+ public int versionCode;
+
+ /**
* Specifies the recommended install location. Can be one of
* {@link #PackageHelper.RECOMMEND_INSTALL_INTERNAL} to install on internal storage
* {@link #PackageHelper.RECOMMEND_INSTALL_EXTERNAL} to install on external media
@@ -58,6 +63,7 @@
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
+ dest.writeInt(versionCode);
dest.writeInt(recommendedInstallLocation);
dest.writeInt(installLocation);
@@ -82,6 +88,7 @@
private PackageInfoLite(Parcel source) {
packageName = source.readString();
+ versionCode = source.readInt();
recommendedInstallLocation = source.readInt();
installLocation = source.readInt();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b3e98e7..0d99d3f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -310,6 +310,23 @@
public static final int INSTALL_FROM_ADB = 0x00000020;
/**
+ * Flag parameter for {@link #installPackage} to indicate that this install
+ * should immediately be visible to all users.
+ *
+ * @hide
+ */
+ public static final int INSTALL_ALL_USERS = 0x00000040;
+
+ /**
+ * Flag parameter for {@link #installPackage} to indicate that it is okay
+ * to install an update to an app where the newly installed app has a lower
+ * version code than the currently installed app.
+ *
+ * @hide
+ */
+ public static final int INSTALL_ALLOW_DOWNGRADE = 0x00000080;
+
+ /**
* Flag parameter for
* {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
* that you don't want to kill the app containing the component. Be careful when you set this
@@ -529,6 +546,14 @@
public static final int INSTALL_FAILED_UID_CHANGED = -24;
/**
+ * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package has an older version code than the currently installed package.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
+
+ /**
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser was given a path that is not a file, or does not end with the expected
@@ -625,7 +650,15 @@
*
* @hide
*/
- public static final int DONT_DELETE_DATA = 0x00000001;
+ public static final int DELETE_KEEP_DATA = 0x00000001;
+
+ /**
+ * Flag parameter for {@link #deletePackage} to indicate that you want the
+ * package deleted for all users.
+ *
+ * @hide
+ */
+ public static final int DELETE_ALL_USERS = 0x00000002;
/**
* Return code for when package deletion succeeds. This is passed to the
@@ -2175,8 +2208,8 @@
if ((flags & GET_SIGNATURES) != 0) {
packageParser.collectCertificates(pkg, 0);
}
- return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, false,
- COMPONENT_ENABLED_STATE_DEFAULT);
+ PackageUserState state = new PackageUserState();
+ return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
}
/**
@@ -2267,6 +2300,14 @@
ContainerEncryptionParams encryptionParams);
/**
+ * If there is already an application with the given package name installed
+ * on the system for other users, also install it for the calling user.
+ * @hide
+ */
+ public abstract int installExistingPackage(String packageName)
+ throws NameNotFoundException;
+
+ /**
* Allows a package listening to the
* {@link Intent#ACTION_PACKAGE_NEEDS_VERIFICATION package verification
* broadcast} to respond to the package manager. The response must include
@@ -2337,7 +2378,8 @@
* @param observer An observer callback to get notified when the package deletion is
* complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
* called when that happens. observer may be null to indicate that no callback is desired.
- * @param flags - possible values: {@link #DONT_DELETE_DATA}
+ * @param flags - possible values: {@link #DELETE_KEEP_DATA},
+ * {@link #DELETE_ALL_USERS}.
*
* @hide
*/
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ac75040..237f5c5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -203,11 +203,14 @@
*/
public static class PackageLite {
public final String packageName;
+ public final int versionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
- public PackageLite(String packageName, int installLocation, List<VerifierInfo> verifiers) {
+ public PackageLite(String packageName, int versionCode,
+ int installLocation, List<VerifierInfo> verifiers) {
this.packageName = packageName;
+ this.versionCode = versionCode;
this.installLocation = installLocation;
this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
}
@@ -243,14 +246,15 @@
return name.endsWith(".apk");
}
+ /*
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
HashSet<String> grantedPermissions) {
-
+ PackageUserState state = new PackageUserState();
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
- grantedPermissions, false, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
- UserHandle.getCallingUserId());
+ grantedPermissions, state, UserHandle.getCallingUserId());
}
+ */
/**
* Generate and return the {@link PackageInfo} for a parsed package.
@@ -260,23 +264,30 @@
*/
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions, boolean stopped, int enabledState) {
+ HashSet<String> grantedPermissions, PackageUserState state) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
- grantedPermissions, stopped, enabledState, UserHandle.getCallingUserId());
+ grantedPermissions, state, UserHandle.getCallingUserId());
+ }
+
+ private static boolean checkUseInstalled(int flags, PackageUserState state) {
+ return state.installed || ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0);
}
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions, boolean stopped, int enabledState, int userId) {
+ HashSet<String> grantedPermissions, PackageUserState state, int userId) {
+ if (!checkUseInstalled(flags, state)) {
+ return null;
+ }
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
pi.versionCode = p.mVersionCode;
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
- pi.applicationInfo = generateApplicationInfo(p, flags, stopped, enabledState, userId);
+ pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
pi.installLocation = p.installLocation;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
@@ -312,7 +323,7 @@
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
- stopped, enabledState, userId);
+ state, userId);
}
}
}
@@ -334,7 +345,7 @@
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
- stopped, enabledState, userId);
+ state, userId);
}
}
}
@@ -355,8 +366,8 @@
final Service service = p.services.get(i);
if (service.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.services[j++] = generateServiceInfo(p.services.get(i), flags, stopped,
- enabledState, userId);
+ pi.services[j++] = generateServiceInfo(p.services.get(i), flags,
+ state, userId);
}
}
}
@@ -377,8 +388,8 @@
final Provider provider = p.providers.get(i);
if (provider.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, stopped,
- enabledState, userId);
+ pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags,
+ state, userId);
}
}
}
@@ -840,11 +851,19 @@
return null;
}
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
+ int versionCode = 0;
+ int numFound = 0;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
String attr = attrs.getAttributeName(i);
if (attr.equals("installLocation")) {
installLocation = attrs.getAttributeIntValue(i,
PARSE_DEFAULT_INSTALL_LOCATION);
+ numFound++;
+ } else if (attr.equals("versionCode")) {
+ versionCode = attrs.getAttributeIntValue(i, 0);
+ numFound++;
+ }
+ if (numFound >= 2) {
break;
}
}
@@ -867,7 +886,7 @@
}
}
- return new PackageLite(pkgName.intern(), installLocation, verifiers);
+ return new PackageLite(pkgName.intern(), versionCode, installLocation, verifiers);
}
/**
@@ -3458,13 +3477,25 @@
}
}
- private static boolean copyNeeded(int flags, Package p, int enabledState, Bundle metaData) {
- if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
- boolean enabled = enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ private static boolean copyNeeded(int flags, Package p,
+ PackageUserState state, Bundle metaData, int userId) {
+ if (userId != 0) {
+ // We always need to copy for other users, since we need
+ // to fix up the uid.
+ return true;
+ }
+ if (state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ boolean enabled = state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
if (p.applicationInfo.enabled != enabled) {
return true;
}
}
+ if (!state.installed) {
+ return true;
+ }
+ if (state.stopped) {
+ return true;
+ }
if ((flags & PackageManager.GET_META_DATA) != 0
&& (metaData != null || p.mAppMetaData != null)) {
return true;
@@ -3476,32 +3507,34 @@
return false;
}
- public static ApplicationInfo generateApplicationInfo(Package p, int flags, boolean stopped,
- int enabledState) {
- return generateApplicationInfo(p, flags, stopped, enabledState, UserHandle.getCallingUserId());
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags,
+ PackageUserState state) {
+ return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId());
}
public static ApplicationInfo generateApplicationInfo(Package p, int flags,
- boolean stopped, int enabledState, int userId) {
+ PackageUserState state, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p, enabledState, null) && userId == 0) {
+ if (!checkUseInstalled(flags, state)) {
+ return null;
+ }
+ if (!copyNeeded(flags, p, state, null, userId)) {
// CompatibilityMode is global state. It's safe to modify the instance
// of the package.
if (!sCompatibilityModeEnabled) {
p.applicationInfo.disableCompatibilityMode();
}
- if (stopped) {
- p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
- } else {
- p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
- }
- if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ // Make sure we report as installed. Also safe to do, since the
+ // default state should be installed (we will always copy if we
+ // need to report it is not installed).
+ p.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+ if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
p.applicationInfo.enabled = true;
- } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
p.applicationInfo.enabled = false;
}
- p.applicationInfo.enabledSetting = enabledState;
+ p.applicationInfo.enabledSetting = state.enabled;
return p.applicationInfo;
}
@@ -3520,18 +3553,23 @@
if (!sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
- if (stopped) {
+ if (state.stopped) {
ai.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
- if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (state.installed) {
+ ai.flags |= ApplicationInfo.FLAG_INSTALLED;
+ } else {
+ ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+ }
+ if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
- } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
ai.enabled = false;
}
- ai.enabledSetting = enabledState;
+ ai.enabledSetting = state.enabled;
return ai;
}
@@ -3578,16 +3616,19 @@
}
}
- public static final ActivityInfo generateActivityInfo(Activity a, int flags, boolean stopped,
- int enabledState, int userId) {
+ public static final ActivityInfo generateActivityInfo(Activity a, int flags,
+ PackageUserState state, int userId) {
if (a == null) return null;
- if (!copyNeeded(flags, a.owner, enabledState, a.metaData) && userId == 0) {
+ if (!checkUseInstalled(flags, state)) {
+ return null;
+ }
+ if (!copyNeeded(flags, a.owner, state, a.metaData, userId)) {
return a.info;
}
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo(a.info);
ai.metaData = a.metaData;
- ai.applicationInfo = generateApplicationInfo(a.owner, flags, stopped, enabledState, userId);
+ ai.applicationInfo = generateApplicationInfo(a.owner, flags, state, userId);
return ai;
}
@@ -3612,17 +3653,19 @@
}
}
- public static final ServiceInfo generateServiceInfo(Service s, int flags, boolean stopped,
- int enabledState, int userId) {
+ public static final ServiceInfo generateServiceInfo(Service s, int flags,
+ PackageUserState state, int userId) {
if (s == null) return null;
- if (!copyNeeded(flags, s.owner, enabledState, s.metaData)
- && userId == UserHandle.getUserId(s.info.applicationInfo.uid)) {
+ if (!checkUseInstalled(flags, state)) {
+ return null;
+ }
+ if (!copyNeeded(flags, s.owner, state, s.metaData, userId)) {
return s.info;
}
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo(s.info);
si.metaData = s.metaData;
- si.applicationInfo = generateApplicationInfo(s.owner, flags, stopped, enabledState, userId);
+ si.applicationInfo = generateApplicationInfo(s.owner, flags, state, userId);
return si;
}
@@ -3655,13 +3698,15 @@
}
}
- public static final ProviderInfo generateProviderInfo(Provider p, int flags, boolean stopped,
- int enabledState, int userId) {
+ public static final ProviderInfo generateProviderInfo(Provider p, int flags,
+ PackageUserState state, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p.owner, enabledState, p.metaData)
+ if (!checkUseInstalled(flags, state)) {
+ return null;
+ }
+ if (!copyNeeded(flags, p.owner, state, p.metaData, userId)
&& ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
- || p.info.uriPermissionPatterns == null)
- && userId == 0) {
+ || p.info.uriPermissionPatterns == null)) {
return p.info;
}
// Make shallow copies so we can store the metadata safely
@@ -3670,7 +3715,7 @@
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
- pi.applicationInfo = generateApplicationInfo(p.owner, flags, stopped, enabledState, userId);
+ pi.applicationInfo = generateApplicationInfo(p.owner, flags, state, userId);
return pi;
}
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
new file mode 100644
index 0000000..1a71bfb
--- /dev/null
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 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 android.content.pm;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+
+import java.util.HashSet;
+
+/**
+ * Per-user state information about a package.
+ */
+public class PackageUserState {
+ public boolean stopped;
+ public boolean notLaunched;
+ public boolean installed;
+ public int enabled;
+
+ public HashSet<String> disabledComponents;
+ public HashSet<String> enabledComponents;
+
+ public PackageUserState() {
+ installed = true;
+ enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+
+ public PackageUserState(PackageUserState o) {
+ installed = o.installed;
+ stopped = o.stopped;
+ notLaunched = o.notLaunched;
+ enabled = o.enabled;
+ disabledComponents = o.disabledComponents != null
+ ? new HashSet<String>(o.disabledComponents) : null;
+ enabledComponents = o.enabledComponents != null
+ ? new HashSet<String>(o.enabledComponents) : null;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 0bc0f91..7642670 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -331,12 +331,16 @@
notifyListener(v1, true /* removed */);
}
if (changes.length() > 0) {
- Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
- serviceInfos.size() + " services:\n" + changes);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
+ serviceInfos.size() + " services:\n" + changes);
+ }
writePersistentServicesLocked();
} else {
- Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
- serviceInfos.size() + " services unchanged");
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
+ serviceInfos.size() + " services unchanged");
+ }
}
mPersistentServicesFileDidNotExist = false;
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index d30ef04..60bf4d68 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -912,4 +912,13 @@
return false;
}
}
+
+ /** {@hide} */
+ public boolean updateLockdownVpn() {
+ try {
+ return mService.updateLockdownVpn();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 92aeff2..3614045 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -26,6 +26,7 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
/**
* Interface that answers queries about, and allows changing, the
@@ -118,7 +119,9 @@
ParcelFileDescriptor establishVpn(in VpnConfig config);
- void startLegacyVpn(in VpnConfig config, in String[] racoon, in String[] mtpd);
+ void startLegacyVpn(in VpnProfile profile);
LegacyVpnInfo getLegacyVpnInfo();
+
+ boolean updateLockdownVpn();
}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 7785368..4e2b5c0 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -101,37 +101,88 @@
}
/**
- * Default constructor associates this handler with the queue for the
+ * Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
- * If there isn't one, this handler won't be able to receive messages.
+ * If this thread does not have a looper, this handler won't be able to receive messages
+ * so an exception is thrown.
*/
public Handler() {
- if (FIND_POTENTIAL_LEAKS) {
- final Class<? extends Handler> klass = getClass();
- if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
- (klass.getModifiers() & Modifier.STATIC) == 0) {
- Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
- klass.getCanonicalName());
- }
- }
-
- mLooper = Looper.myLooper();
- if (mLooper == null) {
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- }
- mQueue = mLooper.mQueue;
- mCallback = null;
- mAsynchronous = false;
+ this(null, false);
}
/**
- * Constructor associates this handler with the queue for the
+ * Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
+ *
+ * If this thread does not have a looper, this handler won't be able to receive messages
+ * so an exception is thrown.
+ *
+ * @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Callback callback) {
+ this(callback, false);
+ }
+
+ /**
+ * Use the provided {@link Looper} instead of the default one.
+ *
+ * @param looper The looper, must not be null.
+ */
+ public Handler(Looper looper) {
+ this(looper, null, false);
+ }
+
+ /**
+ * Use the provided {@link Looper} instead of the default one and take a callback
+ * interface in which to handle messages.
+ *
+ * @param looper The looper, must not be null.
+ * @param callback The callback interface in which to handle messages, or null.
+ */
+ public Handler(Looper looper, Callback callback) {
+ this(looper, callback, false);
+ }
+
+ /**
+ * Use the {@link Looper} for the current thread
+ * and set whether the handler should be asynchronous.
+ *
+ * Handlers are synchronous by default unless this constructor is used to make
+ * one that is strictly asynchronous.
+ *
+ * Asynchronous messages represent interrupts or events that do not require global ordering
+ * with represent to synchronous messages. Asynchronous messages are not subject to
+ * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+ *
+ * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
+ * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
+ *
+ * @hide
+ */
+ public Handler(boolean async) {
+ this(null, async);
+ }
+
+ /**
+ * Use the {@link Looper} for the current thread with the specified callback interface
+ * and set whether the handler should be asynchronous.
+ *
+ * Handlers are synchronous by default unless this constructor is used to make
+ * one that is strictly asynchronous.
+ *
+ * Asynchronous messages represent interrupts or events that do not require global ordering
+ * with represent to synchronous messages. Asynchronous messages are not subject to
+ * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+ *
+ * @param callback The callback interface in which to handle messages, or null.
+ * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
+ * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
+ *
+ * @hide
+ */
+ public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
@@ -148,32 +199,11 @@
}
mQueue = mLooper.mQueue;
mCallback = callback;
- mAsynchronous = false;
+ mAsynchronous = async;
}
/**
- * Use the provided queue instead of the default one.
- */
- public Handler(Looper looper) {
- mLooper = looper;
- mQueue = looper.mQueue;
- mCallback = null;
- mAsynchronous = false;
- }
-
- /**
- * Use the provided queue instead of the default one and take a callback
- * interface in which to handle messages.
- */
- public Handler(Looper looper, Callback callback) {
- mLooper = looper;
- mQueue = looper.mQueue;
- mCallback = callback;
- mAsynchronous = false;
- }
-
- /**
- * Use the provided queue instead of the default one and take a callback
+ * Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages. Also set whether the handler
* should be asynchronous.
*
@@ -184,6 +214,8 @@
* with represent to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
+ * @param looper The looper, must not be null.
+ * @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 20d3ec3..2179fa1 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -362,4 +362,11 @@
* Flush the DNS cache associated with the specified interface.
*/
void flushInterfaceDnsCache(String iface);
+
+ void setFirewallEnabled(boolean enabled);
+ boolean isFirewallEnabled();
+ void setFirewallInterfaceRule(String iface, boolean allow);
+ void setFirewallEgressSourceRule(String addr, boolean allow);
+ void setFirewallEgressDestRule(String addr, int port, boolean allow);
+ void setFirewallUidRule(int uid, boolean allow);
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 0843d85..d33bd80 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -28,6 +28,9 @@
/** @hide A user id to indicate all users on the device */
public static final int USER_ALL = -1;
+ /** @hide A user handle to indicate all users on the device */
+ public static final UserHandle ALL = new UserHandle(USER_ALL);
+
/** @hide A user id to indicate the currently active user */
public static final int USER_CURRENT = -2;
diff --git a/core/java/android/service/dreams/DreamManagerService.java b/core/java/android/service/dreams/DreamManagerService.java
index 4b0f7c5..2cec6c3 100644
--- a/core/java/android/service/dreams/DreamManagerService.java
+++ b/core/java/android/service/dreams/DreamManagerService.java
@@ -105,7 +105,7 @@
public ComponentName[] getDreamComponents() {
// TODO(dsandler) don't load this every time, watch the value
String names = Settings.Secure.getString(mContext.getContentResolver(), SCREENSAVER_COMPONENTS);
- return componentsFromString(names);
+ return names == null ? null : componentsFromString(names);
}
// IDreamManager method
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index ac3dee4..9051285 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -357,11 +357,6 @@
// From the Unicode Line Breaking Algorithm (at least approximately)
boolean isLineBreak = isSpaceOrTab ||
- // .,:; are class IS breakpoints, except when adjacent to digits
- ((c == CHAR_DOT || c == CHAR_COMMA ||
- c == CHAR_COLON || c == CHAR_SEMICOLON) &&
- (j - 1 < here || !Character.isDigit(chs[j - 1 - paraStart])) &&
- (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) ||
// / is class SY and - is class HY, except when followed by a digit
((c == CHAR_SLASH || c == CHAR_HYPHEN) &&
(j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) ||
@@ -959,10 +954,6 @@
private static final char CHAR_NEW_LINE = '\n';
private static final char CHAR_TAB = '\t';
private static final char CHAR_SPACE = ' ';
- private static final char CHAR_DOT = '.';
- private static final char CHAR_COMMA = ',';
- private static final char CHAR_COLON = ':';
- private static final char CHAR_SEMICOLON = ';';
private static final char CHAR_SLASH = '/';
private static final char CHAR_HYPHEN = '-';
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index da10311..211453d 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -1618,7 +1618,7 @@
String result;
long now = System.currentTimeMillis();
- long span = now - millis;
+ long span = Math.abs(now - millis);
synchronized (DateUtils.class) {
if (sNowTime == null) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 61c942d..423135f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -973,6 +973,12 @@
// Start selection mode if needed. We don't need to if we're unchecking something.
if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
+ if (mMultiChoiceModeCallback == null ||
+ !mMultiChoiceModeCallback.hasWrappedCallback()) {
+ throw new IllegalStateException("AbsListView: attempted to start selection mode " +
+ "for CHOICE_MODE_MULTIPLE_MODAL but no choice mode callback was " +
+ "supplied. Call setMultiChoiceModeListener to set a callback.");
+ }
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
}
@@ -5945,6 +5951,10 @@
mWrapped = wrapped;
}
+ public boolean hasWrappedCallback() {
+ return mWrapped != null;
+ }
+
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
if (mWrapped.onCreateActionMode(mode, menu)) {
// Initialize checked graphic state?
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index f91201a..bc78adc 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -175,11 +175,7 @@
@Override
protected void internalSetPadding(int left, int top, int right, int bottom) {
super.internalSetPadding(left, top, right, bottom);
- if (isLayoutRtl()) {
- mBasePadding = mUserPaddingLeft;
- } else {
- mBasePadding = mUserPaddingRight;
- }
+ setBasePadding(isLayoutRtl());
}
@Override
@@ -201,13 +197,21 @@
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
- mBasePadding = getPaddingEnd();
+ setBasePadding(isLayoutRtl());
}
@Override
public void setPaddingRelative(int start, int top, int end, int bottom) {
super.setPaddingRelative(start, top, end, bottom);
- mBasePadding = getPaddingEnd();
+ setBasePadding(isLayoutRtl());
+ }
+
+ private void setBasePadding(boolean isLayoutRtl) {
+ if (isLayoutRtl) {
+ mBasePadding = mPaddingLeft;
+ } else {
+ mBasePadding = mPaddingRight;
+ }
}
@Override
@@ -237,11 +241,11 @@
final int left;
final int right;
if (isLayoutRtl) {
- right = getPaddingEnd();
- left = right - mCheckMarkWidth;
- } else {
- left = width - getPaddingEnd();
+ left = mBasePadding;
right = left + mCheckMarkWidth;
+ } else {
+ right = width - mBasePadding;
+ left = right - mCheckMarkWidth;
}
checkMarkDrawable.setBounds( left, top, right, bottom);
checkMarkDrawable.draw(canvas);
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index ff1dd11..7c8196d 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -284,6 +284,7 @@
mVideoHeight = mp.getVideoHeight();
if (mVideoWidth != 0 && mVideoHeight != 0) {
getHolder().setFixedSize(mVideoWidth, mVideoHeight);
+ requestLayout();
}
}
};
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 2061c90..bcf0ea4 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -751,6 +751,7 @@
public DialogInterface.OnClickListener mNeutralButtonListener;
public boolean mCancelable;
public DialogInterface.OnCancelListener mOnCancelListener;
+ public DialogInterface.OnDismissListener mOnDismissListener;
public DialogInterface.OnKeyListener mOnKeyListener;
public CharSequence[] mItems;
public ListAdapter mAdapter;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 246b0c9..c5e7d9d 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -49,6 +49,7 @@
public static final int RECOMMEND_FAILED_ALREADY_EXISTS = -4;
public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5;
public static final int RECOMMEND_FAILED_INVALID_URI = -6;
+ public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
private static final boolean localLOGV = true;
private static final String TAG = "PackageHelper";
diff --git a/core/java/com/android/internal/net/VpnProfile.aidl b/core/java/com/android/internal/net/VpnProfile.aidl
new file mode 100644
index 0000000..a072160
--- /dev/null
+++ b/core/java/com/android/internal/net/VpnProfile.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2012 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.internal.net;
+
+parcelable VpnProfile;
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 154b16b..7287327 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -16,6 +16,12 @@
package com.android.internal.net;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.net.InetAddress;
import java.nio.charset.Charsets;
/**
@@ -27,7 +33,9 @@
*
* @hide
*/
-public class VpnProfile implements Cloneable {
+public class VpnProfile implements Cloneable, Parcelable {
+ private static final String TAG = "VpnProfile";
+
// Match these constants with R.array.vpn_types.
public static final int TYPE_PPTP = 0;
public static final int TYPE_L2TP_IPSEC_PSK = 1;
@@ -120,4 +128,54 @@
builder.append('\0').append(ipsecServerCert);
return builder.toString().getBytes(Charsets.UTF_8);
}
+
+ /**
+ * Test if profile is valid for lockdown, which requires IPv4 address for
+ * both server and DNS. Server hostnames would require using DNS before
+ * connection.
+ */
+ public boolean isValidLockdownProfile() {
+ try {
+ InetAddress.parseNumericAddress(server);
+
+ for (String dnsServer : dnsServers.split(" +")) {
+ InetAddress.parseNumericAddress(this.dnsServers);
+ }
+ if (TextUtils.isEmpty(dnsServers)) {
+ Log.w(TAG, "DNS required");
+ return false;
+ }
+
+ // Everything checked out above
+ return true;
+
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Invalid address", e);
+ return false;
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(key);
+ out.writeByteArray(encode());
+ }
+
+ public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() {
+ @Override
+ public VpnProfile createFromParcel(Parcel in) {
+ final String key = in.readString();
+ return decode(key, in.createByteArray());
+ }
+
+ @Override
+ public VpnProfile[] newArray(int size) {
+ return new VpnProfile[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index a53a9c0..a327adc 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -54,4 +54,16 @@
return reference;
}
+ /**
+ * Ensures the truth of an expression involving the state of the calling
+ * instance, but not involving any parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @throws IllegalStateException if {@code expression} is false
+ */
+ public static void checkState(boolean expression) {
+ if (!expression) {
+ throw new IllegalStateException();
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index 08d9f49..b620568 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -23,7 +23,9 @@
import android.app.ActionBar;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.view.Gravity;
import android.view.View;
@@ -38,6 +40,7 @@
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
+import android.widget.Toast;
/**
* This widget implements the dynamic action bar tab behavior that can change
@@ -352,7 +355,7 @@
tabView.getTab().select();
}
- private class TabView extends LinearLayout {
+ private class TabView extends LinearLayout implements OnLongClickListener {
private ActionBar.Tab mTab;
private TextView mTextView;
private ImageView mIconView;
@@ -426,7 +429,8 @@
mIconView.setImageDrawable(null);
}
- if (text != null) {
+ final boolean hasText = !TextUtils.isEmpty(text);
+ if (hasText) {
if (mTextView == null) {
TextView textView = new TextView(getContext(), null,
com.android.internal.R.attr.actionBarTabTextStyle);
@@ -448,9 +452,35 @@
if (mIconView != null) {
mIconView.setContentDescription(tab.getContentDescription());
}
+
+ if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
+ setOnLongClickListener(this);
+ } else {
+ setOnLongClickListener(null);
+ setLongClickable(false);
+ }
}
}
+ public boolean onLongClick(View v) {
+ final int[] screenPos = new int[2];
+ getLocationOnScreen(screenPos);
+
+ final Context context = getContext();
+ final int width = getWidth();
+ final int height = getHeight();
+ final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+
+ Toast cheatSheet = Toast.makeText(context, mTab.getContentDescription(),
+ Toast.LENGTH_SHORT);
+ // Show under the tab
+ cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
+ (screenPos[0] + width / 2) - screenWidth / 2, height);
+
+ cheatSheet.show();
+ return true;
+ }
+
public ActionBar.Tab getTab() {
return mTab;
}
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c998ff6..5e63311 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -225,7 +225,7 @@
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"licencia completa para interactuar con los usuarios"</string>
<string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Permite que la aplicación interactúe con los usuarios."</string>
<string name="permlab_manageUsers" msgid="1676150911672282428">"administrar usuarios"</string>
- <string name="permdesc_manageUsers" msgid="8409306667645355638">"Permite a las aplicaciones administrar los usuarios del dispositivo, así como buscarlos, crearlos y eliminarlos."</string>
+ <string name="permdesc_manageUsers" msgid="8409306667645355638">"Permite a las aplicaciones administrar los usuarios del dispositivo, p. ej., buscarlos, crearlos y eliminarlos."</string>
<string name="permlab_getDetailedTasks" msgid="6229468674753529501">"recuperar información de aplicaciones en ejecución"</string>
<string name="permdesc_getDetailedTasks" msgid="153824741440717599">"Permite que la aplicación recupere información sobre tareas que se están ejecutando en este momento o que se han ejecutado recientemente. Las aplicaciones malintencionadas pueden usar este servicio para acceder a información privada sobre otras aplicaciones."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"reorganizar aplicaciones en ejecución"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index cfc9b9b..b778d03 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -225,7 +225,7 @@
<string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"visa licencija, leidžianti sąveikauti su naudotojais"</string>
<string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Leidžiama bet kokia sąveika tarp naudotojų."</string>
<string name="permlab_manageUsers" msgid="1676150911672282428">"tvarkyti naudotojus"</string>
- <string name="permdesc_manageUsers" msgid="8409306667645355638">"Leidžia tvarkyti įrenginio naudotojų duomenis, įskaitant užklausų teikimą, duomenų kūrimą ir ištrynimą."</string>
+ <string name="permdesc_manageUsers" msgid="8409306667645355638">"Leidžia programoms tvarkyti įrenginio naudotojų duomenis, įskaitant užklausų teikimą, duomenų kūrimą ir ištrynimą."</string>
<string name="permlab_getDetailedTasks" msgid="6229468674753529501">"nuskaityti veikiančių programų išsamią informaciją"</string>
<string name="permdesc_getDetailedTasks" msgid="153824741440717599">"Leidžiama programai nuskaityti išsamią informaciją apie šiuo ir pastaruoju metu vykdomas užduotis. Kenkėjiškos programos gali surasti privačios informacijos apie kitas programas."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"pertvarkyti vykdomas programas"</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e17a05d..d761980 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1442,6 +1442,7 @@
<java-symbol type="drawable" name="stat_sys_tether_usb" />
<java-symbol type="drawable" name="stat_sys_throttled" />
<java-symbol type="drawable" name="vpn_connected" />
+ <java-symbol type="drawable" name="vpn_disconnected" />
<java-symbol type="id" name="ask_checkbox" />
<java-symbol type="id" name="compat_checkbox" />
<java-symbol type="id" name="original_app_icon" />
@@ -1557,6 +1558,10 @@
<java-symbol type="string" name="vpn_text_long" />
<java-symbol type="string" name="vpn_title" />
<java-symbol type="string" name="vpn_title_long" />
+ <java-symbol type="string" name="vpn_lockdown_connecting" />
+ <java-symbol type="string" name="vpn_lockdown_connected" />
+ <java-symbol type="string" name="vpn_lockdown_error" />
+ <java-symbol type="string" name="vpn_lockdown_reset" />
<java-symbol type="string" name="wallpaper_binding_label" />
<java-symbol type="style" name="Theme.Dialog.AppError" />
<java-symbol type="style" name="Theme.Toast" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b7ad1e9..e77dde7 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3272,6 +3272,15 @@
<!-- The text of the notification when VPN is active with a session name. -->
<string name="vpn_text_long">Connected to <xliff:g id="session" example="office">%s</xliff:g>. Touch to manage the network.</string>
+ <!-- Notification title when connecting to lockdown VPN. -->
+ <string name="vpn_lockdown_connecting">Always-on VPN connecting\u2026</string>
+ <!-- Notification title when connected to lockdown VPN. -->
+ <string name="vpn_lockdown_connected">Always-on VPN connected</string>
+ <!-- Notification title when error connecting to lockdown VPN. -->
+ <string name="vpn_lockdown_error">Always-on VPN error</string>
+ <!-- Notification body that indicates user can touch to cycle lockdown VPN connection. -->
+ <string name="vpn_lockdown_reset">Touch to reset connection</string>
+
<!-- Localized strings for WebView -->
<!-- Label for button in a WebView that will open a chooser to choose a file to upload -->
<string name="upload_file">Choose file</string>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 6e1b9d6..a1fd14e 100755
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -864,7 +864,7 @@
public void deleteFromRawResource(int iFlags, int dFlags) {
InstallParams ip = sampleInstallFromRawResource(iFlags, false);
- boolean retainData = ((dFlags & PackageManager.DONT_DELETE_DATA) != 0);
+ boolean retainData = ((dFlags & PackageManager.DELETE_KEEP_DATA) != 0);
GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
DeleteObserver observer = new DeleteObserver();
try {
@@ -914,12 +914,12 @@
@LargeTest
public void testDeleteNormalInternalRetainData() {
- deleteFromRawResource(0, PackageManager.DONT_DELETE_DATA);
+ deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
}
@LargeTest
public void testDeleteFwdLockedInternalRetainData() {
- deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DONT_DELETE_DATA);
+ deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DELETE_KEEP_DATA);
}
@LargeTest
@@ -929,7 +929,7 @@
return;
}
- deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DONT_DELETE_DATA);
+ deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DELETE_KEEP_DATA);
}
/* sdcard mount/unmount tests ******/
@@ -1656,7 +1656,7 @@
false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
// Delete the package now retaining data.
GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
- invokeDeletePackage(ip.pkg.packageName, PackageManager.DONT_DELETE_DATA, receiver);
+ invokeDeletePackage(ip.pkg.packageName, PackageManager.DELETE_KEEP_DATA, receiver);
assertTrue(invokeMovePackageFail(ip.pkg.packageName, moveFlags, result));
} catch (Exception e) {
failStr(e);
@@ -2532,7 +2532,7 @@
GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
try {
- invokeDeletePackage(ip.pkg.packageName, PackageManager.DONT_DELETE_DATA, receiver);
+ invokeDeletePackage(ip.pkg.packageName, PackageManager.DELETE_KEEP_DATA, receiver);
} catch (Exception e) {
failStr(e);
}
diff --git a/docs/downloads/design/Android_Design_Downloads_20120823.zip b/docs/downloads/design/Android_Design_Downloads_20120823.zip
new file mode 100644
index 0000000..6d31283
--- /dev/null
+++ b/docs/downloads/design/Android_Design_Downloads_20120823.zip
Binary files differ
diff --git a/docs/downloads/design/Roboto_Hinted_20120823.zip b/docs/downloads/design/Roboto_Hinted_20120823.zip
new file mode 100644
index 0000000..9ead4af
--- /dev/null
+++ b/docs/downloads/design/Roboto_Hinted_20120823.zip
Binary files differ
diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd
index 4503098..5f78aea 100644
--- a/docs/html/design/downloads/index.jd
+++ b/docs/html/design/downloads/index.jd
@@ -12,7 +12,7 @@
<div class="layout-content-col span-4">
<p>
- <a class="download-button" href="{@docRoot}downloads/design/Android_Design_Downloads_20120814.zip">Download All</a>
+ <a class="download-button" href="{@docRoot}downloads/design/Android_Design_Downloads_20120823.zip">Download All</a>
</p>
</div>
@@ -91,7 +91,7 @@
<div class="layout-content-col span-4">
<p>
- <a class="download-button" href="{@docRoot}downloads/design/Roboto_Hinted_20111129.zip">Roboto</a>
+ <a class="download-button" href="{@docRoot}downloads/design/Roboto_Hinted_20120823.zip">Roboto</a>
<a class="download-button" href="{@docRoot}downloads/design/Roboto_Specimen_Book_20111129.pdf">Specimen Book</a>
</p>
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index f6bf432..b233ff6 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -61,6 +61,9 @@
/** Key prefix for WIFI. */
public static final String WIFI = "WIFI_";
+ /** Key containing suffix of lockdown VPN profile. */
+ public static final String LOCKDOWN_VPN = "LOCKDOWN_VPN";
+
/** Data type for public keys. */
public static final String EXTRA_PUBLIC_KEY = "KEY";
diff --git a/media/java/android/media/DataSource.java b/media/java/android/media/DataSource.java
new file mode 100644
index 0000000..347bd5f
--- /dev/null
+++ b/media/java/android/media/DataSource.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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 android.media;
+
+import java.io.Closeable;
+
+/**
+ * An abstraction for a media data source, e.g. a file or an http stream
+ * {@hide}
+ */
+public interface DataSource extends Closeable {
+ /**
+ * Reads data from the data source at the requested position
+ *
+ * @param offset where in the source to read
+ * @param buffer the buffer to read the data into
+ * @param size how many bytes to read
+ * @return the number of bytes read, or -1 if there was an error
+ */
+ public int readAt(long offset, byte[] buffer, int size);
+
+ /**
+ * Gets the size of the data source.
+ *
+ * @return size of data source, or -1 if the length is unknown
+ */
+ public long getSize();
+}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 687d3a5..4b8d3cb 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -22,6 +22,7 @@
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.net.Uri;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -60,6 +61,12 @@
}
/**
+ * Sets the DataSource object to be used as the data source for this extractor
+ * {@hide}
+ */
+ public native final void setDataSource(DataSource source);
+
+ /**
* Sets the data source as a content Uri.
*
* @param context the Context to use when resolving the Uri
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 1b98109..ef0da3a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2265,6 +2265,16 @@
*/
public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
+ /** File or network related operation errors. */
+ public static final int MEDIA_ERROR_IO = -1004;
+ /** Bitstream is not conforming to the related coding standard or file spec. */
+ public static final int MEDIA_ERROR_MALFORMED = -1007;
+ /** Bitstream is conforming to the related coding standard or file spec, but
+ * the media framework does not support the feature. */
+ public static final int MEDIA_ERROR_UNSUPPORTED = -1010;
+ /** Some operation takes too long to complete, usually more than 3-5 seconds. */
+ public static final int MEDIA_ERROR_TIMED_OUT = -110;
+
/**
* Interface definition of a callback to be invoked when there
* has been an error during an asynchronous operation (other errors
@@ -2283,6 +2293,12 @@
* </ul>
* @param extra an extra code, specific to the error. Typically
* implementation dependent.
+ * <ul>
+ * <li>{@link #MEDIA_ERROR_IO}
+ * <li>{@link #MEDIA_ERROR_MALFORMED}
+ * <li>{@link #MEDIA_ERROR_UNSUPPORTED}
+ * <li>{@link #MEDIA_ERROR_TIMED_OUT}
+ * </ul>
* @return True if the method handled the error, false if it didn't.
* Returning false, or not having an OnErrorListener at all, will
* cause the OnCompletionListener to be called.
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index ca2df47..2d1be02 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -351,12 +351,11 @@
*/
public void setCaptureRate(double fps) {
// Make sure that time lapse is enabled when this method is called.
- setParameter(String.format("time-lapse-enable=1"));
+ setParameter("time-lapse-enable=1");
double timeBetweenFrameCapture = 1 / fps;
int timeBetweenFrameCaptureMs = (int) (1000 * timeBetweenFrameCapture);
- setParameter(String.format("time-between-time-lapse-frame-capture=%d",
- timeBetweenFrameCaptureMs));
+ setParameter("time-between-time-lapse-frame-capture=" + timeBetweenFrameCaptureMs);
}
/**
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 351ff04..23949fa 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -44,6 +44,72 @@
static fields_t gFields;
+class JavaDataSourceBridge : public DataSource {
+ jmethodID mReadMethod;
+ jmethodID mGetSizeMethod;
+ jmethodID mCloseMethod;
+ jobject mDataSource;
+ public:
+ JavaDataSourceBridge(JNIEnv *env, jobject source) {
+ mDataSource = env->NewGlobalRef(source);
+
+ jclass datasourceclass = env->GetObjectClass(mDataSource);
+ CHECK(datasourceclass != NULL);
+
+ mReadMethod = env->GetMethodID(datasourceclass, "readAt", "(J[BI)I");
+ CHECK(mReadMethod != NULL);
+
+ mGetSizeMethod = env->GetMethodID(datasourceclass, "getSize", "()J");
+ CHECK(mGetSizeMethod != NULL);
+
+ mCloseMethod = env->GetMethodID(datasourceclass, "close", "()V");
+ CHECK(mCloseMethod != NULL);
+ }
+
+ ~JavaDataSourceBridge() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mDataSource, mCloseMethod);
+ env->DeleteGlobalRef(mDataSource);
+ }
+
+ virtual status_t initCheck() const {
+ return OK;
+ }
+
+ virtual ssize_t readAt(off64_t offset, void* buffer, size_t size) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ // XXX could optimize this by reusing the same array
+ jbyteArray byteArrayObj = env->NewByteArray(size);
+ env->DeleteLocalRef(env->GetObjectClass(mDataSource));
+ env->DeleteLocalRef(env->GetObjectClass(byteArrayObj));
+ ssize_t numread = env->CallIntMethod(mDataSource, mReadMethod, offset, byteArrayObj, size);
+ env->GetByteArrayRegion(byteArrayObj, 0, size, (jbyte*) buffer);
+ env->DeleteLocalRef(byteArrayObj);
+ if (env->ExceptionCheck()) {
+ ALOGW("Exception occurred while reading %d at %lld", size, offset);
+ LOGW_EX(env);
+ env->ExceptionClear();
+ return -1;
+ }
+ return numread;
+ }
+
+ virtual status_t getSize(off64_t *size) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ CHECK(size != NULL);
+
+ int64_t len = env->CallLongMethod(mDataSource, mGetSizeMethod);
+ if (len < 0) {
+ *size = ERROR_UNSUPPORTED;
+ } else {
+ *size = len;
+ }
+ return OK;
+ }
+};
+
////////////////////////////////////////////////////////////////////////////////
JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
@@ -76,6 +142,10 @@
return mImpl->setDataSource(fd, offset, size);
}
+status_t JMediaExtractor::setDataSource(const sp<DataSource> &datasource) {
+ return mImpl->setDataSource(datasource);
+}
+
size_t JMediaExtractor::countTracks() const {
return mImpl->countTracks();
}
@@ -625,6 +695,33 @@
}
}
+static void android_media_MediaExtractor_setDataSourceCallback(
+ JNIEnv *env, jobject thiz,
+ jobject callbackObj) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ if (callbackObj == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+
+ sp<JavaDataSourceBridge> bridge = new JavaDataSourceBridge(env, callbackObj);
+ status_t err = extractor->setDataSource(bridge);
+
+ if (err != OK) {
+ jniThrowException(
+ env,
+ "java/io/IOException",
+ "Failed to instantiate extractor.");
+ return;
+ }
+}
+
static jlong android_media_MediaExtractor_getCachedDurationUs(
JNIEnv *env, jobject thiz) {
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
@@ -713,6 +810,9 @@
{ "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
(void *)android_media_MediaExtractor_setDataSourceFd },
+ { "setDataSource", "(Landroid/media/DataSource;)V",
+ (void *)android_media_MediaExtractor_setDataSourceCallback },
+
{ "getCachedDuration", "()J",
(void *)android_media_MediaExtractor_getCachedDurationUs },
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 2d4627e..03900db 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -19,6 +19,7 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/DataSource.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
@@ -39,6 +40,7 @@
const KeyedVector<String8, String8> *headers);
status_t setDataSource(int fd, off64_t offset, off64_t size);
+ status_t setDataSource(const sp<DataSource> &source);
size_t countTracks() const;
status_t getTrackFormat(size_t index, jobject *format) const;
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index a28b8a4..b36bd55 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -26,6 +26,7 @@
import android.content.pm.ContainerEncryptionParams;
import android.content.pm.IPackageManager;
import android.content.pm.LimitedLengthInputStream;
+import android.content.pm.PackageCleanItem;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
@@ -181,6 +182,7 @@
}
ret.packageName = pkg.packageName;
+ ret.versionCode = pkg.versionCode;
ret.installLocation = pkg.installLocation;
ret.verifiers = pkg.verifiers;
@@ -268,12 +270,14 @@
if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) {
IPackageManager pm = IPackageManager.Stub.asInterface(
ServiceManager.getService("package"));
- String pkg = null;
+ PackageCleanItem pkg = null;
try {
while ((pkg=pm.nextPackageToClean(pkg)) != null) {
- eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg));
- eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg));
- eraseFiles(Environment.getExternalStorageAppObbDirectory(pkg));
+ eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg.packageName));
+ eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg.packageName));
+ if (pkg.andCode) {
+ eraseFiles(Environment.getExternalStorageAppObbDirectory(pkg.packageName));
+ }
}
} catch (RemoteException e) {
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index c18f76c..b185471 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -179,5 +179,17 @@
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
</intent-filter>
</activity>
+
+ <!-- Beans in space -->
+ <service
+ android:name=".BeanBagDream"
+ android:exported="true"
+ android:label="Beans in space">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DREAM" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index b53fbe2..d435670 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -138,7 +138,7 @@
<string name="data_usage_disabled_dialog_title" msgid="2086815304858964954">"Передача данных отключена"</string>
<string name="data_usage_disabled_dialog" msgid="3853117269051806280">"Достигнут лимит трафика."\n\n"При восстановлении подключения оператор может взимать плату за передачу данных."</string>
<string name="data_usage_disabled_dialog_enable" msgid="7729772039208664606">"Восстановить подключение"</string>
- <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Нет подключения к Интернету"</string>
+ <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Нет интернет-подключения"</string>
<string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi подключено"</string>
<string name="gps_notification_searching_text" msgid="8574247005642736060">"Поиск GPS"</string>
<string name="gps_notification_found_text" msgid="4619274244146446464">"Координаты по GPS"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/BeanBagDream.java b/packages/SystemUI/src/com/android/systemui/BeanBagDream.java
new file mode 100644
index 0000000..12e45c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/BeanBagDream.java
@@ -0,0 +1,31 @@
+package com.android.systemui;
+
+import android.service.dreams.Dream;
+
+import com.android.systemui.BeanBag.Board;
+
+public class BeanBagDream extends Dream {
+
+ private Board mBoard;
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ setInteractive(true);
+ mBoard = new Board(this, null);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ setContentView(mBoard);
+ lightsOut();
+ mBoard.startAnimation();
+ }
+
+ @Override
+ public void finish() {
+ mBoard.stopAnimation();
+ super.finish();
+ }
+}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index ff9bbc5..96de1b9 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -123,7 +123,11 @@
if (which == DialogInterface.BUTTON_POSITIVE) {
mConfig.configureIntent.send();
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
- mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
+ if (mConfig.legacy) {
+ mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ } else {
+ mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
+ }
}
} catch (Exception e) {
Log.e(TAG, "onClick", e);
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 3ea50201..236a4ea 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -971,7 +971,7 @@
* interacts with the keyguard ui should be posted to this handler, rather
* than called directly.
*/
- private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
+ private Handler mHandler = new Handler(true /*async*/) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index d0db0d2..776a1a4 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
@@ -32,10 +33,12 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.bluetooth.BluetoothTetheringDataTracker;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -78,6 +81,8 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.security.Credentials;
+import android.security.KeyStore;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
@@ -85,12 +90,14 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
+import com.android.server.net.LockdownVpnTracker;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
@@ -137,9 +144,14 @@
private Tethering mTethering;
private boolean mTetheringConfigValid = false;
+ private KeyStore mKeyStore;
+
private Vpn mVpn;
private VpnCallback mVpnCallback = new VpnCallback();
+ private boolean mLockdownEnabled;
+ private LockdownVpnTracker mLockdownTracker;
+
/** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
private Object mRulesLock = new Object();
/** Currently active network rules by UID. */
@@ -269,6 +281,8 @@
*/
private static final int EVENT_SET_POLICY_DATA_ENABLE = 13;
+ private static final int EVENT_VPN_STATE_CHANGED = 14;
+
/** Handler used for internal events. */
private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
@@ -371,6 +385,7 @@
mContext = checkNotNull(context, "missing Context");
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
+ mKeyStore = KeyStore.getInstance();
try {
mPolicyManager.registerListener(mPolicyListener);
@@ -778,6 +793,9 @@
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
}
+ if (mLockdownTracker != null) {
+ info = mLockdownTracker.augmentNetworkInfo(info);
+ }
return info;
}
@@ -795,6 +813,17 @@
return getNetworkInfo(mActiveDefaultNetwork, uid);
}
+ public NetworkInfo getActiveNetworkInfoUnfiltered() {
+ enforceAccessPermission();
+ if (isNetworkTypeValid(mActiveDefaultNetwork)) {
+ final NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork];
+ if (tracker != null) {
+ return tracker.getNetworkInfo();
+ }
+ }
+ return null;
+ }
+
@Override
public NetworkInfo getActiveNetworkInfoForUid(int uid) {
enforceConnectivityInternalPermission();
@@ -1054,6 +1083,12 @@
// TODO - move this into individual networktrackers
int usedNetworkType = convertFeatureToNetworkType(networkType, feature);
+ if (mLockdownEnabled) {
+ // Since carrier APNs usually aren't available from VPN
+ // endpoint, mark them as unavailable.
+ return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
+ }
+
if (mProtectedNetworks.contains(usedNetworkType)) {
enforceConnectivityInternalPermission();
}
@@ -1761,7 +1796,7 @@
}
}
- private void sendConnectedBroadcast(NetworkInfo info) {
+ public void sendConnectedBroadcast(NetworkInfo info) {
sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE);
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
}
@@ -1776,6 +1811,10 @@
}
private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
+ if (mLockdownTracker != null) {
+ info = mLockdownTracker.augmentNetworkInfo(info);
+ }
+
Intent intent = new Intent(bcastType);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
@@ -1907,8 +1946,26 @@
}
// load the global proxy at startup
mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
+
+ // Try bringing up tracker, but if KeyStore isn't ready yet, wait
+ // for user to unlock device.
+ if (!updateLockdownVpn()) {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
+ mContext.registerReceiver(mUserPresentReceiver, filter);
+ }
}
+ private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Try creating lockdown tracker, since user present usually means
+ // unlocked keystore.
+ if (updateLockdownVpn()) {
+ mContext.unregisterReceiver(this);
+ }
+ }
+ };
+
private void handleConnect(NetworkInfo info) {
final int type = info.getType();
@@ -2587,6 +2644,9 @@
} else if (state == NetworkInfo.State.CONNECTED) {
handleConnect(info);
}
+ if (mLockdownTracker != null) {
+ mLockdownTracker.onNetworkInfoChanged(info);
+ }
break;
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
info = (NetworkInfo) msg.obj;
@@ -2684,6 +2744,13 @@
final int networkType = msg.arg1;
final boolean enabled = msg.arg2 == ENABLED;
handleSetPolicyDataEnable(networkType, enabled);
+ break;
+ }
+ case EVENT_VPN_STATE_CHANGED: {
+ if (mLockdownTracker != null) {
+ mLockdownTracker.onVpnStateChanged((NetworkInfo) msg.obj);
+ }
+ break;
}
}
}
@@ -3082,6 +3149,7 @@
*/
@Override
public boolean protectVpn(ParcelFileDescriptor socket) {
+ throwIfLockdownEnabled();
try {
int type = mActiveDefaultNetwork;
if (ConnectivityManager.isNetworkTypeValid(type)) {
@@ -3108,6 +3176,7 @@
*/
@Override
public boolean prepareVpn(String oldPackage, String newPackage) {
+ throwIfLockdownEnabled();
return mVpn.prepare(oldPackage, newPackage);
}
@@ -3120,18 +3189,22 @@
*/
@Override
public ParcelFileDescriptor establishVpn(VpnConfig config) {
+ throwIfLockdownEnabled();
return mVpn.establish(config);
}
/**
- * Start legacy VPN and return an intent to VpnDialogs. This method is
- * used by VpnSettings and not available in ConnectivityManager.
- * Permissions are checked in Vpn class.
- * @hide
+ * Start legacy VPN, controlling native daemons as needed. Creates a
+ * secondary thread to perform connection work, returning quickly.
*/
@Override
- public void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
- mVpn.startLegacyVpn(config, racoon, mtpd);
+ public void startLegacyVpn(VpnProfile profile) {
+ throwIfLockdownEnabled();
+ final LinkProperties egress = getActiveLinkProperties();
+ if (egress == null) {
+ throw new IllegalStateException("Missing active network connection");
+ }
+ mVpn.startLegacyVpn(profile, mKeyStore, egress);
}
/**
@@ -3142,6 +3215,7 @@
*/
@Override
public LegacyVpnInfo getLegacyVpnInfo() {
+ throwIfLockdownEnabled();
return mVpn.getLegacyVpnInfo();
}
@@ -3160,8 +3234,7 @@
}
public void onStateChanged(NetworkInfo info) {
- // TODO: if connected, release delayed broadcast
- // TODO: if disconnected, consider kicking off reconnect
+ mHandler.obtainMessage(EVENT_VPN_STATE_CHANGED, info).sendToTarget();
}
public void override(List<String> dnsServers, List<String> searchDomains) {
@@ -3230,4 +3303,58 @@
}
}
}
+
+ @Override
+ public boolean updateLockdownVpn() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ // Tear down existing lockdown if profile was removed
+ mLockdownEnabled = LockdownVpnTracker.isEnabled();
+ if (mLockdownEnabled) {
+ if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
+ Slog.w(TAG, "KeyStore locked; unable to create LockdownTracker");
+ return false;
+ }
+
+ final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
+ final VpnProfile profile = VpnProfile.decode(
+ profileName, mKeyStore.get(Credentials.VPN + profileName));
+ setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpn, profile));
+ } else {
+ setLockdownTracker(null);
+ }
+
+ return true;
+ }
+
+ /**
+ * Internally set new {@link LockdownVpnTracker}, shutting down any existing
+ * {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
+ */
+ private void setLockdownTracker(LockdownVpnTracker tracker) {
+ // Shutdown any existing tracker
+ final LockdownVpnTracker existing = mLockdownTracker;
+ mLockdownTracker = null;
+ if (existing != null) {
+ existing.shutdown();
+ }
+
+ try {
+ if (tracker != null) {
+ mNetd.setFirewallEnabled(true);
+ mLockdownTracker = tracker;
+ mLockdownTracker.init();
+ } else {
+ mNetd.setFirewallEnabled(false);
+ }
+ } catch (RemoteException e) {
+ // ignored; NMS lives inside system_server
+ }
+ }
+
+ private void throwIfLockdownEnabled() {
+ if (mLockdownEnabled) {
+ throw new IllegalStateException("Unavailable in lockdown mode");
+ }
+ }
}
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 2bed957..f1ff27f 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -241,7 +241,7 @@
SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK) != 0;
}
- private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
+ private final Handler mHandler = new Handler(true /*async*/) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 39e5186..efa16af 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -35,6 +35,7 @@
import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
+import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.Context;
import android.net.INetworkManagementEventObserver;
import android.net.InterfaceConfiguration;
@@ -55,7 +56,9 @@
import android.util.SparseBooleanArray;
import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.util.Preconditions;
import com.android.server.NativeDaemonConnector.Command;
+import com.android.server.net.LockdownVpnTracker;
import com.google.android.collect.Maps;
import java.io.BufferedReader;
@@ -78,7 +81,6 @@
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
-import android.bluetooth.BluetoothTetheringDataTracker;
/**
* @hide
@@ -92,6 +94,9 @@
private static final String ADD = "add";
private static final String REMOVE = "remove";
+ private static final String ALLOW = "allow";
+ private static final String DENY = "deny";
+
private static final String DEFAULT = "default";
private static final String SECONDARY = "secondary";
@@ -169,6 +174,7 @@
private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap();
private volatile boolean mBandwidthControlEnabled;
+ private volatile boolean mFirewallEnabled;
/**
* Constructs a new NetworkManagementService instance
@@ -363,6 +369,9 @@
}
}
}
+
+ // TODO: Push any existing firewall state
+ setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
}
//
@@ -1425,7 +1434,72 @@
}
}
- /** {@inheritDoc} */
+ @Override
+ public void setFirewallEnabled(boolean enabled) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ try {
+ mConnector.execute("firewall", enabled ? "enable" : "disable");
+ mFirewallEnabled = enabled;
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public boolean isFirewallEnabled() {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ return mFirewallEnabled;
+ }
+
+ @Override
+ public void setFirewallInterfaceRule(String iface, boolean allow) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ Preconditions.checkState(mFirewallEnabled);
+ final String rule = allow ? ALLOW : DENY;
+ try {
+ mConnector.execute("firewall", "set_interface_rule", iface, rule);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void setFirewallEgressSourceRule(String addr, boolean allow) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ Preconditions.checkState(mFirewallEnabled);
+ final String rule = allow ? ALLOW : DENY;
+ try {
+ mConnector.execute("firewall", "set_egress_source_rule", addr, rule);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ Preconditions.checkState(mFirewallEnabled);
+ final String rule = allow ? ALLOW : DENY;
+ try {
+ mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void setFirewallUidRule(int uid, boolean allow) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ Preconditions.checkState(mFirewallEnabled);
+ final String rule = allow ? ALLOW : DENY;
+ try {
+ mConnector.execute("firewall", "set_uid_rule", uid, rule);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
public void monitor() {
if (mConnector != null) {
mConnector.monitor();
@@ -1456,5 +1530,7 @@
}
pw.println("]");
}
+
+ pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a6af144..7097891 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -197,7 +197,7 @@
Slog.i(TAG, "User Service");
ServiceManager.addService(Context.USER_SERVICE,
- UserManagerService.getInstance(context));
+ UserManagerService.getInstance());
mContentResolver = context.getContentResolver();
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index b609867..72fde11 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -441,7 +441,7 @@
private void delay(long duration) {
if (duration > 0) {
- long bedtime = SystemClock.uptimeMillis();
+ long bedtime = duration + SystemClock.uptimeMillis();
do {
try {
this.wait(duration);
@@ -451,8 +451,7 @@
if (mDone) {
break;
}
- duration = duration
- - SystemClock.uptimeMillis() - bedtime;
+ duration = bedtime - SystemClock.uptimeMillis();
} while (duration > 0);
}
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index edbc624..f483576 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -959,10 +959,8 @@
* an AsyncChannel communication with WifiService
*/
public Messenger getWifiServiceMessenger() {
- /* STOPSHIP: Fix this to have old sync API not need these new permissions
- * enforceAccessPermission();
- * enforceChangePermission();
- */
+ enforceAccessPermission();
+ enforceChangePermission();
return new Messenger(mAsyncServiceHandler);
}
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index e222936..ca7faa2 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -210,7 +210,7 @@
ComponentName startServiceLocked(IApplicationThread caller,
Intent service, String resolvedType,
- int callingPid, int callingUid) {
+ int callingPid, int callingUid, int userId) {
if (DEBUG_SERVICE) Slog.v(TAG, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
@@ -226,7 +226,7 @@
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType,
- callingPid, callingUid, UserHandle.getUserId(callingUid), true);
+ callingPid, callingUid, userId, true);
if (res == null) {
return null;
}
@@ -264,7 +264,7 @@
}
int stopServiceLocked(IApplicationThread caller, Intent service,
- String resolvedType) {
+ String resolvedType, int userId) {
if (DEBUG_SERVICE) Slog.v(TAG, "stopService: " + service
+ " type=" + resolvedType);
@@ -278,9 +278,7 @@
// If this service is active, make sure it is stopped.
ServiceLookupResult r = retrieveServiceLocked(service, resolvedType,
- Binder.getCallingPid(), Binder.getCallingUid(),
- callerApp == null ? UserHandle.getCallingUserId() : callerApp.userId,
- false);
+ Binder.getCallingPid(), Binder.getCallingUid(), userId, false);
if (r != null) {
if (r.record != null) {
final long origId = Binder.clearCallingIdentity();
@@ -731,7 +729,7 @@
ServiceInfo sInfo =
rInfo != null ? rInfo.serviceInfo : null;
if (sInfo == null) {
- Slog.w(TAG, "Unable to start service " + service +
+ Slog.w(TAG, "Unable to start service " + service + " U=" + userId +
": not found");
return null;
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8fc12d3..2b4f8b1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -10554,7 +10554,7 @@
}
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType) {
+ String resolvedType, int userId) {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
@@ -10566,9 +10566,10 @@
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
+ checkValidCaller(callingUid, userId);
final long origId = Binder.clearCallingIdentity();
ComponentName res = mServices.startServiceLocked(caller, service,
- resolvedType, callingPid, callingUid);
+ resolvedType, callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
@@ -10581,22 +10582,24 @@
Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
final long origId = Binder.clearCallingIdentity();
ComponentName res = mServices.startServiceLocked(null, service,
- resolvedType, -1, uid);
+ resolvedType, -1, uid, UserHandle.getUserId(uid));
Binder.restoreCallingIdentity(origId);
return res;
}
}
public int stopService(IApplicationThread caller, Intent service,
- String resolvedType) {
+ String resolvedType, int userId) {
enforceNotIsolatedCaller("stopService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ checkValidCaller(Binder.getCallingUid(), userId);
+
synchronized(this) {
- return mServices.stopServiceLocked(caller, service, resolvedType);
+ return mServices.stopServiceLocked(caller, service, resolvedType, userId);
}
}
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index d490f24..b3cbb84 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -34,9 +34,11 @@
import android.net.BaseNetworkStateTracker;
import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
+import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.NetworkInfo;
+import android.net.RouteInfo;
import android.net.NetworkInfo.DetailedState;
import android.os.Binder;
import android.os.FileUtils;
@@ -48,11 +50,15 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemService;
+import android.security.Credentials;
+import android.security.KeyStore;
import android.util.Log;
+import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
import com.android.internal.util.Preconditions;
import com.android.server.ConnectivityService.VpnCallback;
import com.android.server.net.BaseNetworkObserver;
@@ -60,6 +66,8 @@
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.Inet4Address;
+import java.net.InetAddress;
import java.nio.charset.Charsets;
import java.util.Arrays;
@@ -82,6 +90,7 @@
private Connection mConnection;
private LegacyVpnRunner mLegacyVpnRunner;
private PendingIntent mStatusIntent;
+ private boolean mEnableNotif = true;
public Vpn(Context context, VpnCallback callback, INetworkManagementService netService) {
// TODO: create dedicated TYPE_VPN network type
@@ -96,6 +105,10 @@
}
}
+ public void setEnableNotifications(boolean enableNotif) {
+ mEnableNotif = enableNotif;
+ }
+
@Override
protected void startMonitoringInternal() {
// Ignored; events are sent through callbacks for now
@@ -386,6 +399,7 @@
}
private void showNotification(VpnConfig config, String label, Bitmap icon) {
+ if (!mEnableNotif) return;
mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config);
NotificationManager nm = (NotificationManager)
@@ -412,6 +426,7 @@
}
private void hideNotification() {
+ if (!mEnableNotif) return;
mStatusIntent = null;
NotificationManager nm = (NotificationManager)
@@ -430,20 +445,127 @@
private native int jniCheck(String interfaze);
private native void jniProtect(int socket, String interfaze);
- /**
- * Start legacy VPN. This method stops the daemons and restart them
- * if arguments are not null. Heavy things are offloaded to another
- * thread, so callers will not be blocked for a long time.
- *
- * @param config The parameters to configure the network.
- * @param racoon The arguments to be passed to racoon.
- * @param mtpd The arguments to be passed to mtpd.
- */
- public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
- stopLegacyVpn();
+ private static String findLegacyVpnGateway(LinkProperties prop) {
+ for (RouteInfo route : prop.getRoutes()) {
+ // Currently legacy VPN only works on IPv4.
+ if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
+ return route.getGateway().getHostAddress();
+ }
+ }
- // TODO: move legacy definition to settings
+ throw new IllegalStateException("Unable to find suitable gateway");
+ }
+
+ /**
+ * Start legacy VPN, controlling native daemons as needed. Creates a
+ * secondary thread to perform connection work, returning quickly.
+ */
+ public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
+ if (keyStore.state() != KeyStore.State.UNLOCKED) {
+ throw new IllegalStateException("KeyStore isn't unlocked");
+ }
+
+ final String iface = egress.getInterfaceName();
+ final String gateway = findLegacyVpnGateway(egress);
+
+ // Load certificates.
+ String privateKey = "";
+ String userCert = "";
+ String caCert = "";
+ String serverCert = "";
+ if (!profile.ipsecUserCert.isEmpty()) {
+ privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
+ byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
+ userCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+ }
+ if (!profile.ipsecCaCert.isEmpty()) {
+ byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
+ caCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+ }
+ if (!profile.ipsecServerCert.isEmpty()) {
+ byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
+ serverCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+ }
+ if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
+ throw new IllegalStateException("Cannot load credentials");
+ }
+
+ // Prepare arguments for racoon.
+ String[] racoon = null;
+ switch (profile.type) {
+ case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+ racoon = new String[] {
+ iface, profile.server, "udppsk", profile.ipsecIdentifier,
+ profile.ipsecSecret, "1701",
+ };
+ break;
+ case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+ racoon = new String[] {
+ iface, profile.server, "udprsa", privateKey, userCert,
+ caCert, serverCert, "1701",
+ };
+ break;
+ case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
+ racoon = new String[] {
+ iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
+ profile.ipsecSecret, profile.username, profile.password, "", gateway,
+ };
+ break;
+ case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
+ racoon = new String[] {
+ iface, profile.server, "xauthrsa", privateKey, userCert,
+ caCert, serverCert, profile.username, profile.password, "", gateway,
+ };
+ break;
+ case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
+ racoon = new String[] {
+ iface, profile.server, "hybridrsa",
+ caCert, serverCert, profile.username, profile.password, "", gateway,
+ };
+ break;
+ }
+
+ // Prepare arguments for mtpd.
+ String[] mtpd = null;
+ switch (profile.type) {
+ case VpnProfile.TYPE_PPTP:
+ mtpd = new String[] {
+ iface, "pptp", profile.server, "1723",
+ "name", profile.username, "password", profile.password,
+ "linkname", "vpn", "refuse-eap", "nodefaultroute",
+ "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+ (profile.mppe ? "+mppe" : "nomppe"),
+ };
+ break;
+ case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+ case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+ mtpd = new String[] {
+ iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
+ "name", profile.username, "password", profile.password,
+ "linkname", "vpn", "refuse-eap", "nodefaultroute",
+ "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+ };
+ break;
+ }
+
+ VpnConfig config = new VpnConfig();
config.legacy = true;
+ config.user = profile.key;
+ config.interfaze = iface;
+ config.session = profile.name;
+ config.routes = profile.routes;
+ if (!profile.dnsServers.isEmpty()) {
+ config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
+ }
+ if (!profile.searchDomains.isEmpty()) {
+ config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
+ }
+
+ startLegacyVpn(config, racoon, mtpd);
+ }
+
+ private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
+ stopLegacyVpn();
// Prepare for the new request. This also checks the caller.
prepare(null, VpnConfig.LEGACY_VPN);
@@ -483,6 +605,14 @@
return info;
}
+ public VpnConfig getLegacyVpnConfig() {
+ if (mLegacyVpnRunner != null) {
+ return mLegacyVpnRunner.mConfig;
+ } else {
+ return null;
+ }
+ }
+
/**
* Bringing up a VPN connection takes time, and that is all this thread
* does. Here we have plenty of time. The only thing we need to take
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 0b8ff62..29c68eb 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -1438,6 +1438,10 @@
* Private handler for the input manager.
*/
private final class InputManagerHandler extends Handler {
+ public InputManagerHandler() {
+ super(true /*async*/);
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 32b3597..c2c0a71 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -598,6 +598,8 @@
}
mInjectNtpTimePending = STATE_DOWNLOADING;
+ // hold wake lock while task runs
+ mWakeLock.acquire();
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
@@ -628,14 +630,16 @@
delay = RETRY_INTERVAL;
}
- mHandler.sendMessage(Message.obtain(mHandler, INJECT_NTP_TIME_FINISHED));
+ sendMessage(INJECT_NTP_TIME_FINISHED, 0, null);
if (mPeriodicTimeInjection) {
// send delayed message for next NTP injection
// since this is delayed and not urgent we do not hold a wake lock here
- mHandler.removeMessages(INJECT_NTP_TIME);
- mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
+ mHandler.sendEmptyMessageDelayed(INJECT_NTP_TIME, delay);
}
+
+ // release wake lock held by task
+ mWakeLock.release();
}
});
}
@@ -652,6 +656,8 @@
}
mDownloadXtraDataPending = STATE_DOWNLOADING;
+ // hold wake lock while task runs
+ mWakeLock.acquire();
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
@@ -664,17 +670,17 @@
native_inject_xtra_data(data, data.length);
}
- mHandler.sendMessage(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA_FINISHED));
+ sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
if (data == null) {
// try again later
// since this is delayed and not urgent we do not hold a wake lock here
- mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
- mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA),
- RETRY_INTERVAL);
+ mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA, RETRY_INTERVAL);
}
- }
+ // release wake lock held by task
+ mWakeLock.release();
+ }
});
}
@@ -1475,11 +1481,17 @@
private void sendMessage(int message, int arg, Object obj) {
// hold a wake lock until this message is delivered
+ // note that this assumes the message will not be removed from the queue before
+ // it is handled (otherwise the wake lock would be leaked).
mWakeLock.acquire();
mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
}
private final class ProviderHandler extends Handler {
+ public ProviderHandler() {
+ super(true /*async*/);
+ }
+
@Override
public void handleMessage(Message msg) {
int message = msg.what;
diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java
new file mode 100644
index 0000000..541650e
--- /dev/null
+++ b/services/java/com/android/server/net/LockdownVpnTracker.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2012 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.CONNECTIVITY_INTERNAL;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.Preconditions;
+import com.android.server.ConnectivityService;
+import com.android.server.connectivity.Vpn;
+
+/**
+ * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be
+ * connected and kicks off VPN connection, managing any required {@code netd}
+ * firewall rules.
+ */
+public class LockdownVpnTracker {
+ private static final String TAG = "LockdownVpnTracker";
+
+ /** Number of VPN attempts before waiting for user intervention. */
+ private static final int MAX_ERROR_COUNT = 4;
+
+ private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
+
+ private final Context mContext;
+ private final INetworkManagementService mNetService;
+ private final ConnectivityService mConnService;
+ private final Vpn mVpn;
+ private final VpnProfile mProfile;
+
+ private final Object mStateLock = new Object();
+
+ private PendingIntent mResetIntent;
+
+ private String mAcceptedEgressIface;
+ private String mAcceptedIface;
+ private String mAcceptedSourceAddr;
+
+ private int mErrorCount;
+
+ public static boolean isEnabled() {
+ return KeyStore.getInstance().contains(Credentials.LOCKDOWN_VPN);
+ }
+
+ public LockdownVpnTracker(Context context, INetworkManagementService netService,
+ ConnectivityService connService, Vpn vpn, VpnProfile profile) {
+ mContext = Preconditions.checkNotNull(context);
+ mNetService = Preconditions.checkNotNull(netService);
+ mConnService = Preconditions.checkNotNull(connService);
+ mVpn = Preconditions.checkNotNull(vpn);
+ mProfile = Preconditions.checkNotNull(profile);
+
+ final Intent intent = new Intent(ACTION_LOCKDOWN_RESET);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mResetIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ }
+
+ private BroadcastReceiver mResetReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ reset();
+ }
+ };
+
+ /**
+ * Watch for state changes to both active egress network, kicking off a VPN
+ * connection when ready, or setting firewall rules once VPN is connected.
+ */
+ private void handleStateChangedLocked() {
+ Slog.d(TAG, "handleStateChanged()");
+
+ final NetworkInfo egressInfo = mConnService.getActiveNetworkInfoUnfiltered();
+ final LinkProperties egressProp = mConnService.getActiveLinkProperties();
+
+ final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
+ final VpnConfig vpnConfig = mVpn.getLegacyVpnConfig();
+
+ // Restart VPN when egress network disconnected or changed
+ final boolean egressDisconnected = egressInfo == null
+ || State.DISCONNECTED.equals(egressInfo.getState());
+ final boolean egressChanged = egressProp == null
+ || !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName());
+ if (egressDisconnected || egressChanged) {
+ clearSourceRules();
+ mAcceptedEgressIface = null;
+ mVpn.stopLegacyVpn();
+ }
+ if (egressDisconnected) return;
+
+ if (mErrorCount > MAX_ERROR_COUNT) {
+ showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
+
+ } else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) {
+ if (mProfile.isValidLockdownProfile()) {
+ Slog.d(TAG, "Active network connected; starting VPN");
+ showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
+
+ mAcceptedEgressIface = egressProp.getInterfaceName();
+ mVpn.startLegacyVpn(mProfile, KeyStore.getInstance(), egressProp);
+
+ } else {
+ Slog.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
+ showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
+ }
+
+ } else if (vpnInfo.isConnected() && vpnConfig != null) {
+ final String iface = vpnConfig.interfaze;
+ final String sourceAddr = vpnConfig.addresses;
+
+ if (TextUtils.equals(iface, mAcceptedIface)
+ && TextUtils.equals(sourceAddr, mAcceptedSourceAddr)) {
+ return;
+ }
+
+ Slog.d(TAG, "VPN connected using iface=" + iface + ", sourceAddr=" + sourceAddr);
+ showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
+
+ try {
+ clearSourceRules();
+
+ mNetService.setFirewallInterfaceRule(iface, true);
+ mNetService.setFirewallEgressSourceRule(sourceAddr, true);
+
+ mErrorCount = 0;
+ mAcceptedIface = iface;
+ mAcceptedSourceAddr = sourceAddr;
+ } catch (RemoteException e) {
+ throw new RuntimeException("Problem setting firewall rules", e);
+ }
+
+ mConnService.sendConnectedBroadcast(augmentNetworkInfo(egressInfo));
+ }
+ }
+
+ public void init() {
+ Slog.d(TAG, "init()");
+
+ mVpn.setEnableNotifications(false);
+
+ final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
+ mContext.registerReceiver(mResetReceiver, resetFilter, CONNECTIVITY_INTERNAL, null);
+
+ try {
+ // TODO: support non-standard port numbers
+ mNetService.setFirewallEgressDestRule(mProfile.server, 500, true);
+ mNetService.setFirewallEgressDestRule(mProfile.server, 4500, true);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Problem setting firewall rules", e);
+ }
+
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+
+ public void shutdown() {
+ Slog.d(TAG, "shutdown()");
+
+ mAcceptedEgressIface = null;
+ mErrorCount = 0;
+
+ mVpn.stopLegacyVpn();
+ try {
+ mNetService.setFirewallEgressDestRule(mProfile.server, 500, false);
+ mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Problem setting firewall rules", e);
+ }
+ clearSourceRules();
+ hideNotification();
+
+ mContext.unregisterReceiver(mResetReceiver);
+ mVpn.setEnableNotifications(true);
+ }
+
+ public void reset() {
+ // cycle tracker, reset error count, and trigger retry
+ shutdown();
+ init();
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+
+ private void clearSourceRules() {
+ try {
+ if (mAcceptedIface != null) {
+ mNetService.setFirewallInterfaceRule(mAcceptedIface, false);
+ mAcceptedIface = null;
+ }
+ if (mAcceptedSourceAddr != null) {
+ mNetService.setFirewallEgressSourceRule(mAcceptedSourceAddr, false);
+ mAcceptedSourceAddr = null;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Problem setting firewall rules", e);
+ }
+ }
+
+ public void onNetworkInfoChanged(NetworkInfo info) {
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+
+ public void onVpnStateChanged(NetworkInfo info) {
+ if (info.getDetailedState() == DetailedState.FAILED) {
+ mErrorCount++;
+ }
+ synchronized (mStateLock) {
+ handleStateChangedLocked();
+ }
+ }
+
+ public NetworkInfo augmentNetworkInfo(NetworkInfo info) {
+ final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
+ info = new NetworkInfo(info);
+ info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null);
+ return info;
+ }
+
+ private void showNotification(int titleRes, int iconRes) {
+ final Notification.Builder builder = new Notification.Builder(mContext);
+ builder.setWhen(0);
+ builder.setSmallIcon(iconRes);
+ builder.setContentTitle(mContext.getString(titleRes));
+ builder.setContentText(mContext.getString(R.string.vpn_lockdown_reset));
+ builder.setContentIntent(mResetIntent);
+ builder.setPriority(Notification.PRIORITY_LOW);
+ builder.setOngoing(true);
+ NotificationManager.from(mContext).notify(TAG, 0, builder.build());
+ }
+
+ private void hideNotification() {
+ NotificationManager.from(mContext).cancel(TAG, 0);
+ }
+}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 4252b90..b84e25a 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -64,11 +64,12 @@
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageCleanItem;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.UserInfo;
+import android.content.pm.PackageUserState;
import android.content.pm.PackageParser.ActivityIntentInfo;
import android.content.pm.PackageStats;
import android.content.pm.ParceledListSlice;
@@ -103,7 +104,6 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.Settings.Secure;
import android.security.SystemKeyStore;
import android.util.DisplayMetrics;
@@ -168,6 +168,7 @@
static final boolean DEBUG_UPGRADE = false;
private static final boolean DEBUG_INSTALL = false;
private static final boolean DEBUG_REMOVE = false;
+ private static final boolean DEBUG_BROADCASTS = 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;
@@ -400,6 +401,7 @@
// package uri's from external media onto secure containers
// or internal storage.
private IMediaContainerService mContainerService = null;
+ private int mContainerServiceUserId;
static final int SEND_PENDING_BROADCAST = 1;
static final int MCS_BOUND = 3;
@@ -468,8 +470,15 @@
" DefaultContainerService");
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ mContainerServiceUserId = 0;
+ if (mPendingInstalls.size() > 0) {
+ mContainerServiceUserId = mPendingInstalls.get(0).getUser().getIdentifier();
+ if (mContainerServiceUserId == UserHandle.USER_ALL) {
+ mContainerServiceUserId = 0;
+ }
+ }
if (mContext.bindService(service, mDefContainerConn,
- Context.BIND_AUTO_CREATE)) {
+ Context.BIND_AUTO_CREATE, mContainerServiceUserId)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
@@ -546,6 +555,15 @@
} else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
+ // Check if we're connected to the correct service, if it's an install
+ // request.
+ final int installFor = params.getUser().getIdentifier();
+ if (installFor != mContainerServiceUserId
+ && (installFor == UserHandle.USER_ALL
+ && mContainerServiceUserId != 0)) {
+ mHandler.sendEmptyMessage(MCS_RECONNECT);
+ return;
+ }
if (params.startCopy()) {
// We are done... look for more work or to
// go idle.
@@ -662,15 +680,21 @@
break;
}
case START_CLEANING_PACKAGE: {
- String packageName = (String)msg.obj;
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ PackageCleanItem item = new PackageCleanItem((String)msg.obj,
+ msg.arg2 != 0);
synchronized (mPackages) {
- if (!mSettings.mPackagesToBeCleaned.contains(packageName)) {
- mSettings.mPackagesToBeCleaned.add(packageName);
+ if (msg.arg1 == UserHandle.USER_ALL) {
+ int[] users = sUserManager.getUserIds();
+ for (int user : users) {
+ mSettings.addPackageToCleanLPw(user, item);
+ }
+ } else {
+ mSettings.addPackageToCleanLPw(msg.arg1, item);
}
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- startCleaningPackages();
+ startCleaningPackages(-1);
} break;
case POST_INSTALL: {
if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
@@ -692,15 +716,14 @@
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
res.pkg.applicationInfo.packageName,
- extras, null, null, UserHandle.USER_ALL);
+ extras, null, null, res.users);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
res.pkg.applicationInfo.packageName,
- extras, null, null, UserHandle.USER_ALL);
+ extras, null, null, res.users);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null,
- res.pkg.applicationInfo.packageName, null,
- UserHandle.USER_ALL);
+ res.pkg.applicationInfo.packageName, null, res.users);
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
@@ -944,8 +967,8 @@
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- sUserManager = UserManagerService.getInstance(context);
- sUserManager.setInstaller(this, mInstaller);
+ sUserManager = new UserManagerService(context, this,
+ mInstallLock, mPackages);
readPermissions();
@@ -1139,8 +1162,7 @@
String msg = "System package " + ps.name
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
- mInstaller.remove(ps.name, 0);
- sUserManager.removePackageForAllUsers(ps.name);
+ removeDataDirsLI(ps.name);
} else {
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
@@ -1189,9 +1211,7 @@
if (deletedPkg == null) {
msg = "Updated system package " + deletedAppName
+ " no longer exists; wiping its data";
-
- mInstaller.remove(deletedAppName, 0);
- sUserManager.removePackageForAllUsers(deletedAppName);
+ removeDataDirsLI(deletedAppName);
} else {
msg = "Updated system app + " + deletedAppName
+ " no longer present; removing system privileges for "
@@ -1326,13 +1346,7 @@
void cleanupInstallFailedPackage(PackageSetting ps) {
Slog.i(TAG, "Cleaning up incompletely installed app: " + 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 {
- sUserManager.removePackageForAllUsers(ps.name);
- }
+ removeDataDirsLI(ps.name);
if (ps.codePath != null) {
if (!ps.codePath.delete()) {
Slog.w(TAG, "Unable to remove old code file: " + ps.codePath);
@@ -1562,19 +1576,17 @@
PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
PackageInfo pi;
- if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
- // The package has been uninstalled but has retained data and resources.
- pi = PackageParser.generatePackageInfo(p, null, flags, 0, 0, null, false, 0, userId);
- } else {
- final PackageSetting ps = (PackageSetting) p.mExtras;
- if (ps == null) {
- return null;
- }
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
- ps.getStopped(userId), ps.getEnabled(userId), userId);
- pi.applicationInfo.enabledSetting = ps.getEnabled(userId);
+ final PackageSetting ps = (PackageSetting) p.mExtras;
+ if (ps == null) {
+ return null;
+ }
+ final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+ final PackageUserState state = ps.readUserState(userId);
+ pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
+ ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
+ state, userId);
+ if (pi != null) {
+ pi.applicationInfo.enabledSetting = state.enabled;
pi.applicationInfo.enabled =
pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_DEFAULT
|| pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
@@ -1741,14 +1753,15 @@
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
if (ps.pkg == null) {
- PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, flags, userId);
+ PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName,
+ flags, userId);
if (pInfo != null) {
return pInfo.applicationInfo;
}
return null;
}
- return PackageParser.generateApplicationInfo(ps.pkg, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ return PackageParser.generateApplicationInfo(ps.pkg, flags,
+ ps.readUserState(userId), userId);
}
return null;
}
@@ -1758,20 +1771,23 @@
if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
- PackageParser.Package pkg = new PackageParser.Package(packageName);
- 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 =
+ PackageParser.Package pkg = ps.pkg;
+ if (pkg == null) {
+ if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
+ return null;
+ }
+ pkg = new PackageParser.Package(packageName);
+ pkg.applicationInfo.packageName = packageName;
+ pkg.applicationInfo.flags = ps.pkgFlags;
+ pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
+ pkg.applicationInfo.sourceDir = ps.codePathString;
+ pkg.applicationInfo.dataDir =
getDataPathForPackage(ps.pkg.packageName, 0).getPath();
- ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
+ pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
}
- // ps.pkg.mSetEnabled = ps.getEnabled(userId);
- // ps.pkg.mSetStopped = ps.getStopped(userId);
- return generatePackageInfo(ps.pkg, flags, userId);
+ // pkg.mSetEnabled = ps.getEnabled(userId);
+ // pkg.mSetStopped = ps.getStopped(userId);
+ return generatePackageInfo(pkg, flags, userId);
}
return null;
}
@@ -1789,13 +1805,12 @@
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) return null;
// Note: isEnabledLP() does not apply here - always return info
- return PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
- ps.getEnabled(userId));
+ return PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId));
}
if ("android".equals(packageName)||"system".equals(packageName)) {
return mAndroidApplication;
}
- if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+ if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
return generateApplicationInfoFromSettingsLPw(packageName, flags, userId);
}
}
@@ -1811,9 +1826,11 @@
public void run() {
mHandler.removeCallbacks(this);
int retCode = -1;
- retCode = mInstaller.freeCache(freeStorageSize);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't clear application caches");
+ synchronized (mInstallLock) {
+ retCode = mInstaller.freeCache(freeStorageSize);
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't clear application caches");
+ }
}
if (observer != null) {
try {
@@ -1834,9 +1851,11 @@
public void run() {
mHandler.removeCallbacks(this);
int retCode = -1;
- retCode = mInstaller.freeCache(freeStorageSize);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't clear application caches");
+ synchronized (mInstallLock) {
+ retCode = mInstaller.freeCache(freeStorageSize);
+ if (retCode < 0) {
+ Slog.w(TAG, "Couldn't clear application caches");
+ }
}
if(pi != null) {
try {
@@ -1862,8 +1881,8 @@
if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
+ userId);
}
if (mResolveComponentName.equals(component)) {
return mResolveActivity;
@@ -1882,8 +1901,8 @@
if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
+ userId);
}
}
return null;
@@ -1899,8 +1918,8 @@
if (s != null && mSettings.isEnabledLPr(s.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateServiceInfo(s, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ return PackageParser.generateServiceInfo(s, flags, ps.readUserState(userId),
+ userId);
}
}
return null;
@@ -1916,8 +1935,8 @@
if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
- return PackageParser.generateProviderInfo(p, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ return PackageParser.generateProviderInfo(p, flags, ps.readUserState(userId),
+ userId);
}
}
return null;
@@ -2870,8 +2889,8 @@
} else {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null && ps != null) {
- ai = PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
- ps.getEnabled(userId), userId);
+ ai = PackageParser.generateApplicationInfo(p, flags,
+ ps.readUserState(userId), userId);
}
}
@@ -2901,10 +2920,13 @@
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))) {
PackageSetting ps = mSettings.mPackages.get(p.packageName);
- finalList.add(PackageParser.generateApplicationInfo(p, flags,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId));
+ if (ps != null) {
+ ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
+ ps.readUserState(userId), userId);
+ if (ai != null) {
+ finalList.add(ai);
+ }
+ }
}
}
}
@@ -2921,14 +2943,12 @@
PackageSetting ps = provider != null
? mSettings.mPackages.get(provider.owner.packageName)
: null;
- return provider != null
+ return ps != null
&& mSettings.isEnabledLPr(provider.info, flags, userId)
&& (!mSafeMode || (provider.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)
? PackageParser.generateProviderInfo(provider, flags,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId)
+ ps.readUserState(userId), userId)
: null;
}
}
@@ -2948,14 +2968,15 @@
PackageParser.Provider p = entry.getValue();
PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
- if (p.syncable
+ if (ps != null && p.syncable
&& (!mSafeMode || (p.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)) {
- outNames.add(entry.getKey());
- outInfo.add(PackageParser.generateProviderInfo(p, 0,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId));
+ ProviderInfo info = PackageParser.generateProviderInfo(p, 0,
+ ps.readUserState(userId), userId);
+ if (info != null) {
+ outNames.add(entry.getKey());
+ outInfo.add(info);
+ }
}
}
}
@@ -2973,7 +2994,7 @@
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
- if (p.info.authority != null
+ if (ps != null && p.info.authority != null
&& (processName == null
|| (p.info.processName.equals(processName)
&& UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
@@ -2983,10 +3004,11 @@
if (finalList == null) {
finalList = new ArrayList<ProviderInfo>(3);
}
- finalList.add(PackageParser.generateProviderInfo(p, flags,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId));
+ ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
+ ps.readUserState(userId), userId);
+ if (info != null) {
+ finalList.add(info);
+ }
}
}
}
@@ -3019,8 +3041,11 @@
final PackageParser.Instrumentation p = i.next();
if (targetPackage == null
|| targetPackage.equals(p.info.targetPackage)) {
- finalList.add(PackageParser.generateInstrumentationInfo(p,
- flags));
+ InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p,
+ flags);
+ if (ii != null) {
+ finalList.add(ii);
+ }
}
}
}
@@ -3047,7 +3072,7 @@
continue;
}
PackageParser.Package pkg = scanPackageLI(file,
- flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
+ flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
// Don't mess around with apps in system partition.
if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
@@ -3115,7 +3140,7 @@
* Returns null in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile,
- int parseFlags, int scanMode, long currentTime) {
+ int parseFlags, int scanMode, long currentTime, UserHandle user) {
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
String scanPath = scanFile.getPath();
parseFlags |= mDefParseFlags;
@@ -3181,7 +3206,7 @@
InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
- synchronized (mInstaller) {
+ synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
synchronized (mPackages) {
@@ -3215,7 +3240,7 @@
*/
if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
- deletePackageLI(pkg.packageName, true, 0, null, false);
+ deletePackageLI(pkg.packageName, null, true, 0, null, false);
ps = null;
} else {
/*
@@ -3237,7 +3262,7 @@
+ " better than installed " + ps.versionCode);
InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
- synchronized (mInstaller) {
+ synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
}
@@ -3268,7 +3293,7 @@
setApplicationInfoPaths(pkg, codePath, resPath);
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
- | SCAN_UPDATE_SIGNATURE, currentTime);
+ | SCAN_UPDATE_SIGNATURE, currentTime, user);
/*
* If the system app should be overridden by a previously installed
@@ -3471,8 +3496,38 @@
}
}
+ private int createDataDirsLI(String packageName, int uid) {
+ int[] users = sUserManager.getUserIds();
+ int res = mInstaller.install(packageName, uid, uid);
+ if (res < 0) {
+ return res;
+ }
+ for (int user : users) {
+ if (user != 0) {
+ res = mInstaller.createUserData(packageName,
+ UserHandle.getUid(user, uid), user);
+ if (res < 0) {
+ return res;
+ }
+ }
+ }
+ return res;
+ }
+
+ private int removeDataDirsLI(String packageName) {
+ int[] users = sUserManager.getUserIds();
+ int res = 0;
+ for (int user : users) {
+ int resInner = mInstaller.remove(packageName, user);
+ if (resInner < 0) {
+ res = resInner;
+ }
+ }
+ return res;
+ }
+
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
- int parseFlags, int scanMode, long currentTime) {
+ int parseFlags, int scanMode, long currentTime, UserHandle user) {
File scanFile = new File(pkg.mScanPath);
if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
pkg.applicationInfo.publicSourceDir == null) {
@@ -3664,7 +3719,7 @@
// the PkgSetting exists already and doesn't have to be created.
pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryDir,
- pkg.applicationInfo.flags, true, false);
+ pkg.applicationInfo.flags, user, false);
if (pkgSetting == null) {
Slog.w(TAG, "Creating application package " + pkg.packageName + " failed");
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -3823,11 +3878,9 @@
|| (scanMode&SCAN_BOOTING) != 0)) {
// If this is a system app, we can at least delete its
// current data so the application will still work.
- int ret = mInstaller.remove(pkgName, 0);
+ int ret = removeDataDirsLI(pkgName);
if (ret >= 0) {
// TODO: Kill the processes first
- // Remove the data directories for all users
- sUserManager.removePackageForAllUsers(pkgName);
// Old data gone!
String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
? "System package " : "Third party package ";
@@ -3839,8 +3892,7 @@
recovered = true;
// And now re-install the app.
- ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
- pkg.applicationInfo.uid);
+ ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
if (ret == -1) {
// Ack should not happen!
msg = prefix + pkg.packageName
@@ -3849,9 +3901,6 @@
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
- // Create data directories for all users
- sUserManager.installPackageForAllUsers(pkgName,
- pkg.applicationInfo.uid);
}
if (!recovered) {
mHasSystemUidErrors = true;
@@ -3889,15 +3938,12 @@
Log.v(TAG, "Want this data dir: " + dataPath);
}
//invoke installer to do the actual installation
- int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
- pkg.applicationInfo.uid);
+ int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
if (ret < 0) {
// Error from installer
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
- // Create data directories for all users
- sUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
if (dataPath.exists()) {
pkg.applicationInfo.dataDir = dataPath.getPath();
@@ -4024,7 +4070,9 @@
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
// Make sure we don't accidentally delete its data.
- mSettings.mPackagesToBeCleaned.remove(pkgName);
+ for (int i=0; i<mSettings.mPackagesToBeCleaned.size(); i++) {
+ mSettings.mPackagesToBeCleaned.valueAt(i).remove(pkgName);
+ }
// Take care of first install / last update times.
if (currentTime != 0) {
@@ -4840,7 +4888,8 @@
// System apps are never considered stopped for purposes of
// filtering, because there may be no way for the user to
// actually re-launch them.
- return ps.getStopped(userId) && (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0;
+ return (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0
+ && ps.getStopped(userId);
}
}
return false;
@@ -4863,12 +4912,17 @@
&ApplicationInfo.FLAG_SYSTEM) == 0) {
return null;
}
- final ResolveInfo res = new ResolveInfo();
PackageSetting ps = (PackageSetting) activity.owner.mExtras;
- res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId);
+ if (ps == null) {
+ return null;
+ }
+ ActivityInfo ai = PackageParser.generateActivityInfo(activity, mFlags,
+ ps.readUserState(userId), userId);
+ if (ai == null) {
+ return null;
+ }
+ final ResolveInfo res = new ResolveInfo();
+ res.activityInfo = ai;
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
@@ -5031,8 +5085,8 @@
// System apps are never considered stopped for purposes of
// filtering, because there may be no way for the user to
// actually re-launch them.
- return ps.getStopped(userId)
- && (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0;
+ return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
+ && ps.getStopped(userId);
}
}
return false;
@@ -5056,12 +5110,17 @@
&ApplicationInfo.FLAG_SYSTEM) == 0) {
return null;
}
- final ResolveInfo res = new ResolveInfo();
PackageSetting ps = (PackageSetting) service.owner.mExtras;
- res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
- ps != null ? ps.getStopped(userId) : false,
- ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
- userId);
+ if (ps == null) {
+ return null;
+ }
+ ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags,
+ ps.readUserState(userId), userId);
+ if (si == null) {
+ return null;
+ }
+ final ResolveInfo res = new ResolveInfo();
+ res.serviceInfo = si;
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = filter;
}
@@ -5152,13 +5211,14 @@
};
static final void sendPackageBroadcast(String action, String pkg,
- Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, int userId) {
+ Bundle extras, String targetPkg, IIntentReceiver finishedReceiver,
+ int[] userIds) {
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
try {
- int[] userIds = userId == UserHandle.USER_ALL
- ? sUserManager.getUserIds()
- : new int[] {userId};
+ if (userIds == null) {
+ userIds = sUserManager.getUserIds();
+ }
for (int id : userIds) {
final Intent intent = new Intent(action,
pkg != null ? Uri.fromParts("package", pkg, null) : null);
@@ -5170,11 +5230,18 @@
}
// Modify the UID when posting to other users
int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- if (uid > 0 && id > 0) {
+ if (uid > 0 && UserHandle.getUserId(uid) != id) {
uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
intent.putExtra(Intent.EXTRA_UID, uid);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ if (DEBUG_BROADCASTS) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "Sending to user " + id + ": "
+ + intent.toShortString(false, true, false, false)
+ + " " + intent.getExtras(), here);
+ }
am.broadcastIntent(null, intent, null, finishedReceiver,
0, null, null, null, finishedReceiver != null, false, id);
}
@@ -5192,8 +5259,9 @@
return mMediaMounted || Environment.isExternalStorageEmulated();
}
- public String nextPackageToClean(String lastPackage) {
+ public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) {
// writer
+ final int userId = UserHandle.getCallingUserId();
synchronized (mPackages) {
if (!isExternalMediaAvailable()) {
// If the external storage is no longer mounted at this point,
@@ -5201,34 +5269,65 @@
// packages files and can not delete any more. Bail.
return null;
}
- if (lastPackage != null) {
- mSettings.mPackagesToBeCleaned.remove(lastPackage);
+ ArrayList<PackageCleanItem> pkgs = mSettings.mPackagesToBeCleaned.get(userId);
+ if (pkgs != null) {
+ if (lastPackage != null) {
+ pkgs.remove(lastPackage);
+ }
+ if (pkgs.size() > 0) {
+ return pkgs.get(0);
+ }
}
- return mSettings.mPackagesToBeCleaned.size() > 0
- ? mSettings.mPackagesToBeCleaned.get(0) : null;
}
+ // Move on to the next user to clean.
+ long ident = Binder.clearCallingIdentity();
+ try {
+ startCleaningPackages(userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return null;
}
- void schedulePackageCleaning(String packageName) {
- mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName));
+ void schedulePackageCleaning(String packageName, int userId, boolean andCode) {
+ if (false) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "Schedule cleaning " + packageName + " user=" + userId
+ + " andCode=" + andCode, here);
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE,
+ userId, andCode ? 1 : 0, packageName));
}
- void startCleaningPackages() {
+ void startCleaningPackages(int lastUser) {
// reader
+ int nextUser = -1;
synchronized (mPackages) {
if (!isExternalMediaAvailable()) {
return;
}
- if (mSettings.mPackagesToBeCleaned.size() <= 0) {
+ final int N = mSettings.mPackagesToBeCleaned.size();
+ if (N <= 0) {
return;
}
+ for (int i=0; i<N; i++) {
+ int user = mSettings.mPackagesToBeCleaned.keyAt(i);
+ if (user > lastUser) {
+ nextUser = user;
+ break;
+ }
+ }
+ if (nextUser < 0) {
+ nextUser = mSettings.mPackagesToBeCleaned.keyAt(0);
+ }
}
Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE);
intent.setComponent(DEFAULT_CONTAINER_COMPONENT);
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
try {
- am.startService(null, intent, null);
+ am.startService(null, intent, null, nextUser);
} catch (RemoteException e) {
}
}
@@ -5291,7 +5390,7 @@
PackageParser.PARSE_CHATTY |
PackageParser.PARSE_MUST_BE_APK,
SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
- System.currentTimeMillis());
+ System.currentTimeMillis(), null);
if (p != null) {
/*
* TODO this seems dangerous as the package may have
@@ -5320,13 +5419,13 @@
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, UserHandle.USER_ALL);
+ extras, null, null, null);
}
if (addedPackage != null) {
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, addedUid);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
- extras, null, null, UserHandle.USER_ALL);
+ extras, null, null, null);
}
}
@@ -5365,6 +5464,12 @@
null);
final int uid = Binder.getCallingUid();
+ UserHandle user;
+ if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {
+ user = UserHandle.ALL;
+ } else {
+ user = Process.myUserHandle();
+ }
final int filteredFlags;
@@ -5379,10 +5484,51 @@
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
- verificationParams, encryptionParams);
+ verificationParams, encryptionParams, user);
mHandler.sendMessage(msg);
}
+ /**
+ * @hide
+ */
+ @Override
+ public int installExistingPackage(String packageName) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
+ null);
+ PackageSetting pkgSetting;
+ final int uid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(uid);
+
+ long callingId = Binder.clearCallingIdentity();
+ try {
+ boolean sendAdded = false;
+ Bundle extras = new Bundle(1);
+
+ // writer
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting == null) {
+ return PackageManager.INSTALL_FAILED_INVALID_URI;
+ }
+ if (!pkgSetting.getInstalled(userId)) {
+ pkgSetting.setInstalled(true, userId);
+ mSettings.writePackageRestrictionsLPr(userId);
+ extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
+ sendAdded = true;
+ }
+ }
+
+ if (sendAdded) {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ packageName, extras, null, null, new int[] {userId});
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
@Override
public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
@@ -5721,6 +5867,17 @@
*/
private int mRetries = 0;
+ /** User handle for the user requesting the information or installation. */
+ private final UserHandle mUser;
+
+ HandlerParams(UserHandle user) {
+ mUser = user;
+ }
+
+ UserHandle getUser() {
+ return mUser;
+ }
+
final boolean startCopy() {
boolean res;
try {
@@ -5762,6 +5919,7 @@
private final IPackageStatsObserver mObserver;
public MeasureParams(PackageStats stats, IPackageStatsObserver observer) {
+ super(new UserHandle(stats.userHandle));
mObserver = observer;
mStats = stats;
}
@@ -5844,7 +6002,8 @@
InstallParams(Uri packageURI,
IPackageInstallObserver observer, int flags,
String installerPackageName, VerificationParams verificationParams,
- ContainerEncryptionParams encryptionParams) {
+ ContainerEncryptionParams encryptionParams, UserHandle user) {
+ super(user);
this.mPackageURI = packageURI;
this.flags = flags;
this.observer = observer;
@@ -5869,6 +6028,16 @@
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null) {
if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+ // Check for downgrading.
+ if ((flags & PackageManager.INSTALL_ALLOW_DOWNGRADE) == 0) {
+ if (pkgLite.versionCode < pkg.mVersionCode) {
+ Slog.w(TAG, "Can't install update of " + packageName
+ + " update version " + pkgLite.versionCode
+ + " is older than installed version "
+ + pkg.mVersionCode);
+ return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE;
+ }
+ }
// Check for updated system application.
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (onSd) {
@@ -5995,6 +6164,8 @@
ret = PackageManager.INSTALL_FAILED_INVALID_URI;
} else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
+ } else if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
+ ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
} else {
// Override with defaults if needed.
loc = installLocationPolicy(pkgLite, flags);
@@ -6030,8 +6201,9 @@
verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- final List<ResolveInfo> receivers = queryIntentReceivers(verification, null,
- PackageManager.GET_DISABLED_COMPONENTS, 0 /* TODO: Which userId? */);
+ final List<ResolveInfo> receivers = queryIntentReceivers(verification,
+ PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS,
+ 0 /* TODO: Which userId? */);
if (DEBUG_VERIFY) {
Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
@@ -6185,7 +6357,8 @@
int mRet;
MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags,
- String packageName, String dataDir, int uid) {
+ String packageName, String dataDir, int uid, UserHandle user) {
+ super(user);
this.srcArgs = srcArgs;
this.observer = observer;
this.flags = flags;
@@ -6338,14 +6511,17 @@
final Uri packageURI;
final String installerPackageName;
final ManifestDigest manifestDigest;
+ final UserHandle user;
InstallArgs(Uri packageURI, IPackageInstallObserver observer, int flags,
- String installerPackageName, ManifestDigest manifestDigest) {
+ String installerPackageName, ManifestDigest manifestDigest,
+ UserHandle user) {
this.packageURI = packageURI;
this.flags = flags;
this.observer = observer;
this.installerPackageName = installerPackageName;
this.manifestDigest = manifestDigest;
+ this.user = user;
}
abstract void createCopyFile();
@@ -6396,11 +6572,12 @@
FileInstallArgs(InstallParams params) {
super(params.getPackageUri(), params.observer, params.flags,
- params.installerPackageName, params.getManifestDigest());
+ params.installerPackageName, params.getManifestDigest(),
+ params.getUser());
}
FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
- super(null, null, 0, null, null);
+ super(null, null, 0, null, null, null);
File codeFile = new File(fullCodePath);
installDir = codeFile.getParentFile();
codeFileName = fullCodePath;
@@ -6409,7 +6586,7 @@
}
FileInstallArgs(Uri packageURI, String pkgName, String dataDir) {
- super(packageURI, null, 0, null, null);
+ super(packageURI, null, 0, null, null, null);
installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
String apkName = getNextCodePath(null, pkgName, ".apk");
codeFileName = new File(installDir, apkName + ".apk").getPath();
@@ -6683,13 +6860,15 @@
AsecInstallArgs(InstallParams params) {
super(params.getPackageUri(), params.observer, params.flags,
- params.installerPackageName, params.getManifestDigest());
+ params.installerPackageName, params.getManifestDigest(),
+ params.getUser());
}
AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
boolean isExternal, boolean isForwardLocked) {
super(null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+ | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+ null, null, null);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
@@ -6700,14 +6879,16 @@
AsecInstallArgs(String cid, boolean isForwardLocked) {
super(null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+ | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+ null, null, null);
this.cid = cid;
setCachePath(PackageHelper.getSdDir(cid));
}
AsecInstallArgs(Uri packageURI, String cid, boolean isExternal, boolean isForwardLocked) {
super(packageURI, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+ | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+ null, null, null);
this.cid = cid;
}
@@ -7031,6 +7212,7 @@
class PackageInstalledInfo {
String name;
int uid;
+ int[] users;
PackageParser.Package pkg;
int returnCode;
PackageRemovedInfo removedInfo;
@@ -7040,14 +7222,12 @@
* Install a non-existing package.
*/
private void installNewPackageLI(PackageParser.Package pkg,
- int parseFlags,
- int scanMode,
+ int parseFlags, int scanMode, UserHandle user,
String installerPackageName, PackageInstalledInfo res) {
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
- res.name = pkgName;
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
// A package with the same name is already installed, though
@@ -7070,7 +7250,7 @@
}
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
- System.currentTimeMillis());
+ System.currentTimeMillis(), user);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7087,17 +7267,15 @@
// delete the package data and cache directories that it created in
// scanPackageLocked, unless those directories existed before we even tried to
// install.
- deletePackageLI(
- pkgName, false,
- dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,
+ deletePackageLI(pkgName, UserHandle.ALL, false,
+ dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
res.removedInfo, true);
}
}
}
private void replacePackageLI(PackageParser.Package pkg,
- int parseFlags,
- int scanMode,
+ int parseFlags, int scanMode, UserHandle user,
String installerPackageName, PackageInstalledInfo res) {
PackageParser.Package oldPackage;
@@ -7114,15 +7292,16 @@
}
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
- replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
+ replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
+ user, installerPackageName, res);
} else {
- replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
+ replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
+ user, installerPackageName, res);
}
}
private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
- PackageParser.Package pkg,
- int parseFlags, int scanMode,
+ PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
String installerPackageName, PackageInstalledInfo res) {
PackageParser.Package newPackage = null;
String pkgName = deletedPackage.packageName;
@@ -7137,7 +7316,7 @@
}
// First delete the existing package while retaining the data directory
- if (!deletePackageLI(pkgName, true, PackageManager.DONT_DELETE_DATA,
+ if (!deletePackageLI(pkgName, null, true, PackageManager.DELETE_KEEP_DATA,
res.removedInfo, true)) {
// If the existing package wasn't successfully deleted
res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
@@ -7146,7 +7325,7 @@
// Successfully deleted the old package. Now proceed with re-installation
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME,
- System.currentTimeMillis());
+ System.currentTimeMillis(), user);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7167,8 +7346,8 @@
// install.
if(updatedSettings) {
deletePackageLI(
- pkgName, true,
- PackageManager.DONT_DELETE_DATA,
+ pkgName, null, true,
+ PackageManager.DELETE_KEEP_DATA,
res.removedInfo, true);
}
// Since we failed to install the new package we need to restore the old
@@ -7183,7 +7362,7 @@
int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE
| SCAN_UPDATE_TIME;
if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode,
- origUpdateTime) == null) {
+ origUpdateTime, user) == null) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade");
return;
}
@@ -7201,8 +7380,7 @@
}
private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
- PackageParser.Package pkg,
- int parseFlags, int scanMode,
+ PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
String installerPackageName, PackageInstalledInfo res) {
PackageParser.Package newPackage = null;
boolean updatedSettings = false;
@@ -7251,7 +7429,7 @@
// Successfully disabled the old package. Now proceed with re-installation
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
- newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0);
+ newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user);
if (newPackage == null) {
Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7274,7 +7452,7 @@
removePackageLI(newPackage, true);
}
// Add back the old system package
- scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0);
+ scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user);
// Restore the old system information in Settings
synchronized(mPackages) {
if (updatedSettings) {
@@ -7333,6 +7511,10 @@
UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
? UPDATE_PERMISSIONS_ALL : 0));
res.name = pkgName;
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null) {
+ res.users = ps.getInstalledUsers(sUserManager.getUserIds());
+ }
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
@@ -7448,12 +7630,18 @@
setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
if (replace) {
- replacePackageLI(pkg, parseFlags, scanMode,
+ replacePackageLI(pkg, parseFlags, scanMode, args.user,
installerPackageName, res);
} else {
- installNewPackageLI(pkg, parseFlags, scanMode,
+ installNewPackageLI(pkg, parseFlags, scanMode, args.user,
installerPackageName,res);
}
+ synchronized (mPackages) {
+ PackageSetting ps = mSettings.mPackages.get(pkgName);
+ if (ps != null) {
+ res.users = ps.getInstalledUsers(sUserManager.getUserIds());
+ }
+ }
}
private static boolean isForwardLocked(PackageParser.Package pkg) {
@@ -7544,10 +7732,11 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DELETE_PACKAGES, null);
// Queue up an async operation since the package deletion may take a little while.
+ final int uid = Binder.getCallingUid();
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
- final int returnCode = deletePackageX(packageName, true, true, flags);
+ final int returnCode = deletePackageX(packageName, uid, flags);
if (observer != null) {
try {
observer.packageDeleted(packageName, returnCode);
@@ -7573,8 +7762,7 @@
* persisting settings for later use
* sending a broadcast if necessary
*/
- private int deletePackageX(String packageName, boolean sendBroadCast,
- boolean deleteCodeAndResources, int flags) {
+ private int deletePackageX(String packageName, int uid, int flags) {
final PackageRemovedInfo info = new PackageRemovedInfo();
final boolean res;
@@ -7589,27 +7777,30 @@
}
synchronized (mInstallLock) {
- res = deletePackageLI(packageName, deleteCodeAndResources,
- flags | REMOVE_CHATTY, info, true);
+ res = deletePackageLI(packageName,
+ (flags & PackageManager.DELETE_ALL_USERS) != 0
+ ? UserHandle.ALL : new UserHandle(UserHandle.getUserId(uid)),
+ true, flags | REMOVE_CHATTY, info, true);
}
- if (res && sendBroadCast) {
+ if (res) {
boolean systemUpdate = info.isRemovedPackageSystemUpdate;
- info.sendBroadcast(deleteCodeAndResources, systemUpdate);
+ info.sendBroadcast(true, systemUpdate);
// If the removed package was a system update, the old system packaged
// was re-enabled; we need to broadcast this information
if (systemUpdate) {
Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, info.removedUid >= 0 ? info.removedUid : info.uid);
+ extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0
+ ? info.removedAppId : info.uid);
extras.putBoolean(Intent.EXTRA_REPLACING, true);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, null, null, UserHandle.USER_ALL);
+ extras, null, null, null);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, null, null, UserHandle.USER_ALL);
+ extras, null, null, null);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
- null, packageName, null, UserHandle.USER_ALL);
+ null, packageName, null, null);
}
}
// Force a gc here.
@@ -7618,7 +7809,7 @@
// other processes clean up before deleting resources.
if (info.args != null) {
synchronized (mInstallLock) {
- info.args.doPostDeleteLI(deleteCodeAndResources);
+ info.args.doPostDeleteLI(true);
}
}
@@ -7628,29 +7819,30 @@
static class PackageRemovedInfo {
String removedPackage;
int uid = -1;
- int removedUid = -1;
+ int removedAppId = -1;
+ int[] removedUsers = null;
boolean isRemovedPackageSystemUpdate = false;
// Clean up resources deleted packages.
InstallArgs args = null;
void sendBroadcast(boolean fullRemove, boolean replacing) {
Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, removedUid >= 0 ? removedUid : uid);
+ extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove);
if (replacing) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
if (removedPackage != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null, UserHandle.USER_ALL);
+ extras, null, null, removedUsers);
if (fullRemove && !replacing) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
- extras, null, null, UserHandle.USER_ALL);
+ extras, null, null, removedUsers);
}
}
- if (removedUid >= 0) {
+ if (removedAppId >= 0) {
sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
- UserHandle.getUserId(removedUid));
+ removedUsers);
}
}
}
@@ -7664,34 +7856,28 @@
private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo,
int flags, boolean writeSettings) {
String packageName = p.packageName;
- if (outInfo != null) {
- outInfo.removedPackage = packageName;
- }
removePackageLI(p, (flags&REMOVE_CHATTY) != 0);
// Retrieve object to delete permissions for shared user later on
final PackageSetting deletedPs;
// reader
synchronized (mPackages) {
deletedPs = mSettings.mPackages.get(packageName);
- }
- if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
- 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
- sUserManager.removePackageForAllUsers(packageName);
+ if (outInfo != null) {
+ outInfo.removedPackage = packageName;
+ outInfo.removedUsers = deletedPs != null
+ ? deletedPs.getInstalledUsers(sUserManager.getUserIds()) : null;
}
- schedulePackageCleaning(packageName);
+ }
+ if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
+ removeDataDirsLI(packageName);
+ schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);
}
// writer
synchronized (mPackages) {
if (deletedPs != null) {
- if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+ if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
if (outInfo != null) {
- outInfo.removedUid = mSettings.removePackageLPw(packageName);
+ outInfo.removedAppId = mSettings.removePackageLPw(packageName);
}
if (deletedPs != null) {
updatePermissionsLPw(deletedPs.name, null, 0);
@@ -7740,10 +7926,10 @@
outInfo.isRemovedPackageSystemUpdate = true;
if (ps.versionCode < p.mVersionCode) {
// Delete data for downgrades
- flags &= ~PackageManager.DONT_DELETE_DATA;
+ flags &= ~PackageManager.DELETE_KEEP_DATA;
} else {
// Preserve data by setting flag
- flags |= PackageManager.DONT_DELETE_DATA;
+ flags |= PackageManager.DELETE_KEEP_DATA;
}
boolean ret = deleteInstalledPackageLI(p, true, flags, outInfo,
writeSettings);
@@ -7760,7 +7946,7 @@
// Install the system package
PackageParser.Package newPkg = scanPackageLI(ps.codePath,
PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM,
- SCAN_MONITOR | SCAN_NO_PATHS, 0);
+ SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
if (newPkg == null) {
Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError);
@@ -7807,7 +7993,7 @@
/*
* This method handles package deletion in general
*/
- private boolean deletePackageLI(String packageName,
+ private boolean deletePackageLI(String packageName, UserHandle user,
boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo,
boolean writeSettings) {
if (packageName == null) {
@@ -7816,6 +8002,8 @@
}
PackageParser.Package p;
boolean dataOnly = false;
+ int removeUser = -1;
+ int appId = -1;
synchronized (mPackages) {
p = mPackages.get(packageName);
if (p == null) {
@@ -7823,15 +8011,53 @@
dataOnly = true;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
- Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
+ Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
return false;
}
p = ps.pkg;
}
+ if (p == null) {
+ Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
+ return false;
+ }
+ final PackageSetting ps = (PackageSetting)p.mExtras;
+ if (!isSystemApp(p) && ps != null && user != null
+ && user.getIdentifier() != UserHandle.USER_ALL) {
+ // The caller is asking that the package only be deleted for a single
+ // user. To do this, we just mark its uninstalled state and delete
+ // its data.
+ ps.setUserState(user.getIdentifier(),
+ COMPONENT_ENABLED_STATE_DEFAULT,
+ false, //installed
+ true, //stopped
+ true, //notLaunched
+ null, null);
+ if (ps.isAnyInstalled(sUserManager.getUserIds())) {
+ // Other user still have this package installed, so all
+ // we need to do is clear this user's data and save that
+ // it is uninstalled.
+ removeUser = user.getIdentifier();
+ appId = ps.appId;
+ mSettings.writePackageRestrictionsLPr(removeUser);
+ } else {
+ // We need to set it back to 'installed' so the uninstall
+ // broadcasts will be sent correctly.
+ ps.setInstalled(true, user.getIdentifier());
+ }
+ }
}
- if (p == null) {
- Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
- return false;
+
+ if (removeUser >= 0) {
+ // From above, we determined that we are deleting this only
+ // for a single user. Continue the work here.
+ if (outInfo != null) {
+ outInfo.removedPackage = packageName;
+ outInfo.removedAppId = appId;
+ outInfo.removedUsers = new int[] {removeUser};
+ }
+ mInstaller.clearUserData(packageName, removeUser);
+ schedulePackageCleaning(packageName, removeUser, false);
+ return true;
}
if (dataOnly) {
@@ -7876,7 +8102,7 @@
}
}
- private void clearExternalStorageDataSync(String packageName, boolean allData) {
+ private void clearExternalStorageDataSync(String packageName, int userId, boolean allData) {
final boolean mounted;
if (Environment.isExternalStorageEmulated()) {
mounted = true;
@@ -7892,44 +8118,52 @@
}
final Intent containerIntent = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
- ClearStorageConnection conn = new ClearStorageConnection();
- if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE)) {
- try {
- long timeout = SystemClock.uptimeMillis() + 5000;
- synchronized (conn) {
- long now = SystemClock.uptimeMillis();
- while (conn.mContainerService == null && now < timeout) {
- try {
- conn.wait(timeout - now);
- } catch (InterruptedException e) {
+ int[] users;
+ if (userId == UserHandle.USER_ALL) {
+ users = sUserManager.getUserIds();
+ } else {
+ users = new int[] { userId };
+ }
+ for (int curUser : users) {
+ ClearStorageConnection conn = new ClearStorageConnection();
+ if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE, curUser)) {
+ try {
+ long timeout = SystemClock.uptimeMillis() + 5000;
+ synchronized (conn) {
+ long now = SystemClock.uptimeMillis();
+ while (conn.mContainerService == null && now < timeout) {
+ try {
+ conn.wait(timeout - now);
+ } catch (InterruptedException e) {
+ }
}
}
- }
- if (conn.mContainerService == null) {
- return;
- }
- final File externalCacheDir = Environment
- .getExternalStorageAppCacheDirectory(packageName);
- try {
- conn.mContainerService.clearDirectory(externalCacheDir.toString());
- } catch (RemoteException e) {
- }
- if (allData) {
- final File externalDataDir = Environment
- .getExternalStorageAppDataDirectory(packageName);
+ if (conn.mContainerService == null) {
+ return;
+ }
+ final File externalCacheDir = Environment
+ .getExternalStorageAppCacheDirectory(packageName);
try {
- conn.mContainerService.clearDirectory(externalDataDir.toString());
+ conn.mContainerService.clearDirectory(externalCacheDir.toString());
} catch (RemoteException e) {
}
- final File externalMediaDir = Environment
- .getExternalStorageAppMediaDirectory(packageName);
- try {
- conn.mContainerService.clearDirectory(externalMediaDir.toString());
- } catch (RemoteException e) {
+ if (allData) {
+ final File externalDataDir = Environment
+ .getExternalStorageAppDataDirectory(packageName);
+ try {
+ conn.mContainerService.clearDirectory(externalDataDir.toString());
+ } catch (RemoteException e) {
+ }
+ final File externalMediaDir = Environment
+ .getExternalStorageAppMediaDirectory(packageName);
+ try {
+ conn.mContainerService.clearDirectory(externalMediaDir.toString());
+ } catch (RemoteException e) {
+ }
}
+ } finally {
+ mContext.unbindService(conn);
}
- } finally {
- mContext.unbindService(conn);
}
}
}
@@ -7948,7 +8182,7 @@
synchronized (mInstallLock) {
succeeded = clearApplicationUserDataLI(packageName, userId);
}
- clearExternalStorageDataSync(packageName, true);
+ clearExternalStorageDataSync(packageName, userId, true);
if (succeeded) {
// invoke DeviceStorageMonitor's update method to clear any notifications
DeviceStorageMonitorService dsm = (DeviceStorageMonitorService)
@@ -8022,7 +8256,7 @@
synchronized (mInstallLock) {
succeded = deleteApplicationCacheFilesLI(packageName, userId);
}
- clearExternalStorageDataSync(packageName, false);
+ clearExternalStorageDataSync(packageName, userId, false);
if(observer != null) {
try {
observer.onRemoveCompleted(packageName, succeded);
@@ -8480,7 +8714,7 @@
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null,
- UserHandle.getUserId(packageUid));
+ new int[] {UserHandle.getUserId(packageUid)});
}
public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
@@ -9085,7 +9319,7 @@
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Loading packages");
loadMediaPackages(processCids, uidArr, removeCids);
- startCleaningPackages();
+ startCleaningPackages(-1);
} else {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Unloading packages");
@@ -9106,7 +9340,7 @@
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserHandle.USER_ALL);
+ sendPackageBroadcast(action, null, extras, null, finishedReceiver, null);
}
}
@@ -9151,7 +9385,7 @@
doGc = true;
synchronized (mInstallLock) {
final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
- 0, 0);
+ 0, 0, null);
// Scan the package
if (pkg != null) {
/*
@@ -9260,8 +9494,8 @@
// Delete package internally
PackageRemovedInfo outInfo = new PackageRemovedInfo();
synchronized (mInstallLock) {
- boolean res = deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA,
- outInfo, false);
+ boolean res = deletePackageLI(pkgName, null, false,
+ PackageManager.DELETE_KEEP_DATA, outInfo, false);
if (res) {
pkgList.add(pkgName);
} else {
@@ -9298,9 +9532,12 @@
}
}
+ /** Binder call */
+ @Override
public void movePackage(final String packageName, final IPackageMoveObserver observer,
final int flags) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
+ UserHandle user = new UserHandle(UserHandle.getCallingUserId());
int returnCode = PackageManager.MOVE_SUCCEEDED;
int currFlags = 0;
int newFlags = 0;
@@ -9351,14 +9588,15 @@
* anyway.
*/
if (returnCode != PackageManager.MOVE_SUCCEEDED) {
- processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1),
+ processPendingMove(new MoveParams(null, observer, 0, packageName,
+ null, -1, user),
returnCode);
} 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,
- pkg.applicationInfo.dataDir, pkg.applicationInfo.uid);
+ pkg.applicationInfo.dataDir, pkg.applicationInfo.uid, user);
msg.obj = mp;
mHandler.sendMessage(msg);
}
@@ -9550,15 +9788,36 @@
}
/** Called by UserManagerService */
- void cleanUpUser(int userHandle) {
+ void cleanUpUserLILPw(int userHandle) {
// Disable all the packages for the user first
- synchronized (mPackages) {
- Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet();
- for (Entry<String, PackageSetting> entry : entries) {
- entry.getValue().removeUser(userHandle);
+ Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet();
+ for (Entry<String, PackageSetting> entry : entries) {
+ entry.getValue().removeUser(userHandle);
+ }
+ if (mDirtyUsers.remove(userHandle));
+ mSettings.removeUserLPr(userHandle);
+ if (mInstaller != null) {
+ // Technically, we shouldn't be doing this with the package lock
+ // held. However, this is very rare, and there is already so much
+ // other disk I/O going on, that we'll let it slide for now.
+ mInstaller.removeUserDataDirs(userHandle);
+ }
+ }
+
+ /** Called by UserManagerService */
+ void createNewUserLILPw(int userHandle, File path) {
+ if (mInstaller != null) {
+ path.mkdir();
+ FileUtils.setPermissions(path.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+ | FileUtils.S_IXOTH, -1, -1);
+ for (PackageSetting ps : mSettings.mPackages.values()) {
+ // Only system apps are initially installed.
+ ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle);
+ // Need to create a data directory for all apps under this user.
+ mInstaller.createUserData(ps.name,
+ UserHandle.getUid(userHandle, ps.appId), userHandle);
}
- if (mDirtyUsers.remove(userHandle));
- mSettings.removeUserLPr(userHandle);
+ mSettings.writePackageRestrictionsLPr(userHandle);
}
}
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index 56f2166..6d31f0e 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -20,11 +20,13 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import android.content.pm.PackageUserState;
+import android.content.pm.UserInfo;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import java.io.File;
import java.util.HashSet;
+import java.util.List;
/**
* Settings base class for pending and resolved classes.
@@ -62,19 +64,11 @@
boolean permissionsFixed;
boolean haveGids;
+ private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
+
// Whether this package is currently stopped, thus can not be
// started until explicitly launched by the user.
- private SparseArray<Boolean> stopped = new SparseArray<Boolean>();
-
- // Set to true if we have never launched this app.
- private SparseArray<Boolean> notLaunched = new SparseArray<Boolean>();
-
- /* Explicitly disabled components */
- private SparseArray<HashSet<String>> disabledComponents = new SparseArray<HashSet<String>>();
- /* Explicitly enabled components */
- private SparseArray<HashSet<String>> enabledComponents = new SparseArray<HashSet<String>>();
- /* Enabled state */
- private SparseIntArray enabled = new SparseIntArray();
+ private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>();
int installStatus = PKG_INSTALL_COMPLETE;
@@ -115,12 +109,11 @@
permissionsFixed = base.permissionsFixed;
haveGids = base.haveGids;
- notLaunched = base.notLaunched;
-
- disabledComponents = (SparseArray<HashSet<String>>) base.disabledComponents.clone();
- enabledComponents = (SparseArray<HashSet<String>>) base.enabledComponents.clone();
- enabled = (SparseIntArray) base.enabled.clone();
- stopped = (SparseArray<Boolean>) base.stopped.clone();
+ userState.clear();
+ for (int i=0; i<base.userState.size(); i++) {
+ userState.put(base.userState.keyAt(i),
+ new PackageUserState(base.userState.valueAt(i)));
+ }
installStatus = base.installStatus;
origPackage = base.origPackage;
@@ -171,103 +164,174 @@
signatures = base.signatures;
permissionsFixed = base.permissionsFixed;
haveGids = base.haveGids;
- stopped = base.stopped;
- notLaunched = base.notLaunched;
- disabledComponents = base.disabledComponents;
- enabledComponents = base.enabledComponents;
- enabled = base.enabled;
+ userState.clear();
+ for (int i=0; i<base.userState.size(); i++) {
+ userState.put(base.userState.keyAt(i), base.userState.valueAt(i));
+ }
installStatus = base.installStatus;
}
+ private PackageUserState modifyUserState(int userId) {
+ PackageUserState state = userState.get(userId);
+ if (state == null) {
+ state = new PackageUserState();
+ userState.put(userId, state);
+ }
+ return state;
+ }
+
+ public PackageUserState readUserState(int userId) {
+ PackageUserState state = userState.get(userId);
+ return state != null ? state : DEFAULT_USER_STATE;
+ }
+
void setEnabled(int state, int userId) {
- enabled.put(userId, state);
+ modifyUserState(userId).enabled = state;
}
int getEnabled(int userId) {
- return enabled.get(userId, COMPONENT_ENABLED_STATE_DEFAULT);
+ return readUserState(userId).enabled;
+ }
+
+ void setInstalled(boolean inst, int userId) {
+ modifyUserState(userId).installed = inst;
+ }
+
+ boolean getInstalled(int userId) {
+ return readUserState(userId).installed;
+ }
+
+ boolean isAnyInstalled(int[] users) {
+ for (int user: users) {
+ if (readUserState(user).installed) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int[] getInstalledUsers(int[] users) {
+ int num = 0;
+ for (int user : users) {
+ if (getInstalled(user)) {
+ num++;
+ }
+ }
+ int[] res = new int[num];
+ num = 0;
+ for (int user : users) {
+ if (getInstalled(user)) {
+ res[num] = user;
+ num++;
+ }
+ }
+ return res;
}
boolean getStopped(int userId) {
- return stopped.get(userId, false);
+ return readUserState(userId).stopped;
}
void setStopped(boolean stop, int userId) {
- stopped.put(userId, stop);
+ modifyUserState(userId).stopped = stop;
}
boolean getNotLaunched(int userId) {
- return notLaunched.get(userId, false);
+ return readUserState(userId).notLaunched;
}
void setNotLaunched(boolean stop, int userId) {
- notLaunched.put(userId, stop);
+ modifyUserState(userId).notLaunched = stop;
+ }
+
+ void setUserState(int userId, int enabled, boolean installed, boolean stopped,
+ boolean notLaunched, HashSet<String> enabledComponents,
+ HashSet<String> disabledComponents) {
+ PackageUserState state = modifyUserState(userId);
+ state.enabled = enabled;
+ state.installed = installed;
+ state.stopped = stopped;
+ state.notLaunched = notLaunched;
+ state.enabledComponents = enabledComponents;
+ state.disabledComponents = disabledComponents;
}
HashSet<String> getEnabledComponents(int userId) {
- return getComponentHashSet(enabledComponents, userId);
+ return readUserState(userId).enabledComponents;
}
HashSet<String> getDisabledComponents(int userId) {
- return getComponentHashSet(disabledComponents, userId);
+ return readUserState(userId).disabledComponents;
}
void setEnabledComponents(HashSet<String> components, int userId) {
- enabledComponents.put(userId, components);
+ modifyUserState(userId).enabledComponents = components;
}
void setDisabledComponents(HashSet<String> components, int userId) {
- disabledComponents.put(userId, components);
+ modifyUserState(userId).disabledComponents = components;
}
- private HashSet<String> getComponentHashSet(SparseArray<HashSet<String>> setArray, int userId) {
- HashSet<String> set = setArray.get(userId);
- if (set == null) {
- set = new HashSet<String>(1);
- setArray.put(userId, set);
+ void setEnabledComponentsCopy(HashSet<String> components, int userId) {
+ modifyUserState(userId).enabledComponents = components != null
+ ? new HashSet<String>(components) : null;
+ }
+
+ void setDisabledComponentsCopy(HashSet<String> components, int userId) {
+ modifyUserState(userId).disabledComponents = components != null
+ ? new HashSet<String>(components) : null;
+ }
+
+ PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
+ PackageUserState state = modifyUserState(userId);
+ if (disabled && state.disabledComponents == null) {
+ state.disabledComponents = new HashSet<String>(1);
}
- return set;
+ if (enabled && state.enabledComponents == null) {
+ state.enabledComponents = new HashSet<String>(1);
+ }
+ return state;
}
void addDisabledComponent(String componentClassName, int userId) {
- HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
- disabled.add(componentClassName);
+ modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
}
void addEnabledComponent(String componentClassName, int userId) {
- HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
- enabled.add(componentClassName);
+ modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName);
}
boolean enableComponentLPw(String componentClassName, int userId) {
- HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
- HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
- boolean changed = disabled.remove(componentClassName);
- changed |= enabled.add(componentClassName);
+ PackageUserState state = modifyUserStateComponents(userId, false, true);
+ boolean changed = state.disabledComponents != null
+ ? state.disabledComponents.remove(componentClassName) : false;
+ changed |= state.enabledComponents.add(componentClassName);
return changed;
}
boolean disableComponentLPw(String componentClassName, int userId) {
- HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
- HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
- boolean changed = enabled.remove(componentClassName);
- changed |= disabled.add(componentClassName);
+ PackageUserState state = modifyUserStateComponents(userId, true, false);
+ boolean changed = state.enabledComponents != null
+ ? state.enabledComponents.remove(componentClassName) : false;
+ changed |= state.disabledComponents.add(componentClassName);
return changed;
}
boolean restoreComponentLPw(String componentClassName, int userId) {
- HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
- HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
- boolean changed = enabled.remove(componentClassName);
- changed |= disabled.remove(componentClassName);
+ PackageUserState state = modifyUserStateComponents(userId, true, true);
+ boolean changed = state.disabledComponents != null
+ ? state.disabledComponents.remove(componentClassName) : false;
+ changed |= state.enabledComponents != null
+ ? state.enabledComponents.remove(componentClassName) : false;
return changed;
}
int getCurrentEnabledStateLPr(String componentName, int userId) {
- HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
- HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
- if (enabled.contains(componentName)) {
+ PackageUserState state = readUserState(userId);
+ if (state.enabledComponents != null && state.enabledComponents.contains(componentName)) {
return COMPONENT_ENABLED_STATE_ENABLED;
- } else if (disabled.contains(componentName)) {
+ } else if (state.disabledComponents != null
+ && state.disabledComponents.contains(componentName)) {
return COMPONENT_ENABLED_STATE_DISABLED;
} else {
return COMPONENT_ENABLED_STATE_DEFAULT;
@@ -275,11 +339,6 @@
}
void removeUser(int userId) {
- enabled.delete(userId);
- stopped.delete(userId);
- enabledComponents.delete(userId);
- disabledComponents.delete(userId);
- notLaunched.delete(userId);
+ userState.delete(userId);
}
-
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index cfc0f5c..68b594a 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -38,11 +38,13 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
+import android.content.pm.PackageCleanItem;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PermissionInfo;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
+import android.content.pm.PackageUserState;
import android.content.pm.VerifierDeviceIdentity;
import android.os.Binder;
import android.os.Environment;
@@ -92,9 +94,12 @@
private static final String TAG_PACKAGE = "pkg";
private static final String ATTR_NAME = "name";
+ private static final String ATTR_USER = "user";
+ private static final String ATTR_CODE = "code";
private static final String ATTR_NOT_LAUNCHED = "nl";
private static final String ATTR_ENABLED = "enabled";
private static final String ATTR_STOPPED = "stopped";
+ private static final String ATTR_INSTALLED = "inst";
private final File mSettingsFilename;
private final File mBackupSettingsFilename;
@@ -156,7 +161,8 @@
// Packages that have been uninstalled and still need their external
// storage data deleted.
- final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
+ final SparseArray<ArrayList<PackageCleanItem>> mPackagesToBeCleaned
+ = new SparseArray<ArrayList<PackageCleanItem>>();
// Packages that have been renamed since they were first installed.
// Keys are the new names of the packages, values are the original
@@ -200,10 +206,11 @@
PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
+ String nativeLibraryPathString, int pkgFlags, UserHandle user, boolean add) {
final String name = pkg.packageName;
PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
- resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
+ resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags,
+ user, add);
return p;
}
@@ -364,7 +371,8 @@
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) {
+ String nativeLibraryPathString, int vc, int pkgFlags,
+ UserHandle installUser, boolean add) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (!p.codePath.equals(codePath)) {
@@ -407,11 +415,6 @@
}
}
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,
@@ -445,8 +448,20 @@
List<UserInfo> users = getAllUsers();
if (users != null) {
for (UserInfo user : users) {
- p.setStopped(true, user.id);
- p.setNotLaunched(true, user.id);
+ // By default we consider this app to be installed
+ // for the user if no user has been specified (which
+ // means to leave it at its original value, and the
+ // original default value is true), or we are being
+ // asked to install for all users, or this is the
+ // user we are installing for.
+ final boolean installed = installUser == null
+ || installUser.getIdentifier() == UserHandle.USER_ALL
+ || installUser.getIdentifier() == user.id;
+ p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT,
+ installed,
+ true, // stopped,
+ true, // notLaunched
+ null, null);
writePackageRestrictionsLPr(user.id);
}
}
@@ -472,12 +487,10 @@
if (users != null) {
for (UserInfo user : users) {
int userId = user.id;
- p.setDisabledComponents(
- new HashSet<String>(dis.getDisabledComponents(userId)),
- userId);
- p.setEnabledComponents(
- new HashSet<String>(dis.getEnabledComponents(userId)),
- userId);
+ p.setDisabledComponentsCopy(
+ dis.getDisabledComponents(userId), userId);
+ p.setEnabledComponentsCopy(
+ dis.getEnabledComponents(userId), userId);
}
}
// Add new setting to list of user ids
@@ -498,6 +511,25 @@
// user preferences
addPackageSettingLPw(p, name, sharedUser);
}
+ } else {
+ if (installUser != null) {
+ // The caller has explicitly specified the user they want this
+ // package installed for, and the package already exists.
+ // Make sure it conforms to the new request.
+ List<UserInfo> users = getAllUsers();
+ if (users != null) {
+ for (UserInfo user : users) {
+ if (installUser.getIdentifier() == UserHandle.USER_ALL
+ || installUser.getIdentifier() == user.id) {
+ boolean installed = p.getInstalled(user.id);
+ if (!installed) {
+ p.setInstalled(true, user.id);
+ writePackageRestrictionsLPr(user.id);
+ }
+ }
+ }
+ }
+ }
}
return p;
}
@@ -778,10 +810,14 @@
+ "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.
+ // in the stopped state, but not at first boot. Also
+ // consider all applications to be installed.
for (PackageSetting pkg : mPackages.values()) {
- pkg.setStopped(false, userId);
- pkg.setNotLaunched(false, userId);
+ pkg.setUserState(userId, COMPONENT_ENABLED_STATE_DEFAULT,
+ true, // installed
+ false, // stopped
+ false, // notLaunched
+ null, null);
}
return;
}
@@ -823,17 +859,21 @@
XmlUtils.skipCurrentTag(parser);
continue;
}
- String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
- int enabled = enabledStr == null ? COMPONENT_ENABLED_STATE_DEFAULT
- : Integer.parseInt(enabledStr);
- ps.setEnabled(enabled, userId);
- String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
- boolean stopped = stoppedStr == null ? false : Boolean.parseBoolean(stoppedStr);
- ps.setStopped(stopped, userId);
- String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
- boolean notLaunched = stoppedStr == null ? false
- : Boolean.parseBoolean(notLaunchedStr);
- ps.setNotLaunched(notLaunched, userId);
+ final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
+ final int enabled = enabledStr == null
+ ? COMPONENT_ENABLED_STATE_DEFAULT : Integer.parseInt(enabledStr);
+ final String installedStr = parser.getAttributeValue(null, ATTR_INSTALLED);
+ final boolean installed = installedStr == null
+ ? true : Boolean.parseBoolean(installedStr);
+ final String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
+ final boolean stopped = stoppedStr == null
+ ? false : Boolean.parseBoolean(stoppedStr);
+ final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
+ final boolean notLaunched = stoppedStr == null
+ ? false : Boolean.parseBoolean(notLaunchedStr);
+
+ HashSet<String> enabledComponents = null;
+ HashSet<String> disabledComponents = null;
int packageDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -845,13 +885,14 @@
}
tagName = parser.getName();
if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
- HashSet<String> components = readComponentsLPr(parser);
- ps.setEnabledComponents(components, userId);
+ enabledComponents = readComponentsLPr(parser);
} else if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
- HashSet<String> components = readComponentsLPr(parser);
- ps.setDisabledComponents(components, userId);
+ disabledComponents = readComponentsLPr(parser);
}
}
+
+ ps.setUserState(userId, enabled, installed, stopped, notLaunched,
+ enabledComponents, disabledComponents);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ parser.getName());
@@ -876,7 +917,7 @@
private HashSet<String> readComponentsLPr(XmlPullParser parser)
throws IOException, XmlPullParserException {
- HashSet<String> components = new HashSet<String>();
+ HashSet<String> components = null;
int type;
int outerDepth = parser.getDepth();
String tagName;
@@ -891,6 +932,9 @@
if (tagName.equals(TAG_ITEM)) {
String componentName = parser.getAttributeValue(null, ATTR_NAME);
if (componentName != null) {
+ if (components == null) {
+ components = new HashSet<String>();
+ }
components.add(componentName);
}
}
@@ -936,41 +980,44 @@
serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS);
for (final PackageSetting pkg : mPackages.values()) {
- if (pkg.getStopped(userId)
- || pkg.getNotLaunched(userId)
- || pkg.getEnabled(userId) != COMPONENT_ENABLED_STATE_DEFAULT
- || pkg.getEnabledComponents(userId).size() > 0
- || pkg.getDisabledComponents(userId).size() > 0) {
+ PackageUserState ustate = pkg.readUserState(userId);
+ if (ustate.stopped || ustate.notLaunched || !ustate.installed
+ || ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT
+ || (ustate.enabledComponents != null
+ && ustate.enabledComponents.size() > 0)
+ || (ustate.disabledComponents != null
+ && ustate.disabledComponents.size() > 0)) {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, pkg.name);
- boolean stopped = pkg.getStopped(userId);
- boolean notLaunched = pkg.getNotLaunched(userId);
- int enabled = pkg.getEnabled(userId);
- if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + enabled);
- HashSet<String> enabledComponents = pkg.getEnabledComponents(userId);
- HashSet<String> disabledComponents = pkg.getDisabledComponents(userId);
+ if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled);
- if (stopped) {
+ if (!ustate.installed) {
+ serializer.attribute(null, ATTR_INSTALLED, "false");
+ }
+ if (ustate.stopped) {
serializer.attribute(null, ATTR_STOPPED, "true");
}
- if (notLaunched) {
+ if (ustate.notLaunched) {
serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
}
- if (enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
- serializer.attribute(null, ATTR_ENABLED, Integer.toString(enabled));
+ if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
+ serializer.attribute(null, ATTR_ENABLED,
+ Integer.toString(ustate.enabled));
}
- if (enabledComponents.size() > 0) {
+ if (ustate.enabledComponents != null
+ && ustate.enabledComponents.size() > 0) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
- for (final String name : enabledComponents) {
+ for (final String name : ustate.enabledComponents) {
serializer.startTag(null, TAG_ITEM);
serializer.attribute(null, ATTR_NAME, name);
serializer.endTag(null, TAG_ITEM);
}
serializer.endTag(null, TAG_ENABLED_COMPONENTS);
}
- if (disabledComponents.size() > 0) {
+ if (ustate.disabledComponents != null
+ && ustate.disabledComponents.size() > 0) {
serializer.startTag(null, TAG_DISABLED_COMPONENTS);
- for (final String name : disabledComponents) {
+ for (final String name : ustate.disabledComponents) {
serializer.startTag(null, TAG_ITEM);
serializer.attribute(null, ATTR_NAME, name);
serializer.endTag(null, TAG_ITEM);
@@ -1210,9 +1257,17 @@
if (mPackagesToBeCleaned.size() > 0) {
for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
- serializer.startTag(null, "cleaning-package");
- serializer.attribute(null, ATTR_NAME, mPackagesToBeCleaned.get(i));
- serializer.endTag(null, "cleaning-package");
+ final int userId = mPackagesToBeCleaned.keyAt(i);
+ final String userStr = Integer.toString(userId);
+ final ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.valueAt(i);
+ for (int j=0; j<pkgs.size(); j++) {
+ serializer.startTag(null, "cleaning-package");
+ PackageCleanItem item = pkgs.get(i);
+ serializer.attribute(null, ATTR_NAME, item.packageName);
+ serializer.attribute(null, ATTR_CODE, item.andCode ? "true" : "false");
+ serializer.attribute(null, ATTR_USER, userStr);
+ serializer.endTag(null, "cleaning-package");
+ }
}
}
@@ -1468,6 +1523,17 @@
return ret;
}
+ void addPackageToCleanLPw(int userId, PackageCleanItem pkg) {
+ ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.get(userId);
+ if (pkgs == null) {
+ pkgs = new ArrayList<PackageCleanItem>();
+ mPackagesToBeCleaned.put(userId, pkgs);
+ }
+ if (!pkgs.contains(pkg)) {
+ pkgs.add(pkg);
+ }
+ }
+
boolean readLPw(List<UserInfo> users) {
FileInputStream str = null;
if (mBackupSettingsFilename.exists()) {
@@ -1545,8 +1611,21 @@
readDisabledSysPackageLPw(parser);
} else if (tagName.equals("cleaning-package")) {
String name = parser.getAttributeValue(null, ATTR_NAME);
+ String userStr = parser.getAttributeValue(null, ATTR_USER);
+ String codeStr = parser.getAttributeValue(null, ATTR_CODE);
if (name != null) {
- mPackagesToBeCleaned.add(name);
+ int user = 0;
+ boolean andCode = true;
+ try {
+ if (userStr != null) {
+ user = Integer.parseInt(userStr);
+ }
+ } catch (NumberFormatException e) {
+ }
+ if (codeStr != null) {
+ andCode = Boolean.parseBoolean(codeStr);
+ }
+ addPackageToCleanLPw(user, new PackageCleanItem(name, andCode));
}
} else if (tagName.equals("renamed-package")) {
String nname = parser.getAttributeValue(null, "new");
@@ -1623,7 +1702,8 @@
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);
+ pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags,
+ UserHandle.ALL, true);
if (p == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unable to create application package for " + pp.name);
@@ -2292,6 +2372,10 @@
return ps;
}
+ private String compToString(HashSet<String> cmp) {
+ return cmp != null ? Arrays.toString(cmp.toArray()) : "[]";
+ }
+
boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
return true;
@@ -2302,24 +2386,26 @@
Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = "
+ componentInfo.packageName + " componentName = " + componentInfo.name);
Log.v(PackageManagerService.TAG, "enabledComponents: "
- + Arrays.toString(packageSettings.getEnabledComponents(userId).toArray()));
+ + compToString(packageSettings.getEnabledComponents(userId)));
Log.v(PackageManagerService.TAG, "disabledComponents: "
- + Arrays.toString(packageSettings.getDisabledComponents(userId).toArray()));
+ + compToString(packageSettings.getDisabledComponents(userId)));
}
if (packageSettings == null) {
return false;
}
- final int enabled = packageSettings.getEnabled(userId);
- if (enabled == COMPONENT_ENABLED_STATE_DISABLED
- || enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
+ PackageUserState ustate = packageSettings.readUserState(userId);
+ if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED
+ || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
|| (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
- && enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
+ && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
return false;
}
- if (packageSettings.getEnabledComponents(userId).contains(componentInfo.name)) {
+ if (ustate.enabledComponents != null
+ && ustate.enabledComponents.contains(componentInfo.name)) {
return true;
}
- if (packageSettings.getDisabledComponents(userId).contains(componentInfo.name)) {
+ if (ustate.disabledComponents != null
+ && ustate.disabledComponents.contains(componentInfo.name)) {
return false;
}
return componentInfo.enabled;
@@ -2378,7 +2464,7 @@
if (pkgSetting.installerPackageName != null) {
PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
pkgSetting.name, null,
- pkgSetting.installerPackageName, null, userId);
+ pkgSetting.installerPackageName, null, new int[] {userId});
}
pkgSetting.setNotLaunched(false, userId);
}
@@ -2390,7 +2476,7 @@
private List<UserInfo> getAllUsers() {
long id = Binder.clearCallingIdentity();
try {
- return UserManagerService.getInstance(mContext).getUsers();
+ return UserManagerService.getInstance().getUsers();
} catch (NullPointerException npe) {
// packagemanager not yet initialized
} finally {
@@ -2427,7 +2513,6 @@
ApplicationInfo.FLAG_RESTORE_ANY_VERSION, "RESTORE_ANY_VERSION",
ApplicationInfo.FLAG_EXTERNAL_STORAGE, "EXTERNAL_STORAGE",
ApplicationInfo.FLAG_LARGE_HEAP, "LARGE_HEAP",
- ApplicationInfo.FLAG_STOPPED, "STOPPED",
ApplicationInfo.FLAG_FORWARD_LOCK, "FORWARD_LOCK",
ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
};
@@ -2537,22 +2622,28 @@
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(" installStatus="); pw.println(ps.installStatus);
for (UserInfo user : users) {
- pw.print(" User "); pw.print(user.id); pw.print(": ");
+ pw.print(" User "); pw.print(user.id); pw.print(": ");
+ pw.print(" installed=");
+ pw.print(ps.getInstalled(user.id));
pw.print(" stopped=");
pw.print(ps.getStopped(user.id));
+ pw.print(" notLaunched=");
+ pw.print(ps.getNotLaunched(user.id));
pw.print(" enabled=");
pw.println(ps.getEnabled(user.id));
- if (ps.getDisabledComponents(user.id).size() > 0) {
- pw.println(" disabledComponents:");
- for (String s : ps.getDisabledComponents(user.id)) {
+ HashSet<String> cmp = ps.getDisabledComponents(user.id);
+ if (cmp != null && cmp.size() > 0) {
+ pw.println(" disabledComponents:");
+ for (String s : cmp) {
pw.print(" "); pw.println(s);
}
}
- if (ps.getEnabledComponents(user.id).size() > 0) {
- pw.println(" enabledComponents:");
- for (String s : ps.getEnabledComponents(user.id)) {
+ cmp = ps.getEnabledComponents(user.id);
+ if (cmp != null && cmp.size() > 0) {
+ pw.println(" enabledComponents:");
+ for (String s : cmp) {
pw.print(" "); pw.println(s);
}
}
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 6a76c5d..750aa72 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -25,7 +25,6 @@
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Binder;
@@ -34,10 +33,8 @@
import android.os.IUserManager;
import android.os.ParcelFileDescriptor;
import android.os.Process;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.util.AtomicFile;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -72,59 +69,79 @@
private static final String USER_LIST_FILENAME = "userlist.xml";
private static final String USER_PHOTO_FILENAME = "photo.png";
- private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
+ private final Context mContext;
+ private final PackageManagerService mPm;
+ private final Object mInstallLock;
+ private final Object mPackagesLock;
private final File mUsersDir;
private final File mUserListFile;
+ private final File mBaseUserPath;
+
+ private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
+
private int[] mUserIds;
private boolean mGuestEnabled;
private int mNextSerialNumber;
- private Installer mInstaller;
- private File mBaseUserPath;
- private Context mContext;
private static UserManagerService sInstance;
- private PackageManagerService mPm;
- public synchronized static UserManagerService getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new UserManagerService(context);
+ public static UserManagerService getInstance() {
+ synchronized (UserManagerService.class) {
+ return sInstance;
}
- return sInstance;
}
/**
* Available for testing purposes.
*/
UserManagerService(File dataDir, File baseUserPath) {
- mUsersDir = new File(dataDir, USER_INFO_DIR);
- mUsersDir.mkdirs();
- // Make zeroth user directory, for services to migrate their files to that location
- File userZeroDir = new File(mUsersDir, "0");
- userZeroDir.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();
+ this(null, null, new Object(), new Object(), dataDir, baseUserPath);
}
- public UserManagerService(Context context) {
- this(Environment.getDataDirectory(), new File(Environment.getDataDirectory(), "user"));
- mContext = context;
+ /**
+ * Called by package manager to create the service. This is closely
+ * associated with the package manager, and the given lock is the
+ * package manager's own lock.
+ */
+ UserManagerService(Context context, PackageManagerService pm,
+ Object installLock, Object packagesLock) {
+ this(context, pm, installLock, packagesLock,
+ Environment.getDataDirectory(),
+ new File(Environment.getDataDirectory(), "user"));
}
- void setInstaller(PackageManagerService pm, Installer installer) {
- mInstaller = installer;
- mPm = pm;
+ /**
+ * Available for testing purposes.
+ */
+ private UserManagerService(Context context, PackageManagerService pm,
+ Object installLock, Object packagesLock,
+ File dataDir, File baseUserPath) {
+ synchronized (UserManagerService.class) {
+ mContext = context;
+ mPm = pm;
+ mInstallLock = installLock;
+ mPackagesLock = packagesLock;
+ mUsersDir = new File(dataDir, USER_INFO_DIR);
+ mUsersDir.mkdirs();
+ // Make zeroth user directory, for services to migrate their files to that location
+ File userZeroDir = new File(mUsersDir, "0");
+ userZeroDir.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();
+ sInstance = this;
+ }
}
@Override
public List<UserInfo> getUsers() {
checkManageUsersPermission("query users");
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
for (int i = 0; i < mUsers.size(); i++) {
users.add(mUsers.valueAt(i));
@@ -136,7 +153,7 @@
@Override
public UserInfo getUserInfo(int userId) {
checkManageUsersPermission("query user");
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
return getUserInfoLocked(userId);
}
}
@@ -149,7 +166,7 @@
}
public boolean exists(int userId) {
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
return ArrayUtils.contains(mUserIds, userId);
}
}
@@ -157,7 +174,7 @@
@Override
public void setUserName(int userId, String name) {
checkManageUsersPermission("rename users");
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
UserInfo info = mUsers.get(userId);
if (name != null && !name.equals(info.name)) {
info.name = name;
@@ -169,7 +186,7 @@
@Override
public ParcelFileDescriptor setUserIcon(int userId) {
checkManageUsersPermission("update users");
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
UserInfo info = mUsers.get(userId);
if (info == null) return null;
ParcelFileDescriptor fd = updateIconBitmapLocked(info);
@@ -183,7 +200,7 @@
@Override
public void setGuestEnabled(boolean enable) {
checkManageUsersPermission("enable guest users");
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
if (mGuestEnabled != enable) {
mGuestEnabled = enable;
// Erase any guest user that currently exists
@@ -206,7 +223,7 @@
@Override
public boolean isGuestEnabled() {
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
return mGuestEnabled;
}
}
@@ -262,11 +279,17 @@
* @return the array of user ids.
*/
int[] getUserIds() {
+ synchronized (mPackagesLock) {
+ return mUserIds;
+ }
+ }
+
+ int[] getUserIdsLPr() {
return mUserIds;
}
private void readUserList() {
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
readUserListLocked();
}
}
@@ -499,15 +522,15 @@
int userId = getNextAvailableId();
UserInfo userInfo = new UserInfo(userId, name, null, flags);
File userPath = new File(mBaseUserPath, Integer.toString(userId));
- if (!createPackageFolders(userId, userPath)) {
- return null;
- }
- synchronized (mUsers) {
- userInfo.serialNumber = mNextSerialNumber++;
- mUsers.put(userId, userInfo);
- writeUserListLocked();
- writeUserLocked(userInfo);
- updateUserIdsLocked();
+ synchronized (mInstallLock) {
+ synchronized (mPackagesLock) {
+ userInfo.serialNumber = mNextSerialNumber++;
+ mUsers.put(userId, userInfo);
+ writeUserListLocked();
+ writeUserLocked(userInfo);
+ updateUserIdsLocked();
+ mPm.createNewUserLILPw(userId, userPath);
+ }
}
if (userInfo != null) {
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
@@ -526,24 +549,37 @@
*/
public boolean removeUser(int userHandle) {
checkManageUsersPermission("Only the system can remove users");
- boolean result;
- synchronized (mUsers) {
- result = removeUserLocked(userHandle);
- }
+ synchronized (mInstallLock) {
+ synchronized (mPackagesLock) {
+ final UserInfo user = mUsers.get(userHandle);
+ if (userHandle == 0 || user == null) {
+ return false;
+ }
- // Cleanup package manager settings
- mPm.cleanUpUser(userHandle);
+ // Cleanup package manager settings
+ mPm.cleanUpUserLILPw(userHandle);
+
+ // Remove this user from the list
+ mUsers.remove(userHandle);
+ // Remove user file
+ AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
+ userFile.delete();
+ // Update the user list
+ writeUserListLocked();
+ updateUserIdsLocked();
+ }
+ }
// Let other services shutdown any activity
Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
- return result;
+ return true;
}
@Override
public int getUserSerialNumber(int userHandle) {
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
if (!exists(userHandle)) return -1;
return getUserInfoLocked(userHandle).serialNumber;
}
@@ -551,7 +587,7 @@
@Override
public int getUserHandle(int userSerialNumber) {
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
for (int userId : mUserIds) {
if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId;
}
@@ -560,63 +596,15 @@
}
}
- private boolean removeUserLocked(int userHandle) {
- final UserInfo user = mUsers.get(userHandle);
- if (userHandle == 0 || user == null) {
- return false;
- }
-
- // Remove this user from the list
- mUsers.remove(userHandle);
- // Remove user file
- AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
- userFile.delete();
- // Update the user list
- writeUserListLocked();
- updateUserIdsLocked();
-
- removePackageFolders(userHandle);
- return true;
- }
-
- 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, UserHandle.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 updateUserIdsLocked() {
- if (mUserIds == null || mUserIds.length != mUsers.size()) {
- mUserIds = new int[mUsers.size()];
- }
+ int[] newUsers = new int[mUsers.size()];
for (int i = 0; i < mUsers.size(); i++) {
- mUserIds[i] = mUsers.keyAt(i);
+ newUsers[i] = mUsers.keyAt(i);
}
+ mUserIds = newUsers;
}
/**
@@ -626,7 +614,7 @@
* @return
*/
private int getNextAvailableId() {
- synchronized (mUsers) {
+ synchronized (mPackagesLock) {
int i = 0;
while (i < Integer.MAX_VALUE) {
if (mUsers.indexOfKey(i) < 0) {
@@ -637,26 +625,4 @@
return i;
}
}
-
- private boolean createPackageFolders(int id, File userPath) {
- // mInstaller may not be available for unit-tests.
- if (mInstaller == null) return true;
-
- // Create the user path
- userPath.mkdir();
- FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
- | FileUtils.S_IXOTH, -1, -1);
-
- mInstaller.cloneUserData(0, id, false);
-
- return true;
- }
-
- 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/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 5f917af..cd211da 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -1112,7 +1112,7 @@
private final class DisplayControllerHandler extends Handler {
public DisplayControllerHandler(Looper looper) {
- super(looper);
+ super(looper, null, true /*async*/);
}
@Override
diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java
index 64a0462..3524a08 100644
--- a/services/java/com/android/server/power/DisplayPowerState.java
+++ b/services/java/com/android/server/power/DisplayPowerState.java
@@ -49,8 +49,6 @@
private static final int DIRTY_ELECTRON_BEAM = 1 << 1;
private static final int DIRTY_BRIGHTNESS = 1 << 2;
- private static final int DIRTY_ALL = 0xffffffff;
-
private final Choreographer mChoreographer;
private final ElectronBeam mElectronBeam;
private final PhotonicModulator mScreenBrightnessModulator;
@@ -68,10 +66,16 @@
mElectronBeam = electronBean;
mScreenBrightnessModulator = screenBrightnessModulator;
+ // At boot time, we know that the screen is on and the electron beam
+ // animation is not playing. We don't know the screen's brightness though,
+ // so prepare to set it to a known state when the state is next applied.
+ // Although we set the brightness to full on here, the display power controller
+ // will reset the brightness to a new level immediately before the changes
+ // actually have a chance to be applied.
mScreenOn = true;
mElectronBeamLevel = 1.0f;
mScreenBrightness = PowerManager.BRIGHTNESS_ON;
- invalidate(DIRTY_ALL);
+ invalidate(DIRTY_BRIGHTNESS);
}
public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL =
diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java
index 9d928e5..75f8445 100644
--- a/services/java/com/android/server/power/Notifier.java
+++ b/services/java/com/android/server/power/Notifier.java
@@ -422,7 +422,7 @@
private final class NotifierHandler extends Handler {
public NotifierHandler(Looper looper) {
- super(looper);
+ super(looper, null, true /*async*/);
}
@Override
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 9f2b247..6d68104 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -296,6 +296,7 @@
}
nativeInit();
+ nativeSetPowerState(true, true);
}
/**
@@ -305,6 +306,14 @@
public void init(Context context, LightsService ls,
ActivityManagerService am, BatteryService bs, IBatteryStats bss,
DisplayManagerService dm) {
+ // Forcibly turn the screen on at boot so that it is in a known power state.
+ // We do this in init() rather than in the constructor because setting the
+ // screen state requires a call into surface flinger which then needs to call back
+ // into the activity manager to check permissions. Unfortunately the
+ // activity manager is not running when the constructor is called, so we
+ // have to defer setting the screen state until this point.
+ nativeSetScreenState(true);
+
mContext = context;
mLightsService = ls;
mBatteryService = bs;
@@ -1952,7 +1961,7 @@
*/
private final class PowerManagerHandler extends Handler {
public PowerManagerHandler(Looper looper) {
- super(looper);
+ super(looper, null, true /*async*/);
}
@Override
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index 26ea7c1..3fcf680 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -71,12 +71,6 @@
mFlags = flags;
mLocalWin = localWin;
mNotifiedWindows = new ArrayList<WindowState>();
- WindowState win = service.mWindowMap.get(token);
- if (win != null) {
- mDisplayContent = win.mDisplayContent;
- } else {
- Slog.e(WindowManagerService.TAG, "No window associated with token");
- }
}
void reset() {
@@ -92,7 +86,11 @@
mNotifiedWindows = null;
}
- void register() {
+ /**
+ * @param displayContent The display parameters associated with the window being dragged.
+ */
+ void register(DisplayContent displayContent) {
+ mDisplayContent = displayContent;
if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel");
if (mClientChannel != null) {
Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel");
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index d44b170..1ffbecc 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -280,7 +280,7 @@
// !!! FIXME: put all this heavy stuff onto the mH looper, as well as
// the actual drag event dispatch stuff in the dragstate
- mService.mDragState.register();
+ mService.mDragState.register(callingWin.mDisplayContent);
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
mService.mDragState.mServerChannel)) {
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 12ad4fea..6047cda 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -360,6 +360,18 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public ComponentName startServiceAsUser(Intent service, UserHandle user) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
+ @Override
+ public boolean stopServiceAsUser(Intent service, UserHandle user) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
throw new UnsupportedOperationException();
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index ac40fc6b..562f286 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -41,6 +41,7 @@
import android.content.pm.UserInfo;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -531,6 +532,15 @@
throw new UnsupportedOperationException();
}
+ /**
+ * @hide
+ */
+ @Override
+ public int installExistingPackage(String packageName)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public void verifyPendingInstall(int id, int verificationCode) {
throw new UnsupportedOperationException();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 3d45bff..76033d4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1281,6 +1281,18 @@
}
@Override
+ public ComponentName startServiceAsUser(Intent arg0, UserHandle arg1) {
+ // pass
+ return null;
+ }
+
+ @Override
+ public boolean stopServiceAsUser(Intent arg0, UserHandle arg1) {
+ // pass
+ return false;
+ }
+
+ @Override
public void unbindService(ServiceConnection arg0) {
// pass
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 3579b86..284bee8 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1358,7 +1358,11 @@
private void init() {
mWifiServiceMessenger = getWifiServiceMessenger();
- if (mWifiServiceMessenger == null) throw new RuntimeException("Failed to initialize");
+ if (mWifiServiceMessenger == null) {
+ mAsyncChannel = null;
+ return;
+ }
+
HandlerThread t = new HandlerThread("WifiManager");
t.start();
mHandler = new ServiceHandler(t.getLooper());
@@ -1372,7 +1376,7 @@
private void validateChannel() {
if (mAsyncChannel == null) throw new IllegalStateException(
- "Bad WifiManager instance state, re-initialize");
+ "No permission to access and change wifi or a bad initialization");
}
/**
@@ -1517,6 +1521,8 @@
return mService.getWifiServiceMessenger();
} catch (RemoteException e) {
return null;
+ } catch (SecurityException e) {
+ return null;
}
}