diff options
48 files changed, 896 insertions, 318 deletions
diff --git a/api/current.txt b/api/current.txt index f13601ea6ef0..cf2ea6d99ec4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4642,6 +4642,7 @@ package android.app { method public android.content.Context getContext(); method public android.content.Context getTargetContext(); method public android.app.UiAutomation getUiAutomation(); + method public android.app.UiAutomation getUiAutomation(int); method public boolean invokeContextMenuAction(android.app.Activity, int, int); method public boolean invokeMenuActionSync(android.app.Activity, int, int); method public boolean isProfiling(); @@ -5538,6 +5539,7 @@ package android.app { public final class UiAutomation { method public void clearWindowAnimationFrameStats(); method public boolean clearWindowContentFrameStats(int); + method public void destroy(); method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException; method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String); method public android.view.accessibility.AccessibilityNodeInfo findFocus(int); @@ -5554,6 +5556,7 @@ package android.app { method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); method public android.graphics.Bitmap takeScreenshot(); method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException; + field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1 field public static final int ROTATION_FREEZE_0 = 0; // 0x0 field public static final int ROTATION_FREEZE_180 = 2; // 0x2 field public static final int ROTATION_FREEZE_270 = 3; // 0x3 @@ -22496,6 +22499,7 @@ package android.media.session { method public void setRatingType(int); method public void setSessionActivity(android.app.PendingIntent); field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 + field public static final int FLAG_HANDLES_PREPARE_ONLY = 4; // 0x4 field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } @@ -22574,6 +22578,7 @@ package android.media.session { field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L field public static final long ACTION_STOP = 1L; // 0x1L field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR; + field public static final java.lang.String EXTRA_PREPARE_ONLY = "android.media.session.extra.PREPARE_ONLY"; field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL field public static final int STATE_BUFFERING = 6; // 0x6 field public static final int STATE_CONNECTING = 8; // 0x8 @@ -22890,7 +22895,7 @@ package android.media.tv { method public void onInputAdded(java.lang.String); method public void onInputRemoved(java.lang.String); method public void onInputStateChanged(java.lang.String, int); - method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo); + method public void onTvInputInfoChanged(android.media.tv.TvInputInfo); } public abstract class TvInputService extends android.app.Service { @@ -22898,7 +22903,7 @@ package android.media.tv { method public final android.os.IBinder onBind(android.content.Intent); method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String); method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String); - method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo); + method public static final void setTvInputInfo(android.content.Context, android.media.tv.TvInputInfo); field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService"; field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input"; } @@ -28536,6 +28541,10 @@ package android.os { field public static final int OPEN = 32; // 0x20 } + public class FileUriExposedException extends java.lang.RuntimeException { + ctor public FileUriExposedException(java.lang.String); + } + public class Handler { ctor public Handler(); ctor public Handler(android.os.Handler.Callback); @@ -29127,6 +29136,7 @@ package android.os { method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects(); method public android.os.StrictMode.VmPolicy.Builder penaltyDeath(); method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork(); + method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure(); method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox(); method public android.os.StrictMode.VmPolicy.Builder penaltyLog(); method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int); diff --git a/api/system-current.txt b/api/system-current.txt index 0d335036d6cd..6a420e2f06ad 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4774,6 +4774,7 @@ package android.app { method public android.content.Context getContext(); method public android.content.Context getTargetContext(); method public android.app.UiAutomation getUiAutomation(); + method public android.app.UiAutomation getUiAutomation(int); method public boolean invokeContextMenuAction(android.app.Activity, int, int); method public boolean invokeMenuActionSync(android.app.Activity, int, int); method public boolean isProfiling(); @@ -5670,6 +5671,7 @@ package android.app { public final class UiAutomation { method public void clearWindowAnimationFrameStats(); method public boolean clearWindowContentFrameStats(int); + method public void destroy(); method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException; method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String); method public android.view.accessibility.AccessibilityNodeInfo findFocus(int); @@ -5686,6 +5688,7 @@ package android.app { method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); method public android.graphics.Bitmap takeScreenshot(); method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException; + field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1 field public static final int ROTATION_FREEZE_0 = 0; // 0x0 field public static final int ROTATION_FREEZE_180 = 2; // 0x2 field public static final int ROTATION_FREEZE_270 = 3; // 0x3 @@ -23837,6 +23840,7 @@ package android.media.session { method public void setRatingType(int); method public void setSessionActivity(android.app.PendingIntent); field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 + field public static final int FLAG_HANDLES_PREPARE_ONLY = 4; // 0x4 field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } @@ -23915,6 +23919,7 @@ package android.media.session { field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L field public static final long ACTION_STOP = 1L; // 0x1L field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR; + field public static final java.lang.String EXTRA_PREPARE_ONLY = "android.media.session.extra.PREPARE_ONLY"; field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL field public static final int STATE_BUFFERING = 6; // 0x6 field public static final int STATE_CONNECTING = 8; // 0x8 @@ -24405,7 +24410,7 @@ package android.media.tv { method public void onInputRemoved(java.lang.String); method public void onInputStateChanged(java.lang.String, int); method public void onInputUpdated(java.lang.String); - method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo); + method public void onTvInputInfoChanged(android.media.tv.TvInputInfo); } public abstract class TvInputService extends android.app.Service { @@ -24417,7 +24422,7 @@ package android.media.tv { method public java.lang.String onHardwareRemoved(android.media.tv.TvInputHardwareInfo); method public android.media.tv.TvInputInfo onHdmiDeviceAdded(android.hardware.hdmi.HdmiDeviceInfo); method public java.lang.String onHdmiDeviceRemoved(android.hardware.hdmi.HdmiDeviceInfo); - method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo); + method public static final void setTvInputInfo(android.content.Context, android.media.tv.TvInputInfo); field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService"; field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input"; } @@ -30564,6 +30569,10 @@ package android.os { field public static final int OPEN = 32; // 0x20 } + public class FileUriExposedException extends java.lang.RuntimeException { + ctor public FileUriExposedException(java.lang.String); + } + public class Handler { ctor public Handler(); ctor public Handler(android.os.Handler.Callback); @@ -31163,6 +31172,7 @@ package android.os { method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects(); method public android.os.StrictMode.VmPolicy.Builder penaltyDeath(); method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork(); + method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure(); method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox(); method public android.os.StrictMode.VmPolicy.Builder penaltyLog(); method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int); diff --git a/api/test-current.txt b/api/test-current.txt index 48fd6f0a1813..22eeef43a1ae 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -4642,6 +4642,7 @@ package android.app { method public android.content.Context getContext(); method public android.content.Context getTargetContext(); method public android.app.UiAutomation getUiAutomation(); + method public android.app.UiAutomation getUiAutomation(int); method public boolean invokeContextMenuAction(android.app.Activity, int, int); method public boolean invokeMenuActionSync(android.app.Activity, int, int); method public boolean isProfiling(); @@ -5538,6 +5539,7 @@ package android.app { public final class UiAutomation { method public void clearWindowAnimationFrameStats(); method public boolean clearWindowContentFrameStats(int); + method public void destroy(); method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException; method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String); method public android.view.accessibility.AccessibilityNodeInfo findFocus(int); @@ -5556,6 +5558,7 @@ package android.app { method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); method public android.graphics.Bitmap takeScreenshot(); method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException; + field public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 1; // 0x1 field public static final int ROTATION_FREEZE_0 = 0; // 0x0 field public static final int ROTATION_FREEZE_180 = 2; // 0x2 field public static final int ROTATION_FREEZE_270 = 3; // 0x3 @@ -22505,6 +22508,7 @@ package android.media.session { method public void setRatingType(int); method public void setSessionActivity(android.app.PendingIntent); field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 + field public static final int FLAG_HANDLES_PREPARE_ONLY = 4; // 0x4 field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } @@ -22583,6 +22587,7 @@ package android.media.session { field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L field public static final long ACTION_STOP = 1L; // 0x1L field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR; + field public static final java.lang.String EXTRA_PREPARE_ONLY = "android.media.session.extra.PREPARE_ONLY"; field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL field public static final int STATE_BUFFERING = 6; // 0x6 field public static final int STATE_CONNECTING = 8; // 0x8 @@ -22899,7 +22904,7 @@ package android.media.tv { method public void onInputAdded(java.lang.String); method public void onInputRemoved(java.lang.String); method public void onInputStateChanged(java.lang.String, int); - method public void onTvInputInfoChanged(java.lang.String, android.media.tv.TvInputInfo); + method public void onTvInputInfoChanged(android.media.tv.TvInputInfo); } public abstract class TvInputService extends android.app.Service { @@ -22907,7 +22912,7 @@ package android.media.tv { method public final android.os.IBinder onBind(android.content.Intent); method public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(java.lang.String); method public abstract android.media.tv.TvInputService.Session onCreateSession(java.lang.String); - method public final void setTvInputInfo(java.lang.String, android.media.tv.TvInputInfo); + method public static final void setTvInputInfo(android.content.Context, android.media.tv.TvInputInfo); field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService"; field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input"; } @@ -28545,6 +28550,10 @@ package android.os { field public static final int OPEN = 32; // 0x20 } + public class FileUriExposedException extends java.lang.RuntimeException { + ctor public FileUriExposedException(java.lang.String); + } + public class Handler { ctor public Handler(); ctor public Handler(android.os.Handler.Callback); @@ -29136,6 +29145,7 @@ package android.os { method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects(); method public android.os.StrictMode.VmPolicy.Builder penaltyDeath(); method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork(); + method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure(); method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox(); method public android.os.StrictMode.VmPolicy.Builder penaltyLog(); method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 11154f2ac9ea..dc3f64ab270e 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4397,7 +4397,7 @@ public class Activity extends ContextThemeWrapper String resolvedType = null; if (fillInIntent != null) { fillInIntent.migrateExtraStreamToClipData(); - fillInIntent.prepareToLeaveProcess(); + fillInIntent.prepareToLeaveProcess(this); resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -4629,7 +4629,7 @@ public class Activity extends ContextThemeWrapper intent.putExtra(Intent.EXTRA_REFERRER, referrer); } intent.migrateExtraStreamToClipData(); - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); result = ActivityManagerNative.getDefault() .startActivity(mMainThread.getApplicationThread(), getBasePackageName(), intent, intent.resolveTypeIfNeeded(getContentResolver()), mToken, @@ -4700,7 +4700,7 @@ public class Activity extends ContextThemeWrapper if (mParent == null) { try { intent.migrateExtraStreamToClipData(); - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); return ActivityManagerNative.getDefault() .startNextMatchingActivity(mToken, intent, options); } catch (RemoteException e) { @@ -5128,7 +5128,7 @@ public class Activity extends ContextThemeWrapper if (false) Log.v(TAG, "Finishing self: token=" + mToken); try { if (resultData != null) { - resultData.prepareToLeaveProcess(); + resultData.prepareToLeaveProcess(this); } if (ActivityManagerNative.getDefault() .finishActivity(mToken, resultCode, resultData, finishTask)) { @@ -5355,7 +5355,7 @@ public class Activity extends ContextThemeWrapper @PendingIntent.Flags int flags) { String packageName = getPackageName(); try { - data.prepareToLeaveProcess(); + data.prepareToLeaveProcess(this); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, @@ -6335,10 +6335,10 @@ public class Activity extends ContextThemeWrapper resultData = mResultData; } if (resultData != null) { - resultData.prepareToLeaveProcess(); + resultData.prepareToLeaveProcess(this); } try { - upIntent.prepareToLeaveProcess(); + upIntent.prepareToLeaveProcess(this); return ActivityManagerNative.getDefault().navigateUpTo(mToken, upIntent, resultCode, resultData); } catch (RemoteException e) { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 1e7457cdd551..100e67badb43 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -56,6 +56,7 @@ import android.net.Uri; import android.opengl.GLUtils; import android.os.AsyncTask; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.DropBoxManager; @@ -4959,16 +4960,22 @@ public final class ActivityThread { } /** - * For apps targetting SDK Honeycomb or later, we don't allow - * network usage on the main event loop / UI thread. - * - * Note to those grepping: this is what ultimately throws - * NetworkOnMainThreadException ... + * For apps targetting Honeycomb or later, we don't allow network usage + * on the main event loop / UI thread. This is what ultimately throws + * {@link NetworkOnMainThreadException}. */ - if (data.appInfo.targetSdkVersion > 9) { + if (data.appInfo.targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { StrictMode.enableDeathOnNetwork(); } + /** + * For apps targetting N or later, we don't allow file:// Uri exposure. + * This is what ultimately throws {@link FileUriExposedException}. + */ + if (data.appInfo.targetSdkVersion >= Build.VERSION_CODES.N) { + StrictMode.enableDeathOnFileUriExposure(); + } + NetworkSecurityPolicy.getInstance().setCleartextTrafficPermitted( (data.appInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index fab3740610e6..0d6e93d1717a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -752,7 +752,7 @@ class ContextImpl extends Context { String resolvedType = null; if (fillInIntent != null) { fillInIntent.migrateExtraStreamToClipData(); - fillInIntent.prepareToLeaveProcess(); + fillInIntent.prepareToLeaveProcess(this); resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -773,7 +773,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false, @@ -790,7 +790,7 @@ class ContextImpl extends Context { String[] receiverPermissions = receiverPermission == null ? null : new String[] {receiverPermission}; try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, @@ -805,7 +805,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, @@ -822,7 +822,7 @@ class ContextImpl extends Context { String[] receiverPermissions = receiverPermission == null ? null : new String[] {receiverPermission}; try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, @@ -839,7 +839,7 @@ class ContextImpl extends Context { String[] receiverPermissions = receiverPermission == null ? null : new String[] {receiverPermission}; try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false, @@ -856,7 +856,7 @@ class ContextImpl extends Context { String[] receiverPermissions = receiverPermission == null ? null : new String[] {receiverPermission}; try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE, @@ -919,7 +919,7 @@ class ContextImpl extends Context { String[] receiverPermissions = receiverPermission == null ? null : new String[] {receiverPermission}; try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermissions, appOp, @@ -933,7 +933,7 @@ class ContextImpl extends Context { public void sendBroadcastAsUser(Intent intent, UserHandle user) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); @@ -955,7 +955,7 @@ class ContextImpl extends Context { String[] receiverPermissions = receiverPermission == null ? null : new String[] {receiverPermission}; try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false, @@ -1006,7 +1006,7 @@ class ContextImpl extends Context { String[] receiverPermissions = receiverPermission == null ? null : new String[] {receiverPermission}; try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermissions, @@ -1022,7 +1022,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, @@ -1058,7 +1058,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, @@ -1077,7 +1077,7 @@ class ContextImpl extends Context { intent.setDataAndType(intent.getData(), resolvedType); } try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().unbroadcastIntent( mMainThread.getApplicationThread(), intent, getUserId()); } catch (RemoteException e) { @@ -1090,7 +1090,7 @@ class ContextImpl extends Context { public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, @@ -1105,7 +1105,7 @@ class ContextImpl extends Context { public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true, @@ -1140,7 +1140,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, @@ -1159,7 +1159,7 @@ class ContextImpl extends Context { intent.setDataAndType(intent.getData(), resolvedType); } try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(this); ActivityManagerNative.getDefault().unbroadcastIntent( mMainThread.getApplicationThread(), intent, user.getIdentifier()); } catch (RemoteException e) { @@ -1262,7 +1262,7 @@ class ContextImpl extends Context { private ComponentName startServiceCommon(Intent service, UserHandle user) { try { validateServiceIntent(service); - service.prepareToLeaveProcess(); + service.prepareToLeaveProcess(this); ComponentName cn = ActivityManagerNative.getDefault().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( getContentResolver()), getOpPackageName(), user.getIdentifier()); @@ -1291,7 +1291,7 @@ class ContextImpl extends Context { private boolean stopServiceCommon(Intent service, UserHandle user) { try { validateServiceIntent(service); - service.prepareToLeaveProcess(); + service.prepareToLeaveProcess(this); int res = ActivityManagerNative.getDefault().stopService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier()); @@ -1339,7 +1339,7 @@ class ContextImpl extends Context { < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { flags |= BIND_WAIVE_PRIORITY; } - service.prepareToLeaveProcess(); + service.prepareToLeaveProcess(this); int res = ActivityManagerNative.getDefault().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index 2caec36911f1..7640e75ba153 100644 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -33,7 +33,7 @@ import android.os.ParcelFileDescriptor; * {@hide} */ interface IUiAutomationConnection { - void connect(IAccessibilityServiceClient client); + void connect(IAccessibilityServiceClient client, int flags); void disconnect(); boolean injectInputEvent(in InputEvent event, boolean sync); boolean setRotation(int rotation); diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 24a347068b73..9a88f2c67697 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1503,7 +1503,7 @@ public class Instrumentation { } try { intent.migrateExtraStreamToClipData(); - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(who); int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), @@ -1561,7 +1561,7 @@ public class Instrumentation { String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { intents[i].migrateExtraStreamToClipData(); - intents[i].prepareToLeaveProcess(); + intents[i].prepareToLeaveProcess(who); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -1622,7 +1622,7 @@ public class Instrumentation { } try { intent.migrateExtraStreamToClipData(); - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(who); int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), @@ -1682,7 +1682,7 @@ public class Instrumentation { } try { intent.migrateExtraStreamToClipData(); - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(who); int result = ActivityManagerNative.getDefault() .startActivityAsUser(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), @@ -1721,7 +1721,7 @@ public class Instrumentation { } try { intent.migrateExtraStreamToClipData(); - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(who); int result = ActivityManagerNative.getDefault() .startActivityAsCaller(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), @@ -1759,7 +1759,7 @@ public class Instrumentation { } try { intent.migrateExtraStreamToClipData(); - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(who); int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), options); checkStartActivityResult(result, intent); @@ -1837,16 +1837,59 @@ public class Instrumentation { * {@link Instrumentation} APIs. Using both APIs at the same time is not * a mistake by itself but a client has to be aware of the APIs limitations. * </p> - * @return The UI automation instance. + * @return The UI automation instance. If none exists, a new one is created with no flags set. * * @see UiAutomation */ public UiAutomation getUiAutomation() { if (mUiAutomationConnection != null) { if (mUiAutomation == null) { + return getUiAutomation(0); + } + return mUiAutomation; + } + return null; + } + + /** + * Gets the {@link UiAutomation} instance with flags set. + * <p> + * <strong>Note:</strong> Only one UiAutomation can be obtained. Calling this method + * twice with different flags will fail unless the UiAutomation obtained in the first call + * is released with {@link UiAutomation#destroy()}. + * </p> + * <p> + * <strong>Note:</strong> The APIs exposed via the returned {@link UiAutomation} + * work across application boundaries while the APIs exposed by the instrumentation + * do not. For example, {@link Instrumentation#sendPointerSync(MotionEvent)} will + * not allow you to inject the event in an app different from the instrumentation + * target, while {@link UiAutomation#injectInputEvent(android.view.InputEvent, boolean)} + * will work regardless of the current application. + * </p> + * <p> + * A typical test case should be using either the {@link UiAutomation} or + * {@link Instrumentation} APIs. Using both APIs at the same time is not + * a mistake by itself but a client has to be aware of the APIs limitations. + * </p> + * + * @param flags The flags to be passed to the UiAutomation, for example + * {@link UiAutomation#FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES}. + * + * @return The UI automation instance. + * + * @see UiAutomation + */ + public UiAutomation getUiAutomation(int flags) { + if (mUiAutomationConnection != null) { + if ((mUiAutomation == null) || (mUiAutomation.isDestroyed())) { mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(), mUiAutomationConnection); - mUiAutomation.connect(); + mUiAutomation.connect(flags); + } else { + if (mUiAutomation.getFlags() != flags) { + throw new RuntimeException( + "Cannot get a UiAutomation with different flags from the existing one"); + } } return mUiAutomation; } @@ -1861,8 +1904,8 @@ public class Instrumentation { try { Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); } catch (RuntimeException e) { - Log.w(TAG, "Exception setting priority of instrumentation thread " - + Process.myTid(), e); + Log.w(TAG, "Exception setting priority of instrumentation thread " + + Process.myTid(), e); } if (mAutomaticPerformanceSnapshots) { startPerformanceSnapshot(); diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index edafe59ca454..412b098a5872 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -307,7 +307,7 @@ public final class PendingIntent implements Parcelable { context.getContentResolver()) : null; try { intent.migrateExtraStreamToClipData(); - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY, packageName, @@ -332,7 +332,7 @@ public final class PendingIntent implements Parcelable { context.getContentResolver()) : null; try { intent.migrateExtraStreamToClipData(); - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY, packageName, @@ -446,7 +446,7 @@ public final class PendingIntent implements Parcelable { String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { intents[i].migrateExtraStreamToClipData(); - intents[i].prepareToLeaveProcess(); + intents[i].prepareToLeaveProcess(context); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver()); } try { @@ -472,7 +472,7 @@ public final class PendingIntent implements Parcelable { String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { intents[i].migrateExtraStreamToClipData(); - intents[i].prepareToLeaveProcess(); + intents[i].prepareToLeaveProcess(context); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver()); } try { @@ -527,7 +527,7 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_BROADCAST, packageName, @@ -570,7 +570,7 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_SERVICE, packageName, diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index dce2e518d1e0..79d383c53cf6 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -105,6 +105,14 @@ public final class UiAutomation { /** Rotation constant: Freeze rotation to 270 degrees . */ public static final int ROTATION_FREEZE_270 = Surface.ROTATION_270; + /** + * UiAutomation supresses accessibility services by default. This flag specifies that + * existing accessibility services should continue to run, and that new ones may start. + * This flag is set when obtaining the UiAutomation from + * {@link Instrumentation#getUiAutomation(int)}. + */ + public static final int FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES = 0x00000001; + private final Object mLock = new Object(); private final ArrayList<AccessibilityEvent> mEventQueue = new ArrayList<AccessibilityEvent>(); @@ -123,6 +131,10 @@ public final class UiAutomation { private boolean mIsConnecting; + private boolean mIsDestroyed; + + private int mFlags; + /** * Listener for observing the {@link AccessibilityEvent} stream. */ @@ -182,11 +194,22 @@ public final class UiAutomation { } /** - * Connects this UiAutomation to the accessibility introspection APIs. + * Connects this UiAutomation to the accessibility introspection APIs with default flags. * * @hide */ public void connect() { + connect(0); + } + + /** + * Connects this UiAutomation to the accessibility introspection APIs. + * + * @param flags Any flags to apply to the automation as it gets connected + * + * @hide + */ + public void connect(int flags) { synchronized (mLock) { throwIfConnectedLocked(); if (mIsConnecting) { @@ -197,7 +220,8 @@ public final class UiAutomation { try { // Calling out without a lock held. - mUiAutomationConnection.connect(mClient); + mUiAutomationConnection.connect(mClient, flags); + mFlags = flags; } catch (RemoteException re) { throw new RuntimeException("Error while connecting UiAutomation", re); } @@ -227,6 +251,17 @@ public final class UiAutomation { } /** + * Get the flags used to connect the service. + * + * @return The flags used to connect + * + * @hide + */ + public int getFlags() { + return mFlags; + } + + /** * Disconnects this UiAutomation from the accessibility introspection APIs. * * @hide @@ -263,6 +298,17 @@ public final class UiAutomation { } /** + * Reports if the object has been destroyed + * + * @return {code true} if the object has been destroyed. + * + * @hide + */ + public boolean isDestroyed() { + return mIsDestroyed; + } + + /** * Sets a callback for observing the stream of {@link AccessibilityEvent}s. * * @param listener The callback. @@ -274,6 +320,15 @@ public final class UiAutomation { } /** + * Destroy this UiAutomation. After calling this method, attempting to use the object will + * result in errors. + */ + public void destroy() { + disconnect(); + mIsDestroyed = true; + } + + /** * Performs a global action. Such an action can be performed at any moment * regardless of the current application or user location in that application. * For example going back, going home, opening recents, etc. diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index bd10267bc0f5..276f774a8f4e 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -77,7 +77,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { private int mOwningUid; - public void connect(IAccessibilityServiceClient client) { + public void connect(IAccessibilityServiceClient client, int flags) { if (client == null) { throw new IllegalArgumentException("Client cannot be null!"); } @@ -87,7 +87,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { throw new IllegalStateException("Already connected."); } mOwningUid = Binder.getCallingUid(); - registerUiTestAutomationServiceLocked(client); + registerUiTestAutomationServiceLocked(client, flags); storeRotationStateLocked(); } } @@ -322,7 +322,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } } - private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client) { + private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client, + int flags) { IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface( ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); AccessibilityServiceInfo info = new AccessibilityServiceInfo(); @@ -337,7 +338,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { try { // Calling out with a lock held is fine since if the system // process is gone the client calling in will be killed. - manager.registerUiTestAutomationService(mToken, client, info); + manager.registerUiTestAutomationService(mToken, client, info, flags); mClient = client; } catch (RemoteException re) { throw new IllegalStateException("Error while registering UiTestAutomationService.", re); diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index 2260d7e07e03..10e6fb233249 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -522,7 +522,7 @@ public abstract class BroadcastReceiver { IActivityManager am = ActivityManagerNative.getDefault(); IBinder binder = null; try { - service.prepareToLeaveProcess(); + service.prepareToLeaveProcess(myContext); binder = am.peekService(service, service.resolveTypeIfNeeded( myContext.getContentResolver()), myContext.getOpPackageName()); } catch (RemoteException e) { diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index 6dfefacb2cb4..0ec58ea40fe4 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -823,14 +823,14 @@ public class ClipData implements Parcelable { * * @hide */ - public void prepareToLeaveProcess() { + public void prepareToLeaveProcess(boolean leavingPackage) { final int size = mItems.size(); for (int i = 0; i < size; i++) { final Item item = mItems.get(i); if (item.mIntent != null) { - item.mIntent.prepareToLeaveProcess(); + item.mIntent.prepareToLeaveProcess(leavingPackage); } - if (item.mUri != null && StrictMode.vmFileUriExposureEnabled()) { + if (item.mUri != null && StrictMode.vmFileUriExposureEnabled() && leavingPackage) { item.mUri.checkFileUriExposed("ClipData.Item.getUri()"); } } diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java index 5653cadfa44c..e67da2bfe7f8 100644 --- a/core/java/android/content/ClipboardManager.java +++ b/core/java/android/content/ClipboardManager.java @@ -118,7 +118,7 @@ public class ClipboardManager extends android.text.ClipboardManager { public void setPrimaryClip(ClipData clip) { try { if (clip != null) { - clip.prepareToLeaveProcess(); + clip.prepareToLeaveProcess(true); } getService().setPrimaryClip(clip, mContext.getOpPackageName()); } catch (RemoteException e) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 49b836318f5c..ee469dabcf43 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -8912,23 +8912,46 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ - public void prepareToLeaveProcess() { + public void prepareToLeaveProcess(Context context) { + final boolean leavingPackage = (mComponent == null) + || !Objects.equals(mComponent.getPackageName(), context.getPackageName()); + prepareToLeaveProcess(leavingPackage); + } + + /** + * Prepare this {@link Intent} to leave an app process. + * + * @hide + */ + public void prepareToLeaveProcess(boolean leavingPackage) { setAllowFds(false); if (mSelector != null) { - mSelector.prepareToLeaveProcess(); + mSelector.prepareToLeaveProcess(leavingPackage); } if (mClipData != null) { - mClipData.prepareToLeaveProcess(); - } - - if (mData != null && StrictMode.vmFileUriExposureEnabled()) { - // There are several ACTION_MEDIA_* broadcasts that send file:// - // Uris, so only check common actions. - if (ACTION_VIEW.equals(mAction) || - ACTION_EDIT.equals(mAction) || - ACTION_ATTACH_DATA.equals(mAction)) { - mData.checkFileUriExposed("Intent.getData()"); + mClipData.prepareToLeaveProcess(leavingPackage); + } + + if (mData != null && StrictMode.vmFileUriExposureEnabled() && leavingPackage) { + switch (mAction) { + case ACTION_MEDIA_REMOVED: + case ACTION_MEDIA_UNMOUNTED: + case ACTION_MEDIA_CHECKING: + case ACTION_MEDIA_NOFS: + case ACTION_MEDIA_MOUNTED: + case ACTION_MEDIA_SHARED: + case ACTION_MEDIA_UNSHARED: + case ACTION_MEDIA_BAD_REMOVAL: + case ACTION_MEDIA_UNMOUNTABLE: + case ACTION_MEDIA_EJECT: + case ACTION_MEDIA_SCANNER_STARTED: + case ACTION_MEDIA_SCANNER_FINISHED: + case ACTION_MEDIA_SCANNER_SCAN_FILE: + // Ignore legacy actions + break; + default: + mData.checkFileUriExposed("Intent.getData()"); } } } diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java index 584008c1db33..2cafa088665a 100644 --- a/core/java/android/hardware/input/KeyboardLayout.java +++ b/core/java/android/hardware/input/KeyboardLayout.java @@ -16,8 +16,10 @@ package android.hardware.input; +import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; +import android.util.LocaleList; import java.util.Locale; @@ -32,7 +34,8 @@ public final class KeyboardLayout implements Parcelable, private final String mLabel; private final String mCollection; private final int mPriority; - private final Locale[] mLocales; + @NonNull + private final LocaleList mLocales; private final int mVendorId; private final int mProductId; @@ -47,16 +50,12 @@ public final class KeyboardLayout implements Parcelable, }; public KeyboardLayout(String descriptor, String label, String collection, int priority, - Locale[] locales, int vid, int pid) { + LocaleList locales, int vid, int pid) { mDescriptor = descriptor; mLabel = label; mCollection = collection; mPriority = priority; - if (locales != null) { - mLocales = locales; - } else { - mLocales = new Locale[0]; - } + mLocales = locales; mVendorId = vid; mProductId = pid; } @@ -66,11 +65,7 @@ public final class KeyboardLayout implements Parcelable, mLabel = source.readString(); mCollection = source.readString(); mPriority = source.readInt(); - int N = source.readInt(); - mLocales = new Locale[N]; - for (int i = 0; i < N; i++) { - mLocales[i] = Locale.forLanguageTag(source.readString()); - } + mLocales = LocaleList.CREATOR.createFromParcel(source); mVendorId = source.readInt(); mProductId = source.readInt(); } @@ -108,7 +103,7 @@ public final class KeyboardLayout implements Parcelable, * This may be empty if a locale has not been assigned to this keyboard layout. * @return The keyboard layout's intended locale. */ - public Locale[] getLocales() { + public LocaleList getLocales() { return mLocales; } @@ -141,14 +136,7 @@ public final class KeyboardLayout implements Parcelable, dest.writeString(mLabel); dest.writeString(mCollection); dest.writeInt(mPriority); - if (mLocales != null) { - dest.writeInt(mLocales.length); - for (Locale l : mLocales) { - dest.writeString(l.toLanguageTag()); - } - } else { - dest.writeInt(0); - } + mLocales.writeToParcel(dest, 0); dest.writeInt(mVendorId); dest.writeInt(mProductId); } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 4a8dfbc5aefe..53b027b1888b 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -2343,7 +2343,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { */ public void checkFileUriExposed(String location) { if ("file".equals(getScheme())) { - StrictMode.onFileUriExposed(location); + StrictMode.onFileUriExposed(this, location); } } diff --git a/core/java/android/os/FileUriExposedException.java b/core/java/android/os/FileUriExposedException.java new file mode 100644 index 000000000000..e47abe288d22 --- /dev/null +++ b/core/java/android/os/FileUriExposedException.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 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.os; + +import android.content.Intent; + +/** + * The exception that is thrown when an application exposes a {@code file://} + * {@link android.net.Uri} to another app. + * <p> + * This exposure is discouraged since the receiving app may not have access to + * the shared path. For example, the receiving app may not have requested the + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime permission, + * or the platform may be sharing the {@link android.net.Uri} across user + * profile boundaries. + * <p> + * Instead, apps should use {@code content://} Uris so the platform can extend + * temporary permission for the receiving app to access the resource. + * <p> + * This is only thrown for applications targeting {@link Build.VERSION_CODES#N} + * or higher. Applications targeting earlier SDK versions are allowed to share + * {@code file://} {@link android.net.Uri}, but it's strongly discouraged. + * + * @see android.support.v4.content.FileProvider + * @see Intent#FLAG_GRANT_READ_URI_PERMISSION + */ +public class FileUriExposedException extends RuntimeException { + public FileUriExposedException(String message) { + super(message); + } +} diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index f1672df15109..91d88da8b733 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -24,6 +24,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.net.Uri; import android.util.ArrayMap; import android.util.Log; import android.util.Printer; @@ -46,7 +47,6 @@ import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** @@ -243,42 +243,15 @@ public final class StrictMode { // Byte 3: Penalty - /** - * @hide - */ + /** {@hide} */ public static final int PENALTY_LOG = 0x01 << 16; // normal android.util.Log - - // Used for both process and thread policy: - - /** - * @hide - */ + /** {@hide} */ public static final int PENALTY_DIALOG = 0x02 << 16; - - /** - * Death on any detected violation. - * - * @hide - */ + /** {@hide} */ public static final int PENALTY_DEATH = 0x04 << 16; - - /** - * Death just for detected network usage. - * - * @hide - */ - public static final int PENALTY_DEATH_ON_NETWORK = 0x08 << 16; - - /** - * Flash the screen during violations. - * - * @hide - */ + /** {@hide} */ public static final int PENALTY_FLASH = 0x10 << 16; - - /** - * @hide - */ + /** {@hide} */ public static final int PENALTY_DROPBOX = 0x20 << 16; /** @@ -294,12 +267,28 @@ public final class StrictMode { */ public static final int PENALTY_GATHER = 0x40 << 16; + // Byte 4: Special cases + + /** + * Death when network traffic is detected on main thread. + * + * @hide + */ + public static final int PENALTY_DEATH_ON_NETWORK = 0x01 << 24; + /** * Death when cleartext network traffic is detected. * * @hide */ - public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 0x80 << 16; + public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 0x02 << 24; + + /** + * Death when file exposure is detected. + * + * @hide + */ + public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 0x04 << 24; /** * Mask of all the penalty bits valid for thread policies. @@ -312,7 +301,7 @@ public final class StrictMode { * Mask of all the penalty bits valid for VM policies. */ private static final int VM_PENALTY_MASK = PENALTY_LOG | PENALTY_DEATH | PENALTY_DROPBOX - | PENALTY_DEATH_ON_CLEARTEXT_NETWORK; + | PENALTY_DEATH_ON_CLEARTEXT_NETWORK | PENALTY_DEATH_ON_FILE_URI_EXPOSURE; /** {@hide} */ public static final int NETWORK_POLICY_ACCEPT = 0; @@ -748,10 +737,22 @@ public final class StrictMode { } /** - * Detect when a {@code file://} {@link android.net.Uri} is exposed beyond this - * app. The receiving app may not have access to the sent path. - * Instead, when sharing files between apps, {@code content://} - * should be used with permission grants. + * Detect when this application exposes a {@code file://} + * {@link android.net.Uri} to another app. + * <p> + * This exposure is discouraged since the receiving app may not have + * access to the shared path. For example, the receiving app may not + * have requested the + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime + * permission, or the platform may be sharing the + * {@link android.net.Uri} across user profile boundaries. + * <p> + * Instead, apps should use {@code content://} Uris so the platform + * can extend temporary permission for the receiving app to access + * the resource. + * + * @see android.support.v4.content.FileProvider + * @see Intent#FLAG_GRANT_READ_URI_PERMISSION */ public Builder detectFileUriExposure() { return enable(DETECT_VM_FILE_URI_EXPOSURE); @@ -798,6 +799,16 @@ public final class StrictMode { } /** + * Crashes the whole process when a {@code file://} + * {@link android.net.Uri} is exposed beyond this app. + * + * @see #detectFileUriExposure() + */ + public Builder penaltyDeathOnFileUriExposure() { + return enable(PENALTY_DEATH_ON_FILE_URI_EXPOSURE); + } + + /** * Log detected violations to the system log. */ public Builder penaltyLog() { @@ -1111,6 +1122,25 @@ public final class StrictMode { } /** + * Used by the framework to make file usage a fatal error. + * + * @hide + */ + public static void enableDeathOnFileUriExposure() { + sVmPolicyMask |= DETECT_VM_FILE_URI_EXPOSURE | PENALTY_DEATH_ON_FILE_URI_EXPOSURE; + } + + /** + * Used by lame internal apps that haven't done the hard work to get + * themselves off file:// Uris yet. + * + * @hide + */ + public static void disableDeathOnFileUriExposure() { + sVmPolicyMask &= ~(DETECT_VM_FILE_URI_EXPOSURE | PENALTY_DEATH_ON_FILE_URI_EXPOSURE); + } + + /** * Parses the BlockGuard policy mask out from the Exception's * getMessage() String value. Kinda gross, but least * invasive. :/ @@ -1755,9 +1785,13 @@ public final class StrictMode { /** * @hide */ - public static void onFileUriExposed(String location) { - final String message = "file:// Uri exposed through " + location; - onVmPolicyViolation(null, new Throwable(message)); + public static void onFileUriExposed(Uri uri, String location) { + final String message = uri + " exposed beyond app through " + location; + if ((sVmPolicyMask & PENALTY_DEATH_ON_FILE_URI_EXPOSURE) != 0) { + throw new FileUriExposedException(message); + } else { + onVmPolicyViolation(null, new Throwable(message)); + } } /** diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 0c6a0c6e1d26..6ff9fe74859d 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1170,7 +1170,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } try { intent.migrateExtraStreamToClipData(); - intent.prepareToLeaveProcess(); + intent.prepareToLeaveProcess(mContext); int res = mSystemService.startVoiceActivity(mToken, intent, intent.resolveType(mContext.getContentResolver())); Instrumentation.checkStartActivityResult(res, intent); diff --git a/core/java/android/view/KeyboardShortcutGroup.java b/core/java/android/view/KeyboardShortcutGroup.java index 013255b9a612..57d07c0d0eac 100644 --- a/core/java/android/view/KeyboardShortcutGroup.java +++ b/core/java/android/view/KeyboardShortcutGroup.java @@ -32,6 +32,8 @@ import static com.android.internal.util.Preconditions.checkNotNull; public final class KeyboardShortcutGroup implements Parcelable { private final CharSequence mLabel; private final List<KeyboardShortcutInfo> mItems; + // The system group looks different UI wise. + private boolean mSystemGroup; /** * @param label The title to be used for this group, or null if there is none. @@ -50,10 +52,33 @@ public final class KeyboardShortcutGroup implements Parcelable { this(label, Collections.<KeyboardShortcutInfo>emptyList()); } + /** + * @param label The title to be used for this group, or null if there is none. + * @param items The set of items to be included. + * @param isSystemGroup Set this to {@code true} if this is s system group. + * @hide + */ + public KeyboardShortcutGroup(@Nullable CharSequence label, + @NonNull List<KeyboardShortcutInfo> items, boolean isSystemGroup) { + mLabel = label; + mItems = new ArrayList<>(checkNotNull(items)); + mSystemGroup = isSystemGroup; + } + + /** + * @param label The title to be used for this group, or null if there is none. + * @param isSystemGroup Set this to {@code true} if this is s system group. + * @hide + */ + public KeyboardShortcutGroup(@Nullable CharSequence label, boolean isSystemGroup) { + this(label, Collections.<KeyboardShortcutInfo>emptyList(), isSystemGroup); + } + private KeyboardShortcutGroup(Parcel source) { mItems = new ArrayList<>(); mLabel = source.readCharSequence(); source.readTypedList(mItems, KeyboardShortcutInfo.CREATOR); + mSystemGroup = source.readInt() == 1; } /** @@ -70,6 +95,11 @@ public final class KeyboardShortcutGroup implements Parcelable { return mItems; } + /** @hide **/ + public boolean isSystemGroup() { + return mSystemGroup; + } + /** * Adds an item to the existing list. * @@ -88,6 +118,7 @@ public final class KeyboardShortcutGroup implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeCharSequence(mLabel); dest.writeTypedList(mItems); + dest.writeInt(mSystemGroup ? 1 : 0); } public static final Creator<KeyboardShortcutGroup> CREATOR = @@ -99,4 +130,4 @@ public final class KeyboardShortcutGroup implements Parcelable { return new KeyboardShortcutGroup[size]; } }; -}
\ No newline at end of file +} diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 7ba046b0ca1f..81bb638e62fb 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -139,9 +139,7 @@ public final class PointerIcon implements Parcelable { private static final PointerIcon gNullIcon = new PointerIcon(STYLE_NULL); private static final SparseArray<PointerIcon> gSystemIcons = new SparseArray<PointerIcon>(); - - /** @hide */ - public static boolean sUseLargeIcons = false; + private static boolean sUseLargeIcons = false; private final int mStyle; private int mSystemIconResourceId; @@ -235,6 +233,15 @@ public final class PointerIcon implements Parcelable { } /** + * Updates wheter accessibility large icons are used or not. + * @hide + */ + public static void setUseLargeIcons(boolean use) { + sUseLargeIcons = use; + gSystemIcons.clear(); + } + + /** * Creates a custom pointer from the given bitmap and hotspot information. * * @param bitmap The bitmap for the icon. diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 9e7905759f2d..655c9b32c032 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -51,7 +51,7 @@ interface IAccessibilityManager { void removeAccessibilityInteractionConnection(IWindow windowToken); void registerUiTestAutomationService(IBinder owner, IAccessibilityServiceClient client, - in AccessibilityServiceInfo info); + in AccessibilityServiceInfo info, int flags); void unregisterUiTestAutomationService(IAccessibilityServiceClient client); diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index b2eab4c7498c..26421fb59ebb 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4191,10 +4191,10 @@ <string name="new_sms_notification_content">Open SMS app to view</string> <!-- Notification title shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] --> - <string name="user_encrypted_title">Device encrypted</string> + <string name="user_encrypted_title">Some functions might not be available</string> <!-- Notification message shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] --> - <string name="user_encrypted_message">Some functions might not be available</string> + <string name="user_encrypted_message">Touch to continue</string> <!-- Notification detail shown when user profile is credential encrypted and requires the user to unlock before some features are usable [CHAR LIMIT=30] --> - <string name="user_encrypted_detail">Touch to continue</string> + <string name="user_encrypted_detail">User profile locked</string> </resources> diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index b1a51a56db28..3d9b60deed2f 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -605,6 +605,7 @@ public final class MediaController { /** * Request that the player start playback for a specific media id. * + * @see PlaybackState#EXTRA_PREPARE_ONLY * @param mediaId The id of the requested media. * @param extras Optional extras that can include extra information about the media item * to be played. @@ -626,6 +627,7 @@ public final class MediaController { * An empty or null query should be treated as a request to play any * music. * + * @see PlaybackState#EXTRA_PREPARE_ONLY * @param query The search query. * @param extras Optional extras that can include extra information * about the query. @@ -646,6 +648,7 @@ public final class MediaController { /** * Request that the player start playback for a specific {@link Uri}. * + * @see PlaybackState#EXTRA_PREPARE_ONLY * @param uri The URI of the requested media. * @param extras Optional extras that can include extra information about the media item * to be played. diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index e1e9b792810f..8c5b19c36f11 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -87,6 +87,12 @@ public final class MediaSession { public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1; /** + * Set this flag on the session to indicate that it can handle + * the {@link PlaybackState#EXTRA_PREPARE_ONLY} field. + */ + public static final int FLAG_HANDLES_PREPARE_ONLY = 1 << 2; + + /** * System only flag for a session that needs to have priority over all other * sessions. This flag ensures this session will receive media button events * regardless of the current ordering in the system. @@ -100,6 +106,7 @@ public final class MediaSession { @IntDef(flag = true, value = { FLAG_HANDLES_MEDIA_BUTTONS, FLAG_HANDLES_TRANSPORT_CONTROLS, + FLAG_HANDLES_PREPARE_ONLY, FLAG_EXCLUSIVE_GLOBAL_PRIORITY }) public @interface SessionFlags { } diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java index bbe04b5875eb..1079a1f9a107 100644 --- a/media/java/android/media/session/PlaybackState.java +++ b/media/java/android/media/session/PlaybackState.java @@ -133,6 +133,21 @@ public final class PlaybackState implements Parcelable { public static final long ACTION_PLAY_FROM_URI = 1 << 13; /** + * Used as an optional boolean extra field in + * {@link MediaController.TransportControls#playFromMediaId}, + * {@link MediaController.TransportControls#playFromSearch}, and + * {@link MediaController.TransportControls#playFromUri}. Value of {@code true} overrides + * the default behavior of starting the playback after preparing. Check + * {@link MediaSession#FLAG_HANDLES_PREPARE_ONLY} to see if the media session supports this. + * + * @see MediaSession#FLAG_HANDLES_PREPARE_ONLY + * @see MediaController.TransportControls#playFromMediaId + * @see MediaController.TransportControls#playFromSearch + * @see MediaController.TransportControls#playFromUri + */ + public static final String EXTRA_PREPARE_ONLY = "android.media.session.extra.PREPARE_ONLY"; + + /** * This is the default playback state and indicates that no media has been * added yet, or the performer has been reset and has no content to play. * diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index 0febc162e5a7..d18933385463 100644 --- a/media/java/android/media/tv/ITvInputManager.aidl +++ b/media/java/android/media/tv/ITvInputManager.aidl @@ -41,6 +41,7 @@ import android.view.Surface; interface ITvInputManager { List<TvInputInfo> getTvInputList(int userId); TvInputInfo getTvInputInfo(in String inputId, int userId); + void setTvInputInfo(in TvInputInfo inputInfo, int userId); int getTvInputState(in String inputId, int userId); List<TvContentRatingSystemInfo> getTvContentRatingSystemList(int userId); diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl index 3bf415ba4bc5..395c9f3b384e 100644 --- a/media/java/android/media/tv/ITvInputManagerCallback.aidl +++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl @@ -29,5 +29,5 @@ oneway interface ITvInputManagerCallback { void onInputStateChanged(in String inputId, int state); - void onTvInputInfoChanged(in String inputId, in TvInputInfo TvInputInfo); + void onTvInputInfoChanged(in TvInputInfo TvInputInfo); } diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl index 9f13882685aa..74ab56215ff2 100644 --- a/media/java/android/media/tv/ITvInputServiceCallback.aidl +++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl @@ -27,6 +27,4 @@ oneway interface ITvInputServiceCallback { void addHardwareTvInput(in int deviceId, in TvInputInfo inputInfo); void addHdmiTvInput(in int id, in TvInputInfo inputInfo); void removeTvInput(in String inputId); - - void setTvInputInfo(in String inputId, in TvInputInfo inputInfo); } diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index f1de8fdc1817..86bded946ef2 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -726,12 +726,11 @@ public final class TvInputManager { } /** - * This is called when the information about a given TV input is changed. + * This is called when the information about a given TV input has been changed. * - * @param inputId The ID of the TV input. * @param inputInfo TvInputInfo object that contains the information about the TV input. */ - public void onTvInputInfoChanged(String inputId, TvInputInfo inputInfo) { + public void onTvInputInfoChanged(TvInputInfo inputInfo) { } } @@ -784,11 +783,11 @@ public final class TvInputManager { }); } - public void postTvInputInfoChanged(final String inputId, final TvInputInfo inputInfo) { + public void postTvInputInfoChanged(final TvInputInfo inputInfo) { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onTvInputInfoChanged(inputId, inputInfo); + mCallback.onTvInputInfoChanged(inputInfo); } }); } @@ -1089,10 +1088,10 @@ public final class TvInputManager { } @Override - public void onTvInputInfoChanged(String inputId, TvInputInfo inputInfo) { + public void onTvInputInfoChanged(TvInputInfo inputInfo) { synchronized (mLock) { for (TvInputCallbackRecord record : mCallbackRecords) { - record.postTvInputInfoChanged(inputId, inputInfo); + record.postTvInputInfoChanged(inputInfo); } } } @@ -1143,6 +1142,23 @@ public final class TvInputManager { } /** + * Sets a new TvInputInfo object for a given input. + * + * <p>This is called internally only by {@link TvInputService}. + * + * @param inputInfo The TvInputInfo object to set. + * @throws IllegalArgumentException if the argument is {@code null}. + */ + void setTvInputInfo(@NonNull TvInputInfo inputInfo) { + Preconditions.checkNotNull(inputInfo); + try { + mService.setTvInputInfo(inputInfo, mUserId); + } catch (RemoteException e) { + throw new RuntimeException("Error trying to set " + inputInfo, e); + } + } + + /** * Returns the state of a given TV input. * * <p>The state is one of the following: diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index f74ae660669a..a2b6346c5b08 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -262,16 +262,16 @@ public abstract class TvInputService extends Service { * <p>The system service automatically creates the TvInputInfo for each TV input based on * information collected from the AndroidManifest.xml, thus it is not necessary to call this * method unless the TV input has additional information to pass such as ability to record and - * tuner count. + * tuner count. Attempting to change information about a TV input that the calling package does + * not own does nothing. * - * @param inputId The ID of the TV input. + * @param context The application context. * @param inputInfo The TvInputInfo object that contains that new information. */ - public final void setTvInputInfo(String inputId, TvInputInfo inputInfo) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = inputId; - args.arg2 = inputInfo; - mServiceHandler.obtainMessage(ServiceHandler.DO_SET_TV_INPUT_INFO, args).sendToTarget(); + public static final void setTvInputInfo(Context context, TvInputInfo inputInfo) { + TvInputManager manager = (TvInputManager) context.getSystemService( + Context.TV_INPUT_SERVICE); + manager.setTvInputInfo(inputInfo); } private boolean isPassthroughInput(String inputId) { @@ -1938,7 +1938,6 @@ public abstract class TvInputService extends Service { private static final int DO_REMOVE_HARDWARE_TV_INPUT = 5; private static final int DO_ADD_HDMI_TV_INPUT = 6; private static final int DO_REMOVE_HDMI_TV_INPUT = 7; - private static final int DO_SET_TV_INPUT_INFO = 8; private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) { int n = mCallbacks.beginBroadcast(); @@ -1976,18 +1975,6 @@ public abstract class TvInputService extends Service { mCallbacks.finishBroadcast(); } - private void broadcastSetTvInputInfo(String inputId, TvInputInfo inputInfo) { - int n = mCallbacks.beginBroadcast(); - for (int i = 0; i < n; ++i) { - try { - mCallbacks.getBroadcastItem(i).setTvInputInfo(inputId, inputInfo); - } catch (RemoteException e) { - Log.e(TAG, "error in broadcastSetTvInputInfo", e); - } - } - mCallbacks.finishBroadcast(); - } - @Override public final void handleMessage(Message msg) { switch (msg.what) { @@ -2120,16 +2107,6 @@ public abstract class TvInputService extends Service { } return; } - case DO_SET_TV_INPUT_INFO: { - SomeArgs args = (SomeArgs) msg.obj; - String inputId = (String) args.arg1; - TvInputInfo inputInfo = (TvInputInfo) args.arg2; - if (inputInfo != null) { - broadcastSetTvInputInfo(inputId, inputInfo); - } - args.recycle(); - return; - } default: { Log.w(TAG, "Unhandled message code: " + msg.what); return; diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml new file mode 100644 index 000000000000..5a6553ff90db --- /dev/null +++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 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 + --> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/keyboard_shortcuts_keyword_wrapper" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="24dp" + android:paddingEnd="24dp" + android:paddingBottom="8dp"> + <TextView + android:id="@+id/keyboard_shortcuts_keyword" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingEnd="12dp" + android:background="@android:color/white" + android:textColor="#D9000000" + android:textSize="16sp" + android:maxLines="5" + android:singleLine="false" + android:scrollHorizontally="false" + android:layout_alignParentStart="true" + android:minWidth="100dp" + android:maxWidth="260dp"/> + <!--TODO: introduce and use a layout that allows wrapping and right align --> + <LinearLayout + android:id="@+id/keyboard_shortcuts_item_container" + android:layout_toEndOf="@+id/keyboard_shortcuts_keyword" + android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@android:color/white" + android:layout_alignParentEnd="true" + android:gravity="end" + android:textSize="14sp" + android:paddingStart="0dp" + android:paddingEnd="0dp" + android:scrollHorizontally="false" + android:minWidth="100dp" + android:maxWidth="260dp"/> +</RelativeLayout> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml new file mode 100644 index 000000000000..80a478a6d3d8 --- /dev/null +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2016 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 + --> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:textSize="14sp" + android:paddingStart="24dp" + android:paddingTop="20dp" + android:paddingEnd="24dp" + android:paddingBottom="13dp" /> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml new file mode 100644 index 000000000000..fa07eb1b105c --- /dev/null +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_container.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 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 + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content"> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml new file mode 100644 index 000000000000..5002c1298eb2 --- /dev/null +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 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 + --> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="4dp" + android:padding="4dp" + android:background="#EEEEEE" + android:textColor="#8C000000" + android:singleLine="true" + android:textSize="14sp"/> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml index 460433ea1c21..77b12641c8c6 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2015 The Android Open Source Project + ~ Copyright (C) 2016 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. @@ -14,11 +14,25 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<RelativeLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/keyboard_shortcuts_wrapper" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_marginTop="40dp" - android:focusable="true"> -</RelativeLayout> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/keyboard_shortcuts_wrapper" + android:layout_width="488dp" + android:layout_height="wrap_content" + android:focusable="true"> + <ScrollView + android:id="@+id/keyboard_shortcuts_scroll_view" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1"> + <LinearLayout + android:id="@+id/keyboard_shortcuts_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"/> + </ScrollView> + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?android:attr/listDivider"/> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml new file mode 100644 index 000000000000..802acfed73e8 --- /dev/null +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_wrapper.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 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 + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> +</LinearLayout> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index f0d9949fa90c..9bb6dc6abb75 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -154,4 +154,9 @@ <color name="switch_bar_background">#ff37474f</color> <color name="switch_accent_color">#ff7fcac3</color> + + <!-- Keyboard shortcuts colors --> + <color name="ksh_system_group_color">#ff00bcd4</color> + <color name="ksh_application_group_color">#fff44336</color> + <color name="ksh_dialog_background_color">#ffffffff</color> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 240d32ec95bb..4edb976b0c4d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -1175,7 +1175,7 @@ public abstract class BaseStatusBar extends SystemUI implements } protected void toggleKeyboardShortcuts() { - getKeyboardShortcuts().toggleKeyboardShortcuts(mContext); + getKeyboardShortcuts().toggleKeyboardShortcuts(); } protected void cancelPreloadingRecents() { @@ -1518,7 +1518,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected KeyboardShortcuts getKeyboardShortcuts() { if (mKeyboardShortcuts == null) { - mKeyboardShortcuts = new KeyboardShortcuts(); + mKeyboardShortcuts = new KeyboardShortcuts(mContext); } return mKeyboardShortcuts; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index b36fb7e65f7e..25e9a7aa1d8b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -19,25 +19,38 @@ package com.android.systemui.statusbar; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.graphics.Color; +import android.graphics.Typeface; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.os.Handler; +import android.os.Looper; +import android.util.DisplayMetrics; import android.util.Log; +import android.util.TypedValue; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; import android.view.KeyboardShortcutInfo; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.view.Window; -import android.view.WindowManager; import android.view.WindowManager.KeyboardShortcutsReceiver; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.recents.Recents; +import java.util.ArrayList; import java.util.List; import static android.content.Context.LAYOUT_INFLATER_SERVICE; -import static android.graphics.Color.TRANSPARENT; +import static android.graphics.Color.WHITE; import static android.view.Gravity.TOP; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; @@ -45,33 +58,44 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; * Contains functionality for handling keyboard shortcuts. */ public class KeyboardShortcuts { - private static final String TAG = "KeyboardShortcuts"; + private static final char SYSTEM_HOME_BASE_CHARACTER = '\u2386'; + private static final char SYSTEM_BACK_BASE_CHARACTER = '\u007F'; + private static final char SYSTEM_RECENTS_BASE_CHARACTER = '\u0009'; + + private final Handler mHandler = new Handler(Looper.getMainLooper()); + private final Context mContext; + private final OnClickListener dialogCloseListener = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismissKeyboardShortcutsDialog(); + } + }; private Dialog mKeyboardShortcutsDialog; - public KeyboardShortcuts() {} + public KeyboardShortcuts(Context context) { + this.mContext = context; + } - public void toggleKeyboardShortcuts(final Context context) { + public void toggleKeyboardShortcuts() { if (mKeyboardShortcutsDialog == null) { - Recents.getSystemServices().requestKeyboardShortcuts(context, + Recents.getSystemServices().requestKeyboardShortcuts(mContext, new KeyboardShortcutsReceiver() { @Override public void onKeyboardShortcutsReceived( final List<KeyboardShortcutGroup> result) { KeyboardShortcutGroup systemGroup = new KeyboardShortcutGroup( - context.getString(R.string.keyboard_shortcut_group_system)); + mContext.getString(R.string.keyboard_shortcut_group_system), true); systemGroup.addItem(new KeyboardShortcutInfo( - context.getString(R.string.keyboard_shortcut_group_system_home), - '\u2386', KeyEvent.META_META_ON)); + mContext.getString(R.string.keyboard_shortcut_group_system_home), + SYSTEM_HOME_BASE_CHARACTER, KeyEvent.META_META_ON)); systemGroup.addItem(new KeyboardShortcutInfo( - context.getString(R.string.keyboard_shortcut_group_system_back), - '\u007F', KeyEvent.META_META_ON)); + mContext.getString(R.string.keyboard_shortcut_group_system_back), + SYSTEM_BACK_BASE_CHARACTER, KeyEvent.META_META_ON)); systemGroup.addItem(new KeyboardShortcutInfo( - context.getString(R.string.keyboard_shortcut_group_system_recents), - '\u0009', KeyEvent.META_ALT_ON)); + mContext.getString(R.string.keyboard_shortcut_group_system_recents), + SYSTEM_RECENTS_BASE_CHARACTER, KeyEvent.META_ALT_ON)); result.add(systemGroup); - Log.i(TAG, "Keyboard shortcuts received: " + String.valueOf(result)); - showKeyboardShortcutsDialog(context); + showKeyboardShortcutsDialog(result); } }); } else { @@ -79,45 +103,106 @@ public class KeyboardShortcuts { } } - private void showKeyboardShortcutsDialog(Context context) { - // Create dialog. - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context); - LayoutInflater inflater = (LayoutInflater) context.getSystemService( - LAYOUT_INFLATER_SERVICE); - final View keyboardShortcutsView = inflater.inflate( - R.layout.keyboard_shortcuts_view, null); - - populateKeyboardShortcuts(keyboardShortcutsView.findViewById( - R.id.keyboard_shortcuts_wrapper)); - dialogBuilder.setView(keyboardShortcutsView); - mKeyboardShortcutsDialog = dialogBuilder.create(); - mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true); - - // Setup window. - Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow(); - keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG); - keyboardShortcutsWindow.setBackgroundDrawable( - new ColorDrawable(TRANSPARENT)); - keyboardShortcutsWindow.setGravity(TOP); - keyboardShortcutsView.post(new Runnable() { + public void dismissKeyboardShortcutsDialog() { + if (mKeyboardShortcutsDialog != null) { + mKeyboardShortcutsDialog.dismiss(); + mKeyboardShortcutsDialog = null; + } + } + + private void showKeyboardShortcutsDialog( + final List<KeyboardShortcutGroup> keyboardShortcutGroups) { + // Need to post on the main thread. + mHandler.post(new Runnable() { + @Override public void run() { + // TODO: break all this code out into a handleShowKeyboard... + // Might add more things posted; should consider adding a custom handler so + // you can send the keyboardShortcutsGroups as part of the message. + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext); + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( + LAYOUT_INFLATER_SERVICE); + final View keyboardShortcutsView = inflater.inflate( + R.layout.keyboard_shortcuts_view, null); + DisplayMetrics dm = mContext.getResources().getDisplayMetrics(); + ScrollView scrollView = (ScrollView) keyboardShortcutsView.findViewById( + R.id.keyboard_shortcuts_scroll_view); + // TODO: find a better way to set the height. + scrollView.setLayoutParams(new LinearLayout.LayoutParams( + LayoutParams.WRAP_CONTENT, + (int) (dm.heightPixels * dm.density))); + + populateKeyboardShortcuts((LinearLayout) keyboardShortcutsView.findViewById( + R.id.keyboard_shortcuts_container), keyboardShortcutGroups); + dialogBuilder.setView(keyboardShortcutsView); + dialogBuilder.setPositiveButton(R.string.quick_settings_done, dialogCloseListener); + mKeyboardShortcutsDialog = dialogBuilder.create(); + mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true); + + // Setup window. + Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow(); + keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG); + keyboardShortcutsWindow.setBackgroundDrawable( + mContext.getDrawable(R.color.ksh_dialog_background_color)); + keyboardShortcutsWindow.setGravity(TOP); mKeyboardShortcutsDialog.show(); } }); } - public void dismissKeyboardShortcutsDialog() { - if (mKeyboardShortcutsDialog != null) { - mKeyboardShortcutsDialog.dismiss(); - mKeyboardShortcutsDialog = null; + private void populateKeyboardShortcuts(LinearLayout keyboardShortcutsLayout, + List<KeyboardShortcutGroup> keyboardShortcutGroups) { + LayoutInflater inflater = LayoutInflater.from(mContext); + final int keyboardShortcutGroupsSize = keyboardShortcutGroups.size(); + for (int i = 0; i < keyboardShortcutGroupsSize; i++) { + KeyboardShortcutGroup group = keyboardShortcutGroups.get(i); + TextView categoryTitle = (TextView) inflater.inflate( + R.layout.keyboard_shortcuts_category_title, keyboardShortcutsLayout, false); + categoryTitle.setText(group.getLabel()); + categoryTitle.setTextColor(group.isSystemGroup() + ? mContext.getColor(R.color.ksh_system_group_color) + : mContext.getColor(R.color.ksh_application_group_color)); + keyboardShortcutsLayout.addView(categoryTitle); + + LinearLayout shortcutWrapper = (LinearLayout) inflater.inflate( + R.layout.keyboard_shortcuts_wrapper, null); + final int itemsSize = group.getItems().size(); + for (int j = 0; j < itemsSize; j++) { + KeyboardShortcutInfo info = group.getItems().get(j); + View shortcutView = inflater.inflate(R.layout.keyboard_shortcut_app_item, null); + TextView textView = (TextView) shortcutView + .findViewById(R.id.keyboard_shortcuts_keyword); + textView.setText(info.getLabel()); + + List<String> shortcutKeys = getHumanReadableShortcutKeys(info); + final int shortcutKeysSize = shortcutKeys.size(); + for (int k = 0; k < shortcutKeysSize; k++) { + String shortcutKey = shortcutKeys.get(k); + TextView shortcutKeyView = (TextView) inflater.inflate( + R.layout.keyboard_shortcuts_key_view, null); + shortcutKeyView.setText(shortcutKey); + LinearLayout shortcutItemsContainer = (LinearLayout) shortcutView + .findViewById(R.id.keyboard_shortcuts_item_container); + shortcutItemsContainer.addView(shortcutKeyView); + } + shortcutWrapper.addView(shortcutView); + } + + // TODO: merge container and wrapper into one xml file - wrapper is always a child of + // container. + LinearLayout shortcutsContainer = (LinearLayout) inflater.inflate( + R.layout.keyboard_shortcuts_container, null); + shortcutsContainer.addView(shortcutWrapper); + keyboardShortcutsLayout.addView(shortcutsContainer); } } - /** - * @return {@code true} if the keyboard shortcuts have been successfully populated. - */ - private boolean populateKeyboardShortcuts(View keyboardShortcutsLayout) { - // TODO: Populate shortcuts. - return true; + private List<String> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) { + // TODO: fix the shortcuts. Find or build an util which can produce human readable + // names of the baseCharacter and the modifiers. + List<String> shortcutKeys = new ArrayList<>(); + shortcutKeys.add(KeyEvent.metaStateToString(info.getModifiers()).toUpperCase()); + shortcutKeys.add(Character.getName(info.getBaseCharacter()).toUpperCase()); + return shortcutKeys; } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 03809c07ea10..2a8672dd099d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -27,6 +27,7 @@ import android.annotation.NonNull; import android.app.AlertDialog; import android.app.PendingIntent; import android.app.StatusBarManager; +import android.app.UiAutomation; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -51,6 +52,7 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.Debug; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -258,10 +260,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { UserState userState = getCurrentUserStateLocked(); // We have to reload the installed services since some services may // have different attributes, resolve info (does not support equals), - // etc. Remove them then to force reload. Do it even if automation is - // running since when it goes away, we will have to reload as well. + // etc. Remove them then to force reload. userState.mInstalledServices.clear(); - if (userState.mUiAutomationService == null) { + if (!userState.isUiAutomationSuppressingOtherServices()) { if (readConfigurationForUserStateLocked(userState)) { onUserStateChangedLocked(userState); } @@ -296,7 +297,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, userState.mTouchExplorationGrantedServices, userId); // We will update when the automation service dies. - if (userState.mUiAutomationService == null) { + if (!userState.isUiAutomationSuppressingOtherServices()) { onUserStateChangedLocked(userState); } return; @@ -330,7 +331,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userState.mEnabledServices, userId); // We will update when the automation service dies. - if (userState.mUiAutomationService == null) { + if (!userState.isUiAutomationSuppressingOtherServices()) { onUserStateChangedLocked(userState); } } @@ -362,7 +363,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } else if (Intent.ACTION_USER_PRESENT.equals(action)) { // We will update when the automation service dies. UserState userState = getCurrentUserStateLocked(); - if (userState.mUiAutomationService == null) { + if (!userState.isUiAutomationSuppressingOtherServices()) { if (readConfigurationForUserStateLocked(userState)) { onUserStateChangedLocked(userState); } @@ -473,11 +474,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); - // The automation service is a fake one and should not be reported - // to clients as being enabled. The automation service is always the - // only active one, if it exists. + // The automation service can suppress other services. UserState userState = getUserStateLocked(resolvedUserId); - if (userState.mUiAutomationService != null) { + if (userState.isUiAutomationSuppressingOtherServices()) { return Collections.emptyList(); } @@ -490,7 +489,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int serviceCount = services.size(); for (int i = 0; i < serviceCount; i++) { Service service = services.get(i); - if ((service.mFeedbackType & feedbackTypeBit) != 0) { + // Don't report the UIAutomation (fake service) + if (!sFakeAccessibilityServiceComponentName.equals(service.mComponentName) + && (service.mFeedbackType & feedbackTypeBit) != 0) { result.add(service.mAccessibilityServiceInfo); } } @@ -621,7 +622,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public void registerUiTestAutomationService(IBinder owner, IAccessibilityServiceClient serviceClient, - AccessibilityServiceInfo accessibilityServiceInfo) { + AccessibilityServiceInfo accessibilityServiceInfo, + int flags) { mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT, FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE); @@ -645,15 +647,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mUiAutomationServiceOwner = owner; userState.mUiAutomationServiceClient = serviceClient; - - // Set the temporary state. + userState.mUiAutomationFlags = flags; userState.mIsAccessibilityEnabled = true; - userState.mIsTouchExplorationEnabled = false; - userState.mIsEnhancedWebAccessibilityEnabled = false; - userState.mIsDisplayMagnificationEnabled = false; - userState.mIsAutoclickEnabled = false; userState.mInstalledServices.add(accessibilityServiceInfo); - userState.mEnabledServices.clear(); + if (userState.isUiAutomationSuppressingOtherServices()) { + // Set the temporary state. + userState.mIsTouchExplorationEnabled = false; + userState.mIsEnhancedWebAccessibilityEnabled = false; + userState.mIsDisplayMagnificationEnabled = false; + userState.mIsAutoclickEnabled = false; + userState.mEnabledServices.clear(); + } userState.mEnabledServices.add(sFakeAccessibilityServiceComponentName); userState.mTouchExplorationGrantedServices.add(sFakeAccessibilityServiceComponentName); @@ -694,7 +698,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { UserState userState = getCurrentUserStateLocked(); // This is a nop if UI automation is enabled. - if (userState.mUiAutomationService != null) { + if (userState.isUiAutomationSuppressingOtherServices()) { return; } @@ -1027,6 +1031,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (!mTempComponentNameSet.equals(userState.mEnabledServices)) { userState.mEnabledServices.clear(); userState.mEnabledServices.addAll(mTempComponentNameSet); + if (userState.mUiAutomationService != null) { + userState.mEnabledServices.add(sFakeAccessibilityServiceComponentName); + } mTempComponentNameSet.clear(); return true; } @@ -3981,6 +3988,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public boolean mAccessibilityFocusOnlyInActiveWindow; private Service mUiAutomationService; + private int mUiAutomationFlags; private IAccessibilityServiceClient mUiAutomationServiceClient; private IBinder mUiAutomationServiceOwner; @@ -4044,6 +4052,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public void destroyUiAutomationService() { mUiAutomationService = null; + mUiAutomationFlags = 0; mUiAutomationServiceClient = null; if (mUiAutomationServiceOwner != null) { mUiAutomationServiceOwner.unlinkToDeath( @@ -4051,6 +4060,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mUiAutomationServiceOwner = null; } } + + boolean isUiAutomationSuppressingOtherServices() { + return ((mUiAutomationService != null) && (mUiAutomationFlags + & UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES) == 0); + } } private final class AccessibilityContentObserver extends ContentObserver { @@ -4130,8 +4144,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // we are checking for changes only the parent settings. UserState userState = getCurrentUserStateLocked(); - // We will update when the automation service dies. - if (userState.mUiAutomationService != null) { + // If the automation service is suppressing, we will update when it dies. + if (userState.isUiAutomationSuppressingOtherServices()) { return; } diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index e9f0a7ae63de..d0cd5365db15 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -200,7 +200,7 @@ public class LockSettingsService extends ILockSettings.Stub { PendingIntent.FLAG_UPDATE_CURRENT); Notification notification = new Notification.Builder(mContext) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning) + .setSmallIcon(com.android.internal.R.drawable.ic_secure) .setWhen(0) .setOngoing(true) .setTicker(title) diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 573afd67d545..033a243d2c3e 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -16,7 +16,9 @@ package com.android.server.input; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.LocaleList; import android.view.Display; import com.android.internal.inputmethod.InputMethodSubtypeHandle; import com.android.internal.os.SomeArgs; @@ -780,8 +782,10 @@ public class InputManagerService extends IInputManager.Stub || layout.getProductId() != d.getProductId()) { return; } - for (Locale l : layout.getLocales()) { - if (isCompatibleLocale(systemLocale, l)) { + final LocaleList locales = layout.getLocales(); + final int numLocales = locales.size(); + for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) { + if (isCompatibleLocale(systemLocale, locales.get(localeIndex))) { layouts.add(layout); break; } @@ -799,9 +803,12 @@ public class InputManagerService extends IInputManager.Stub final int N = layouts.size(); for (int i = 0; i < N; i++) { KeyboardLayout layout = layouts.get(i); - for (Locale l : layout.getLocales()) { - if (l.getCountry().equals(systemLocale.getCountry()) - && l.getVariant().equals(systemLocale.getVariant())) { + final LocaleList locales = layout.getLocales(); + final int numLocales = locales.size(); + for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) { + final Locale locale = locales.get(localeIndex); + if (locale.getCountry().equals(systemLocale.getCountry()) + && locale.getVariant().equals(systemLocale.getVariant())) { return layout.getDescriptor(); } } @@ -809,8 +816,11 @@ public class InputManagerService extends IInputManager.Stub // Then try an exact match of language and country for (int i = 0; i < N; i++) { KeyboardLayout layout = layouts.get(i); - for (Locale l : layout.getLocales()) { - if (l.getCountry().equals(systemLocale.getCountry())) { + final LocaleList locales = layout.getLocales(); + final int numLocales = locales.size(); + for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) { + final Locale locale = locales.get(localeIndex); + if (locale.getCountry().equals(systemLocale.getCountry())) { return layout.getDescriptor(); } } @@ -1170,7 +1180,7 @@ public class InputManagerService extends IInputManager.Stub 0); String languageTags = a.getString( com.android.internal.R.styleable.KeyboardLayout_locale); - Locale[] locales = getLocalesFromLanguageTags(languageTags); + LocaleList locales = getLocalesFromLanguageTags(languageTags); int vid = a.getInt( com.android.internal.R.styleable.KeyboardLayout_vendorId, -1); int pid = a.getInt( @@ -1210,16 +1220,12 @@ public class InputManagerService extends IInputManager.Stub } } - private static Locale[] getLocalesFromLanguageTags(String languageTags) { + @NonNull + private static LocaleList getLocalesFromLanguageTags(String languageTags) { if (TextUtils.isEmpty(languageTags)) { - return new Locale[0]; + return LocaleList.getEmptyLocaleList(); } - String[] tags = languageTags.split("\\|"); - Locale[] locales = new Locale[tags.length]; - for (int i = 0; i < tags.length; i++) { - locales[i] = Locale.forLanguageTag(tags[i]); - } - return locales; + return LocaleList.forLanguageTags(languageTags.replace('|', ',')); } /** @@ -1596,7 +1602,7 @@ public class InputManagerService extends IInputManager.Stub final int accessibilityConfig = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, 0, UserHandle.USER_CURRENT); - PointerIcon.sUseLargeIcons = (accessibilityConfig == 1); + PointerIcon.setUseLargeIcons(accessibilityConfig == 1); nativeReloadPointerIcons(mPtr); } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 62e7fb49e082..318f966d6fcd 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -71,6 +71,7 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.view.InputChannel; @@ -779,15 +780,14 @@ public final class TvInputManagerService extends SystemService { } } - private void notifyTvInputInfoChanged(UserState userState, String inputId, - TvInputInfo inputInfo) { + private void setTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) { if (DEBUG) { - Slog.d(TAG, "notifyTvInputInfoChanged(inputId=" + inputId + ", inputInfo=" + inputInfo - + ")"); + Slog.d(TAG, "setTvInputInfoLocked(inputInfo=" + inputInfo + ")"); } + // TODO: Also update the internal input list. for (ITvInputManagerCallback callback : userState.callbackSet) { try { - callback.onTvInputInfoChanged(inputId, inputInfo); + callback.onTvInputInfoChanged(inputInfo); } catch (RemoteException e) { Slog.e(TAG, "failed to report changed input info to callback", e); } @@ -846,6 +846,36 @@ public final class TvInputManagerService extends SystemService { } } + public void setTvInputInfo(TvInputInfo inputInfo, int userId) { + String inputInfoPackageName = inputInfo.getServiceInfo().packageName; + String callingPackageName = getCallingPackageName(); + if (!TextUtils.equals(inputInfoPackageName, callingPackageName)) { + throw new IllegalArgumentException("calling package " + callingPackageName + + " is not allowed to change TvInputInfo for " + inputInfoPackageName); + } + + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), + Binder.getCallingUid(), userId, "setTvInputInfoChanged"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + UserState userState = getOrCreateUserStateLocked(resolvedUserId); + setTvInputInfoLocked(userState, inputInfo); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private String getCallingPackageName() { + final String[] packages = mContext.getPackageManager().getPackagesForUid( + Binder.getCallingUid()); + if (packages != null && packages.length > 0) { + return packages[0]; + } + return "unknown"; + } + @Override public int getTvInputState(String inputId, int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), @@ -2233,18 +2263,6 @@ public final class TvInputManagerService extends SystemService { } } } - - @Override - public void setTvInputInfo(String inputId, TvInputInfo inputInfo) { - ensureValidInput(inputInfo); - synchronized (mLock) { - if (DEBUG) { - Slog.d(TAG, "setTvInputInfo(" + inputInfo + ")"); - } - UserState userState = getOrCreateUserStateLocked(mUserId); - notifyTvInputInfoChanged(userState, inputId, inputInfo); - } - } } private final class SessionCallback extends ITvInputSessionCallback.Stub { diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java index 0979cd32a1e2..66aa8632b9a2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java +++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java @@ -35,10 +35,10 @@ public class WindowManagerDebugConfig { static final boolean DEBUG_RESIZE = false; static final boolean DEBUG = false; - static final boolean DEBUG_ADD_REMOVE = false; + static final boolean DEBUG_ADD_REMOVE = true; static final boolean DEBUG_FOCUS = false; static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false; - static final boolean DEBUG_ANIM = false; + static final boolean DEBUG_ANIM = true; static final boolean DEBUG_KEYGUARD = false; static final boolean DEBUG_LAYOUT = false; static final boolean DEBUG_LAYERS = false; @@ -50,7 +50,7 @@ public class WindowManagerDebugConfig { static final boolean DEBUG_ORIENTATION = false; static final boolean DEBUG_APP_ORIENTATION = false; static final boolean DEBUG_CONFIGURATION = false; - static final boolean DEBUG_APP_TRANSITIONS = false; + static final boolean DEBUG_APP_TRANSITIONS = true; static final boolean DEBUG_STARTING_WINDOW = false; static final boolean DEBUG_WALLPAPER = false; static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 1214948911e1..4dd2b4dfa9ad 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -751,15 +751,16 @@ final class WindowState implements WindowManagerPolicy.WindowState { Math.min(mStableFrame.bottom, frame.bottom)); } - mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0), - Math.max(mOverscanFrame.top - frame.top, 0), - Math.max(frame.right - mOverscanFrame.right, 0), - Math.max(frame.bottom - mOverscanFrame.bottom, 0)); - - + if (!inFreeformWorkspace()) { + // Freeform windows can be positioned outside of the display frame, but that is not a + // reason to provide them with overscan insets. + mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0), + Math.max(mOverscanFrame.top - frame.top, 0), + Math.max(frame.right - mOverscanFrame.right, 0), + Math.max(frame.bottom - mOverscanFrame.bottom, 0)); + } if (mAttrs.type == TYPE_DOCK_DIVIDER) { - // For the docked divider, we calculate the stable insets like a full-screen window // so it can use it to calculate the snap positions. mStableInsets.set(Math.max(mStableFrame.left - mDisplayFrame.left, 0), @@ -2238,7 +2239,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { } pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface); pw.print(" mShownPosition="); mShownPosition.printShortString(pw); - pw.print(" isReadyForDisplay()="); pw.println(isReadyForDisplay()); + pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay()); + pw.print(" hasSavedSurface()="); pw.println(hasSavedSurface()); if (dumpAll) { pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw); pw.print(" last="); mLastFrame.printShortString(pw); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index cffcc5db36ff..428ab7a5eb5d 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -567,6 +567,10 @@ class WindowStateAnimator { WindowSurfaceController createSurfaceLocked() { final WindowState w = mWin; + if (w.hasSavedSurface()) { + Slog.i(TAG, "***** createSurface: " + this + ": called when we had a saved surface"); + } + if (mSurfaceController == null) { if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG, "createSurface " + this + ": mDrawState=DRAW_PENDING"); |