diff options
35 files changed, 483 insertions, 211 deletions
diff --git a/api/current.txt b/api/current.txt index f0148b4..e0736b6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -62,21 +62,17 @@ package android { field public static final java.lang.String INJECT_EVENTS = "android.permission.INJECT_EVENTS"; field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER"; field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES"; - field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS"; field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW"; field public static final java.lang.String INTERNET = "android.permission.INTERNET"; field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES"; field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS"; field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS"; - field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS"; field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR"; field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS"; field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE"; field public static final java.lang.String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS"; field public static final java.lang.String MOUNT_UNMOUNT_FILESYSTEMS = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS"; - field public static final java.lang.String NET_TUNNELING = "android.permission.NET_TUNNELING"; field public static final java.lang.String NFC = "android.permission.NFC"; - field public static final java.lang.String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT"; field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY"; field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"; field public static final java.lang.String READ_CALENDAR = "android.permission.READ_CALENDAR"; @@ -3901,11 +3897,12 @@ package android.app { method public static android.app.PendingIntent getActivity(android.content.Context, int, android.content.Intent, int); method public static android.app.PendingIntent getActivity(android.content.Context, int, android.content.Intent, int, android.os.Bundle); method public static android.app.PendingIntent getBroadcast(android.content.Context, int, android.content.Intent, int); + method public java.lang.String getCreatorPackage(); + method public int getCreatorUid(); + method public android.os.UserHandle getCreatorUserHandle(); method public android.content.IntentSender getIntentSender(); method public static android.app.PendingIntent getService(android.content.Context, int, android.content.Intent, int); - method public java.lang.String getTargetPackage(); - method public int getTargetUid(); - method public android.os.UserHandle getTargetUserHandle(); + method public deprecated java.lang.String getTargetPackage(); method public static android.app.PendingIntent readPendingIntentOrNullFromParcel(android.os.Parcel); method public void send() throws android.app.PendingIntent.CanceledException; method public void send(int) throws android.app.PendingIntent.CanceledException; @@ -6060,9 +6057,10 @@ package android.content { public class IntentSender implements android.os.Parcelable { method public int describeContents(); - method public java.lang.String getTargetPackage(); - method public int getTargetUid(); - method public android.os.UserHandle getTargetUserHandle(); + method public java.lang.String getCreatorPackage(); + method public int getCreatorUid(); + method public android.os.UserHandle getCreatorUserHandle(); + method public deprecated java.lang.String getTargetPackage(); method public static android.content.IntentSender readIntentSenderOrNullFromParcel(android.os.Parcel); method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler) throws android.content.IntentSender.SendIntentException; method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler, java.lang.String) throws android.content.IntentSender.SendIntentException; @@ -6705,17 +6703,6 @@ package android.content.pm { 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); diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index eae3b1f..773f73c 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -404,6 +404,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case ACTIVITY_RESUMED_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + activityResumed(token); + reply.writeNoException(); + return true; + } + case ACTIVITY_PAUSED_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); @@ -2152,6 +2160,17 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + public void activityResumed(IBinder token) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(ACTIVITY_RESUMED_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } public void activityPaused(IBinder token) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index cb4d4a1..9b82f2a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1245,7 +1245,7 @@ public final class ActivityThread { case RESUME_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume"); handleResumeActivity((IBinder)msg.obj, true, - msg.arg1 != 0); + msg.arg1 != 0, true); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case SEND_RESULT: @@ -2175,7 +2175,8 @@ public final class ActivityThread { if (a != null) { r.createdConfig = new Configuration(mConfiguration); Bundle oldState = r.state; - handleResumeActivity(r.token, false, r.isForward); + handleResumeActivity(r.token, false, r.isForward, + !r.activity.mFinished && !r.startsNotResumed); if (!r.activity.mFinished && r.startsNotResumed) { // The activity manager actually wants this one to start out @@ -2684,7 +2685,8 @@ public final class ActivityThread { r.mPendingRemoveWindowManager = null; } - final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { + final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, + boolean reallyResume) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); @@ -2781,6 +2783,14 @@ public final class ActivityThread { } r.onlyLocalRequest = false; + // Tell the activity manager we have resumed. + if (reallyResume) { + try { + ActivityManagerNative.getDefault().activityResumed(token); + } catch (RemoteException ex) { + } + } + } else { // If an exception was thrown when trying to resume, then // just end this activity. @@ -2865,7 +2875,7 @@ public final class ActivityThread { if (r.isPreHoneycomb()) { QueuedWork.waitToFinish(); } - + // Tell the activity manager we have paused. try { ActivityManagerNative.getDefault().activityPaused(token); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 9ef375a..2fb17b6 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -87,19 +87,15 @@ public interface IActivityManager extends IInterface { String resultData, Bundle map, String requiredPermission, boolean serialized, boolean sticky, int userId) throws RemoteException; public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) throws RemoteException; - /* oneway */ public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException; public void attachApplication(IApplicationThread app) throws RemoteException; - /* oneway */ + public void activityResumed(IBinder token) throws RemoteException; public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) throws RemoteException; public void activityPaused(IBinder token) throws RemoteException; - /* oneway */ public void activityStopped(IBinder token, Bundle state, Bitmap thumbnail, CharSequence description) throws RemoteException; - /* oneway */ public void activitySlept(IBinder token) throws RemoteException; - /* oneway */ public void activityDestroyed(IBinder token) throws RemoteException; public String getCallingPackage(IBinder token) throws RemoteException; public ComponentName getCallingActivity(IBinder token) throws RemoteException; @@ -496,7 +492,7 @@ public interface IActivityManager extends IInterface { int BIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35; int UNBIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36; int PUBLISH_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37; - + int ACTIVITY_RESUMED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38; int GOING_TO_SLEEP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39; int WAKING_UP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40; int SET_DEBUG_APP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+41; diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index a3c1838..e7cea57 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -32,8 +32,8 @@ import android.util.AndroidException; /** * A description of an Intent and target action to perform with it. Instances - * of this class are created with {@link #getActivity}, - * {@link #getBroadcast}, {@link #getService}; the returned object can be + * of this class are created with {@link #getActivity}, {@link #getActivities}, + * {@link #getBroadcast}, and {@link #getService}; the returned object can be * handed to other applications so that they can perform the action you * described on your behalf at a later time. * @@ -54,6 +54,34 @@ import android.util.AndroidException; * categories, and components, and same flags), it will receive a PendingIntent * representing the same token if that is still valid, and can thus call * {@link #cancel} to remove it. + * + * <p>Because of this behavior, it is important to know when two Intents + * are considered to be the same for purposes of retrieving a PendingIntent. + * A common mistake people make is to create multiple PendingIntent objects + * with Intents that only vary in their "extra" contents, expecting to get + * a different PendingIntent each time. This does <em>not</em> happen. The + * parts of the Intent that are used for matching are the same ones defined + * by {@link Intent#filterEquals(Intent) Intent.filterEquals}. If you use two + * Intent objects that are equivalent as per + * {@link Intent#filterEquals(Intent) Intent.filterEquals}, then you will get + * the same PendingIntent for both of them. + * + * <p>There are two typical ways to deal with this. + * + * <p>If you truly need multiple distinct PendingIntent objects active at + * the same time (such as to use as two notifications that are both shown + * at the same time), then you will need to ensure there is something that + * is different about them to associate them with different PendingIntents. + * This may be any of the Intent attributes considered by + * {@link Intent#filterEquals(Intent) Intent.filterEquals}, or different + * request code integers supplied to {@link #getActivity}, {@link #getActivities}, + * {@link #getBroadcast}, or {@link #getService}. + * + * <p>If you only need one PendingIntent active at a time for any of the + * Intents you will use, then you can alternatively use the flags + * {@link #FLAG_CANCEL_CURRENT} or {@link #FLAG_UPDATE_CURRENT} to either + * cancel or modify whatever current PendingIntent is associated with the + * Intent you are supplying. */ public final class PendingIntent implements Parcelable { private final IIntentSender mTarget; @@ -622,6 +650,20 @@ public final class PendingIntent implements Parcelable { } /** + * @deprecated Renamed to {@link #getCreatorPackage()}. + */ + @Deprecated + public String getTargetPackage() { + try { + return ActivityManagerNative.getDefault() + .getPackageForIntentSender(mTarget); + } catch (RemoteException e) { + // Should never happen. + return null; + } + } + + /** * Return the package name of the application that created this * PendingIntent, that is the identity under which you will actually be * sending the Intent. The returned string is supplied by the system, so @@ -630,7 +672,7 @@ public final class PendingIntent implements Parcelable { * @return The package name of the PendingIntent, or null if there is * none associated with it. */ - public String getTargetPackage() { + public String getCreatorPackage() { try { return ActivityManagerNative.getDefault() .getPackageForIntentSender(mTarget); @@ -649,7 +691,7 @@ public final class PendingIntent implements Parcelable { * @return The uid of the PendingIntent, or -1 if there is * none associated with it. */ - public int getTargetUid() { + public int getCreatorUid() { try { return ActivityManagerNative.getDefault() .getUidForIntentSender(mTarget); @@ -670,7 +712,7 @@ public final class PendingIntent implements Parcelable { * @return The user handle of the PendingIntent, or null if there is * none associated with it. */ - public UserHandle getTargetUserHandle() { + public UserHandle getCreatorUserHandle() { try { int uid = ActivityManagerNative.getDefault() .getUidForIntentSender(mTarget); diff --git a/core/java/android/app/TaskStackBuilder.java b/core/java/android/app/TaskStackBuilder.java index 63987f1..cadf5e4 100644 --- a/core/java/android/app/TaskStackBuilder.java +++ b/core/java/android/app/TaskStackBuilder.java @@ -216,11 +216,7 @@ public class TaskStackBuilder { "No intents added to TaskStackBuilder; cannot startActivities"); } - Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]); - intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_CLEAR_TASK | - Intent.FLAG_ACTIVITY_TASK_ON_HOME); - mSourceContext.startActivities(intents, options); + mSourceContext.startActivities(getIntents(), options); } /** @@ -260,11 +256,8 @@ public class TaskStackBuilder { "No intents added to TaskStackBuilder; cannot getPendingIntent"); } - Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]); - intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_CLEAR_TASK | - Intent.FLAG_ACTIVITY_TASK_ON_HOME); - return PendingIntent.getActivities(mSourceContext, requestCode, intents, flags, options); + return PendingIntent.getActivities(mSourceContext, requestCode, getIntents(), + flags, options); } /** @@ -275,6 +268,15 @@ public class TaskStackBuilder { * @return An array containing the intents added to this builder. */ public Intent[] getIntents() { - return mIntents.toArray(new Intent[mIntents.size()]); + Intent[] intents = new Intent[mIntents.size()]; + if (intents.length == 0) return intents; + + intents[0] = new Intent(mIntents.get(0)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_CLEAR_TASK | + Intent.FLAG_ACTIVITY_TASK_ON_HOME); + for (int i = 1; i < intents.length; i++) { + intents[i] = new Intent(mIntents.get(i)); + } + return intents; } } diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 51a81c5..84a447f 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -117,7 +117,7 @@ public class AppWidgetHost { private OnClickHandler mOnClickHandler; public AppWidgetHost(Context context, int hostId) { - this(context, hostId, null, Looper.getMainLooper()); + this(context, hostId, null, context.getMainLooper()); } /** diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index ece8841..cd1e882 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1219,9 +1219,16 @@ public abstract class ContentResolver { public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer) { + registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId()); + } + + /** @hide - designated user version */ + public final void registerContentObserver(Uri uri, boolean notifyForDescendents, + ContentObserver observer, int userHandle) + { try { getContentService().registerContentObserver(uri, notifyForDescendents, - observer.getContentObserver()); + observer.getContentObserver(), userHandle); } catch (RemoteException e) { } } @@ -1276,10 +1283,21 @@ public abstract class ContentResolver { * @see #requestSync(android.accounts.Account, String, android.os.Bundle) */ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { + notifyChange(uri, observer, syncToNetwork, UserHandle.myUserId()); + } + + /** + * Notify registered observers within the designated user(s) that a row was updated. + * + * @hide + */ + public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork, + int userHandle) { try { getContentService().notifyChange( uri, observer == null ? null : observer.getContentObserver(), - observer != null && observer.deliverSelfNotifications(), syncToNetwork); + observer != null && observer.deliverSelfNotifications(), syncToNetwork, + userHandle); } catch (RemoteException e) { } } diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java index 472fe94..0f6488a 100644 --- a/core/java/android/content/ContentService.java +++ b/core/java/android/content/ContentService.java @@ -17,6 +17,7 @@ package android.content; import android.accounts.Account; +import android.app.ActivityManager; import android.database.IContentObserver; import android.database.sqlite.SQLiteException; import android.net.Uri; @@ -33,6 +34,7 @@ import android.Manifest; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -138,19 +140,49 @@ public final class ContentService extends IContentService.Stub { getSyncManager(); } - public void registerContentObserver(Uri uri, boolean notifyForDescendents, - IContentObserver observer) { + /** + * Register a content observer tied to a specific user's view of the provider. + * @param userHandle the user whose view of the provider is to be observed. May be + * the calling user without requiring any permission, otherwise the caller needs to + * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and + * USER_CURRENT are properly handled; all other pseudousers are forbidden. + */ + @Override + public void registerContentObserver(Uri uri, boolean notifyForDescendants, + IContentObserver observer, int userHandle) { if (observer == null || uri == null) { throw new IllegalArgumentException("You must pass a valid uri and observer"); } + + final int callingUser = UserHandle.getCallingUserId(); + if (callingUser != userHandle) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "no permission to observe other users' provider view"); + } + + if (userHandle < 0) { + if (userHandle == UserHandle.USER_CURRENT) { + userHandle = ActivityManager.getCurrentUser(); + } else if (userHandle != UserHandle.USER_ALL) { + throw new InvalidParameterException("Bad user handle for registerContentObserver: " + + userHandle); + } + } + synchronized (mRootNode) { - mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode, - Binder.getCallingUid(), Binder.getCallingPid()); + mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode, + Binder.getCallingUid(), Binder.getCallingPid(), userHandle); if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri + - " with notifyForDescendents " + notifyForDescendents); + " with notifyForDescendants " + notifyForDescendants); } } + public void registerContentObserver(Uri uri, boolean notifyForDescendants, + IContentObserver observer) { + registerContentObserver(uri, notifyForDescendants, observer, + UserHandle.getCallingUserId()); + } + public void unregisterContentObserver(IContentObserver observer) { if (observer == null) { throw new IllegalArgumentException("You must pass a valid observer"); @@ -161,14 +193,39 @@ public final class ContentService extends IContentService.Stub { } } + /** + * Notify observers of a particular user's view of the provider. + * @param userHandle the user whose view of the provider is to be notified. May be + * the calling user without requiring any permission, otherwise the caller needs to + * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and + * USER_CURRENT are properly interpreted; no other pseudousers are allowed. + */ + @Override public void notifyChange(Uri uri, IContentObserver observer, - boolean observerWantsSelfNotifications, boolean syncToNetwork) { + boolean observerWantsSelfNotifications, boolean syncToNetwork, + int userHandle) { if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "Notifying update of " + uri + " from observer " + observer - + ", syncToNetwork " + syncToNetwork); + Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle + + " from observer " + observer + ", syncToNetwork " + syncToNetwork); + } + + // Notify for any user other than the caller's own requires permission. + final int callingUserHandle = UserHandle.getCallingUserId(); + if (userHandle != callingUserHandle) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "no permission to notify other users"); + } + + // We passed the permission check; resolve pseudouser targets as appropriate + if (userHandle < 0) { + if (userHandle == UserHandle.USER_CURRENT) { + userHandle = ActivityManager.getCurrentUser(); + } else if (userHandle != UserHandle.USER_ALL) { + throw new InvalidParameterException("Bad user handle for notifyChange: " + + userHandle); + } } - int userId = UserHandle.getCallingUserId(); // This makes it so that future permission checks will be in the context of this // process rather than the caller's process. We will restore this before returning. long identityToken = clearCallingIdentity(); @@ -176,7 +233,7 @@ public final class ContentService extends IContentService.Stub { ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); synchronized (mRootNode) { mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, - calls); + userHandle, calls); } final int numCalls = calls.size(); for (int i=0; i<numCalls; i++) { @@ -207,7 +264,7 @@ public final class ContentService extends IContentService.Stub { if (syncToNetwork) { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - syncManager.scheduleLocalSync(null /* all accounts */, userId, + syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uri.getAuthority()); } } @@ -216,6 +273,12 @@ public final class ContentService extends IContentService.Stub { } } + public void notifyChange(Uri uri, IContentObserver observer, + boolean observerWantsSelfNotifications, boolean syncToNetwork) { + notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork, + UserHandle.getCallingUserId()); + } + /** * Hide this class since it is not part of api, * but current unittest framework requires it to be public @@ -543,16 +606,18 @@ public final class ContentService extends IContentService.Stub { public final IContentObserver observer; public final int uid; public final int pid; - public final boolean notifyForDescendents; + public final boolean notifyForDescendants; + private final int userHandle; private final Object observersLock; public ObserverEntry(IContentObserver o, boolean n, Object observersLock, - int _uid, int _pid) { + int _uid, int _pid, int _userHandle) { this.observersLock = observersLock; observer = o; uid = _uid; pid = _pid; - notifyForDescendents = n; + userHandle = _userHandle; + notifyForDescendants = n; try { observer.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { @@ -571,7 +636,8 @@ public final class ContentService extends IContentService.Stub { pidCounts.put(pid, pidCounts.get(pid)+1); pw.print(prefix); pw.print(name); pw.print(": pid="); pw.print(pid); pw.print(" uid="); - pw.print(uid); pw.print(" target="); + pw.print(uid); pw.print(" user="); + pw.print(userHandle); pw.print(" target="); pw.println(Integer.toHexString(System.identityHashCode( observer != null ? observer.asBinder() : null))); } @@ -639,17 +705,21 @@ public final class ContentService extends IContentService.Stub { return uri.getPathSegments().size() + 1; } + // Invariant: userHandle is either a hard user number or is USER_ALL public void addObserverLocked(Uri uri, IContentObserver observer, - boolean notifyForDescendents, Object observersLock, int uid, int pid) { - addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock, uid, pid); + boolean notifyForDescendants, Object observersLock, + int uid, int pid, int userHandle) { + addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock, + uid, pid, userHandle); } private void addObserverLocked(Uri uri, int index, IContentObserver observer, - boolean notifyForDescendents, Object observersLock, int uid, int pid) { + boolean notifyForDescendants, Object observersLock, + int uid, int pid, int userHandle) { // If this is the leaf node add the observer if (index == countUriSegments(uri)) { - mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock, - uid, pid)); + mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock, + uid, pid, userHandle)); return; } @@ -662,8 +732,8 @@ public final class ContentService extends IContentService.Stub { for (int i = 0; i < N; i++) { ObserverNode node = mChildren.get(i); if (node.mName.equals(segment)) { - node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, - observersLock, uid, pid); + node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, + observersLock, uid, pid, userHandle); return; } } @@ -671,8 +741,8 @@ public final class ContentService extends IContentService.Stub { // No child found, create one ObserverNode node = new ObserverNode(segment); mChildren.add(node); - node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, - observersLock, uid, pid); + node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, + observersLock, uid, pid, userHandle); } public boolean removeObserverLocked(IContentObserver observer) { @@ -705,37 +775,49 @@ public final class ContentService extends IContentService.Stub { } private void collectMyObserversLocked(boolean leaf, IContentObserver observer, - boolean observerWantsSelfNotifications, ArrayList<ObserverCall> calls) { + boolean observerWantsSelfNotifications, int targetUserHandle, + ArrayList<ObserverCall> calls) { int N = mObservers.size(); IBinder observerBinder = observer == null ? null : observer.asBinder(); for (int i = 0; i < N; i++) { ObserverEntry entry = mObservers.get(i); - // Don't notify the observer if it sent the notification and isn't interesed + // Don't notify the observer if it sent the notification and isn't interested // in self notifications boolean selfChange = (entry.observer.asBinder() == observerBinder); if (selfChange && !observerWantsSelfNotifications) { continue; } - // Make sure the observer is interested in the notification - if (leaf || (!leaf && entry.notifyForDescendents)) { - calls.add(new ObserverCall(this, entry.observer, selfChange)); + // Does this observer match the target user? + if (targetUserHandle == UserHandle.USER_ALL + || entry.userHandle == UserHandle.USER_ALL + || targetUserHandle == entry.userHandle) { + // Make sure the observer is interested in the notification + if (leaf || (!leaf && entry.notifyForDescendants)) { + calls.add(new ObserverCall(this, entry.observer, selfChange)); + } } } } + /** + * targetUserHandle is either a hard user handle or is USER_ALL + */ public void collectObserversLocked(Uri uri, int index, IContentObserver observer, - boolean observerWantsSelfNotifications, ArrayList<ObserverCall> calls) { + boolean observerWantsSelfNotifications, int targetUserHandle, + ArrayList<ObserverCall> calls) { String segment = null; int segmentCount = countUriSegments(uri); if (index >= segmentCount) { // This is the leaf node, notify all observers - collectMyObserversLocked(true, observer, observerWantsSelfNotifications, calls); + collectMyObserversLocked(true, observer, observerWantsSelfNotifications, + targetUserHandle, calls); } else if (index < segmentCount){ segment = getUriSegment(uri, index); - // Notify any observers at this level who are interested in descendents - collectMyObserversLocked(false, observer, observerWantsSelfNotifications, calls); + // Notify any observers at this level who are interested in descendants + collectMyObserversLocked(false, observer, observerWantsSelfNotifications, + targetUserHandle, calls); } int N = mChildren.size(); @@ -744,7 +826,7 @@ public final class ContentService extends IContentService.Stub { if (segment == null || node.mName.equals(segment)) { // We found the child, node.collectObserversLocked(uri, index + 1, - observer, observerWantsSelfNotifications, calls); + observer, observerWantsSelfNotifications, targetUserHandle, calls); if (segment != null) { break; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 7438ba8..161670f 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -856,8 +856,10 @@ public abstract class Context { public abstract void startActivity(Intent intent); /** - * Same as {@link #startActivity(Intent)}, but for a specific user. It requires holding - * the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. + * Version of {@link #startActivity(Intent)} that allows you to specify the + * user the activity will be started for. This is not available to applications + * that are not pre-installed on the system image. Using it requires holding + * the INTERACT_ACROSS_USERS_FULL permission. * @param intent The description of the activity to start. * @param user The UserHandle of the user to start this activity for. * @throws ActivityNotFoundException @@ -895,8 +897,10 @@ public abstract class Context { public abstract void startActivity(Intent intent, Bundle options); /** - * Same as {@link #startActivity(Intent, Bundle)}, but for a specific user. It requires holding - * the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. + * Version of {@link #startActivity(Intent, Bundle)} that allows you to specify the + * user the activity will be started for. This is not available to applications + * that are not pre-installed on the system image. Using it requires holding + * the INTERACT_ACROSS_USERS_FULL permission. * @param intent The description of the activity to start. * @param options Additional options for how the Activity should be started. * May be null if there are no options. See {@link android.app.ActivityOptions} @@ -1118,10 +1122,10 @@ public abstract class Context { Bundle initialExtras); /** - * Same as {@link #sendBroadcast(Intent)}, but for a specific user. This broadcast - * can only be sent to receivers that are part of the calling application. It - * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} - * permission. + * Version of {@link #sendBroadcast(Intent)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. Using it requires holding + * the INTERACT_ACROSS_USERS permission. * @param intent The intent to broadcast * @param user UserHandle to send the intent to. * @see #sendBroadcast(Intent) @@ -1129,10 +1133,10 @@ public abstract class Context { public abstract void sendBroadcastAsUser(Intent intent, UserHandle user); /** - * Same as {@link #sendBroadcast(Intent, String)}, but for a specific user. This broadcast - * can only be sent to receivers that are part of the calling application. It - * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} - * permission. + * Version of {@link #sendBroadcast(Intent, String)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. Using it requires holding + * the INTERACT_ACROSS_USERS permission. * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. @@ -1147,12 +1151,12 @@ public abstract class Context { String receiverPermission); /** - * Same as - * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)}, - * but for a specific user. This broadcast - * can only be sent to receivers that are part of the calling application. It - * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} - * permission. + * Version of + * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)} + * that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. Using it requires holding + * the INTERACT_ACROSS_USERS permission. * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * @@ -1261,11 +1265,10 @@ public abstract class Context { public abstract void removeStickyBroadcast(Intent intent); /** - * Same as {@link #sendStickyBroadcast(Intent)}, - * but for a specific user. This broadcast - * can only be sent to receivers that are part of the calling application. It - * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} - * permission. + * Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. Using it requires holding + * the INTERACT_ACROSS_USERS permission. * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast, and the Intent will be held to @@ -1277,12 +1280,12 @@ public abstract class Context { public abstract void sendStickyBroadcastAsUser(Intent intent, UserHandle user); /** - * Same as - * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) - * but for a specific user. This broadcast - * can only be sent to receivers that are part of the calling application. It - * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} - * permission. + * Version of + * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)} + * that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. Using it requires holding + * the INTERACT_ACROSS_USERS permission. * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * @@ -1309,12 +1312,10 @@ public abstract class Context { Bundle initialExtras); /** - * Same as - * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) - * but for a specific user. This broadcast - * can only be sent to receivers that are part of the calling application. It - * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} - * permission. + * Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the + * user the broadcast will be sent to. This is not available to applications + * that are not pre-installed on the system image. Using it requires holding + * the INTERACT_ACROSS_USERS permission. * * <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY} * permission in order to use this API. If you do not hold that diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl index 86a9392..f956bcf 100644 --- a/core/java/android/content/IContentService.aidl +++ b/core/java/android/content/IContentService.aidl @@ -30,12 +30,28 @@ import android.database.IContentObserver; * @hide */ interface IContentService { - void registerContentObserver(in Uri uri, boolean notifyForDescendentsn, - IContentObserver observer); void unregisterContentObserver(IContentObserver observer); + /** + * Register a content observer tied to a specific user's view of the provider. + * @param userHandle the user whose view of the provider is to be observed. May be + * the calling user without requiring any permission, otherwise the caller needs to + * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and + * USER_CURRENT are properly handled. + */ + void registerContentObserver(in Uri uri, boolean notifyForDescendants, + IContentObserver observer, int userHandle); + + /** + * Notify observers of a particular user's view of the provider. + * @param userHandle the user whose view of the provider is to be notified. May be + * the calling user without requiring any permission, otherwise the caller needs to + * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL + * USER_CURRENT are properly interpreted. + */ void notifyChange(in Uri uri, IContentObserver observer, - boolean observerWantsSelfNotifications, boolean syncToNetwork); + boolean observerWantsSelfNotifications, boolean syncToNetwork, + int userHandle); void requestSync(in Account account, String authority, in Bundle extras); void cancelSync(in Account account, String authority); diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java index 6c3cf99..166495b 100644 --- a/core/java/android/content/IntentSender.java +++ b/core/java/android/content/IntentSender.java @@ -205,6 +205,20 @@ public class IntentSender implements Parcelable { } /** + * @deprecated Renamed to {@link #getCreatorPackage()}. + */ + @Deprecated + public String getTargetPackage() { + try { + return ActivityManagerNative.getDefault() + .getPackageForIntentSender(mTarget); + } catch (RemoteException e) { + // Should never happen. + return null; + } + } + + /** * Return the package name of the application that created this * IntentSender, that is the identity under which you will actually be * sending the Intent. The returned string is supplied by the system, so @@ -213,7 +227,7 @@ public class IntentSender implements Parcelable { * @return The package name of the PendingIntent, or null if there is * none associated with it. */ - public String getTargetPackage() { + public String getCreatorPackage() { try { return ActivityManagerNative.getDefault() .getPackageForIntentSender(mTarget); @@ -232,7 +246,7 @@ public class IntentSender implements Parcelable { * @return The uid of the PendingIntent, or -1 if there is * none associated with it. */ - public int getTargetUid() { + public int getCreatorUid() { try { return ActivityManagerNative.getDefault() .getUidForIntentSender(mTarget); @@ -253,7 +267,7 @@ public class IntentSender implements Parcelable { * @return The user handle of the PendingIntent, or null if there is * none associated with it. */ - public UserHandle getTargetUserHandle() { + public UserHandle getCreatorUserHandle() { try { int uid = ActivityManagerNative.getDefault() .getUidForIntentSender(mTarget); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 4784d7f..fd488ae 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2434,8 +2434,7 @@ public abstract class PackageManager { * @param verificationCode either {@link PackageManager#VERIFICATION_ALLOW} * or {@link PackageManager#VERIFICATION_REJECT}. * @throws SecurityException if the caller does not have the - * {@link android.Manifest.permission#PACKAGE_VERIFICATION_AGENT} - * permission. + * PACKAGE_VERIFICATION_AGENT permission. */ public abstract void verifyPendingInstall(int id, int verificationCode); @@ -2469,8 +2468,7 @@ public abstract class PackageManager { * bounds value; namely, 0 or * {@link PackageManager#MAXIMUM_VERIFICATION_TIMEOUT}. * @throws SecurityException if the caller does not have the - * {@link android.Manifest.permission#PACKAGE_VERIFICATION_AGENT} - * permission. + * PACKAGE_VERIFICATION_AGENT permission. */ public abstract void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay); diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 1a71bfb..3579977 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -22,6 +22,7 @@ import java.util.HashSet; /** * Per-user state information about a package. + * @hide */ public class PackageUserState { public boolean stopped; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 81d5b63..392aff7 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -57,8 +57,9 @@ public class UserManager { } /** - * Returns the user name of the user making this call. - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * Returns the user name of the user making this call. This call is only + * available to applications on the system image; it requires the + * MANAGE_USERS permission. * @return the user name */ public String getUserName() { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a182234..68ce72f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5614,6 +5614,13 @@ public final class Settings { "wifi_supplicant_scan_interval_ms"; /** + * The interval in milliseconds to scan at supplicant when p2p is connected + * @hide + */ + public static final String WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS = + "wifi_scan_interval_p2p_connected_ms"; + + /** * Whether the Wi-Fi watchdog is enabled. */ public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on"; diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index f906e24..446a51e 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -733,13 +733,17 @@ public abstract class HardwareRenderer { */ void checkEglErrors() { if (isEnabled()) { - int error = sEgl.eglGetError(); - if (error != EGL_SUCCESS) { - // something bad has happened revert to - // normal rendering. - Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error)); - fallback(error != EGL11.EGL_CONTEXT_LOST); - } + checkEglErrorsForced(); + } + } + + private void checkEglErrorsForced() { + int error = sEgl.eglGetError(); + if (error != EGL_SUCCESS) { + // something bad has happened revert to + // normal rendering. + Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error)); + fallback(error != EGL11.EGL_CONTEXT_LOST); } } @@ -812,7 +816,9 @@ public abstract class HardwareRenderer { throw new RuntimeException("eglInitialize failed " + GLUtils.getEGLErrorString(sEgl.eglGetError())); } - + + checkEglErrorsForced(); + sEglConfig = chooseEglConfig(); if (sEglConfig == null) { // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index c93da06..d6f63a7 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -832,7 +832,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie sConfirmSafeVolumeDialog.dismiss(); } sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext) - .setTitle(android.R.string.dialog_alert_title) .setMessage(com.android.internal.R.string.safe_media_volume_warning) .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0bc09aa..89d78b6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -981,7 +981,7 @@ android:protectionLevel="dangerous" android:label="@string/permlab_getTasks" android:description="@string/permdesc_getTasks" /> - <!-- Allows an application to call APIs that allow it to do interactions + <!-- @hide Allows an application to call APIs that allow it to do interactions across the users on the device, using singleton services and user-targeted broadcasts. This permission is not available to third party applications. --> @@ -1000,7 +1000,7 @@ android:label="@string/permlab_interactAcrossUsersFull" android:description="@string/permdesc_interactAcrossUsersFull" /> - <!-- Allows an application to call APIs that allow it to query and manage + <!-- @hide Allows an application to call APIs that allow it to query and manage users on the device. This permission is not available to third party applications. --> <permission android:name="android.permission.MANAGE_USERS" @@ -1769,7 +1769,7 @@ android:description="@string/permdesc_devicePower" android:protectionLevel="signature" /> - <!-- Allows low-level access to tun tap driver --> + <!-- @hide Allows low-level access to tun tap driver --> <permission android:name="android.permission.NET_TUNNELING" android:permissionGroup="android.permission-group.SYSTEM_TOOLS" android:protectionLevel="signature" /> @@ -1981,7 +1981,7 @@ android:protectionLevel="signature" /> <uses-permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"/> - <!-- Package verifier needs to have this permission before the PackageManager will + <!-- @hide Package verifier needs to have this permission before the PackageManager will trust it to verify packages. --> <permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 3c0c0a7..7209e4e 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -299,8 +299,9 @@ with this component (by binding to a service for example) those processes will always interact with the instance running for user 0. Enabling single user mode forces "exported" of the component to be false, to - help avoid introducing multi-user security bugs. You must hold the - permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS} in order + help avoid introducing multi-user security bugs. This feature is only + available to applications built in to the system image; you must hold the + permission INTERACT_ACROSS_USERS in order to use this feature. This flag can only be used with services, receivers, and providers; it can not be used with activities. --> <attr name="singleUser" format="boolean" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1a9a0a9..297e72a 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -292,6 +292,9 @@ <!-- Integer indicating wpa_supplicant scan interval in milliseconds --> <integer translatable="false" name="config_wifi_supplicant_scan_interval">15000</integer> + <!-- Integer indicating wpa_supplicant scan interval when p2p is connected in milliseconds --> + <integer translatable="false" name="config_wifi_scan_interval_p2p_connected">60000</integer> + <!-- Integer indicating the framework scan interval in milliseconds. This is used in the scenario where the chipset does not support background scanning (config_wifi_background_scan_suport is false) to set up a periodic wake up scan so that the device can connect to a new access diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 56e5ce1..b904c60 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3890,7 +3890,7 @@ <!-- Message shown in dialog when user is attempting to set the music volume above the recommended maximum level for headphones --> <string name="safe_media_volume_warning" product="default"> - "Raise volume above the recommended level?" + "Raise volume above safe level?\nListening at high volume for long periods may damage your hearing." </string> <string name="kg_temp_back_string"> < </string> <!-- TODO: remove this --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index eb56d08..0360224 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -275,6 +275,7 @@ <java-symbol type="integer" name="config_ntpTimeout" /> <java-symbol type="integer" name="config_wifi_framework_scan_interval" /> <java-symbol type="integer" name="config_wifi_supplicant_scan_interval" /> + <java-symbol type="integer" name="config_wifi_scan_interval_p2p_connected" /> <java-symbol type="integer" name="db_connection_pool_size" /> <java-symbol type="integer" name="db_journal_size_limit" /> <java-symbol type="integer" name="db_wal_autocheckpoint" /> diff --git a/core/tests/coretests/src/android/content/ObserverNodeTest.java b/core/tests/coretests/src/android/content/ObserverNodeTest.java index 95b8465..1acff9c 100644 --- a/core/tests/coretests/src/android/content/ObserverNodeTest.java +++ b/core/tests/coretests/src/android/content/ObserverNodeTest.java @@ -23,6 +23,7 @@ import android.content.ContentService.ObserverNode; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; +import android.os.UserHandle; import android.test.AndroidTestCase; public class ObserverNodeTest extends AndroidTestCase { @@ -33,6 +34,8 @@ public class ObserverNodeTest extends AndroidTestCase { } public void testUri() { + final int myUserHandle = UserHandle.myUserId(); + ObserverNode root = new ObserverNode(""); Uri[] uris = new Uri[] { Uri.parse("content://c/a/"), @@ -48,21 +51,25 @@ public class ObserverNodeTest extends AndroidTestCase { int[] nums = new int[] {4, 7, 1, 4, 2, 2, 3, 3}; // special case - root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root, 0, 0); + root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root, + 0, 0, myUserHandle); for(int i = 1; i < uris.length; i++) { - root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root, 0, 0); + root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root, + 0, 0, myUserHandle); } ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); for (int i = nums.length - 1; i >=0; --i) { - root.collectObserversLocked(uris[i], 0, null, false, calls); + root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls); assertEquals(nums[i], calls.size()); calls.clear(); } } public void testUriNotNotify() { + final int myUserHandle = UserHandle.myUserId(); + ObserverNode root = new ObserverNode(""); Uri[] uris = new Uri[] { Uri.parse("content://c/"), @@ -77,13 +84,14 @@ public class ObserverNodeTest extends AndroidTestCase { int[] nums = new int[] {7, 1, 3, 3, 1, 1, 1, 1}; for(int i = 0; i < uris.length; i++) { - root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root, 0, 0); + root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root, + 0, 0, myUserHandle); } ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); for (int i = uris.length - 1; i >=0; --i) { - root.collectObserversLocked(uris[i], 0, null, false, calls); + root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls); assertEquals(nums[i], calls.size()); calls.clear(); } diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 6e7f35d..2d13f60 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -231,10 +231,10 @@ const char* gFS_Fast_SingleGradient[2] = { }; const char* gFS_Fast_SingleModulateGradient[2] = { "\nvoid main(void) {\n" - " gl_FragColor " FS_MAIN_DITHER " + color.a * texture2D(gradientSampler, linear);\n" + " gl_FragColor = " FS_MAIN_DITHER " + color.a * texture2D(gradientSampler, linear);\n" "}\n\n", "\nvoid main(void) {\n" - " gl_FragColor " FS_MAIN_DITHER " + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" + " gl_FragColor = " FS_MAIN_DITHER " + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" "}\n\n" }; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 7cc5dec..a0ae9e3 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -317,13 +317,14 @@ public class SettingsProvider extends ContentProvider { boolean backedUpDataChanged = false; String property = null, table = uri.getPathSegments().get(0); + final boolean isGlobal = table.equals(TABLE_GLOBAL); if (table.equals(TABLE_SYSTEM)) { property = Settings.System.SYS_PROP_SETTING_VERSION + '_' + userHandle; backedUpDataChanged = true; } else if (table.equals(TABLE_SECURE)) { property = Settings.Secure.SYS_PROP_SETTING_VERSION + '_' + userHandle; backedUpDataChanged = true; - } else if (table.equals(TABLE_GLOBAL)) { + } else if (isGlobal) { property = Settings.Global.SYS_PROP_SETTING_VERSION; // this one is global backedUpDataChanged = true; } @@ -342,8 +343,9 @@ public class SettingsProvider extends ContentProvider { String notify = uri.getQueryParameter("notify"); if (notify == null || "true".equals(notify)) { - getContext().getContentResolver().notifyChange(uri, null); - if (LOCAL_LOGV) Log.v(TAG, "notifying: " + uri); + final int notifyTarget = isGlobal ? UserHandle.USER_ALL : userHandle; + getContext().getContentResolver().notifyChange(uri, null, true, notifyTarget); + if (LOCAL_LOGV) Log.v(TAG, "notifying for " + notifyTarget + ": " + uri); } else { if (LOCAL_LOGV) Log.v(TAG, "notification suppressed: " + uri); } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java index 8fe28a3..47ec243 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -667,8 +667,12 @@ public class KeyguardHostView extends KeyguardViewBase { private void addWidget(int appId) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId); - AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo); - addWidget(view); + if (appWidgetInfo != null) { + AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo); + addWidget(view); + } else { + Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId); + } } private void maybePopulateWidgets() { diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index f4ad756..f960833 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -353,7 +353,7 @@ class AlarmManagerService extends IAlarmManager.Stub { while (it.hasNext()) { Alarm alarm = it.next(); - if (UserHandle.getUserId(alarm.operation.getTargetUid()) == userHandle) { + if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) { it.remove(); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 989477f..ce5424b 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1794,7 +1794,7 @@ public final class ActivityManagerService extends ActivityManagerNative } private final void updateLruProcessInternalLocked(ProcessRecord app, - boolean oomAdj, boolean updateActivityTime, int bestPos) { + boolean updateActivityTime, int bestPos) { // put it on the LRU to keep track of when it should be exited. int lrui = mLruProcesses.indexOf(app); if (lrui >= 0) mLruProcesses.remove(lrui); @@ -1851,7 +1851,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (cr.binding != null && cr.binding.service != null && cr.binding.service.app != null && cr.binding.service.app.lruSeq != mLruSeq) { - updateLruProcessInternalLocked(cr.binding.service.app, false, + updateLruProcessInternalLocked(cr.binding.service.app, updateActivityTime, i+1); } } @@ -1859,21 +1859,21 @@ public final class ActivityManagerService extends ActivityManagerNative for (int j=app.conProviders.size()-1; j>=0; j--) { ContentProviderRecord cpr = app.conProviders.get(j).provider; if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) { - updateLruProcessInternalLocked(cpr.proc, false, + updateLruProcessInternalLocked(cpr.proc, updateActivityTime, i+1); } } - - //Slog.i(TAG, "Putting proc to front: " + app.processName); - if (oomAdj) { - updateOomAdjLocked(); - } } final void updateLruProcessLocked(ProcessRecord app, boolean oomAdj, boolean updateActivityTime) { mLruSeq++; - updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0); + updateLruProcessInternalLocked(app, updateActivityTime, 0); + + //Slog.i(TAG, "Putting proc to front: " + app.processName); + if (oomAdj) { + updateOomAdjLocked(); + } } final ProcessRecord getProcessRecordLocked( @@ -4455,7 +4455,13 @@ public final class ActivityManagerService extends ActivityManagerNative enableScreenAfterBoot(); } } - + + public final void activityResumed(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + mMainStack.activityResumed(token); + Binder.restoreCallingIdentity(origId); + } + public final void activityPaused(IBinder token) { final long origId = Binder.clearCallingIdentity(); mMainStack.activityPaused(token, false); @@ -11432,7 +11438,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Add the new results to the existing results, tracking // and de-dupping single user receivers. for (int i=0; i<newReceivers.size(); i++) { - ResolveInfo ri = receivers.get(i); + ResolveInfo ri = newReceivers.get(i); if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) { ComponentName cn = new ComponentName( ri.activityInfo.packageName, ri.activityInfo.name); diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index f72d318..29ee0bc 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -52,7 +52,6 @@ import android.os.IBinder; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.PowerManager; -import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -755,8 +754,6 @@ final class ActivityStack { completeResumeLocked(r); checkReadyForSleepLocked(); if (DEBUG_SAVED_STATE) Slog.i(TAG, "Launch completed; removing icicle of " + r.icicle); - r.icicle = null; - r.haveState = false; } else { // This activity is not starting in the resumed state... which // should look like we asked it to pause+stop (but remain visible), @@ -1010,7 +1007,21 @@ final class ActivityStack { resumeTopActivityLocked(null); } } - + + final void activityResumed(IBinder token) { + ActivityRecord r = null; + + synchronized (mService) { + int index = indexOfTokenLocked(token); + if (index >= 0) { + r = mHistory.get(index); + if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; dropping state of: " + r); + r.icicle = null; + r.haveState = false; + } + } + } + final void activityPaused(IBinder token, boolean timeout) { if (DEBUG_PAUSE) Slog.v( TAG, "Activity paused: token=" + token + ", timeout=" + timeout); @@ -1488,6 +1499,15 @@ final class ActivityStack { // can be resumed... if (mResumedActivity != null) { if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing"); + // At this point we want to put the upcoming activity's process + // at the top of the LRU list, since we know we will be needing it + // very soon and it would be a waste to let it get killed if it + // happens to be sitting towards the end. + if (next.app != null && next.app.thread != null) { + // No reason to do full oom adj update here; we'll let that + // happen whenever it needs to later. + mService.updateLruProcessLocked(next.app, false, true); + } startPausingLocked(userLeaving, false); return true; } @@ -1728,11 +1748,6 @@ final class ActivityStack { "resume-exception", true); return true; } - - // Didn't need to use the icicle, and it is now out of date. - if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; didn't need icicle of: " + next); - next.icicle = null; - next.haveState = false; next.stopped = false; } else { @@ -2578,7 +2593,6 @@ final class ActivityStack { mDismissKeyguardOnNextActivity = false; mService.mWindowManager.dismissKeyguard(); } - Slog.i(TAG, "DONE STARTING!"); return err; } diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index 1b6ff7a..b0af081 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -965,12 +965,12 @@ public class BroadcastQueue { if (!printed) { if (needSep) { pw.println(); - needSep = false; } + needSep = true; printed = true; pw.println(" Active broadcasts [" + mQueueName + "]:"); } - pw.println(" Broadcast #" + i + ":"); + pw.println(" Active Broadcast " + mQueueName + " #" + i + ":"); br.dump(pw, " "); } printed = false; @@ -985,9 +985,10 @@ public class BroadcastQueue { pw.println(); } needSep = true; + printed = true; pw.println(" Active ordered broadcasts [" + mQueueName + "]:"); } - pw.println(" Ordered Broadcast #" + i + ":"); + pw.println(" Active Ordered Broadcast " + mQueueName + " #" + i + ":"); mOrderedBroadcasts.get(i).dump(pw, " "); } if (dumpPackage == null || (mPendingBroadcast != null @@ -1023,7 +1024,8 @@ public class BroadcastQueue { printed = true; } if (dumpAll) { - pw.print(" Historical Broadcast #"); pw.print(i); pw.println(":"); + pw.print(" Historical Broadcast " + mQueueName + " #"); + pw.print(i); pw.println(":"); r.dump(pw, " "); } else { if (i >= 50) { diff --git a/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java index c1f45a8..a74a648 100644 --- a/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java +++ b/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java @@ -89,12 +89,15 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { // get the hash of the currently used value String currentHash = getCurrentHash(getCurrentContent()); if (!verifyVersion(currentVersion, altVersion)) { - Slog.e(TAG, "New version is not greater than current version, aborting!"); + EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, + "New version is not greater than current version"); } else if (!verifyPreviousHash(currentHash, altRequiredHash)) { - Slog.e(TAG, "Current hash did not match required value, aborting!"); + EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, + "Current hash did not match required value"); } else if (!verifySignature(altContent, altVersion, altRequiredHash, altSig, cert)) { - Slog.e(TAG, "Signature did not verify, aborting!"); + EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, + "Signature did not verify"); } else { // install the new content Slog.i(TAG, "Found new update, installing..."); @@ -103,8 +106,12 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { } } catch (Exception e) { Slog.e(TAG, "Could not update content!", e); - EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, - updateDir.toString()); + // keep the error message <= 100 chars + String errMsg = e.toString(); + if (errMsg.length() > 100) { + errMsg = errMsg.substring(0, 99); + } + EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, errMsg); } } }.start(); diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java index e39d53c..5e27ba7 100644 --- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java +++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java @@ -72,6 +72,7 @@ public class MemoryUsageTest extends InstrumentationTestCase { try { processName = startApp(app); measureMemory(app, processName, results); + closeApp(); } catch (NameNotFoundException e) { Log.i(TAG, "Application " + app + " not found"); } @@ -139,13 +140,22 @@ public class MemoryUsageTest extends InstrumentationTestCase { return process; } + private void closeApp() { + Intent homeIntent = new Intent(Intent.ACTION_MAIN); + homeIntent.addCategory(Intent.CATEGORY_HOME); + homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + getInstrumentation().getContext().startActivity(homeIntent); + sleep(3000); + } + private void measureMemory(String appName, String processName, Bundle results) { List<Integer> pssData = new ArrayList<Integer>(); int pss = 0; int iteration = 0; while (iteration < MAX_ITERATIONS) { - sleep(); + sleep(SLEEP_TIME); pss = getPss(processName); Log.i(TAG, appName + "=" + pss); if (pss < 0) { @@ -184,9 +194,9 @@ public class MemoryUsageTest extends InstrumentationTestCase { return (diff1 + diff2) < THRESHOLD; } - private void sleep() { + private void sleep(int time) { try { - Thread.sleep(SLEEP_TIME); + Thread.sleep(time); } catch (InterruptedException e) { // ignore } diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 9fe1c60..4aa092b 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -75,6 +75,7 @@ import android.util.EventLog; import android.util.Log; import android.util.LruCache; +import com.android.internal.R; import com.android.internal.app.IBatteryStats; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; @@ -112,6 +113,7 @@ public class WifiStateMachine extends StateMachine { private ConnectivityManager mCm; private final boolean mP2pSupported; + private final AtomicBoolean mP2pConnected = new AtomicBoolean(false); private final String mPrimaryDeviceType; /* Scan results handling */ @@ -595,16 +597,16 @@ public class WifiStateMachine extends StateMachine { mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0); mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger( - com.android.internal.R.integer.config_wifi_framework_scan_interval); + R.integer.config_wifi_framework_scan_interval); mDriverStopDelayMs = mContext.getResources().getInteger( - com.android.internal.R.integer.config_wifi_driver_stop_delay); + R.integer.config_wifi_driver_stop_delay); mBackgroundScanSupported = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_wifi_background_scan_support); + R.bool.config_wifi_background_scan_support); mPrimaryDeviceType = mContext.getResources().getString( - com.android.internal.R.string.config_wifi_p2p_device_type); + R.string.config_wifi_p2p_device_type); mUserWantsSuspendOpt.set(Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1); @@ -2011,6 +2013,10 @@ public class WifiStateMachine extends StateMachine { replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_FAILED, WifiManager.BUSY); break; + case WifiP2pService.P2P_CONNECTION_CHANGED: + NetworkInfo info = (NetworkInfo) message.obj; + mP2pConnected.set(info.isConnected()); + break; default: loge("Error! unhandled message" + message); break; @@ -2408,7 +2414,7 @@ public class WifiStateMachine extends StateMachine { mNetworkInfo.setIsAvailable(true); int defaultInterval = mContext.getResources().getInteger( - com.android.internal.R.integer.config_wifi_supplicant_scan_interval); + R.integer.config_wifi_supplicant_scan_interval); mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(), Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS, @@ -3486,7 +3492,7 @@ public class WifiStateMachine extends StateMachine { * The scans are useful to notify the user of the presence of an open network. * Note that these are not wake up scans. */ - if (mWifiConfigStore.getConfiguredNetworks().size() == 0) { + if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) { sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN, ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs); } @@ -3497,6 +3503,7 @@ public class WifiStateMachine extends StateMachine { boolean ret = HANDLED; switch (message.what) { case CMD_NO_NETWORKS_PERIODIC_SCAN: + if (mP2pConnected.get()) break; if (message.arg1 == mPeriodicScanToken && mWifiConfigStore.getConfiguredNetworks().size() == 0) { sendMessage(CMD_START_SCAN); @@ -3557,6 +3564,21 @@ public class WifiStateMachine extends StateMachine { /* Handled in parent state */ ret = NOT_HANDLED; break; + case WifiP2pService.P2P_CONNECTION_CHANGED: + NetworkInfo info = (NetworkInfo) message.obj; + mP2pConnected.set(info.isConnected()); + if (mP2pConnected.get()) { + int defaultInterval = mContext.getResources().getInteger( + R.integer.config_wifi_scan_interval_p2p_connected); + long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(), + Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS, + defaultInterval); + mWifiNative.setScanInterval((int) scanIntervalMs/1000); + } else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) { + if (DBG) log("Turn on scanning after p2p disconnected"); + sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN, + ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs); + } default: ret = NOT_HANDLED; } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java index edd1dac..35dd764 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java @@ -137,18 +137,6 @@ public class WifiP2pService extends IWifiP2pManager.Stub { /* Idle time after a peer is gone when the group is torn down */ private static final int GROUP_IDLE_TIME_S = 5; - /** - * Delay between restarts upon failure to setup connection with supplicant - */ - private static final int P2P_RESTART_INTERVAL_MSECS = 5000; - - /** - * Number of times we attempt to restart p2p - */ - private static final int P2P_RESTART_TRIES = 5; - - private int mP2pRestartCount = 0; - private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE; /* Delayed message to timeout group creation */ @@ -159,6 +147,9 @@ public class WifiP2pService extends IWifiP2pManager.Stub { /* User rejected a peer request */ private static final int PEER_CONNECTION_USER_REJECT = BASE + 3; + /* Commands to the WifiStateMachine */ + public static final int P2P_CONNECTION_CHANGED = BASE + 11; + private final boolean mP2pSupported; private WifiP2pDevice mThisDevice = new WifiP2pDevice(); @@ -1597,6 +1588,8 @@ public class WifiP2pService extends IWifiP2pManager.Stub { intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo)); intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo)); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + mWifiChannel.sendMessage(WifiP2pService.P2P_CONNECTION_CHANGED, + new NetworkInfo(mNetworkInfo)); } private void sendP2pPersistentGroupsChangedBroadcast() { |