diff options
Diffstat (limited to 'core/java')
69 files changed, 1650 insertions, 1281 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 06f79e7..0eda6b4 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -864,7 +864,17 @@ public class ActivityManager { return null; } } - + + /** @hide */ + public Bitmap getTaskTopThumbnail(int id) throws SecurityException { + try { + return ActivityManagerNative.getDefault().getTaskTopThumbnail(id); + } catch (RemoteException e) { + // System dead, we will be dead too soon! + return null; + } + } + /** * Flag for {@link #moveTaskToFront(int, int)}: also move the "home" * activity along with the task, so it is positioned immediately behind diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index eae3b1f..8436b2c 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(); @@ -490,7 +498,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeTypedList(list); return true; } - + case GET_TASK_THUMBNAILS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int id = data.readInt(); @@ -504,7 +512,21 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM } return true; } - + + case GET_TASK_TOP_THUMBNAIL_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + int id = data.readInt(); + Bitmap bm = getTaskTopThumbnail(id); + reply.writeNoException(); + if (bm != null) { + reply.writeInt(1); + bm.writeToParcel(reply, 0); + } else { + reply.writeInt(0); + } + return true; + } + case GET_SERVICES_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int maxNum = data.readInt(); @@ -2152,6 +2174,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(); @@ -2288,6 +2321,21 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return bm; } + public Bitmap getTaskTopThumbnail(int id) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(id); + mRemote.transact(GET_TASK_TOP_THUMBNAIL_TRANSACTION, data, reply, 0); + reply.readException(); + Bitmap bm = null; + if (reply.readInt() != 0) { + bm = Bitmap.CREATOR.createFromParcel(reply); + } + data.recycle(); + reply.recycle(); + return bm; + } public List getServices(int maxNum, int flags) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = 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/ContextImpl.java b/core/java/android/app/ContextImpl.java index 92104fa..56b745f 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -129,32 +129,33 @@ class ReceiverRestrictedContext extends ContextWrapper { @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { - throw new ReceiverCallNotAllowedException( - "IntentReceiver components are not allowed to register to receive intents"); - //ex.fillInStackTrace(); - //Log.e("IntentReceiver", ex.getMessage(), ex); - //return mContext.registerReceiver(receiver, filter, broadcastPermission, - // scheduler); + if (receiver == null) { + // Allow retrieving current sticky broadcast; this is safe since we + // aren't actually registering a receiver. + return super.registerReceiver(null, filter, broadcastPermission, scheduler); + } else { + throw new ReceiverCallNotAllowedException( + "BroadcastReceiver components are not allowed to register to receive intents"); + } } @Override public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, IntentFilter filter, String broadcastPermission, Handler scheduler) { - throw new ReceiverCallNotAllowedException( - "IntentReceiver components are not allowed to register to receive intents"); - //ex.fillInStackTrace(); - //Log.e("IntentReceiver", ex.getMessage(), ex); - //return mContext.registerReceiver(receiver, filter, broadcastPermission, - // scheduler); + if (receiver == null) { + // Allow retrieving current sticky broadcast; this is safe since we + // aren't actually registering a receiver. + return super.registerReceiverAsUser(null, user, filter, broadcastPermission, scheduler); + } else { + throw new ReceiverCallNotAllowedException( + "BroadcastReceiver components are not allowed to register to receive intents"); + } } @Override public boolean bindService(Intent service, ServiceConnection conn, int flags) { throw new ReceiverCallNotAllowedException( - "IntentReceiver components are not allowed to bind to services"); - //ex.fillInStackTrace(); - //Log.e("IntentReceiver", ex.getMessage(), ex); - //return mContext.bindService(service, interfaceName, conn, flags); + "BroadcastReceiver components are not allowed to bind to services"); } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 9ef375a..d6ebc9b 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; @@ -108,6 +104,7 @@ public interface IActivityManager extends IInterface { public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) throws RemoteException; public ActivityManager.TaskThumbnails getTaskThumbnails(int taskId) throws RemoteException; + public Bitmap getTaskTopThumbnail(int taskId) throws RemoteException; public List getServices(int maxNum, int flags) throws RemoteException; public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() throws RemoteException; @@ -496,7 +493,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; @@ -552,7 +549,7 @@ public interface IActivityManager extends IInterface { int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91; int GET_UID_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+92; int HANDLE_INCOMING_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+93; - + int GET_TASK_TOP_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94; int KILL_APPLICATION_WITH_APPID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95; int CLOSE_SYSTEM_DIALOGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+96; int GET_PROCESS_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+97; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index cb83dc2..7896450 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1923,6 +1923,7 @@ public class Notification implements Parcelable contentView.setViewVisibility(rowId, View.GONE); } + int i=0; while (i < mTexts.size() && i < rowIds.length) { CharSequence str = mTexts.get(i); @@ -1933,11 +1934,11 @@ public class Notification implements Parcelable i++; } - if (mTexts.size() > rowIds.length) { - contentView.setViewVisibility(R.id.inbox_more, View.VISIBLE); - } else { - contentView.setViewVisibility(R.id.inbox_more, View.GONE); - } + contentView.setViewVisibility(R.id.inbox_end_pad, + mTexts.size() > 0 ? View.VISIBLE : View.GONE); + + contentView.setViewVisibility(R.id.inbox_more, + mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE); return contentView; } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index a3c1838..8fb6948 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; @@ -385,6 +413,13 @@ public final class PendingIntent implements Parcelable { */ public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags) { + return getBroadcastAsUser(context, requestCode, intent, flags, + new UserHandle(UserHandle.myUserId())); + } + + /** @hide */ + public static PendingIntent getBroadcastAsUser(Context context, int requestCode, + Intent intent, int flags, UserHandle userHandle) { String packageName = context.getPackageName(); String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; @@ -395,7 +430,7 @@ public final class PendingIntent implements Parcelable { ActivityManager.INTENT_SENDER_BROADCAST, packageName, null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, - flags, null, UserHandle.myUserId()); + flags, null, userHandle.getIdentifier()); return target != null ? new PendingIntent(target) : null; } catch (RemoteException e) { } @@ -622,6 +657,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 +679,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 +698,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 +719,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 9c83362..cadf5e4 100644 --- a/core/java/android/app/TaskStackBuilder.java +++ b/core/java/android/app/TaskStackBuilder.java @@ -162,7 +162,7 @@ public class TaskStackBuilder { ActivityInfo info = pm.getActivityInfo(sourceActivityName, 0); String parentActivity = info.parentActivityName; while (parentActivity != null) { - final ComponentName target = new ComponentName(mSourceContext, parentActivity); + final ComponentName target = new ComponentName(info.packageName, parentActivity); info = pm.getActivityInfo(target, 0); parentActivity = info.parentActivityName; final Intent parent = parentActivity == null && insertAt == 0 @@ -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/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 4c55bb3..600d02a 100755 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -29,6 +29,7 @@ import android.os.Handler; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.util.Log; import java.io.IOException; @@ -131,7 +132,7 @@ public class DevicePolicyManager { public boolean isAdminActive(ComponentName who) { if (mService != null) { try { - return mService.isAdminActive(who); + return mService.isAdminActive(who, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -147,7 +148,7 @@ public class DevicePolicyManager { public List<ComponentName> getActiveAdmins() { if (mService != null) { try { - return mService.getActiveAdmins(); + return mService.getActiveAdmins(UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -156,12 +157,14 @@ public class DevicePolicyManager { } /** + * Used by package administration code to determine if a package can be stopped + * or uninstalled. * @hide */ public boolean packageHasActiveAdmins(String packageName) { if (mService != null) { try { - return mService.packageHasActiveAdmins(packageName); + return mService.packageHasActiveAdmins(packageName, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -178,7 +181,7 @@ public class DevicePolicyManager { public void removeActiveAdmin(ComponentName who) { if (mService != null) { try { - mService.removeActiveAdmin(who); + mService.removeActiveAdmin(who, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -197,7 +200,7 @@ public class DevicePolicyManager { public boolean hasGrantedPolicy(ComponentName admin, int usesPolicy) { if (mService != null) { try { - return mService.hasGrantedPolicy(admin, usesPolicy); + return mService.hasGrantedPolicy(admin, usesPolicy, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -289,7 +292,7 @@ public class DevicePolicyManager { public void setPasswordQuality(ComponentName admin, int quality) { if (mService != null) { try { - mService.setPasswordQuality(admin, quality); + mService.setPasswordQuality(admin, quality, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -303,9 +306,14 @@ public class DevicePolicyManager { * all admins. */ public int getPasswordQuality(ComponentName admin) { + return getPasswordQuality(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getPasswordQuality(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getPasswordQuality(admin); + return mService.getPasswordQuality(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -337,7 +345,7 @@ public class DevicePolicyManager { public void setPasswordMinimumLength(ComponentName admin, int length) { if (mService != null) { try { - mService.setPasswordMinimumLength(admin, length); + mService.setPasswordMinimumLength(admin, length, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -351,9 +359,14 @@ public class DevicePolicyManager { * all admins. */ public int getPasswordMinimumLength(ComponentName admin) { + return getPasswordMinimumLength(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getPasswordMinimumLength(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getPasswordMinimumLength(admin); + return mService.getPasswordMinimumLength(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -386,7 +399,7 @@ public class DevicePolicyManager { public void setPasswordMinimumUpperCase(ComponentName admin, int length) { if (mService != null) { try { - mService.setPasswordMinimumUpperCase(admin, length); + mService.setPasswordMinimumUpperCase(admin, length, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -406,9 +419,14 @@ public class DevicePolicyManager { * password. */ public int getPasswordMinimumUpperCase(ComponentName admin) { + return getPasswordMinimumUpperCase(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getPasswordMinimumUpperCase(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getPasswordMinimumUpperCase(admin); + return mService.getPasswordMinimumUpperCase(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -441,7 +459,7 @@ public class DevicePolicyManager { public void setPasswordMinimumLowerCase(ComponentName admin, int length) { if (mService != null) { try { - mService.setPasswordMinimumLowerCase(admin, length); + mService.setPasswordMinimumLowerCase(admin, length, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -461,9 +479,14 @@ public class DevicePolicyManager { * password. */ public int getPasswordMinimumLowerCase(ComponentName admin) { + return getPasswordMinimumLowerCase(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getPasswordMinimumLowerCase(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getPasswordMinimumLowerCase(admin); + return mService.getPasswordMinimumLowerCase(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -495,7 +518,7 @@ public class DevicePolicyManager { public void setPasswordMinimumLetters(ComponentName admin, int length) { if (mService != null) { try { - mService.setPasswordMinimumLetters(admin, length); + mService.setPasswordMinimumLetters(admin, length, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -514,9 +537,14 @@ public class DevicePolicyManager { * @return The minimum number of letters required in the password. */ public int getPasswordMinimumLetters(ComponentName admin) { + return getPasswordMinimumLetters(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getPasswordMinimumLetters(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getPasswordMinimumLetters(admin); + return mService.getPasswordMinimumLetters(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -548,7 +576,7 @@ public class DevicePolicyManager { public void setPasswordMinimumNumeric(ComponentName admin, int length) { if (mService != null) { try { - mService.setPasswordMinimumNumeric(admin, length); + mService.setPasswordMinimumNumeric(admin, length, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -567,9 +595,14 @@ public class DevicePolicyManager { * @return The minimum number of numerical digits required in the password. */ public int getPasswordMinimumNumeric(ComponentName admin) { + return getPasswordMinimumNumeric(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getPasswordMinimumNumeric(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getPasswordMinimumNumeric(admin); + return mService.getPasswordMinimumNumeric(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -601,7 +634,7 @@ public class DevicePolicyManager { public void setPasswordMinimumSymbols(ComponentName admin, int length) { if (mService != null) { try { - mService.setPasswordMinimumSymbols(admin, length); + mService.setPasswordMinimumSymbols(admin, length, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -620,9 +653,14 @@ public class DevicePolicyManager { * @return The minimum number of symbols required in the password. */ public int getPasswordMinimumSymbols(ComponentName admin) { + return getPasswordMinimumSymbols(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getPasswordMinimumSymbols(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getPasswordMinimumSymbols(admin); + return mService.getPasswordMinimumSymbols(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -654,7 +692,7 @@ public class DevicePolicyManager { public void setPasswordMinimumNonLetter(ComponentName admin, int length) { if (mService != null) { try { - mService.setPasswordMinimumNonLetter(admin, length); + mService.setPasswordMinimumNonLetter(admin, length, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -673,9 +711,14 @@ public class DevicePolicyManager { * @return The minimum number of letters required in the password. */ public int getPasswordMinimumNonLetter(ComponentName admin) { + return getPasswordMinimumNonLetter(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getPasswordMinimumNonLetter(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getPasswordMinimumNonLetter(admin); + return mService.getPasswordMinimumNonLetter(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -708,7 +751,7 @@ public class DevicePolicyManager { public void setPasswordHistoryLength(ComponentName admin, int length) { if (mService != null) { try { - mService.setPasswordHistoryLength(admin, length); + mService.setPasswordHistoryLength(admin, length, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -737,7 +780,7 @@ public class DevicePolicyManager { public void setPasswordExpirationTimeout(ComponentName admin, long timeout) { if (mService != null) { try { - mService.setPasswordExpirationTimeout(admin, timeout); + mService.setPasswordExpirationTimeout(admin, timeout, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -756,7 +799,7 @@ public class DevicePolicyManager { public long getPasswordExpirationTimeout(ComponentName admin) { if (mService != null) { try { - return mService.getPasswordExpirationTimeout(admin); + return mService.getPasswordExpirationTimeout(admin, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -776,7 +819,7 @@ public class DevicePolicyManager { public long getPasswordExpiration(ComponentName admin) { if (mService != null) { try { - return mService.getPasswordExpiration(admin); + return mService.getPasswordExpiration(admin, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -792,9 +835,14 @@ public class DevicePolicyManager { * @return The length of the password history */ public int getPasswordHistoryLength(ComponentName admin) { + return getPasswordHistoryLength(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getPasswordHistoryLength(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getPasswordHistoryLength(admin); + return mService.getPasswordHistoryLength(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -828,7 +876,7 @@ public class DevicePolicyManager { public boolean isActivePasswordSufficient() { if (mService != null) { try { - return mService.isActivePasswordSufficient(); + return mService.isActivePasswordSufficient(UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -847,7 +895,7 @@ public class DevicePolicyManager { public int getCurrentFailedPasswordAttempts() { if (mService != null) { try { - return mService.getCurrentFailedPasswordAttempts(); + return mService.getCurrentFailedPasswordAttempts(UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -877,7 +925,7 @@ public class DevicePolicyManager { public void setMaximumFailedPasswordsForWipe(ComponentName admin, int num) { if (mService != null) { try { - mService.setMaximumFailedPasswordsForWipe(admin, num); + mService.setMaximumFailedPasswordsForWipe(admin, num, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -892,9 +940,14 @@ public class DevicePolicyManager { * all admins. */ public int getMaximumFailedPasswordsForWipe(ComponentName admin) { + return getMaximumFailedPasswordsForWipe(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getMaximumFailedPasswordsForWipe(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getMaximumFailedPasswordsForWipe(admin); + return mService.getMaximumFailedPasswordsForWipe(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -933,7 +986,7 @@ public class DevicePolicyManager { public boolean resetPassword(String password, int flags) { if (mService != null) { try { - return mService.resetPassword(password, flags); + return mService.resetPassword(password, flags, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -957,7 +1010,7 @@ public class DevicePolicyManager { public void setMaximumTimeToLock(ComponentName admin, long timeMs) { if (mService != null) { try { - mService.setMaximumTimeToLock(admin, timeMs); + mService.setMaximumTimeToLock(admin, timeMs, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -971,9 +1024,14 @@ public class DevicePolicyManager { * all admins. */ public long getMaximumTimeToLock(ComponentName admin) { + return getMaximumTimeToLock(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public long getMaximumTimeToLock(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getMaximumTimeToLock(admin); + return mService.getMaximumTimeToLock(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1021,7 +1079,7 @@ public class DevicePolicyManager { public void wipeData(int flags) { if (mService != null) { try { - mService.wipeData(flags); + mService.wipeData(flags, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1090,7 +1148,7 @@ public class DevicePolicyManager { } android.net.Proxy.validate(hostName, Integer.toString(port), exclSpec); } - return mService.setGlobalProxy(admin, hostSpec, exclSpec); + return mService.setGlobalProxy(admin, hostSpec, exclSpec, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1107,7 +1165,7 @@ public class DevicePolicyManager { public ComponentName getGlobalProxyAdmin() { if (mService != null) { try { - return mService.getGlobalProxyAdmin(); + return mService.getGlobalProxyAdmin(UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1199,7 +1257,7 @@ public class DevicePolicyManager { public int setStorageEncryption(ComponentName admin, boolean encrypt) { if (mService != null) { try { - return mService.setStorageEncryption(admin, encrypt); + return mService.setStorageEncryption(admin, encrypt, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1219,7 +1277,7 @@ public class DevicePolicyManager { public boolean getStorageEncryption(ComponentName admin) { if (mService != null) { try { - return mService.getStorageEncryption(admin); + return mService.getStorageEncryption(admin, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1244,9 +1302,14 @@ public class DevicePolicyManager { * {@link #ENCRYPTION_STATUS_ACTIVATING}, or{@link #ENCRYPTION_STATUS_ACTIVE}. */ public int getStorageEncryptionStatus() { + return getStorageEncryptionStatus(UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getStorageEncryptionStatus(int userHandle) { if (mService != null) { try { - return mService.getStorageEncryptionStatus(); + return mService.getStorageEncryptionStatus(userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1269,7 +1332,7 @@ public class DevicePolicyManager { public void setCameraDisabled(ComponentName admin, boolean disabled) { if (mService != null) { try { - mService.setCameraDisabled(admin, disabled); + mService.setCameraDisabled(admin, disabled, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1283,9 +1346,14 @@ public class DevicePolicyManager { * have disabled the camera */ public boolean getCameraDisabled(ComponentName admin) { + return getCameraDisabled(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public boolean getCameraDisabled(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getCameraDisabled(admin); + return mService.getCameraDisabled(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1309,7 +1377,7 @@ public class DevicePolicyManager { public void setKeyguardWidgetsDisabled(ComponentName admin, int which) { if (mService != null) { try { - mService.setKeyguardWidgetsDisabled(admin, which); + mService.setKeyguardWidgetsDisabled(admin, which, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1323,9 +1391,14 @@ public class DevicePolicyManager { * have disabled widgets in keyguard. */ public int getKeyguardWidgetsDisabled(ComponentName admin) { + return getKeyguardWidgetsDisabled(admin, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public int getKeyguardWidgetsDisabled(ComponentName admin, int userHandle) { if (mService != null) { try { - return mService.getKeyguardWidgetsDisabled(admin); + return mService.getKeyguardWidgetsDisabled(admin, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1339,7 +1412,7 @@ public class DevicePolicyManager { public void setActiveAdmin(ComponentName policyReceiver, boolean refreshing) { if (mService != null) { try { - mService.setActiveAdmin(policyReceiver, refreshing); + mService.setActiveAdmin(policyReceiver, refreshing, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1380,7 +1453,7 @@ public class DevicePolicyManager { public void getRemoveWarning(ComponentName admin, RemoteCallback result) { if (mService != null) { try { - mService.getRemoveWarning(admin, result); + mService.getRemoveWarning(admin, result, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1391,11 +1464,11 @@ public class DevicePolicyManager { * @hide */ public void setActivePasswordState(int quality, int length, int letters, int uppercase, - int lowercase, int numbers, int symbols, int nonletter) { + int lowercase, int numbers, int symbols, int nonletter, int userHandle) { if (mService != null) { try { mService.setActivePasswordState(quality, length, letters, uppercase, lowercase, - numbers, symbols, nonletter); + numbers, symbols, nonletter, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1405,10 +1478,10 @@ public class DevicePolicyManager { /** * @hide */ - public void reportFailedPasswordAttempt() { + public void reportFailedPasswordAttempt(int userHandle) { if (mService != null) { try { - mService.reportFailedPasswordAttempt(); + mService.reportFailedPasswordAttempt(userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1418,14 +1491,13 @@ public class DevicePolicyManager { /** * @hide */ - public void reportSuccessfulPasswordAttempt() { + public void reportSuccessfulPasswordAttempt(int userHandle) { if (mService != null) { try { - mService.reportSuccessfulPasswordAttempt(); + mService.reportSuccessfulPasswordAttempt(userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } } } - } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 0b7ec12..bdfb177 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -25,76 +25,76 @@ import android.os.RemoteCallback; * {@hide} */ interface IDevicePolicyManager { - void setPasswordQuality(in ComponentName who, int quality); - int getPasswordQuality(in ComponentName who); + void setPasswordQuality(in ComponentName who, int quality, int userHandle); + int getPasswordQuality(in ComponentName who, int userHandle); - void setPasswordMinimumLength(in ComponentName who, int length); - int getPasswordMinimumLength(in ComponentName who); + void setPasswordMinimumLength(in ComponentName who, int length, int userHandle); + int getPasswordMinimumLength(in ComponentName who, int userHandle); - void setPasswordMinimumUpperCase(in ComponentName who, int length); - int getPasswordMinimumUpperCase(in ComponentName who); + void setPasswordMinimumUpperCase(in ComponentName who, int length, int userHandle); + int getPasswordMinimumUpperCase(in ComponentName who, int userHandle); - void setPasswordMinimumLowerCase(in ComponentName who, int length); - int getPasswordMinimumLowerCase(in ComponentName who); + void setPasswordMinimumLowerCase(in ComponentName who, int length, int userHandle); + int getPasswordMinimumLowerCase(in ComponentName who, int userHandle); - void setPasswordMinimumLetters(in ComponentName who, int length); - int getPasswordMinimumLetters(in ComponentName who); + void setPasswordMinimumLetters(in ComponentName who, int length, int userHandle); + int getPasswordMinimumLetters(in ComponentName who, int userHandle); - void setPasswordMinimumNumeric(in ComponentName who, int length); - int getPasswordMinimumNumeric(in ComponentName who); + void setPasswordMinimumNumeric(in ComponentName who, int length, int userHandle); + int getPasswordMinimumNumeric(in ComponentName who, int userHandle); - void setPasswordMinimumSymbols(in ComponentName who, int length); - int getPasswordMinimumSymbols(in ComponentName who); + void setPasswordMinimumSymbols(in ComponentName who, int length, int userHandle); + int getPasswordMinimumSymbols(in ComponentName who, int userHandle); - void setPasswordMinimumNonLetter(in ComponentName who, int length); - int getPasswordMinimumNonLetter(in ComponentName who); + void setPasswordMinimumNonLetter(in ComponentName who, int length, int userHandle); + int getPasswordMinimumNonLetter(in ComponentName who, int userHandle); - void setPasswordHistoryLength(in ComponentName who, int length); - int getPasswordHistoryLength(in ComponentName who); + void setPasswordHistoryLength(in ComponentName who, int length, int userHandle); + int getPasswordHistoryLength(in ComponentName who, int userHandle); - void setPasswordExpirationTimeout(in ComponentName who, long expiration); - long getPasswordExpirationTimeout(in ComponentName who); + void setPasswordExpirationTimeout(in ComponentName who, long expiration, int userHandle); + long getPasswordExpirationTimeout(in ComponentName who, int userHandle); - long getPasswordExpiration(in ComponentName who); + long getPasswordExpiration(in ComponentName who, int userHandle); - boolean isActivePasswordSufficient(); - int getCurrentFailedPasswordAttempts(); + boolean isActivePasswordSufficient(int userHandle); + int getCurrentFailedPasswordAttempts(int userHandle); - void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num); - int getMaximumFailedPasswordsForWipe(in ComponentName admin); + void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num, int userHandle); + int getMaximumFailedPasswordsForWipe(in ComponentName admin, int userHandle); - boolean resetPassword(String password, int flags); + boolean resetPassword(String password, int flags, int userHandle); - void setMaximumTimeToLock(in ComponentName who, long timeMs); - long getMaximumTimeToLock(in ComponentName who); + void setMaximumTimeToLock(in ComponentName who, long timeMs, int userHandle); + long getMaximumTimeToLock(in ComponentName who, int userHandle); void lockNow(); - void wipeData(int flags); + void wipeData(int flags, int userHandle); - ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList); - ComponentName getGlobalProxyAdmin(); + ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList, int userHandle); + ComponentName getGlobalProxyAdmin(int userHandle); - int setStorageEncryption(in ComponentName who, boolean encrypt); - boolean getStorageEncryption(in ComponentName who); - int getStorageEncryptionStatus(); + int setStorageEncryption(in ComponentName who, boolean encrypt, int userHandle); + boolean getStorageEncryption(in ComponentName who, int userHandle); + int getStorageEncryptionStatus(int userHandle); - void setCameraDisabled(in ComponentName who, boolean disabled); - boolean getCameraDisabled(in ComponentName who); + void setCameraDisabled(in ComponentName who, boolean disabled, int userHandle); + boolean getCameraDisabled(in ComponentName who, int userHandle); - void setKeyguardWidgetsDisabled(in ComponentName who, int which); - int getKeyguardWidgetsDisabled(in ComponentName who); + void setKeyguardWidgetsDisabled(in ComponentName who, int which, int userHandle); + int getKeyguardWidgetsDisabled(in ComponentName who, int userHandle); - void setActiveAdmin(in ComponentName policyReceiver, boolean refreshing); - boolean isAdminActive(in ComponentName policyReceiver); - List<ComponentName> getActiveAdmins(); - boolean packageHasActiveAdmins(String packageName); - void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result); - void removeActiveAdmin(in ComponentName policyReceiver); - boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy); + void setActiveAdmin(in ComponentName policyReceiver, boolean refreshing, int userHandle); + boolean isAdminActive(in ComponentName policyReceiver, int userHandle); + List<ComponentName> getActiveAdmins(int userHandle); + boolean packageHasActiveAdmins(String packageName, int userHandle); + void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result, int userHandle); + void removeActiveAdmin(in ComponentName policyReceiver, int userHandle); + boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle); void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase, - int numbers, int symbols, int nonletter); - void reportFailedPasswordAttempt(); - void reportSuccessfulPasswordAttempt(); + int numbers, int symbols, int nonletter, int userHandle); + void reportFailedPasswordAttempt(int userHandle); + void reportSuccessfulPasswordAttempt(int userHandle); } diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 51a81c5..185fb5a 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -19,6 +19,7 @@ package android.appwidget; import java.util.ArrayList; import java.util.HashMap; +import android.app.ActivityThread; import android.content.Context; import android.os.Handler; import android.os.IBinder; @@ -117,7 +118,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()); } /** @@ -201,12 +202,15 @@ public class AppWidgetHost { * @return a appWidgetId * @hide */ - public static int allocateAppWidgetIdForHost(String packageName, int hostId) { + public static int allocateAppWidgetIdForSystem(int hostId) { checkCallerIsSystem(); try { if (sService == null) { bindService(); } + Context systemContext = + (Context) ActivityThread.currentActivityThread().getSystemContext(); + String packageName = systemContext.getPackageName(); return sService.allocateAppWidgetId(packageName, hostId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -240,7 +244,7 @@ public class AppWidgetHost { * Stop listening to changes for this AppWidget. * @hide */ - public static void deleteAppWidgetIdForHost(int appWidgetId) { + public static void deleteAppWidgetIdForSystem(int appWidgetId) { checkCallerIsSystem(); try { if (sService == null) { 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..5986dcd 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,53 @@ 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"); } + + // STOPSHIP: disable the multi-user permission checks until a solid fix for the + // content provider / observer case is in place. + /* + 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 +197,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 +237,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 +268,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 +277,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 +610,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 +640,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 +709,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 +736,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 +745,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 +779,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 +830,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/Intent.java b/core/java/android/content/Intent.java index d3b8648..383739b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1254,7 +1254,9 @@ public class Intent implements Parcelable, Cloneable { * Activity Action: Launch application installer. * <p> * Input: The data must be a content: or file: URI at which the application - * can be retrieved. You can optionally supply + * can be retrieved. As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, + * you can also use "package:<package-name>" to install an application for the + * current user that is already installed for another user. You can optionally supply * {@link #EXTRA_INSTALLER_PACKAGE_NAME}, {@link #EXTRA_NOT_UNKNOWN_SOURCE}, * {@link #EXTRA_ALLOW_REPLACE}, and {@link #EXTRA_RETURN_RESULT}. * <p> @@ -2376,6 +2378,16 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED"; + /** + * Broadcast sent to the system when a user's information changes. Carries an extra + * {@link #EXTRA_USER_HANDLE} to indicate which user's information changed. + * This is only sent to registered receivers, not manifest receivers. It is sent to the user + * whose information has changed. + * @hide + */ + public static final String ACTION_USER_INFO_CHANGED = + "android.intent.action.USER_INFO_CHANGED"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). 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/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 7164713..51b8d25 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -19,7 +19,7 @@ package android.content.res; import android.content.pm.ActivityInfo; import android.os.Parcel; import android.os.Parcelable; -import android.util.LocaleUtil; +import android.text.TextUtils; import android.view.View; import java.util.Locale; @@ -1169,7 +1169,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration public void setLayoutDirection(Locale locale) { // There is a "1" difference between the configuration values for // layout direction and View constants for layout direction, just add "1". - final int layoutDirection = 1 + LocaleUtil.getLayoutDirectionFromLocale(locale); + final int layoutDirection = 1 + TextUtils.getLayoutDirectionFromLocale(locale); screenLayout = (screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK)| (layoutDirection << SCREENLAYOUT_LAYOUTDIR_SHIFT); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index b316f23..492e5e9 100755 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1554,6 +1554,8 @@ public class Resources { public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics, CompatibilityInfo compat) { if (mSystem != null) { + // TODO: Remove once b/7094175 is fixed + Slog.v(TAG, "updateSystemConfiguration: b/7094175 config=" + config); mSystem.updateConfiguration(config, metrics, compat); //Log.i(TAG, "Updated system resources " + mSystem // + ": " + mSystem.getConfiguration()); diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 4347e75..58a0f13 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -160,6 +160,10 @@ public final class DisplayManager { /** * Connects to a Wifi display. * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. + * <p> + * Automatically remembers the display after a successful connection, if not + * already remembered. + * </p> * * @param deviceAddress The MAC address of the device to which we should connect. * @hide @@ -178,6 +182,36 @@ public final class DisplayManager { } /** + * Renames a Wifi display. + * <p> + * The display must already be remembered for this call to succeed. In other words, + * we must already have successfully connected to the display at least once and then + * not forgotten it. + * </p> + * + * @param deviceAddress The MAC address of the device to rename. + * @param alias The alias name by which to remember the device, or null + * or empty if no alias should be used. + * @hide + */ + public void renameWifiDisplay(String deviceAddress, String alias) { + mGlobal.renameWifiDisplay(deviceAddress, alias); + } + + /** + * Forgets a previously remembered Wifi display. + * <p> + * Automatically disconnects from the display if currently connected to it. + * </p> + * + * @param deviceAddress The MAC address of the device to forget. + * @hide + */ + public void forgetWifiDisplay(String deviceAddress) { + mGlobal.forgetWifiDisplay(deviceAddress); + } + + /** * Gets the current Wifi display status. * Watch for changes in the status by registering a broadcast receiver for * {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED}. diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 14b5440..a858681 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -281,6 +281,31 @@ public final class DisplayManagerGlobal { } } + public void renameWifiDisplay(String deviceAddress, String alias) { + if (deviceAddress == null) { + throw new IllegalArgumentException("deviceAddress must not be null"); + } + + try { + mDm.renameWifiDisplay(deviceAddress, alias); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to rename Wifi display " + deviceAddress + + " with alias " + alias + ".", ex); + } + } + + public void forgetWifiDisplay(String deviceAddress) { + if (deviceAddress == null) { + throw new IllegalArgumentException("deviceAddress must not be null"); + } + + try { + mDm.forgetWifiDisplay(deviceAddress); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to forget Wifi display.", ex); + } + } + public WifiDisplayStatus getWifiDisplayStatus() { try { return mDm.getWifiDisplayStatus(); diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 36a9a7f..4b6fb53 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -38,5 +38,11 @@ interface IDisplayManager { void disconnectWifiDisplay(); // Requires CONFIGURE_WIFI_DISPLAY permission. + void renameWifiDisplay(String address, String alias); + + // Requires CONFIGURE_WIFI_DISPLAY permission. + void forgetWifiDisplay(String address); + + // Requires CONFIGURE_WIFI_DISPLAY permission. WifiDisplayStatus getWifiDisplayStatus(); } diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java index e51e97e..0138b1c 100644 --- a/core/java/android/hardware/display/WifiDisplay.java +++ b/core/java/android/hardware/display/WifiDisplay.java @@ -19,6 +19,8 @@ package android.hardware.display; import android.os.Parcel; import android.os.Parcelable; +import libcore.util.Objects; + /** * Describes the properties of a Wifi display. * <p> @@ -30,6 +32,7 @@ import android.os.Parcelable; public final class WifiDisplay implements Parcelable { private final String mDeviceAddress; private final String mDeviceName; + private final String mDeviceAlias; public static final WifiDisplay[] EMPTY_ARRAY = new WifiDisplay[0]; @@ -37,7 +40,8 @@ public final class WifiDisplay implements Parcelable { public WifiDisplay createFromParcel(Parcel in) { String deviceAddress = in.readString(); String deviceName = in.readString(); - return new WifiDisplay(deviceAddress, deviceName); + String deviceAlias = in.readString(); + return new WifiDisplay(deviceAddress, deviceName, deviceAlias); } public WifiDisplay[] newArray(int size) { @@ -45,7 +49,7 @@ public final class WifiDisplay implements Parcelable { } }; - public WifiDisplay(String deviceAddress, String deviceName) { + public WifiDisplay(String deviceAddress, String deviceName, String deviceAlias) { if (deviceAddress == null) { throw new IllegalArgumentException("deviceAddress must not be null"); } @@ -55,6 +59,7 @@ public final class WifiDisplay implements Parcelable { mDeviceAddress = deviceAddress; mDeviceName = deviceName; + mDeviceAlias = deviceAlias; } /** @@ -71,6 +76,25 @@ public final class WifiDisplay implements Parcelable { return mDeviceName; } + /** + * Gets the user-specified alias of the Wifi display device, or null if none. + * <p> + * The alias should be used in the UI whenever available. It is the value + * provided by the user when renaming the device. + * </p> + */ + public String getDeviceAlias() { + return mDeviceAlias; + } + + /** + * Gets the name to show in the UI. + * Uses the device alias if available, otherwise uses the device name. + */ + public String getFriendlyDisplayName() { + return mDeviceAlias != null ? mDeviceAlias : mDeviceName; + } + @Override public boolean equals(Object o) { return o instanceof WifiDisplay && equals((WifiDisplay)o); @@ -79,7 +103,8 @@ public final class WifiDisplay implements Parcelable { public boolean equals(WifiDisplay other) { return other != null && mDeviceAddress.equals(other.mDeviceAddress) - && mDeviceName.equals(other.mDeviceName); + && mDeviceName.equals(other.mDeviceName) + && Objects.equal(mDeviceAlias, other.mDeviceAlias); } @Override @@ -92,6 +117,7 @@ public final class WifiDisplay implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(mDeviceAddress); dest.writeString(mDeviceName); + dest.writeString(mDeviceAlias); } @Override @@ -102,6 +128,10 @@ public final class WifiDisplay implements Parcelable { // For debugging purposes only. @Override public String toString() { - return mDeviceName + " (" + mDeviceAddress + ")"; + String result = mDeviceName + " (" + mDeviceAddress + ")"; + if (mDeviceAlias != null) { + result += ", alias " + mDeviceAlias; + } + return result; } } diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java index d5fe45d..f7e72c4 100644 --- a/core/java/android/hardware/display/WifiDisplayStatus.java +++ b/core/java/android/hardware/display/WifiDisplayStatus.java @@ -23,7 +23,7 @@ import java.util.Arrays; /** * Describes the current global state of Wifi display connectivity, including the - * currently connected display and all known displays. + * currently connected display and all available or remembered displays. * <p> * This object is immutable. * </p> @@ -31,22 +31,37 @@ import java.util.Arrays; * @hide */ public final class WifiDisplayStatus implements Parcelable { - private final boolean mEnabled; + private final int mFeatureState; private final int mScanState; private final int mActiveDisplayState; private final WifiDisplay mActiveDisplay; - private final WifiDisplay[] mKnownDisplays; - + private final WifiDisplay[] mAvailableDisplays; + private final WifiDisplay[] mRememberedDisplays; + + /** Feature state: Wifi display is not available on this device. */ + public static final int FEATURE_STATE_UNAVAILABLE = 0; + /** Feature state: Wifi display is disabled, probably because Wifi is disabled. */ + public static final int FEATURE_STATE_DISABLED = 1; + /** Feature state: Wifi display is turned off in settings. */ + public static final int FEATURE_STATE_OFF = 2; + /** Feature state: Wifi display is turned on in settings. */ + public static final int FEATURE_STATE_ON = 3; + + /** Scan state: Not currently scanning. */ public static final int SCAN_STATE_NOT_SCANNING = 0; + /** Scan state: Currently scanning. */ public static final int SCAN_STATE_SCANNING = 1; + /** Display state: Not connected. */ public static final int DISPLAY_STATE_NOT_CONNECTED = 0; + /** Display state: Connecting to active display. */ public static final int DISPLAY_STATE_CONNECTING = 1; + /** Display state: Connected to active display. */ public static final int DISPLAY_STATE_CONNECTED = 2; public static final Creator<WifiDisplayStatus> CREATOR = new Creator<WifiDisplayStatus>() { public WifiDisplayStatus createFromParcel(Parcel in) { - boolean enabled = (in.readInt() != 0); + int featureState = in.readInt(); int scanState = in.readInt(); int activeDisplayState= in.readInt(); @@ -55,13 +70,18 @@ public final class WifiDisplayStatus implements Parcelable { activeDisplay = WifiDisplay.CREATOR.createFromParcel(in); } - WifiDisplay[] knownDisplays = WifiDisplay.CREATOR.newArray(in.readInt()); - for (int i = 0; i < knownDisplays.length; i++) { - knownDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in); + WifiDisplay[] availableDisplays = WifiDisplay.CREATOR.newArray(in.readInt()); + for (int i = 0; i < availableDisplays.length; i++) { + availableDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in); + } + + WifiDisplay[] rememberedDisplays = WifiDisplay.CREATOR.newArray(in.readInt()); + for (int i = 0; i < rememberedDisplays.length; i++) { + rememberedDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in); } - return new WifiDisplayStatus(enabled, scanState, activeDisplayState, - activeDisplay, knownDisplays); + return new WifiDisplayStatus(featureState, scanState, activeDisplayState, + activeDisplay, availableDisplays, rememberedDisplays); } public WifiDisplayStatus[] newArray(int size) { @@ -70,33 +90,38 @@ public final class WifiDisplayStatus implements Parcelable { }; public WifiDisplayStatus() { - this(false, SCAN_STATE_NOT_SCANNING, DISPLAY_STATE_NOT_CONNECTED, - null, WifiDisplay.EMPTY_ARRAY); + this(FEATURE_STATE_UNAVAILABLE, SCAN_STATE_NOT_SCANNING, DISPLAY_STATE_NOT_CONNECTED, + null, WifiDisplay.EMPTY_ARRAY, WifiDisplay.EMPTY_ARRAY); } - public WifiDisplayStatus(boolean enabled, int scanState, int activeDisplayState, - WifiDisplay activeDisplay, WifiDisplay[] knownDisplays) { - if (knownDisplays == null) { - throw new IllegalArgumentException("knownDisplays must not be null"); + public WifiDisplayStatus(int featureState, int scanState, + int activeDisplayState, WifiDisplay activeDisplay, + WifiDisplay[] availableDisplays, WifiDisplay[] rememberedDisplays) { + if (availableDisplays == null) { + throw new IllegalArgumentException("availableDisplays must not be null"); + } + if (rememberedDisplays == null) { + throw new IllegalArgumentException("rememberedDisplays must not be null"); } - mEnabled = enabled; + mFeatureState = featureState; mScanState = scanState; mActiveDisplayState = activeDisplayState; mActiveDisplay = activeDisplay; - mKnownDisplays = knownDisplays; + mAvailableDisplays = availableDisplays; + mRememberedDisplays = rememberedDisplays; } /** - * Returns true if the Wifi display feature is enabled and available for use. + * Returns the state of the Wifi display feature on this device. * <p> - * The value of this property reflects whether Wifi and Wifi P2P functions - * are enabled. Enablement is not directly controllable by the user at this - * time, except indirectly such as by turning off Wifi altogether. + * The value of this property reflects whether the device supports the Wifi display, + * whether it has been enabled by the user and whether the prerequisites for + * connecting to displays have been met. * </p> */ - public boolean isEnabled() { - return mEnabled; + public int getFeatureState() { + return mFeatureState; } /** @@ -127,15 +152,29 @@ public final class WifiDisplayStatus implements Parcelable { } /** - * Gets the list of all known Wifi displays, never null. + * Gets the list of all available Wifi displays as reported by the most recent + * scan, never null. + * <p> + * Some of these displays may already be remembered, others may be unknown. + * </p> */ - public WifiDisplay[] getKnownDisplays() { - return mKnownDisplays; + public WifiDisplay[] getAvailableDisplays() { + return mAvailableDisplays; + } + + /** + * Gets the list of all remembered Wifi displays, never null. + * <p> + * Not all remembered displays will necessarily be available. + * </p> + */ + public WifiDisplay[] getRememberedDisplays() { + return mRememberedDisplays; } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mEnabled ? 1 : 0); + dest.writeInt(mFeatureState); dest.writeInt(mScanState); dest.writeInt(mActiveDisplayState); @@ -146,8 +185,13 @@ public final class WifiDisplayStatus implements Parcelable { dest.writeInt(0); } - dest.writeInt(mKnownDisplays.length); - for (WifiDisplay display : mKnownDisplays) { + dest.writeInt(mAvailableDisplays.length); + for (WifiDisplay display : mAvailableDisplays) { + display.writeToParcel(dest, flags); + } + + dest.writeInt(mRememberedDisplays.length); + for (WifiDisplay display : mRememberedDisplays) { display.writeToParcel(dest, flags); } } @@ -160,11 +204,12 @@ public final class WifiDisplayStatus implements Parcelable { // For debugging purposes only. @Override public String toString() { - return "WifiDisplayStatus{enabled=" + mEnabled + return "WifiDisplayStatus{featureState=" + mFeatureState + ", scanState=" + mScanState + ", activeDisplayState=" + mActiveDisplayState + ", activeDisplay=" + mActiveDisplay - + ", knownDisplays=" + Arrays.toString(mKnownDisplays) + + ", availableDisplays=" + Arrays.toString(mAvailableDisplays) + + ", rememberedDisplays=" + Arrays.toString(mRememberedDisplays) + "}"; } } diff --git a/core/java/android/net/CaptivePortalTracker.java b/core/java/android/net/CaptivePortalTracker.java index aa392d0..8218e37 100644 --- a/core/java/android/net/CaptivePortalTracker.java +++ b/core/java/android/net/CaptivePortalTracker.java @@ -251,7 +251,7 @@ public class CaptivePortalTracker { if (visible) { CharSequence title = r.getString(R.string.wifi_available_sign_in, 0); - CharSequence details = r.getString(R.string.wifi_available_sign_in_detailed, + CharSequence details = r.getString(R.string.network_available_sign_in_detailed, mNetworkInfo.getExtraInfo()); Notification notification = new Notification(); diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index fb7a4f8..446bbf0 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -21,6 +21,7 @@ import android.os.Parcelable; import android.os.SystemClock; import android.util.SparseBooleanArray; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.Objects; import java.io.CharArrayWriter; @@ -608,13 +609,13 @@ public class NetworkStats implements Parcelable { * Return all rows except those attributed to the requested UID; doesn't * mutate the original structure. */ - public NetworkStats withoutUid(int uid) { + public NetworkStats withoutUids(int[] uids) { final NetworkStats stats = new NetworkStats(elapsedRealtime, 10); Entry entry = new Entry(); for (int i = 0; i < size; i++) { entry = getValues(i, entry); - if (entry.uid != uid) { + if (!ArrayUtils.contains(uids, entry.uid)) { stats.addValues(entry); } } diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index 2703f1d..27cabef 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -300,9 +300,10 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { * null if no protocol was negotiated. * * @param socket a socket created by this factory. + * @throws IllegalArgumentException if the socket was not created by this factory. */ public byte[] getNpnSelectedProtocol(Socket socket) { - return ((OpenSSLSocketImpl) socket).getNpnSelectedProtocol(); + return castToOpenSSLSocket(socket).getNpnSelectedProtocol(); } /** @@ -316,6 +317,38 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { mInsecureFactory = null; } + /** + * Enables <a href="http://tools.ietf.org/html/rfc5077#section-3.2">session ticket</a> + * support on the given socket. + * + * @param socket a socket created by this factory + * @param useSessionTickets {@code true} to enable session ticket support on this socket. + * @throws IllegalArgumentException if the socket was not created by this factory. + */ + public void setUseSessionTickets(Socket socket, boolean useSessionTickets) { + castToOpenSSLSocket(socket).setUseSessionTickets(useSessionTickets); + } + + /** + * Turns on <a href="http://tools.ietf.org/html/rfc6066#section-3">Server + * Name Indication (SNI)</a> on a given socket. + * + * @param socket a socket created by this factory. + * @param hostName the desired SNI hostname, null to disable. + * @throws IllegalArgumentException if the socket was not created by this factory. + */ + public void setHostname(Socket socket, String hostName) { + castToOpenSSLSocket(socket).setHostname(hostName); + } + + private static OpenSSLSocketImpl castToOpenSSLSocket(Socket socket) { + if (!(socket instanceof OpenSSLSocketImpl)) { + throw new IllegalArgumentException("Socket not created by this factory: " + + socket); + } + + return (OpenSSLSocketImpl) socket; + } /** * {@inheritDoc} diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java new file mode 100644 index 0000000..64eacbc --- /dev/null +++ b/core/java/android/net/http/X509TrustManagerExtensions.java @@ -0,0 +1,66 @@ +/* + * 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.net.http; + +import org.apache.harmony.xnet.provider.jsse.TrustManagerImpl; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.List; + +import javax.net.ssl.X509TrustManager; + +/** + * X509TrustManager wrapper exposing Android-added features. + * + * <p> The checkServerTrusted method allows callers to perform additional + * verification of certificate chains after they have been successfully + * verified by the platform.</p> + */ +public class X509TrustManagerExtensions { + + TrustManagerImpl mDelegate; + + /** + * Constructs a new X509TrustManagerExtensions wrapper. + * + * @param tm A {@link X509TrustManager} as returned by TrustManagerFactory.getInstance(); + * @throws IllegalArgumentException If tm is an unsupported TrustManager type. + */ + public X509TrustManagerExtensions(X509TrustManager tm) throws IllegalArgumentException { + if (tm instanceof TrustManagerImpl) { + mDelegate = (TrustManagerImpl) tm; + } else { + throw new IllegalArgumentException("tm is not a supported type of X509TrustManager"); + } + } + + /** + * Verifies the given certificate chain. + * + * <p>See {@link X509TrustManager#checkServerTrusted(X509Certificate[], String)} for a + * description of the chain and authType parameters. The final parameter, host, should be the + * hostname of the server.</p> + * + * @throws CertificateException if the chain does not verify correctly. + * @return the properly ordered chain used for verification as a list of X509Certificates. + */ + public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType, + String host) throws CertificateException { + return mDelegate.checkServerTrusted(chain, authType, host); + } +} diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 1607b96..364004b 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -32,6 +32,7 @@ public class Environment { private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE"; private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET"; + private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE"; /** {@hide} */ public static String DIRECTORY_ANDROID = "Android"; @@ -88,21 +89,30 @@ public class Environment { private final File mExternalStorageAndroidData; private final File mExternalStorageAndroidMedia; private final File mExternalStorageAndroidObb; + private final File mMediaStorage; public UserEnvironment(int userId) { // See storage config details at http://source.android.com/tech/storage/ String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE); String rawEmulatedStorageTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET); + String rawMediaStorage = System.getenv(ENV_MEDIA_STORAGE); + if (TextUtils.isEmpty(rawMediaStorage)) { + rawMediaStorage = "/data/media"; + } if (!TextUtils.isEmpty(rawEmulatedStorageTarget)) { // Device has emulated storage; external storage paths should have // userId burned into them. + final String rawUserId = Integer.toString(userId); final File emulatedBase = new File(rawEmulatedStorageTarget); + final File mediaBase = new File(rawMediaStorage); // /storage/emulated/0 - mExternalStorage = buildPath(emulatedBase, Integer.toString(userId)); + mExternalStorage = buildPath(emulatedBase, rawUserId); // /storage/emulated/obb mExternalStorageAndroidObb = buildPath(emulatedBase, "obb"); + // /data/media/0 + mMediaStorage = buildPath(mediaBase, rawUserId); } else { // Device has physical external storage; use plain paths. @@ -115,6 +125,8 @@ public class Environment { mExternalStorage = new File(rawExternalStorage); // /storage/sdcard0/Android/obb mExternalStorageAndroidObb = buildPath(mExternalStorage, DIRECTORY_ANDROID, "obb"); + // /data/media + mMediaStorage = new File(rawMediaStorage); } mExternalStorageAndroidData = buildPath(mExternalStorage, DIRECTORY_ANDROID, "data"); @@ -152,6 +164,10 @@ public class Environment { public File getExternalStorageAppCacheDirectory(String packageName) { return new File(new File(mExternalStorageAndroidData, packageName), "cache"); } + + public File getMediaStorageDirectory() { + return mMediaStorage; + } } /** @@ -198,7 +214,8 @@ public class Environment { * @hide */ public static File getMediaStorageDirectory() { - return MEDIA_STORAGE_DIRECTORY; + throwIfSystem(); + return sCurrentUser.getMediaStorageDirectory(); } /** @@ -231,10 +248,6 @@ public class Environment { private static final File SECURE_DATA_DIRECTORY = getDirectory("ANDROID_SECURE_DATA", "/data/secure"); - /** @hide */ - private static final File MEDIA_STORAGE_DIRECTORY - = getDirectory("MEDIA_STORAGE", "/data/media"); - private static final File DOWNLOAD_CACHE_DIRECTORY = getDirectory("DOWNLOAD_CACHE", "/cache"); /** diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index c7a8493..0798913 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -19,6 +19,7 @@ package android.os; import android.os.ParcelFileDescriptor; import android.content.pm.UserInfo; +import android.graphics.Bitmap; /** * {@hide} @@ -27,7 +28,8 @@ interface IUserManager { UserInfo createUser(in String name, int flags); boolean removeUser(int userHandle); void setUserName(int userHandle, String name); - ParcelFileDescriptor setUserIcon(int userHandle); + void setUserIcon(int userHandle, in Bitmap icon); + Bitmap getUserIcon(int userHandle); List<UserInfo> getUsers(); UserInfo getUserInfo(int userHandle); void setGuestEnabled(boolean enable); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index de8e78c..b532966 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -18,6 +18,8 @@ package android.os; import com.android.internal.R; import android.content.Context; import android.content.pm.UserInfo; +import android.graphics.Bitmap; +import android.content.res.Resources; import android.util.Log; import java.util.List; @@ -40,8 +42,9 @@ public class UserManager { /** * Returns whether the system supports multiple users. * @return true if multiple users can be created, false if it is a single user device. + * @hide */ - public boolean supportsMultipleUsers() { + public static boolean supportsMultipleUsers() { return getMaxSupportedUsers() > 1; } @@ -55,8 +58,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() { @@ -151,16 +155,30 @@ public class UserManager { } /** - * Returns a file descriptor for the user's photo. PNG data can be written into this file. + * Sets the user's photo. * @param userHandle the user for whom to change the photo. - * @return a {@link ParcelFileDescriptor} to which to write the photo. + * @param icon the bitmap to set as the photo. * @hide */ - public ParcelFileDescriptor setUserIcon(int userHandle) { + public void setUserIcon(int userHandle, Bitmap icon) { try { - return mService.setUserIcon(userHandle); + mService.setUserIcon(userHandle, icon); } catch (RemoteException re) { Log.w(TAG, "Could not set the user icon ", re); + } + } + + /** + * Returns a file descriptor for the user's photo. PNG data can be read from this file. + * @param userHandle the user whose photo we want to read. + * @return a {@link Bitmap} of the user's photo, or null if there's no photo. + * @hide + */ + public Bitmap getUserIcon(int userHandle) { + try { + return mService.getUserIcon(userHandle); + } catch (RemoteException re) { + Log.w(TAG, "Could not get the user icon ", re); return null; } } @@ -215,8 +233,9 @@ public class UserManager { * @hide * @return a value greater than or equal to 1 */ - public int getMaxSupportedUsers() { - return mContext.getResources().getInteger(R.integer.config_multiuserMaximumUsers); + public static int getMaxSupportedUsers() { + return SystemProperties.getInt("fw.max_users", + Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers)); } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a182234..bb118b2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -214,6 +214,21 @@ public final class Settings { "android.settings.BLUETOOTH_SETTINGS"; /** + * Activity Action: Show settings to allow configuration of Wifi Displays. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_WIFI_DISPLAY_SETTINGS = + "android.settings.WIFI_DISPLAY_SETTINGS"; + + /** * Activity Action: Show settings to allow configuration of date and time. * <p> * In some cases, a matching Activity may not exist, so ensure you @@ -1338,7 +1353,7 @@ public final class Settings { /** @deprecated - Do not use */ @Deprecated public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) { - /* intentionally empty */ + setShowGTalkServiceStatusForUser(cr, flag, UserHandle.myUserId()); } /** @@ -5540,6 +5555,13 @@ public final class Settings { "web_autofill_query_url"; /** + * Whether Wifi display is enabled/disabled + * 0=disabled. 1=enabled. + * @hide + */ + public static final String WIFI_DISPLAY_ON = "wifi_display_on"; + + /** * Whether to notify the user of open networks. * <p> * If not connected and the scan results have an open network, we will @@ -5614,6 +5636,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/service/dreams/Dream.java b/core/java/android/service/dreams/Dream.java index ba2ac67..4a23d39 100644 --- a/core/java/android/service/dreams/Dream.java +++ b/core/java/android/service/dreams/Dream.java @@ -22,7 +22,6 @@ import android.content.Intent; import android.graphics.drawable.ColorDrawable; import android.os.Handler; import android.os.IBinder; -import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; import android.view.ActionMode; @@ -40,12 +39,16 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.policy.PolicyManager; /** - * Extend this class to implement a custom screensaver. + * Extend this class to implement a custom Dream. + * + * <p>Dreams are interactive screensavers launched when a charging device is idle, or docked in a + * desk dock. Dreams provide another modality for apps to express themselves, tailored for + * an exhibition/lean-back experience.</p> */ public class Dream extends Service implements Window.Callback { private final static boolean DEBUG = true; - private final static String TAG = "Dream"; - + private final String TAG = Dream.class.getSimpleName() + "[" + getClass().getSimpleName() + "]"; + /** * The {@link Intent} that must be declared as handled by the service. * To be supported, the service must also require the @@ -60,28 +63,43 @@ public class Dream extends Service implements Window.Callback { public static final String METADATA_NAME_CONFIG_ACTIVITY = "android.service.dreams.config_activity"; - private Window mWindow; + /** + * Broadcast Action: Sent after the system starts dreaming. + * + * <p class="note">This is a protected intent that can only be sent by the system. + * It is only sent to registered receivers.</p> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DREAMING_STARTED = "android.intent.action.DREAMING_STARTED"; + /** + * Broadcast Action: Sent after the system stops dreaming. + * + * <p class="note">This is a protected intent that can only be sent by the system. + * It is only sent to registered receivers.</p> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DREAMING_STOPPED = "android.intent.action.DREAMING_STOPPED"; + + private final Handler mHandler = new Handler(); + private IBinder mWindowToken; + private Window mWindow; private WindowManager mWindowManager; private IDreamManager mSandman; - private boolean mInteractive; - - final Handler mHandler = new Handler(); - - boolean mFinished = false; + private boolean mFinished; // begin Window.Callback methods @Override public boolean dispatchKeyEvent(KeyEvent event) { // TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on keyEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on keyEvent"); + safelyFinish(); return true; } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - if (DEBUG) Slog.v(TAG, "finishing on back key"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on back key"); + safelyFinish(); return true; } return mWindow.superDispatchKeyEvent(event); @@ -90,8 +108,8 @@ public class Dream extends Service implements Window.Callback { @Override public boolean dispatchKeyShortcutEvent(KeyEvent event) { if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on keyShortcutEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on keyShortcutEvent"); + safelyFinish(); return true; } return mWindow.superDispatchKeyShortcutEvent(event); @@ -102,8 +120,8 @@ public class Dream extends Service implements Window.Callback { // TODO: create more flexible version of mInteractive that allows clicks // but finish()es on any other kind of activity if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on touchEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on touchEvent"); + safelyFinish(); return true; } return mWindow.superDispatchTouchEvent(event); @@ -112,8 +130,8 @@ public class Dream extends Service implements Window.Callback { @Override public boolean dispatchTrackballEvent(MotionEvent event) { if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on trackballEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on trackballEvent"); + safelyFinish(); return true; } return mWindow.superDispatchTrackballEvent(event); @@ -122,8 +140,8 @@ public class Dream extends Service implements Window.Callback { @Override public boolean dispatchGenericMotionEvent(MotionEvent event) { if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on genericMotionEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on genericMotionEvent"); + safelyFinish(); return true; } return mWindow.superDispatchGenericMotionEvent(event); @@ -212,30 +230,9 @@ public class Dream extends Service implements Window.Callback { public Window getWindow() { return mWindow; } - - /** - * Called when this Dream is constructed. Place your initialization here. - * - * Subclasses must call through to the superclass implementation. - */ - @Override - public void onCreate() { - super.onCreate(); - - if (DEBUG) Slog.v(TAG, "Dream created on thread " + Thread.currentThread().getId()); - - mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams")); - } - - /** - * Called when this Dream is started. - */ - public void onStart() { - // hook for subclasses - } /** - * Inflate a layout resource and set it to be the content view for this Dream. + * Inflates a layout resource and set it to be the content view for this Dream. * Behaves similarly to {@link android.app.Activity#setContentView(int)}. * * @param layoutResID Resource ID to be inflated. @@ -248,7 +245,7 @@ public class Dream extends Service implements Window.Callback { } /** - * Set a view to be the content view for this Dream. + * Sets a view to be the content view for this Dream. * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)}, * including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view. * @@ -262,7 +259,7 @@ public class Dream extends Service implements Window.Callback { } /** - * Set a view to be the content view for this Dream. + * Sets a view to be the content view for this Dream. * Behaves similarly to * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}. * @@ -277,7 +274,7 @@ public class Dream extends Service implements Window.Callback { } /** - * Add a view to the Dream's window, leaving other content views in place. + * Adds a view to the Dream's window, leaving other content views in place. * * @param view The desired content to display. * @param params Layout parameters for the view. @@ -285,21 +282,27 @@ public class Dream extends Service implements Window.Callback { public void addContentView(View view, ViewGroup.LayoutParams params) { getWindow().addContentView(view, params); } - + /** - * @param mInteractive the mInteractive to set + * Marks this dream as interactive to receive input events. + * + * <p>Non-interactive dreams (default) will dismiss on the first input event.</p> + * + * <p>Interactive dreams should call {@link #finish()} to dismiss themselves.</p> + * + * @param interactive True if this dream will handle input events. */ - public void setInteractive(boolean mInteractive) { - this.mInteractive = mInteractive; + public void setInteractive(boolean interactive) { + mInteractive = interactive; } /** - * @return the mInteractive + * Returns whether or not this dream is interactive. */ public boolean isInteractive() { return mInteractive; } - + /** Convenience method for setting View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. */ protected void lightsOut() { // turn the lights down low @@ -319,14 +322,29 @@ public class Dream extends Service implements Window.Callback { public View findViewById(int id) { return getWindow().findViewById(id); } - + /** - * Called when this Dream is being removed from the screen and stopped. + * Called when this Dream is constructed. Place your initialization here. + * + * Subclasses must call through to the superclass implementation. */ @Override - public void onDestroy() { - super.onDestroy(); - mWindowManager.removeView(mWindow.getDecorView()); + public void onCreate() { + if (DEBUG) Slog.v(TAG, "onCreate() on thread " + Thread.currentThread().getId()); + super.onCreate(); + loadSandman(); + } + + /** + * Called when this Dream is started. + */ + public void onStart() { + // hook for subclasses + Slog.v(TAG, "called Dream.onStart()"); + } + + private void loadSandman() { + mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams")); } /** @@ -335,16 +353,21 @@ public class Dream extends Service implements Window.Callback { * @param windowToken Binder to attach to the window to allow access to the correct window type. * @hide */ - final /*package*/ void attach(IBinder windowToken) { - if (DEBUG) Slog.v(TAG, "Dream attached on thread " + Thread.currentThread().getId()); - + private final void attach(IBinder windowToken) { + if (DEBUG) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId()); + + if (mSandman == null) { + Slog.w(TAG, "No dream manager found, super.onCreate may not have been called"); + loadSandman(); + } + mWindowToken = windowToken; mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); mWindow.requestFeature(Window.FEATURE_NO_TITLE); mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); - if (DEBUG) Slog.v(TAG, "attaching window token: " + windowToken - + " to window of type " + WindowManager.LayoutParams.TYPE_DREAM); + if (DEBUG) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", + windowToken, WindowManager.LayoutParams.TYPE_DREAM)); WindowManager.LayoutParams lp = mWindow.getAttributes(); lp.type = WindowManager.LayoutParams.TYPE_DREAM; @@ -355,58 +378,105 @@ public class Dream extends Service implements Window.Callback { | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON ); mWindow.setAttributes(lp); - - //WindowManagerImpl.getDefault().addView(mWindow.getDecorView(), lp); - - if (DEBUG) Slog.v(TAG, "created and attached window: " + mWindow); + + if (DEBUG) Slog.v(TAG, "Created and attached window: " + mWindow); mWindow.setWindowManager(null, windowToken, "dream", true); mWindowManager = mWindow.getWindowManager(); - - // now make it visible + + // now make it visible (on the ui thread) mHandler.post(new Runnable(){ @Override public void run() { - if (DEBUG) Slog.v(TAG, "Dream window added on thread " + Thread.currentThread().getId()); + if (DEBUG) Slog.v(TAG, "Window added on thread " + Thread.currentThread().getId()); - getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + try { + getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + } catch (Throwable t) { + Slog.w("Crashed adding window view", t); + safelyFinish(); + return; + } // start it up - onStart(); + try { + onStart(); + } catch (Throwable t) { + Slog.w("Crashed in onStart()", t); + safelyFinish(); + } }}); } - + + private void safelyFinish() { + if (DEBUG) Slog.v(TAG, "safelyFinish()"); + try { + finish(); + } catch (Throwable t) { + Slog.w(TAG, "Crashed in safelyFinish()", t); + finishInternal(); + return; + } + + if (!mFinished) { + Slog.w(TAG, "Bad dream, did not call super.finish()"); + finishInternal(); + } + } + /** - * Stop the dream and wake up. - * - * After this method is called, the service will be stopped. + * Stops the dream, detaches from the window, and wakes up. + * + * Subclasses must call through to the superclass implementation. + * + * <p>After this method is called, the service will be stopped.</p> */ public void finish() { + if (DEBUG) Slog.v(TAG, "finish()"); + finishInternal(); + } + + private void finishInternal() { + if (DEBUG) Slog.v(TAG, "finishInternal() mFinished = " + mFinished); if (mFinished) return; try { - mSandman.awaken(); // assuming we were started by the DreamManager - stopSelf(); // if launched via any other means mFinished = true; - } catch (RemoteException ex) { - // sigh + + if (mSandman != null) { + mSandman.awakenSelf(mWindowToken); + } else { + Slog.w(TAG, "No dream manager found"); + } + stopSelf(); // if launched via any other means + + } catch (Throwable t) { + Slog.w(TAG, "Crashed in finishInternal()", t); } } - class IDreamServiceWrapper extends IDreamService.Stub { - public IDreamServiceWrapper() { - } + @Override + public void onDestroy() { + if (DEBUG) Slog.v(TAG, "onDestroy()"); + super.onDestroy(); - public void attach(IBinder windowToken) { - Dream.this.attach(windowToken); + if (DEBUG) Slog.v(TAG, "Removing window"); + try { + mWindowManager.removeView(mWindow.getDecorView()); + } catch (Throwable t) { + Slog.w(TAG, "Crashed removing window view", t); } } - /** - * Implement to return the implementation of the internal accessibility - * service interface. Subclasses should not override. - */ @Override public final IBinder onBind(Intent intent) { - return new IDreamServiceWrapper(); + if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent); + return new DreamServiceWrapper(); } + + private class DreamServiceWrapper extends IDreamService.Stub { + public void attach(IBinder windowToken) { + Dream.this.attach(windowToken); + } + } + } diff --git a/core/java/android/service/dreams/DreamManagerService.java b/core/java/android/service/dreams/DreamManagerService.java deleted file mode 100644 index 4aa1cbb..0000000 --- a/core/java/android/service/dreams/DreamManagerService.java +++ /dev/null @@ -1,247 +0,0 @@ -package android.service.dreams; - -import static android.provider.Settings.Secure.SCREENSAVER_COMPONENTS; -import static android.provider.Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT; -import java.io.FileDescriptor; -import java.io.PrintWriter; - -import android.app.ActivityManagerNative; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.os.Binder; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.UserHandle; -import android.provider.Settings; -import android.util.Slog; -import android.view.IWindowManager; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; - -/** - * - * @hide - * - */ - -public class DreamManagerService - extends IDreamManager.Stub - implements ServiceConnection -{ - private static final boolean DEBUG = true; - private static final String TAG = "DreamManagerService"; - - final Object mLock = new Object[0]; - - private Context mContext; - private IWindowManager mIWindowManager; - - private ComponentName mCurrentDreamComponent; - private IDreamService mCurrentDream; - private Binder mCurrentDreamToken; - private int mCurrentUserId; - - public DreamManagerService(Context context) { - if (DEBUG) Slog.v(TAG, "DreamManagerService startup"); - mContext = context; - mIWindowManager = WindowManagerGlobal.getWindowManagerService(); - } - - private void checkPermission(String permission) { - if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(permission)) { - throw new SecurityException("Access denied to process: " + Binder.getCallingPid() - + ", must have permission " + permission); - } - } - - // IDreamManager method - @Override - public void dream() { - ComponentName[] dreams = getDreamComponentsForUser(mCurrentUserId); - ComponentName name = dreams != null && dreams.length > 0 ? dreams[0] : null; - if (name != null) { - synchronized (mLock) { - final long ident = Binder.clearCallingIdentity(); - try { - bindDreamComponentL(name, false); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - } - - // IDreamManager method - @Override - public void setDreamComponents(ComponentName[] componentNames) { - Settings.Secure.putStringForUser(mContext.getContentResolver(), - SCREENSAVER_COMPONENTS, - componentsToString(componentNames), - UserHandle.getCallingUserId()); - } - - private static String componentsToString(ComponentName[] componentNames) { - StringBuilder names = new StringBuilder(); - if (componentNames != null) { - for (ComponentName componentName : componentNames) { - if (names.length() > 0) - names.append(','); - names.append(componentName.flattenToString()); - } - } - return names.toString(); - } - - private static ComponentName[] componentsFromString(String names) { - String[] namesArray = names.split(","); - ComponentName[] componentNames = new ComponentName[namesArray.length]; - for (int i = 0; i < namesArray.length; i++) - componentNames[i] = ComponentName.unflattenFromString(namesArray[i]); - return componentNames; - } - - // IDreamManager method - @Override - public ComponentName[] getDreamComponents() { - return getDreamComponentsForUser(UserHandle.getCallingUserId()); - } - - private ComponentName[] getDreamComponentsForUser(int userId) { - String names = Settings.Secure.getStringForUser(mContext.getContentResolver(), - SCREENSAVER_COMPONENTS, - userId); - return names == null ? null : componentsFromString(names); - } - - // IDreamManager method - @Override - public ComponentName getDefaultDreamComponent() { - String name = Settings.Secure.getStringForUser(mContext.getContentResolver(), - SCREENSAVER_DEFAULT_COMPONENT, - UserHandle.getCallingUserId()); - return name == null ? null : ComponentName.unflattenFromString(name); - } - - // IDreamManager method - @Override - public void testDream(ComponentName name) { - if (DEBUG) Slog.v(TAG, "startDream name=" + name - + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); -// checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); - synchronized (mLock) { - final long ident = Binder.clearCallingIdentity(); - try { - bindDreamComponentL(name, true); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - // IDreamManager method - @Override - public void awaken() { - if (DEBUG) Slog.v(TAG, "awaken()"); - synchronized (mLock) { - if (mCurrentDream != null) { - if (DEBUG) Slog.v(TAG, "disconnecting: " + mCurrentDreamComponent + " service: " + mCurrentDream); - mContext.unbindService(this); - mCurrentDream = null; - mCurrentDreamToken = null; - } - } - } - - // IDreamManager method - @Override - public boolean isDreaming() { - synchronized (mLock) { - return mCurrentDreamToken != null; - } - } - - public void bindDreamComponentL(ComponentName componentName, boolean test) { - if (DEBUG) Slog.v(TAG, "bindDreamComponent: componentName=" + componentName - + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); - - Intent intent = new Intent(Intent.ACTION_MAIN) - .setComponent(componentName) - .addFlags( - Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - ) - .putExtra("android.dreams.TEST", test); - - mCurrentDreamComponent = componentName; - mCurrentDreamToken = new Binder(); - try { - if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurrentDreamToken - + " for window type: " + WindowManager.LayoutParams.TYPE_DREAM); - mIWindowManager.addWindowToken(mCurrentDreamToken, - WindowManager.LayoutParams.TYPE_DREAM); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to add window token. Proceed at your own risk."); - } - - if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) { - Slog.w(TAG, "unable to bind service: " + componentName); - } - } - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) Slog.v(TAG, "connected to dream: " + name + " binder=" + service + " thread=" + Thread.currentThread().getId()); - - mCurrentDream = IDreamService.Stub.asInterface(service); - try { - if (DEBUG) Slog.v(TAG, "attaching with token:" + mCurrentDreamToken); - mCurrentDream.attach(mCurrentDreamToken); - } catch (RemoteException ex) { - Slog.w(TAG, "Unable to send window token to dream:" + ex); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - if (DEBUG) Slog.v(TAG, "disconnected: " + name + " service: " + mCurrentDream); - // Only happens in exceptional circumstances - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - pw.println("Dreamland:"); - pw.print(" component="); pw.println(mCurrentDreamComponent); - pw.print(" token="); pw.println(mCurrentDreamToken); - pw.print(" dream="); pw.println(mCurrentDream); - } - - public void systemReady() { - - // dream settings are kept per user, so keep track of current user - try { - mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id; - } catch (RemoteException e) { - Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); - } - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_USER_SWITCHED); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_USER_SWITCHED.equals(action)) { - mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - if (DEBUG) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house"); - } - }}, filter); - - if (DEBUG) Slog.v(TAG, "ready to dream!"); - } - -} diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index b6fcdf0..bd1c524 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -19,6 +19,7 @@ package android.service.dreams; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.content.ComponentName; +import android.os.IBinder; /** @hide */ interface IDreamManager { @@ -29,4 +30,5 @@ interface IDreamManager { ComponentName getDefaultDreamComponent(); void testDream(in ComponentName componentName); boolean isDreaming(); + void awakenSelf(in IBinder token); }
\ No newline at end of file diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java index bbaa173..df8c4c6 100644 --- a/core/java/android/text/TextDirectionHeuristics.java +++ b/core/java/android/text/TextDirectionHeuristics.java @@ -17,7 +17,6 @@ package android.text; -import android.util.LocaleUtil; import android.view.View; /** @@ -242,7 +241,7 @@ public class TextDirectionHeuristics { @Override protected boolean defaultIsRtl() { - final int dir = LocaleUtil.getLayoutDirectionFromLocale(java.util.Locale.getDefault()); + final int dir = TextUtils.getLayoutDirectionFromLocale(java.util.Locale.getDefault()); return (dir == View.LAYOUT_DIRECTION_RTL); } diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 987062a..1508d10 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -46,11 +46,14 @@ import android.text.style.URLSpan; import android.text.style.UnderlineSpan; import android.util.Printer; +import android.view.View; import com.android.internal.R; import com.android.internal.util.ArrayUtils; +import libcore.icu.ICU; import java.lang.reflect.Array; import java.util.Iterator; +import java.util.Locale; import java.util.regex.Pattern; public class TextUtils { @@ -1706,10 +1709,64 @@ public class TextUtils { return (int) (range & 0x00000000FFFFFFFFL); } + /** + * Return the layout direction for a given Locale + * + * @param locale the Locale for which we want the layout direction. Can be null. + * @return the layout direction. This may be one of: + * {@link android.view.View#LAYOUT_DIRECTION_LTR} or + * {@link android.view.View#LAYOUT_DIRECTION_RTL}. + * + * Be careful: this code will need to be updated when vertical scripts will be supported + */ + public static int getLayoutDirectionFromLocale(Locale locale) { + if (locale != null && !locale.equals(Locale.ROOT)) { + final String scriptSubtag = ICU.getScript(ICU.addLikelySubtags(locale.toString())); + if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale); + + if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) || + scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) { + return View.LAYOUT_DIRECTION_RTL; + } + } + + return View.LAYOUT_DIRECTION_LTR; + } + + /** + * Fallback algorithm to detect the locale direction. Rely on the fist char of the + * localized locale name. This will not work if the localized locale name is in English + * (this is the case for ICU 4.4 and "Urdu" script) + * + * @param locale + * @return the layout direction. This may be one of: + * {@link View#LAYOUT_DIRECTION_LTR} or + * {@link View#LAYOUT_DIRECTION_RTL}. + * + * Be careful: this code will need to be updated when vertical scripts will be supported + * + * @hide + */ + private static int getLayoutDirectionFromFirstChar(Locale locale) { + switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) { + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + return View.LAYOUT_DIRECTION_RTL; + + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + default: + return View.LAYOUT_DIRECTION_LTR; + } + } + private static Object sLock = new Object(); + private static char[] sTemp = null; private static String[] EMPTY_STRING_ARRAY = new String[]{}; private static final char ZWNBS_CHAR = '\uFEFF'; + + private static String ARAB_SCRIPT_SUBTAG = "Arab"; + private static String HEBR_SCRIPT_SUBTAG = "Hebr"; } diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 524f941..c36273e 100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -35,10 +35,18 @@ import java.text.SimpleDateFormat; Utility class for producing strings with formatted date/time. <p> - This class takes as inputs a format string and a representation of a date/time. - The format string controls how the output is generated. + Most callers should avoid supplying their own format strings to this + class' {@code format} methods and rely on the correctly localized ones + supplied by the system. This class' factory methods return + appropriately-localized {@link java.text.DateFormat} instances, suitable + for both formatting and parsing dates. For the canonical documentation + of format strings, see {@link java.text.SimpleDateFormat}. </p> <p> + The format methods in this class takes as inputs a format string and a representation of a date/time. + The format string controls how the output is generated. + This class only supports a subset of the full Unicode specification. + Use {@link java.text.SimpleDateFormat} if you need more. Formatting characters may be repeated in order to get more detailed representations of that field. For instance, the format character 'M' is used to represent the month. Depending on how many times that character is repeated @@ -152,7 +160,8 @@ public class DateFormat { public static final char MINUTE = 'm'; /** - This designator indicates the month of the year + This designator indicates the month of the year. See also + {@link #STANDALONE_MONTH}. Examples for September: M -> 9 @@ -163,6 +172,14 @@ public class DateFormat { public static final char MONTH = 'M'; /** + This designator indicates the standalone month of the year, + necessary in some format strings in some languages. For + example, Russian distinguishes between the "June" in + "June" and that in "June 2010". + */ + public static final char STANDALONE_MONTH = 'L'; + + /** This designator indicates the seconds of the minute. Examples for 7 seconds past the minute: @@ -374,7 +391,7 @@ public class DateFormat { index++; } - if (!foundMonth && (c == MONTH)) { + if (!foundMonth && (c == MONTH || c == STANDALONE_MONTH)) { foundMonth = true; order[index] = MONTH; index++; @@ -494,9 +511,10 @@ public class DateFormat { break; case MONTH: - replacement = getMonthString(inDate, count); + case STANDALONE_MONTH: + replacement = getMonthString(inDate, count, c); break; - + case SECONDS: replacement = zeroPad(inDate.get(Calendar.SECOND), count); break; @@ -527,14 +545,19 @@ public class DateFormat { return s.toString(); } - private static final String getMonthString(Calendar inDate, int count) { + private static final String getMonthString(Calendar inDate, int count, int kind) { + boolean standalone = (kind == STANDALONE_MONTH); int month = inDate.get(Calendar.MONTH); - if (count >= 4) - return DateUtils.getMonthString(month, DateUtils.LENGTH_LONG); - else if (count == 3) - return DateUtils.getMonthString(month, DateUtils.LENGTH_MEDIUM); - else { + if (count >= 4) { + return standalone + ? DateUtils.getStandaloneMonthString(month, DateUtils.LENGTH_LONG) + : DateUtils.getMonthString(month, DateUtils.LENGTH_LONG); + } else if (count == 3) { + return standalone + ? DateUtils.getStandaloneMonthString(month, DateUtils.LENGTH_MEDIUM) + : DateUtils.getMonthString(month, DateUtils.LENGTH_MEDIUM); + } else { // Calendar.JANUARY == 0, so add 1 to month. return zeroPad(month+1, count); } @@ -574,7 +597,8 @@ public class DateFormat { private static final String getYearString(Calendar inDate, int count) { int year = inDate.get(Calendar.YEAR); - return (count <= 2) ? zeroPad(year % 100, 2) : String.valueOf(year); + return (count <= 2) ? zeroPad(year % 100, 2) + : String.format(Locale.getDefault(), "%d", year); } private static final int appendQuotedText(SpannableStringBuilder s, int i, int len) { @@ -615,17 +639,6 @@ public class DateFormat { } private static final String zeroPad(int inValue, int inMinDigits) { - String val = String.valueOf(inValue); - - if (val.length() < inMinDigits) { - char[] buf = new char[inMinDigits]; - - for (int i = 0; i < inMinDigits; i++) - buf[i] = '0'; - - val.getChars(0, val.length(), buf, inMinDigits - val.length()); - val = new String(buf); - } - return val; + return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue); } } diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index 0babcc5..1060bd8 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -45,7 +45,6 @@ public class DateUtils private static final String FAST_FORMAT_HMMSS = "%1$d:%2$02d:%3$02d"; private static final String FAST_FORMAT_MMSS = "%1$02d:%2$02d"; - private static final char TIME_PADDING = '0'; private static final char TIME_SEPARATOR = ':'; @@ -648,33 +647,36 @@ public class DateUtils } } + private static void append(StringBuilder sb, long value, boolean pad, char zeroDigit) { + if (value < 10) { + if (pad) { + sb.append(zeroDigit); + } + } else { + sb.append((char) (zeroDigit + (value / 10))); + } + sb.append((char) (zeroDigit + (value % 10))); + } + /** - * Fast formatting of h:mm:ss + * Fast formatting of h:mm:ss. */ private static String formatElapsedTime(StringBuilder recycle, String format, long hours, long minutes, long seconds) { if (FAST_FORMAT_HMMSS.equals(format)) { + char zeroDigit = LocaleData.get(Locale.getDefault()).zeroDigit; + StringBuilder sb = recycle; if (sb == null) { sb = new StringBuilder(8); } else { sb.setLength(0); } - sb.append(hours); + append(sb, hours, false, zeroDigit); sb.append(TIME_SEPARATOR); - if (minutes < 10) { - sb.append(TIME_PADDING); - } else { - sb.append(toDigitChar(minutes / 10)); - } - sb.append(toDigitChar(minutes % 10)); + append(sb, minutes, true, zeroDigit); sb.append(TIME_SEPARATOR); - if (seconds < 10) { - sb.append(TIME_PADDING); - } else { - sb.append(toDigitChar(seconds / 10)); - } - sb.append(toDigitChar(seconds % 10)); + append(sb, seconds, true, zeroDigit); return sb.toString(); } else { return String.format(format, hours, minutes, seconds); @@ -682,40 +684,28 @@ public class DateUtils } /** - * Fast formatting of m:ss + * Fast formatting of mm:ss. */ private static String formatElapsedTime(StringBuilder recycle, String format, long minutes, long seconds) { if (FAST_FORMAT_MMSS.equals(format)) { + char zeroDigit = LocaleData.get(Locale.getDefault()).zeroDigit; + StringBuilder sb = recycle; if (sb == null) { sb = new StringBuilder(8); } else { sb.setLength(0); } - if (minutes < 10) { - sb.append(TIME_PADDING); - } else { - sb.append(toDigitChar(minutes / 10)); - } - sb.append(toDigitChar(minutes % 10)); + append(sb, minutes, false, zeroDigit); sb.append(TIME_SEPARATOR); - if (seconds < 10) { - sb.append(TIME_PADDING); - } else { - sb.append(toDigitChar(seconds / 10)); - } - sb.append(toDigitChar(seconds % 10)); + append(sb, seconds, true, zeroDigit); return sb.toString(); } else { return String.format(format, minutes, seconds); } } - private static char toDigitChar(long digit) { - return (char) (digit + '0'); - } - /** * Format a date / time such that if the then is on the same day as now, it shows * just the time and if it's a different day, it shows just the date. @@ -1387,6 +1377,14 @@ public class DateUtils String endMonthDayString = isInstant ? null : endDate.format(MONTH_DAY_FORMAT); String endYearString = isInstant ? null : endDate.format(YEAR_FORMAT); + String startStandaloneMonthString = startMonthString; + String endStandaloneMonthString = endMonthString; + // We need standalone months for these strings in Persian (fa): http://b/6811327 + if (!numericDate && !abbrevMonth && Locale.getDefault().getLanguage().equals("fa")) { + startStandaloneMonthString = startDate.format("%-B"); + endStandaloneMonthString = endDate.format("%-B"); + } + if (startMonthNum != endMonthNum) { // Same year, different month. // Example: "October 28 - November 3" @@ -1407,7 +1405,8 @@ public class DateUtils startWeekDayString, startMonthString, startMonthDayString, startYearString, startTimeString, endWeekDayString, endMonthString, endMonthDayString, - endYearString, endTimeString); + endYearString, endTimeString, + startStandaloneMonthString, endStandaloneMonthString); } if (startDay != endDay) { @@ -1426,7 +1425,8 @@ public class DateUtils startWeekDayString, startMonthString, startMonthDayString, startYearString, startTimeString, endWeekDayString, endMonthString, endMonthDayString, - endYearString, endTimeString); + endYearString, endTimeString, + startStandaloneMonthString, endStandaloneMonthString); } // Same start and end day diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index 45d5a70..5ef86b1 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -151,6 +151,9 @@ public class Time { private static String sDateTimeFormat; private static String sAm; private static String sPm; + private static char sZeroDigit; + + // Referenced by native code. private static String sDateCommand = "%a %b %e %H:%M:%S %Z %Y"; /** @@ -323,6 +326,7 @@ public class Time { sAm = localeData.amPm[0]; sPm = localeData.amPm[1]; + sZeroDigit = localeData.zeroDigit; sShortMonths = localeData.shortMonthNames; sLongMonths = localeData.longMonthNames; @@ -338,12 +342,32 @@ public class Time { sLocale = locale; } - return format1(format); + String result = format1(format); + if (sZeroDigit != '0') { + result = localizeDigits(result); + } + return result; } } native private String format1(String format); + // TODO: unify this with java.util.Formatter's copy. + private String localizeDigits(String s) { + int length = s.length(); + int offsetToLocalizedDigits = sZeroDigit - '0'; + StringBuilder result = new StringBuilder(length); + for (int i = 0; i < length; ++i) { + char ch = s.charAt(i); + if (ch >= '0' && ch <= '9') { + ch += offsetToLocalizedDigits; + } + result.append(ch); + } + return result.toString(); + } + + /** * Return the current time in YYYYMMDDTHHMMSS<tz> format */ @@ -673,7 +697,7 @@ public class Time { int minutes = (offset % 3600) / 60; int hours = offset / 3600; - return String.format("%s%s%02d:%02d", base, sign, hours, minutes); + return String.format(Locale.US, "%s%s%02d:%02d", base, sign, hours, minutes); } } diff --git a/core/java/android/util/LocaleUtil.java b/core/java/android/util/LocaleUtil.java deleted file mode 100644 index 60526e1..0000000 --- a/core/java/android/util/LocaleUtil.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.util; - -import java.util.Locale; - -import android.view.View; -import libcore.icu.ICU; - -/** - * Various utilities for Locales - * - */ -public class LocaleUtil { - - private LocaleUtil() { /* cannot be instantiated */ } - - private static String ARAB_SCRIPT_SUBTAG = "Arab"; - private static String HEBR_SCRIPT_SUBTAG = "Hebr"; - - /** - * Return the layout direction for a given Locale - * - * @param locale the Locale for which we want the layout direction. Can be null. - * @return the layout direction. This may be one of: - * {@link View#LAYOUT_DIRECTION_LTR} or - * {@link View#LAYOUT_DIRECTION_RTL}. - * - * Be careful: this code will need to be updated when vertical scripts will be supported - */ - public static int getLayoutDirectionFromLocale(Locale locale) { - if (locale != null && !locale.equals(Locale.ROOT)) { - final String scriptSubtag = ICU.getScript(ICU.addLikelySubtags(locale.toString())); - if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale); - - if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) || - scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) { - return View.LAYOUT_DIRECTION_RTL; - } - } - - return View.LAYOUT_DIRECTION_LTR; - } - - /** - * Fallback algorithm to detect the locale direction. Rely on the fist char of the - * localized locale name. This will not work if the localized locale name is in English - * (this is the case for ICU 4.4 and "Urdu" script) - * - * @param locale - * @return the layout direction. This may be one of: - * {@link View#LAYOUT_DIRECTION_LTR} or - * {@link View#LAYOUT_DIRECTION_RTL}. - * - * Be careful: this code will need to be updated when vertical scripts will be supported - * - * @hide - */ - private static int getLayoutDirectionFromFirstChar(Locale locale) { - switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) { - case Character.DIRECTIONALITY_RIGHT_TO_LEFT: - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: - return View.LAYOUT_DIRECTION_RTL; - - case Character.DIRECTIONALITY_LEFT_TO_RIGHT: - default: - return View.LAYOUT_DIRECTION_LTR; - } - } -} diff --git a/core/java/android/util/PropertyValueModel.java b/core/java/android/util/PropertyValueModel.java deleted file mode 100755 index eb9c47d..0000000 --- a/core/java/android/util/PropertyValueModel.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.util; - -/** - * A value model for a {@link Property property} of a host object. This class can be used for - * both reflective and non-reflective property implementations. - * - * @param <H> the host type, where the host is the object that holds this property - * @param <T> the value type - * - * @see Property - * @see ValueModel - */ -public class PropertyValueModel<H, T> extends ValueModel<T> { - private final H mHost; - private final Property<H, T> mProperty; - - private PropertyValueModel(H host, Property<H, T> property) { - mProperty = property; - mHost = host; - } - - /** - * Returns the host. - * - * @return the host - */ - public H getHost() { - return mHost; - } - - /** - * Returns the property. - * - * @return the property - */ - public Property<H, T> getProperty() { - return mProperty; - } - - @Override - public Class<T> getType() { - return mProperty.getType(); - } - - @Override - public T get() { - return mProperty.get(mHost); - } - - @Override - public void set(T value) { - mProperty.set(mHost, value); - } - - /** - * Return an appropriate PropertyValueModel for this host and property. - * - * @param host the host - * @param property the property - * @return the value model - */ - public static <H, T> PropertyValueModel<H, T> of(H host, Property<H, T> property) { - return new PropertyValueModel<H, T>(host, property); - } - - /** - * Return a PropertyValueModel for this {@code host} and a - * reflective property, constructed from this {@code propertyType} and {@code propertyName}. - * - * @param host - * @param propertyType the property type - * @param propertyName the property name - * @return a value model with this host and a reflective property with this type and name - * - * @see Property#of - */ - public static <H, T> PropertyValueModel<H, T> of(H host, Class<T> propertyType, - String propertyName) { - return of(host, Property.of((Class<H>) host.getClass(), propertyType, propertyName)); - } - - private static Class getNullaryMethodReturnType(Class c, String name) { - try { - return c.getMethod(name).getReturnType(); - } catch (NoSuchMethodException e) { - return null; - } - } - - private static Class getFieldType(Class c, String name) { - try { - return c.getField(name).getType(); - } catch (NoSuchFieldException e) { - return null; - } - } - - private static String capitalize(String name) { - if (name.isEmpty()) { - return name; - } - return Character.toUpperCase(name.charAt(0)) + name.substring(1); - } - - /** - * Return a PropertyValueModel for this {@code host} and and {@code propertyName}. - * - * @param host the host - * @param propertyName the property name - * @return a value model with this host and a reflective property with this name - */ - public static PropertyValueModel of(Object host, String propertyName) { - Class clazz = host.getClass(); - String suffix = capitalize(propertyName); - Class propertyType = getNullaryMethodReturnType(clazz, "get" + suffix); - if (propertyType == null) { - propertyType = getNullaryMethodReturnType(clazz, "is" + suffix); - } - if (propertyType == null) { - propertyType = getFieldType(clazz, propertyName); - } - if (propertyType == null) { - throw new NoSuchPropertyException(propertyName); - } - return of(host, propertyType, propertyName); - } -} diff --git a/core/java/android/util/ValueModel.java b/core/java/android/util/ValueModel.java deleted file mode 100755 index 4789682..0000000 --- a/core/java/android/util/ValueModel.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.util; - -/** - * A ValueModel is an abstraction for a 'slot' or place in memory in which a value - * may be stored and retrieved. A common implementation of ValueModel is a regular property of - * an object, whose value may be retrieved by calling the appropriate <em>getter</em> - * method and set by calling the corresponding <em>setter</em> method. - * - * @param <T> the value type - * - * @see PropertyValueModel - */ -public abstract class ValueModel<T> { - /** - * The empty model should be used in place of {@code null} to indicate that a - * model has not been set. The empty model has no value and does nothing when it is set. - */ - public static final ValueModel EMPTY = new ValueModel() { - @Override - public Class getType() { - return Object.class; - } - - @Override - public Object get() { - return null; - } - - @Override - public void set(Object value) { - - } - }; - - protected ValueModel() { - } - - /** - * Returns the type of this property. - * - * @return the property type - */ - public abstract Class<T> getType(); - - /** - * Returns the value of this property. - * - * @return the property value - */ - public abstract T get(); - - /** - * Sets the value of this property. - * - * @param value the new value for this property - */ - public abstract void set(T value); -}
\ No newline at end of file diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index 351c5c3..b2988ed 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -228,7 +228,7 @@ public class FocusFinder { if (focusable == focused || focusable == root) continue; // get focus bounds of other view in same coordinate system - focusable.getFocusRect(mOtherRect); + focusable.getFocusedRect(mOtherRect); root.offsetDescendantRectToMyCoords(focusable, mOtherRect); if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) { diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 869cd00..032ff7b 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -150,7 +150,7 @@ class GLES20Canvas extends HardwareCanvas { static native int nCreateTextureLayer(boolean opaque, int[] layerInfo); static native int nCreateLayer(int width, int height, boolean isOpaque, int[] layerInfo); - static native void nResizeLayer(int layerId, int width, int height, int[] layerInfo); + static native boolean nResizeLayer(int layerId, int width, int height, int[] layerInfo); static native void nSetOpaqueLayer(int layerId, boolean isOpaque); static native void nSetLayerPaint(int layerId, int nativePaint); static native void nSetLayerColorFilter(int layerId, int nativeColorFilter); diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java index a77425a..fcfc8e1 100644 --- a/core/java/android/view/GLES20RenderLayer.java +++ b/core/java/android/view/GLES20RenderLayer.java @@ -54,8 +54,8 @@ class GLES20RenderLayer extends GLES20Layer { } @Override - void resize(int width, int height) { - if (!isValid() || width <= 0 || height <= 0) return; + boolean resize(int width, int height) { + if (!isValid() || width <= 0 || height <= 0) return false; mWidth = width; mHeight = height; @@ -63,11 +63,17 @@ class GLES20RenderLayer extends GLES20Layer { if (width != mLayerWidth || height != mLayerHeight) { int[] layerInfo = new int[2]; - GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo); - - mLayerWidth = layerInfo[0]; - mLayerHeight = layerInfo[1]; + if (GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo)) { + mLayerWidth = layerInfo[0]; + mLayerHeight = layerInfo[1]; + } else { + // Failure: not enough GPU resources for requested size + mLayer = 0; + mLayerWidth = 0; + mLayerHeight = 0; + } } + return isValid(); } @Override diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java index e198ef6..b0ee2aa 100644 --- a/core/java/android/view/GLES20TextureLayer.java +++ b/core/java/android/view/GLES20TextureLayer.java @@ -48,7 +48,8 @@ class GLES20TextureLayer extends GLES20Layer { } @Override - void resize(int width, int height) { + boolean resize(int width, int height) { + return isValid(); } @Override diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java index f031fe7..9a89fa5 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -153,8 +153,8 @@ public class Gravity * container. * @param layoutDirection The layout direction. * - * @see {@link View#LAYOUT_DIRECTION_LTR} - * @see {@link View#LAYOUT_DIRECTION_RTL} + * @see View#LAYOUT_DIRECTION_LTR + * @see View#LAYOUT_DIRECTION_RTL */ public static void apply(int gravity, int w, int h, Rect container, Rect outRect, int layoutDirection) { @@ -290,8 +290,8 @@ public class Gravity * container. * @param layoutDirection The layout direction. * - * @see {@link View#LAYOUT_DIRECTION_LTR} - * @see {@link View#LAYOUT_DIRECTION_RTL} + * @see View#LAYOUT_DIRECTION_LTR + * @see View#LAYOUT_DIRECTION_RTL */ public static void apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj, Rect outRect, int layoutDirection) { @@ -370,8 +370,8 @@ public class Gravity * modified if needed to fit in the display. * @param layoutDirection The layout direction. * - * @see {@link View#LAYOUT_DIRECTION_LTR} - * @see {@link View#LAYOUT_DIRECTION_RTL} + * @see View#LAYOUT_DIRECTION_LTR + * @see View#LAYOUT_DIRECTION_RTL */ public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) { int absGravity = getAbsoluteGravity(gravity, layoutDirection); diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java index 6e763b2..d798e73 100644 --- a/core/java/android/view/HardwareLayer.java +++ b/core/java/android/view/HardwareLayer.java @@ -135,8 +135,9 @@ abstract class HardwareLayer { * * @param width The new desired minimum width for this layer * @param height The new desired minimum height for this layer + * @return True if the resulting layer is valid, false otherwise */ - abstract void resize(int width, int height); + abstract boolean resize(int width, int height); /** * Returns a hardware canvas that can be used to render onto diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index f906e24..ef5dc56 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 @@ -1130,9 +1136,8 @@ public abstract class HardwareRenderer { } } - int status = onPreDraw(dirty); - int saveCount = canvas.save(); - callbacks.onHardwarePreDraw(canvas); + int saveCount = 0; + int status = DisplayList.STATUS_DONE; try { view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) @@ -1158,6 +1163,10 @@ public abstract class HardwareRenderer { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + status = onPreDraw(dirty); + saveCount = canvas.save(); + callbacks.onHardwarePreDraw(canvas); + if (mProfileEnabled) { long now = System.nanoTime(); float total = (now - getDisplayListStartTime) * 0.000001f; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 03f9b72..87221e0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -48,9 +48,9 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.FloatProperty; -import android.util.LocaleUtil; import android.util.Log; import android.util.Pool; import android.util.Poolable; @@ -83,12 +83,18 @@ import static java.lang.Math.max; import com.android.internal.R; import com.android.internal.util.Predicate; import com.android.internal.view.menu.MenuBuilder; +import com.google.android.collect.Lists; +import com.google.android.collect.Maps; import java.lang.ref.WeakReference; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.Locale; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; @@ -1503,7 +1509,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private SparseArray<Object> mKeyedTags; /** - * The next available accessiiblity id. + * The next available accessibility id. */ private static int sNextAccessibilityViewId; @@ -1727,7 +1733,51 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static final int PFLAG_INVALIDATED = 0x80000000; - /* Masks for mPrivateFlags2 */ + /** + * Masks for mPrivateFlags2, as generated by dumpFlags(): + * + * -------|-------|-------|-------| + * PFLAG2_TEXT_ALIGNMENT_FLAGS[0] + * PFLAG2_TEXT_DIRECTION_FLAGS[0] + * 1 PFLAG2_DRAG_CAN_ACCEPT + * 1 PFLAG2_DRAG_HOVERED + * 1 PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT + * 11 PFLAG2_TEXT_DIRECTION_MASK_SHIFT + * 1 1 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT + * 11 PFLAG2_LAYOUT_DIRECTION_MASK + * 11 1 PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT + * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL + * 1 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT + * 1 1 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT + * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED + * 11 PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK + * 1 PFLAG2_TEXT_DIRECTION_FLAGS[1] + * 1 PFLAG2_TEXT_DIRECTION_FLAGS[2] + * 11 PFLAG2_TEXT_DIRECTION_FLAGS[3] + * 1 PFLAG2_TEXT_DIRECTION_FLAGS[4] + * 1 1 PFLAG2_TEXT_DIRECTION_FLAGS[5] + * 111 PFLAG2_TEXT_DIRECTION_MASK + * 1 PFLAG2_TEXT_DIRECTION_RESOLVED + * 1 PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT + * 111 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK + * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[1] + * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[2] + * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[3] + * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[4] + * 1 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[5] + * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[6] + * 111 PFLAG2_TEXT_ALIGNMENT_MASK + * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED + * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT + * 111 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK + * 11 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK + * 1 PFLAG2_HAS_TRANSIENT_STATE + * 1 PFLAG2_ACCESSIBILITY_FOCUSED + * 1 PFLAG2_ACCESSIBILITY_STATE_CHANGED + * 1 PFLAG2_VIEW_QUICK_REJECTED + * 1 PFLAG2_PADDING_RESOLVED + * -------|-------|-------|-------| + */ /** * Indicates that this view has reported that it can accept the current drag's content. @@ -1825,8 +1875,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - static final int PFLAG2_HAS_TRANSIENT_STATE = 0x00000100; - + static final int PFLAG2_HAS_TRANSIENT_STATE = 0x1 << 22; /** * Text direction is inherited thru {@link ViewGroup} @@ -2053,7 +2102,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002; /** - * The default whether the view is important for accessiblity. + * The default whether the view is important for accessibility. */ static final int IMPORTANT_FOR_ACCESSIBILITY_DEFAULT = IMPORTANT_FOR_ACCESSIBILITY_AUTO; @@ -2814,18 +2863,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mUserPaddingEnd; /** - * Whether a left padding has been defined during layout inflation. + * Cache initial left padding. * * @hide */ - boolean mUserPaddingLeftDefined = false; + int mUserPaddingLeftInitial = UNDEFINED_PADDING; /** - * Whether a right padding has been defined during layout inflation. + * Cache initial right padding. * * @hide */ - boolean mUserPaddingRightDefined = false; + int mUserPaddingRightInitial = UNDEFINED_PADDING; /** * Default undefined padding @@ -3125,7 +3174,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private boolean mSendingHoverAccessibilityEvents; /** - * Delegate for injecting accessiblity functionality. + * Delegate for injecting accessibility functionality. */ AccessibilityDelegate mAccessibilityDelegate; @@ -3247,19 +3296,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, break; case com.android.internal.R.styleable.View_padding: padding = a.getDimensionPixelSize(attr, -1); - mUserPaddingLeftDefined = true; - mUserPaddingRightDefined = true; + mUserPaddingLeftInitial = padding; + mUserPaddingRightInitial = padding; break; case com.android.internal.R.styleable.View_paddingLeft: leftPadding = a.getDimensionPixelSize(attr, -1); - mUserPaddingLeftDefined = true; + mUserPaddingLeftInitial = leftPadding; break; case com.android.internal.R.styleable.View_paddingTop: topPadding = a.getDimensionPixelSize(attr, -1); break; case com.android.internal.R.styleable.View_paddingRight: rightPadding = a.getDimensionPixelSize(attr, -1); - mUserPaddingRightDefined = true; + mUserPaddingRightInitial = rightPadding; break; case com.android.internal.R.styleable.View_paddingBottom: bottomPadding = a.getDimensionPixelSize(attr, -1); @@ -3561,15 +3610,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, topPadding = padding; rightPadding = padding; bottomPadding = padding; + mUserPaddingLeftInitial = padding; + mUserPaddingRightInitial = padding; } // If the user specified the padding (either with android:padding or // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise // use the default padding or the padding from the background drawable // (stored at this point in mPadding*) - internalSetPadding(leftPadding >= 0 ? leftPadding : mPaddingLeft, + mUserPaddingLeftInitial = leftPadding >= 0 ? leftPadding : mPaddingLeft; + mUserPaddingRightInitial = rightPadding >= 0 ? rightPadding : mPaddingRight; + internalSetPadding(mUserPaddingLeftInitial, topPadding >= 0 ? topPadding : mPaddingTop, - rightPadding >= 0 ? rightPadding : mPaddingRight, + mUserPaddingRightInitial, bottomPadding >= 0 ? bottomPadding : mPaddingBottom); if (viewFlagMasks != 0) { @@ -4475,7 +4528,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Sends an accessibility event of the given type. If accessiiblity is + * Sends an accessibility event of the given type. If accessibility is * not enabled this method has no effect. The default implementation calls * {@link #onInitializeAccessibilityEvent(AccessibilityEvent)} first * to populate information about the event source (this View), then calls @@ -4894,7 +4947,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, info.setLongClickable(isLongClickable()); // TODO: These make sense only if we are in an AdapterView but all - // views can be selected. Maybe from accessiiblity perspective + // views can be selected. Maybe from accessibility perspective // we should report as selectable view in an AdapterView. info.addAction(AccessibilityNodeInfo.ACTION_SELECT); info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION); @@ -5004,7 +5057,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Sets a delegate for implementing accessibility support via compositon as + * Sets a delegate for implementing accessibility support via composition as * opposed to inheritance. The delegate's primary use is for implementing * backwards compatible widgets. For more details see {@link AccessibilityDelegate}. * @@ -6680,7 +6733,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Returns whether the View is considered actionable from * accessibility perspective. Such view are important for - * accessiiblity. + * accessibility. * * @return True if the view is actionable for accessibility. * @@ -6692,7 +6745,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Returns whether the View has registered callbacks wich makes it - * important for accessiiblity. + * important for accessibility. * * @return True if the view is actionable for accessibility. */ @@ -6731,7 +6784,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Reset the state indicating the this view has requested clients - * interested in its accessiblity state to be notified. + * interested in its accessibility state to be notified. * * @hide */ @@ -8708,18 +8761,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - /** - * When searching for a view to focus this rectangle is used when considering if this view is - * a good candidate for receiving focus. - * - * By default, the rectangle is the {@link #getDrawingRect}) of the view. - * - * @param r The rectangle to fill in, in this view's coordinates. - */ - public void getFocusRect(Rect r) { - getDrawingRect(r); - } - /** * Utility method to retrieve the inverse of the current mMatrix property. * We cache the matrix to avoid recalculating it when transform properties @@ -9698,7 +9739,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * When a view has focus and the user navigates away from it, the next view is searched for * starting from the rectangle filled in by this method. * - * By default, the rectange is the {@link #getDrawingRect(android.graphics.Rect)}) + * By default, the rectangle is the {@link #getDrawingRect(android.graphics.Rect)}) * of the view. However, if your view maintains some idea of internal selection, * such as a cursor, or a selected row or column, you should override this method and * fill in a more specific rectangle. @@ -11552,10 +11593,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // left / right padding are used if defined. If they are not defined and start / end // padding are defined (e.g. in Frameworks resources), then we use start / end and // resolve them as left / right (layout direction is not taken into account). - if (!mUserPaddingLeftDefined && mUserPaddingStart != UNDEFINED_PADDING) { + if (mUserPaddingLeftInitial == UNDEFINED_PADDING && + mUserPaddingStart != UNDEFINED_PADDING) { mUserPaddingLeft = mUserPaddingStart; } - if (!mUserPaddingRightDefined && mUserPaddingEnd != UNDEFINED_PADDING) { + if (mUserPaddingRightInitial == UNDEFINED_PADDING + && mUserPaddingEnd != UNDEFINED_PADDING) { mUserPaddingRight = mUserPaddingEnd; } @@ -11569,6 +11612,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // left / right or right / left depending on the resolved layout direction. // If start / end padding are not defined, use the left / right ones. int resolvedLayoutDirection = getResolvedLayoutDirection(); + // Set user padding to initial values ... + mUserPaddingLeft = (mUserPaddingLeftInitial == UNDEFINED_PADDING) ? + 0 : mUserPaddingLeftInitial; + mUserPaddingRight = (mUserPaddingRightInitial == UNDEFINED_PADDING) ? + 0 : mUserPaddingRightInitial; + // ... then resolve it. switch (resolvedLayoutDirection) { case LAYOUT_DIRECTION_RTL: if (mUserPaddingStart != UNDEFINED_PADDING) { @@ -11605,8 +11654,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @param layoutDirection the direction of the layout * - * @see {@link #LAYOUT_DIRECTION_LTR} - * @see {@link #LAYOUT_DIRECTION_RTL} + * @see #LAYOUT_DIRECTION_LTR + * @see #LAYOUT_DIRECTION_RTL */ public void onPaddingChanged(int layoutDirection) { } @@ -11655,7 +11704,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return true if the Locale uses an RTL script. */ protected static boolean isLayoutDirectionRtl(Locale locale) { - return (LAYOUT_DIRECTION_RTL == LocaleUtil.getLayoutDirectionFromLocale(locale)); + return (LAYOUT_DIRECTION_RTL == TextUtils.getLayoutDirectionFromLocale(locale)); } /** @@ -12213,8 +12262,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mLocalDirtyRect.set(0, 0, width, height); } else { if (mHardwareLayer.getWidth() != width || mHardwareLayer.getHeight() != height) { - mHardwareLayer.resize(width, height); - mLocalDirtyRect.set(0, 0, width, height); + if (mHardwareLayer.resize(width, height)) { + mLocalDirtyRect.set(0, 0, width, height); + } } // This should not be necessary but applications that change @@ -12225,7 +12275,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, computeOpaqueFlags(); final boolean opaque = isOpaque(); - if (mHardwareLayer.isOpaque() != opaque) { + if (mHardwareLayer.isValid() && mHardwareLayer.isOpaque() != opaque) { mHardwareLayer.setOpaque(opaque); mLocalDirtyRect.set(0, 0, width, height); } @@ -14052,8 +14102,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @param layoutDirection The resolved layout direction. * - * @see {@link #LAYOUT_DIRECTION_LTR} - * @see {@link #LAYOUT_DIRECTION_RTL} + * @see #LAYOUT_DIRECTION_LTR + * @see #LAYOUT_DIRECTION_RTL */ public void onResolveDrawables(int layoutDirection) { } @@ -14333,10 +14383,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags2 &= ~PFLAG2_PADDING_RESOLVED; switch (background.getLayoutDirection()) { case LAYOUT_DIRECTION_RTL: + mUserPaddingLeftInitial = padding.right; + mUserPaddingRightInitial = padding.left; internalSetPadding(padding.right, padding.top, padding.left, padding.bottom); break; case LAYOUT_DIRECTION_LTR: default: + mUserPaddingLeftInitial = padding.left; + mUserPaddingRightInitial = padding.right; internalSetPadding(padding.left, padding.top, padding.right, padding.bottom); } } @@ -14433,6 +14487,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; + mUserPaddingLeftInitial = left; + mUserPaddingRightInitial = right; + internalSetPadding(left, top, right, bottom); } @@ -14522,10 +14579,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, switch(getResolvedLayoutDirection()) { case LAYOUT_DIRECTION_RTL: + mUserPaddingLeftInitial = end; + mUserPaddingRightInitial = start; internalSetPadding(end, top, start, bottom); break; case LAYOUT_DIRECTION_LTR: default: + mUserPaddingLeftInitial = start; + mUserPaddingRightInitial = end; internalSetPadding(start, top, end, bottom); } } @@ -18102,4 +18163,46 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (view.mLabelForId == mLabeledId); } } + + /** + * Dump all private flags in readable format, useful for documentation and + * sanity checking. + */ + private static void dumpFlags() { + final HashMap<String, String> found = Maps.newHashMap(); + try { + for (Field field : View.class.getDeclaredFields()) { + final int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { + if (field.getType().equals(int.class)) { + final int value = field.getInt(null); + dumpFlag(found, field.getName(), value); + } else if (field.getType().equals(int[].class)) { + final int[] values = (int[]) field.get(null); + for (int i = 0; i < values.length; i++) { + dumpFlag(found, field.getName() + "[" + i + "]", values[i]); + } + } + } + } + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + + final ArrayList<String> keys = Lists.newArrayList(); + keys.addAll(found.keySet()); + Collections.sort(keys); + for (String key : keys) { + Log.d(VIEW_LOG_TAG, found.get(key)); + } + } + + private static void dumpFlag(HashMap<String, String> found, String name, int value) { + // Sort flags by prefix, then by bits, always keeping unique keys + final String bits = String.format("%32s", Integer.toBinaryString(value)).replace('0', ' '); + final int prefix = name.indexOf('_'); + final String key = (prefix > 0 ? name.substring(0, prefix) : name) + bits + name; + final String output = bits + " " + name; + found.put(key, output); + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d6a0203..27fd374 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1402,6 +1402,7 @@ public final class ViewRootImpl implements ViewParent, mResizeBuffer.getHeight() != mHeight) { mResizeBuffer.resize(mWidth, mHeight); } + // TODO: should handle create/resize failure layerCanvas = mResizeBuffer.start(hwRendererCanvas); layerCanvas.setViewport(mWidth, mHeight); layerCanvas.onPreDraw(null); 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/java/android/view/Window.java b/core/java/android/view/Window.java index a242895..06974d3 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -89,6 +89,13 @@ public abstract class Window { * If overlay is enabled, the action mode UI will be allowed to cover existing window content. */ public static final int FEATURE_ACTION_MODE_OVERLAY = 10; + + /** + * Max value used as a feature ID + * @hide + */ + public static final int FEATURE_MAX = FEATURE_ACTION_MODE_OVERLAY; + /** Flag for setting the progress bar's visibility to VISIBLE */ public static final int PROGRESS_VISIBILITY_ON = -1; /** Flag for setting the progress bar's visibility to GONE */ diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 75554da..82f07c7 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1111,7 +1111,15 @@ public interface WindowManagerPolicy { * @param attrs The window's LayoutParams. * @return Whether magnification can be applied. */ - public boolean canMagnifyWindow(WindowManager.LayoutParams attrs); + public boolean canMagnifyWindowLw(WindowManager.LayoutParams attrs); + + /** + * Called when the current user changes. Guaranteed to be called before the broadcast + * of the new user id is made to all listeners. + * + * @param newUserId The id of the incoming user. + */ + public void setCurrentUserLw(int newUserId); /** * Print the WindowManagerPolicy's state into the given stream. diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index d9aeb70..e6eaa14 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -89,7 +89,6 @@ final class JWebCoreJavaBridge extends Handler { private void fireSharedTimer() { // clear the flag so that sharedTimerFired() can set a new timer mHasInstantTimer = false; - removeMessages(TIMER_MESSAGE); sharedTimerFired(); } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index da0cc5f..33fe834 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -2198,20 +2198,12 @@ public final class WebViewCore { mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); } m_skipDrawFlag = false; - m_skipDrawFlagLock.notify(); } } private void webkitDraw() { synchronized (m_skipDrawFlagLock) { if (m_skipDrawFlag) { - try { - // Aggressively throttle webkit to give the UI more CPU - // to catch up with - m_skipDrawFlagLock.wait(50); - } catch (InterruptedException e) {} - } - if (m_skipDrawFlag) { m_drawWasSkipped = true; return; } diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index f79ec42..27d15f6 100755 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -24,8 +24,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.PackageParser; -import android.content.pm.PackageUserState; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.graphics.drawable.Drawable; @@ -272,7 +270,7 @@ public class AppSecurityPermissions { setPermissions(mPermsList); } - public AppSecurityPermissions(Context context, PackageParser.Package pkg) { + public AppSecurityPermissions(Context context, PackageInfo info) { mContext = context; mPm = mContext.getPackageManager(); loadResources(); @@ -280,14 +278,11 @@ public class AppSecurityPermissions { mPermGroupComparator = new PermissionGroupInfoComparator(); mPermsList = new ArrayList<MyPermissionInfo>(); Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>(); - if(pkg == null) { + if(info == null) { return; } // Convert to a PackageInfo - PackageInfo info = PackageParser.generatePackageInfo(pkg, null, - PackageManager.GET_PERMISSIONS, 0, 0, null, - new PackageUserState()); PackageInfo installedPkgInfo = null; // Get requested permissions if (info.requestedPermissions != null) { @@ -299,13 +294,13 @@ public class AppSecurityPermissions { extractPerms(info, permSet, installedPkgInfo); } // Get permissions related to shared user if any - if (pkg.mSharedUserId != null) { + if (info.sharedUserId != null) { int sharedUid; try { - sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId); + sharedUid = mPm.getUidForSharedUser(info.sharedUserId); getAllUsedPermissions(sharedUid, permSet); } catch (NameNotFoundException e) { - Log.w(TAG, "Could'nt retrieve shared user id for:"+pkg.packageName); + Log.w(TAG, "Could'nt retrieve shared user id for:"+info.packageName); } } // Retrieve list of permissions diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java index 41ab5f2..f1804f8 100644 --- a/core/java/android/widget/CheckBox.java +++ b/core/java/android/widget/CheckBox.java @@ -20,7 +20,6 @@ import android.content.Context; import android.util.AttributeSet; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; -import android.util.ValueModel; /** @@ -56,9 +55,7 @@ import android.util.ValueModel; * {@link android.R.styleable#View View Attributes} * </p> */ -public class CheckBox extends CompoundButton implements ValueEditor<Boolean> { - private ValueModel<Boolean> mValueModel = ValueModel.EMPTY; - +public class CheckBox extends CompoundButton { public CheckBox(Context context) { this(context, null); } @@ -82,22 +79,4 @@ public class CheckBox extends CompoundButton implements ValueEditor<Boolean> { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(CheckBox.class.getName()); } - - @Override - public ValueModel<Boolean> getValueModel() { - return mValueModel; - } - - @Override - public void setValueModel(ValueModel<Boolean> valueModel) { - mValueModel = valueModel; - setChecked(mValueModel.get()); - } - - @Override - public boolean performClick() { - boolean handled = super.performClick(); - mValueModel.set(isChecked()); - return handled; - } } diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index ec81214..57e51c2 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -17,7 +17,6 @@ package android.widget; import android.content.Context; -import android.graphics.Rect; import android.text.Editable; import android.text.Selection; import android.text.Spannable; @@ -25,7 +24,6 @@ import android.text.TextUtils; import android.text.method.ArrowKeyMovementMethod; import android.text.method.MovementMethod; import android.util.AttributeSet; -import android.util.ValueModel; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -49,9 +47,7 @@ import android.view.accessibility.AccessibilityNodeInfo; * {@link android.R.styleable#TextView TextView Attributes}, * {@link android.R.styleable#View View Attributes} */ -public class EditText extends TextView implements ValueEditor<CharSequence> { - private ValueModel<CharSequence> mValueModel = ValueModel.EMPTY; - +public class EditText extends TextView { public EditText(Context context) { this(context, null); } @@ -132,21 +128,4 @@ public class EditText extends TextView implements ValueEditor<CharSequence> { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(EditText.class.getName()); } - - @Override - public ValueModel<CharSequence> getValueModel() { - return mValueModel; - } - - @Override - public void setValueModel(ValueModel<CharSequence> valueModel) { - mValueModel = valueModel; - setText(mValueModel.get()); - } - - @Override - void sendAfterTextChanged(Editable text) { - super.sendAfterTextChanged(text); - mValueModel.set(text); - } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 7f9dab9..237275a 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1801,13 +1801,13 @@ public class Editor { mTextView.deleteText_internal(dragSourceStart, dragSourceEnd); // Make sure we do not leave two adjacent spaces. - CharSequence t = mTextView.getTransformedText(dragSourceStart - 1, dragSourceStart + 1); - if ( (dragSourceStart == 0 || Character.isSpaceChar(t.charAt(0))) && - (dragSourceStart == mTextView.getText().length() || - Character.isSpaceChar(t.charAt(1))) ) { - final int pos = dragSourceStart == mTextView.getText().length() ? - dragSourceStart - 1 : dragSourceStart; - mTextView.deleteText_internal(pos, pos + 1); + final int prevCharIdx = Math.max(0, dragSourceStart - 1); + final int nextCharIdx = Math.min(mTextView.getText().length(), dragSourceStart + 1); + if (nextCharIdx > prevCharIdx + 1) { + CharSequence t = mTextView.getTransformedText(prevCharIdx, nextCharIdx); + if (Character.isSpaceChar(t.charAt(0)) && Character.isSpaceChar(t.charAt(1))) { + mTextView.deleteText_internal(prevCharIdx, prevCharIdx + 1); + } } } } diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 3baf5a9..1c81d11 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -21,8 +21,8 @@ import android.database.DataSetObserver; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Handler; +import android.text.TextUtils; import android.util.AttributeSet; -import android.util.LocaleUtil; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; @@ -200,7 +200,7 @@ public class ListPopupWindow { mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); // Set the default layout direction to match the default locale one final Locale locale = mContext.getResources().getConfiguration().locale; - mLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale); + mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(locale); } /** diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java index a6486a8..2737f94 100644 --- a/core/java/android/widget/SeekBar.java +++ b/core/java/android/widget/SeekBar.java @@ -18,7 +18,6 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; -import android.util.ValueModel; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -34,7 +33,7 @@ import android.view.accessibility.AccessibilityNodeInfo; * * @attr ref android.R.styleable#SeekBar_thumb */ -public class SeekBar extends AbsSeekBar implements ValueEditor<Integer> { +public class SeekBar extends AbsSeekBar { /** * A callback that notifies clients when the progress level has been @@ -70,9 +69,8 @@ public class SeekBar extends AbsSeekBar implements ValueEditor<Integer> { void onStopTrackingTouch(SeekBar seekBar); } - private ValueModel<Integer> mValueModel = ValueModel.EMPTY; private OnSeekBarChangeListener mOnSeekBarChangeListener; - + public SeekBar(Context context) { this(context, null); } @@ -91,23 +89,9 @@ public class SeekBar extends AbsSeekBar implements ValueEditor<Integer> { if (mOnSeekBarChangeListener != null) { mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser); - if (fromUser) { - mValueModel.set(getProgress()); - } } } - @Override - public ValueModel<Integer> getValueModel() { - return mValueModel; - } - - @Override - public void setValueModel(ValueModel<Integer> valueModel) { - mValueModel = valueModel; - setProgress(mValueModel.get()); - } - /** * Sets a listener to receive notifications of changes to the SeekBar's progress level. Also * provides notifications of when the user starts and stops a touch gesture within the SeekBar. diff --git a/core/java/android/widget/ValueEditor.java b/core/java/android/widget/ValueEditor.java deleted file mode 100755 index 2b91abf..0000000 --- a/core/java/android/widget/ValueEditor.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.widget; - -import android.util.ValueModel; - -/** - * An interface for editors of simple values. Classes implementing this interface are normally - * UI controls (subclasses of {@link android.view.View View}) that can provide a suitable - * user interface to display and edit values of the specified type. This interface is - * intended to describe editors for simple types, like {@code boolean}, {@code int} or - * {@code String}, where the values themselves are immutable. - * <p> - * For example, {@link android.widget.CheckBox CheckBox} implements - * this interface for the Boolean type as it is capable of providing an appropriate - * mechanism for displaying and changing the value of a Boolean property. - * - * @param <T> the value type that this editor supports - */ -public interface ValueEditor<T> { - /** - * Return the last value model that was set. If no value model has been set, the editor - * should return the value {@link android.util.ValueModel#EMPTY}. - * - * @return the value model - */ - public ValueModel<T> getValueModel(); - - /** - * Sets the value model for this editor. When the value model is set, the editor should - * retrieve the value from the value model, using {@link android.util.ValueModel#get()}, - * and set its internal state accordingly. Likewise, when the editor's internal state changes - * it should update the value model by calling {@link android.util.ValueModel#set(T)} - * with the appropriate value. - * - * @param valueModel the new value model for this editor. - */ - public void setValueModel(ValueModel<T> valueModel); -} diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index bcf0ea4..43a02cf 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -347,6 +347,18 @@ public class AlertController { } } + /** + * @param attrId the attributeId of the theme-specific drawable + * to resolve the resourceId for. + * + * @return resId the resourceId of the theme-specific drawable + */ + public int getIconAttributeResId(int attrId) { + TypedValue out = new TypedValue(); + mContext.getTheme().resolveAttribute(attrId, out, true); + return out.resourceId; + } + public void setInverseBackgroundForced(boolean forceInverseBackground) { mForceInverseBackground = forceInverseBackground; } @@ -740,6 +752,7 @@ public class AlertController { public int mIconId = 0; public Drawable mIcon; + public int mIconAttrId = 0; public CharSequence mTitle; public View mCustomTitleView; public CharSequence mMessage; @@ -807,6 +820,9 @@ public class AlertController { if (mIconId >= 0) { dialog.setIcon(mIconId); } + if (mIconAttrId > 0) { + dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId)); + } } if (mMessage != null) { dialog.setMessage(mMessage); diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java index dc4ee3a..34ade74 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuView.java @@ -400,6 +400,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo int nonOverflowCount = 0; int widthRemaining = right - left - getPaddingRight() - getPaddingLeft(); boolean hasOverflow = false; + final boolean isLayoutRtl = isLayoutRtl(); for (int i = 0; i < childCount; i++) { final View v = getChildAt(i); if (v.getVisibility() == GONE) { @@ -414,8 +415,15 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } int height = v.getMeasuredHeight(); - int r = getWidth() - getPaddingRight() - p.rightMargin; - int l = r - overflowWidth; + int r; + int l; + if (isLayoutRtl) { + l = getPaddingLeft() + p.leftMargin; + r = l + overflowWidth; + } else { + r = getWidth() - getPaddingRight() - p.rightMargin; + l = r - overflowWidth; + } int t = midVertical - (height / 2); int b = t + height; v.layout(l, t, r, b); @@ -448,20 +456,38 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1); final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0); - int startLeft = getPaddingLeft(); - for (int i = 0; i < childCount; i++) { - final View v = getChildAt(i); - final LayoutParams lp = (LayoutParams) v.getLayoutParams(); - if (v.getVisibility() == GONE || lp.isOverflowButton) { - continue; + if (isLayoutRtl) { + int startRight = getWidth() - getPaddingRight(); + for (int i = 0; i < childCount; i++) { + final View v = getChildAt(i); + final LayoutParams lp = (LayoutParams) v.getLayoutParams(); + if (v.getVisibility() == GONE || lp.isOverflowButton) { + continue; + } + + startRight -= lp.rightMargin; + int width = v.getMeasuredWidth(); + int height = v.getMeasuredHeight(); + int t = midVertical - height / 2; + v.layout(startRight - width, t, startRight, t + height); + startRight -= width + lp.leftMargin + spacerSize; } + } else { + int startLeft = getPaddingLeft(); + for (int i = 0; i < childCount; i++) { + final View v = getChildAt(i); + final LayoutParams lp = (LayoutParams) v.getLayoutParams(); + if (v.getVisibility() == GONE || lp.isOverflowButton) { + continue; + } - startLeft += lp.leftMargin; - int width = v.getMeasuredWidth(); - int height = v.getMeasuredHeight(); - int t = midVertical - height / 2; - v.layout(startLeft, t, startLeft + width, t + height); - startLeft += width + lp.rightMargin + spacerSize; + startLeft += lp.leftMargin; + int width = v.getMeasuredWidth(); + int height = v.getMeasuredHeight(); + int t = midVertical - height / 2; + v.layout(startLeft, t, startLeft + width, t + height); + startLeft += width + lp.rightMargin + spacerSize; + } } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 0710d96..8756950 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -22,14 +22,11 @@ import com.google.android.collect.Lists; import android.app.ActivityManagerNative; import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.os.Binder; -import android.os.FileObserver; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; @@ -45,16 +42,10 @@ import android.util.Log; import android.view.View; import android.widget.Button; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.util.Arrays; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; /** * Utilities for the lock pattern and its settings. @@ -134,7 +125,7 @@ public class LockPatternUtils { private final ContentResolver mContentResolver; private DevicePolicyManager mDevicePolicyManager; private ILockSettings mLockSettingsService; - private int mCurrentUserId = 0; + private int mCurrentUserId = UserHandle.USER_NULL; public DevicePolicyManager getDevicePolicyManager() { if (mDevicePolicyManager == null) { @@ -165,7 +156,7 @@ public class LockPatternUtils { } public int getRequestedMinimumPasswordLength() { - return getDevicePolicyManager().getPasswordMinimumLength(null); + return getDevicePolicyManager().getPasswordMinimumLength(null, getCurrentOrCallingUserId()); } /** @@ -173,47 +164,54 @@ public class LockPatternUtils { * MODE_PATTERN which allows the user to choose anything. */ public int getRequestedPasswordQuality() { - return getDevicePolicyManager().getPasswordQuality(null); + return getDevicePolicyManager().getPasswordQuality(null, getCurrentOrCallingUserId()); } public int getRequestedPasswordHistoryLength() { - return getDevicePolicyManager().getPasswordHistoryLength(null); + return getDevicePolicyManager().getPasswordHistoryLength(null, getCurrentOrCallingUserId()); } public int getRequestedPasswordMinimumLetters() { - return getDevicePolicyManager().getPasswordMinimumLetters(null); + return getDevicePolicyManager().getPasswordMinimumLetters(null, + getCurrentOrCallingUserId()); } public int getRequestedPasswordMinimumUpperCase() { - return getDevicePolicyManager().getPasswordMinimumUpperCase(null); + return getDevicePolicyManager().getPasswordMinimumUpperCase(null, + getCurrentOrCallingUserId()); } public int getRequestedPasswordMinimumLowerCase() { - return getDevicePolicyManager().getPasswordMinimumLowerCase(null); + return getDevicePolicyManager().getPasswordMinimumLowerCase(null, + getCurrentOrCallingUserId()); } public int getRequestedPasswordMinimumNumeric() { - return getDevicePolicyManager().getPasswordMinimumNumeric(null); + return getDevicePolicyManager().getPasswordMinimumNumeric(null, + getCurrentOrCallingUserId()); } public int getRequestedPasswordMinimumSymbols() { - return getDevicePolicyManager().getPasswordMinimumSymbols(null); + return getDevicePolicyManager().getPasswordMinimumSymbols(null, + getCurrentOrCallingUserId()); } public int getRequestedPasswordMinimumNonLetter() { - return getDevicePolicyManager().getPasswordMinimumNonLetter(null); + return getDevicePolicyManager().getPasswordMinimumNonLetter(null, + getCurrentOrCallingUserId()); } + /** * Returns the actual password mode, as set by keyguard after updating the password. * * @return */ public void reportFailedPasswordAttempt() { - getDevicePolicyManager().reportFailedPasswordAttempt(); + getDevicePolicyManager().reportFailedPasswordAttempt(getCurrentOrCallingUserId()); } public void reportSuccessfulPasswordAttempt() { - getDevicePolicyManager().reportSuccessfulPasswordAttempt(); + getDevicePolicyManager().reportSuccessfulPasswordAttempt(getCurrentOrCallingUserId()); } public void setCurrentUser(int userId) { @@ -226,10 +224,14 @@ public class LockPatternUtils { public int getCurrentUser() { if (Process.myUid() == Process.SYSTEM_UID) { + if (mCurrentUserId != UserHandle.USER_NULL) { + // Someone is regularly updating using setCurrentUser() use that value. + return mCurrentUserId; + } try { return ActivityManagerNative.getDefault().getCurrentUser().id; } catch (RemoteException re) { - return mCurrentUserId; + return UserHandle.USER_OWNER; } } else { throw new SecurityException("Only the system process can get the current user"); @@ -249,7 +251,9 @@ public class LockPatternUtils { private int getCurrentOrCallingUserId() { int callingUid = Binder.getCallingUid(); if (callingUid == android.os.Process.SYSTEM_UID) { - return mCurrentUserId; + // TODO: This is a little inefficient. See if all users of this are able to + // handle USER_CURRENT and pass that instead. + return getCurrentUser(); } else { return UserHandle.getUserId(callingUid); } @@ -481,21 +485,21 @@ public class LockPatternUtils { deleteGallery(); setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, - pattern.size(), 0, 0, 0, 0, 0, 0); + pattern.size(), 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); } else { setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK); setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); finishBiometricWeak(); dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, - 0, 0, 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); } } else { if (keyStore.isEmpty()) { keyStore.reset(); } dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, - 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); } } catch (RemoteException re) { Log.e(TAG, "Couldn't save lock pattern " + re); @@ -532,7 +536,8 @@ public class LockPatternUtils { /** Update the encryption password if it is enabled **/ private void updateEncryptionPassword(String password) { DevicePolicyManager dpm = getDevicePolicyManager(); - if (dpm.getStorageEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) { + if (dpm.getStorageEncryptionStatus(getCurrentOrCallingUserId()) + != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) { return; } @@ -558,7 +563,7 @@ public class LockPatternUtils { * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} */ public void saveLockPassword(String password, int quality) { - this.saveLockPassword(password, quality, false); + this.saveLockPassword(password, quality, false, getCurrentOrCallingUserId()); } /** @@ -570,23 +575,42 @@ public class LockPatternUtils { * @param isFallback Specifies if this is a fallback to biometric weak */ public void saveLockPassword(String password, int quality, boolean isFallback) { + saveLockPassword(password, quality, isFallback, getCurrentOrCallingUserId()); + } + + /** + * Save a lock password. Does not ensure that the password is as good + * as the requested mode, but will adjust the mode to be as good as the + * pattern. + * @param password The password to save + * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} + * @param isFallback Specifies if this is a fallback to biometric weak + * @param userHandle The userId of the user to change the password for + */ + public void saveLockPassword(String password, int quality, boolean isFallback, int userHandle) { // Compute the hash final byte[] hash = passwordToHash(password); try { - getLockSettings().setLockPassword(hash, getCurrentOrCallingUserId()); + if (Process.myUid() != Process.SYSTEM_UID && userHandle != UserHandle.myUserId()) { + throw new SecurityException( + "Only the system process can save lock password for another user"); + } + getLockSettings().setLockPassword(hash, userHandle); DevicePolicyManager dpm = getDevicePolicyManager(); KeyStore keyStore = KeyStore.getInstance(); if (password != null) { - // Update the encryption password. - updateEncryptionPassword(password); + if (userHandle == UserHandle.USER_OWNER) { + // Update the encryption password. + updateEncryptionPassword(password); - // Update the keystore password - keyStore.password(password); + // Update the keystore password + keyStore.password(password); + } int computedQuality = computePasswordQuality(password); if (!isFallback) { deleteGallery(); - setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality)); + setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle); if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { int letters = 0; int uppercase = 0; @@ -612,25 +636,27 @@ public class LockPatternUtils { } dpm.setActivePasswordState(Math.max(quality, computedQuality), password.length(), letters, uppercase, lowercase, - numbers, symbols, nonletter); + numbers, symbols, nonletter, userHandle); } else { // The password is not anything. dpm.setActivePasswordState( DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, - 0, 0, 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0, 0, userHandle); } } else { // Case where it's a fallback for biometric weak - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK); - setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality)); + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, + userHandle); + setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality), + userHandle); finishBiometricWeak(); dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, - 0, 0, 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0, 0, userHandle); } // Add the password to the password history. We assume all // password // hashes have the same length for simplicity of implementation. - String passwordHistory = getString(PASSWORD_HISTORY_KEY); + String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle); if (passwordHistory == null) { passwordHistory = new String(); } @@ -645,7 +671,7 @@ public class LockPatternUtils { * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory .length())); } - setString(PASSWORD_HISTORY_KEY, passwordHistory); + setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle); } else { // Conditionally reset the keystore if empty. If // non-empty, we are just switching key guard type @@ -653,7 +679,8 @@ public class LockPatternUtils { keyStore.reset(); } dpm.setActivePasswordState( - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0); + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, + userHandle); } } catch (RemoteException re) { // Cant do much @@ -849,7 +876,7 @@ public class LockPatternUtils { if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) { return false; } - if (getDevicePolicyManager().getCameraDisabled(null)) { + if (getDevicePolicyManager().getCameraDisabled(null, getCurrentOrCallingUserId())) { return false; } @@ -1027,6 +1054,10 @@ public class LockPatternUtils { } private void setLong(String secureSettingKey, long value) { + setLong(secureSettingKey, value, getCurrentOrCallingUserId()); + } + + private void setLong(String secureSettingKey, long value, int userHandle) { try { getLockSettings().setLong(secureSettingKey, value, getCurrentOrCallingUserId()); } catch (RemoteException re) { @@ -1036,17 +1067,20 @@ public class LockPatternUtils { } private String getString(String secureSettingKey) { + return getString(secureSettingKey, getCurrentOrCallingUserId()); + } + + private String getString(String secureSettingKey, int userHandle) { try { - return getLockSettings().getString(secureSettingKey, null, - getCurrentOrCallingUserId()); + return getLockSettings().getString(secureSettingKey, null, userHandle); } catch (RemoteException re) { return null; } } - private void setString(String secureSettingKey, String value) { + private void setString(String secureSettingKey, String value, int userHandle) { try { - getLockSettings().setString(secureSettingKey, value, getCurrentOrCallingUserId()); + getLockSettings().setString(secureSettingKey, value, userHandle); } catch (RemoteException re) { // What can we do? Log.e(TAG, "Couldn't write string " + secureSettingKey + re); @@ -1078,8 +1112,13 @@ public class LockPatternUtils { * {@link TelephonyManager#CALL_STATE_RINGING} * {@link TelephonyManager#CALL_STATE_OFFHOOK} * @param shown indicates whether the given screen wants the emergency button to show at all + * @param button + * @param phoneState + * @param shown shown if true; hidden if false + * @param upperCase if true, converts button label string to upper case */ - public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown) { + public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown, + boolean upperCase, boolean showIcon) { if (isEmergencyCallCapable() && shown) { button.setVisibility(View.VISIBLE); } else { @@ -1091,14 +1130,30 @@ public class LockPatternUtils { if (phoneState == TelephonyManager.CALL_STATE_OFFHOOK) { // show "return to call" text and show phone icon textId = R.string.lockscreen_return_to_call; - int phoneCallIcon = R.drawable.stat_sys_phone_call; + int phoneCallIcon = showIcon ? R.drawable.stat_sys_phone_call : 0; button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0); } else { textId = R.string.lockscreen_emergency_call; - int emergencyIcon = R.drawable.ic_emergency; + int emergencyIcon = showIcon ? R.drawable.ic_emergency : 0; button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0); } - button.setText(textId); + if (upperCase) { + CharSequence original = mContext.getResources().getText(textId); + String upper = original != null ? original.toString().toUpperCase() : null; + button.setText(upper); + } else { + button.setText(textId); + } + } + + /** + * @deprecated + * @param button + * @param phoneState + * @param shown + */ + public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown) { + updateEmergencyCallButtonState(button, phoneState, shown, false, true); } /** |
