diff options
110 files changed, 3374 insertions, 956 deletions
@@ -483,6 +483,7 @@ aidl_files := \ frameworks/base/graphics/java/android/graphics/Rect.aidl \ frameworks/base/core/java/android/accounts/AuthenticatorDescription.aidl \ frameworks/base/core/java/android/accounts/Account.aidl \ + frameworks/base/core/java/android/app/admin/SystemUpdatePolicy.aidl \ frameworks/base/core/java/android/print/PrintDocumentInfo.aidl \ frameworks/base/core/java/android/print/PageRange.aidl \ frameworks/base/core/java/android/print/PrintAttributes.aidl \ diff --git a/api/current.txt b/api/current.txt index f57e1f6..9b7042a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3761,7 +3761,9 @@ package android.app { method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock(); method public void set(int, long, android.app.PendingIntent); method public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent); + method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent); method public void setExact(int, long, android.app.PendingIntent); + method public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent); method public void setInexactRepeating(int, long, long, android.app.PendingIntent); method public void setRepeating(int, long, long, android.app.PendingIntent); method public void setTime(long); @@ -5863,23 +5865,21 @@ package android.app.admin { field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2 } - public class SystemUpdatePolicy { - ctor public SystemUpdatePolicy(); + public class SystemUpdatePolicy implements android.os.Parcelable { + method public static android.app.admin.SystemUpdatePolicy createAutomaticInstallPolicy(); + method public static android.app.admin.SystemUpdatePolicy createPostponeInstallPolicy(); + method public static android.app.admin.SystemUpdatePolicy createWindowedInstallPolicy(int, int); + method public int describeContents(); method public int getInstallWindowEnd(); method public int getInstallWindowStart(); method public int getPolicyType(); - method public void setAutomaticInstallPolicy(); - method public void setPostponeInstallPolicy(); - method public void setWindowedInstallPolicy(int, int) throws android.app.admin.SystemUpdatePolicy.InvalidWindowException; + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR; field public static final int TYPE_INSTALL_AUTOMATIC = 1; // 0x1 field public static final int TYPE_INSTALL_WINDOWED = 2; // 0x2 field public static final int TYPE_POSTPONE = 3; // 0x3 } - public static class SystemUpdatePolicy.InvalidWindowException extends java.lang.Exception { - ctor public SystemUpdatePolicy.InvalidWindowException(java.lang.String); - } - } package android.app.backup { @@ -8251,7 +8251,6 @@ package android.content { field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list"; field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list"; field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER"; - field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER"; field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED"; @@ -17827,6 +17826,7 @@ package android.media.tv { public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback { ctor public TvInputService.Session(android.content.Context); + method public void layoutSurface(int, int, int, int); method public void notifyChannelRetuned(android.net.Uri); method public void notifyContentAllowed(); method public void notifyContentBlocked(android.media.tv.TvContentRating); @@ -17841,6 +17841,7 @@ package android.media.tv { method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); method public boolean onKeyUp(int, android.view.KeyEvent); + method public void onOverlayViewSizeChanged(int, int); method public abstract void onRelease(); method public boolean onSelectTrack(int, java.lang.String); method public abstract void onSetCaptionEnabled(boolean); @@ -27231,7 +27232,6 @@ package android.renderscript { method public void copyTo(short[]); method public void copyTo(int[]); method public void copyTo(float[]); - method public void copyToFieldPacker(int, int, int, int, android.renderscript.FieldPacker); method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int); method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap); method public static android.renderscript.Allocation createCubemapFromCubeFaces(android.renderscript.RenderScript, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int); @@ -28684,10 +28684,8 @@ package android.service.chooser { public final class ChooserTarget implements android.os.Parcelable { ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.app.PendingIntent); ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.content.IntentSender); - ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.content.Intent); method public int describeContents(); method public android.graphics.Bitmap getIcon(); - method public android.content.Intent getIntent(); method public android.content.IntentSender getIntentSender(); method public float getScore(); method public java.lang.CharSequence getTitle(); diff --git a/api/system-current.txt b/api/system-current.txt index 79908fa..3f734ed 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3851,7 +3851,9 @@ package android.app { method public void set(int, long, android.app.PendingIntent); method public void set(int, long, long, long, android.app.PendingIntent, android.os.WorkSource); method public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent); + method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent); method public void setExact(int, long, android.app.PendingIntent); + method public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent); method public void setInexactRepeating(int, long, long, android.app.PendingIntent); method public void setRepeating(int, long, long, android.app.PendingIntent); method public void setTime(long); @@ -5974,23 +5976,21 @@ package android.app.admin { field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2 } - public class SystemUpdatePolicy { - ctor public SystemUpdatePolicy(); + public class SystemUpdatePolicy implements android.os.Parcelable { + method public static android.app.admin.SystemUpdatePolicy createAutomaticInstallPolicy(); + method public static android.app.admin.SystemUpdatePolicy createPostponeInstallPolicy(); + method public static android.app.admin.SystemUpdatePolicy createWindowedInstallPolicy(int, int); + method public int describeContents(); method public int getInstallWindowEnd(); method public int getInstallWindowStart(); method public int getPolicyType(); - method public void setAutomaticInstallPolicy(); - method public void setPostponeInstallPolicy(); - method public void setWindowedInstallPolicy(int, int) throws android.app.admin.SystemUpdatePolicy.InvalidWindowException; + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR; field public static final int TYPE_INSTALL_AUTOMATIC = 1; // 0x1 field public static final int TYPE_INSTALL_WINDOWED = 2; // 0x2 field public static final int TYPE_POSTPONE = 3; // 0x3 } - public static class SystemUpdatePolicy.InvalidWindowException extends java.lang.Exception { - ctor public SystemUpdatePolicy.InvalidWindowException(java.lang.String); - } - } package android.app.backup { @@ -8477,7 +8477,6 @@ package android.content { field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list"; field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list"; field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER"; - field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT"; field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER"; field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED"; @@ -9505,6 +9504,7 @@ package android.content.pm { method public abstract android.content.pm.PackageInstaller getPackageInstaller(); method public abstract java.lang.String[] getPackagesForUid(int); method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int); + method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle); method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String); @@ -9522,7 +9522,7 @@ package android.content.pm { method public abstract android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle); method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle); method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo); - method public abstract void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle); + method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); method public abstract boolean hasSystemFeature(java.lang.String); method public abstract boolean isSafeMode(); method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int); @@ -9538,10 +9538,11 @@ package android.content.pm { method public abstract android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int); method public abstract android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int); method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int); - method public abstract void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle); + method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); method public abstract void setApplicationEnabledSetting(java.lang.String, int, int); method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int); method public abstract void setInstallerPackageName(java.lang.String, java.lang.String); + method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle); method public abstract void verifyPendingInstall(int, int); field public static final java.lang.String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS"; field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0 @@ -9627,6 +9628,10 @@ package android.content.pm { field public static final java.lang.String FEATURE_WEBVIEW = "android.software.webview"; field public static final java.lang.String FEATURE_WIFI = "android.hardware.wifi"; field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct"; + field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4 + field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8 + field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2 + field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1 field public static final int GET_ACTIVITIES = 1; // 0x1 field public static final int GET_CONFIGURATIONS = 16384; // 0x4000 field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200 @@ -9679,6 +9684,7 @@ package android.content.pm { field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99 field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a field public static final int INSTALL_SUCCEEDED = 1; // 0x1 + field public static final int MASK_PERMISSION_FLAGS = 15; // 0xf field public static final int MATCH_ALL = 131072; // 0x20000 field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000 field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L @@ -9699,6 +9705,9 @@ package android.content.pm { ctor public PackageManager.NameNotFoundException(java.lang.String); } + public static abstract class PackageManager.PermissionFlags implements java.lang.annotation.Annotation { + } + public class PackageStats implements android.os.Parcelable { ctor public PackageStats(java.lang.String); ctor public PackageStats(android.os.Parcel); @@ -19364,7 +19373,7 @@ package android.media.tv { method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int); method protected void onLayout(boolean, int, int, int, int); method public boolean onUnhandledInputEvent(android.view.InputEvent); - method public void requestUnblockContent(android.media.tv.TvContentRating); + method public deprecated void requestUnblockContent(android.media.tv.TvContentRating); method public void reset(); method public void selectTrack(int, java.lang.String); method public void sendAppPrivateCommand(java.lang.String, android.os.Bundle); @@ -19382,6 +19391,7 @@ package android.media.tv { method public void timeShiftSetPlaybackRate(float, int); method public void tune(java.lang.String, android.net.Uri); method public void tune(java.lang.String, android.net.Uri, android.os.Bundle); + method public void unblockContent(android.media.tv.TvContentRating); } public static abstract interface TvView.OnUnhandledInputEventListener { @@ -29245,7 +29255,6 @@ package android.renderscript { method public void copyTo(short[]); method public void copyTo(int[]); method public void copyTo(float[]); - method public void copyToFieldPacker(int, int, int, int, android.renderscript.FieldPacker); method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int); method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap); method public static android.renderscript.Allocation createCubemapFromCubeFaces(android.renderscript.RenderScript, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int); @@ -30698,10 +30707,8 @@ package android.service.chooser { public final class ChooserTarget implements android.os.Parcelable { ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.app.PendingIntent); ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.content.IntentSender); - ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.content.Intent); method public int describeContents(); method public android.graphics.Bitmap getIcon(); - method public android.content.Intent getIntent(); method public android.content.IntentSender getIntentSender(); method public float getScore(); method public java.lang.CharSequence getTitle(); @@ -34042,6 +34049,7 @@ package android.test.mock { method public android.content.pm.PackageInstaller getPackageInstaller(); method public java.lang.String[] getPackagesForUid(int); method public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int); + method public int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle); method public android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException; method public int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String); @@ -34059,7 +34067,7 @@ package android.test.mock { method public android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle); method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle); method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo); - method public void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle); + method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); method public boolean hasSystemFeature(java.lang.String); method public boolean isSafeMode(); method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int); @@ -34075,11 +34083,12 @@ package android.test.mock { method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int); method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int); method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int); - method public void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle); + method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); method public void setApplicationEnabledSetting(java.lang.String, int, int); method public void setComponentEnabledSetting(android.content.ComponentName, int, int); method public boolean setDefaultBrowserPackageName(java.lang.String, int); method public void setInstallerPackageName(java.lang.String, java.lang.String); + method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle); method public void verifyPendingInstall(int, int); } diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 39de1dc7..eb834f2 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -1609,9 +1609,9 @@ public final class Pm { try { if (grant) { - mPm.grantPermission(pkg, perm, userId); + mPm.grantRuntimePermission(pkg, perm, userId); } else { - mPm.revokePermission(pkg, perm, userId); + mPm.revokeRuntimePermission(pkg, perm, userId); } return 0; } catch (RemoteException e) { diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 6503d89..951fe49 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -16,9 +16,10 @@ package android.animation; +import android.util.ArrayMap; + import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; /** @@ -68,7 +69,7 @@ public final class AnimatorSet extends Animator { * to a single node representing that Animator, not create a new Node * if one already exists. */ - private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>(); + private ArrayMap<Animator, Node> mNodeMap = new ArrayMap<Animator, Node>(); /** * Set of all nodes created for this AnimatorSet. This list is used upon @@ -646,7 +647,7 @@ public final class AnimatorSet extends Animator { anim.mTerminated = false; anim.mStarted = false; anim.mPlayingSet = new ArrayList<Animator>(); - anim.mNodeMap = new HashMap<Animator, Node>(); + anim.mNodeMap = new ArrayMap<Animator, Node>(); anim.mNodes = new ArrayList<Node>(nodeCount); anim.mSortedNodes = new ArrayList<Node>(nodeCount); anim.mReversible = mReversible; diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index b0fda9c..5e7bd0d 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -71,10 +71,7 @@ import java.io.IOException; * {@link android.content.Context#getSystemService * Context.getSystemService(Context.ALARM_SERVICE)}. */ -public class AlarmManager -{ - private static final String TAG = "AlarmManager"; - +public class AlarmManager { /** * Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()} * (wall clock time in UTC), which will wake up the device when @@ -558,7 +555,93 @@ public class AlarmManager long intervalMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null, null); } - + + /** + * Like {@link #set(int, long, PendingIntent)}, but this alarm will be allowed to execute + * even when the system is in low-power idle modes. This type of alarm must <b>only</b> + * be used for situations where it is actually required that the alarm go off while in + * idle -- a reasonable example would be for a calendar notification that should make a + * sound so the user is aware of it. These alarms can significantly impact the power use + * of the device when idle (and thus cause significant battery blame to the app scheduling + * them), so they should be used with care. + * + * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen + * out of order with any other alarms, even those from the same app. This will clearly happen + * when the device is idle (since this alarm can go off while idle, when any other alarms + * from the app will be held until later), but may also happen even when not idle.</p> + * + * <p>Regardless of the app's target SDK version, this call always allows batching of the + * alarm.</p> + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. + * @param triggerAtMillis time in milliseconds that the alarm should go + * off, using the appropriate clock (depending on the alarm type). + * @param operation Action to perform when the alarm goes off; + * typically comes from {@link PendingIntent#getBroadcast + * IntentSender.getBroadcast()}. + * + * @see #set(int, long, PendingIntent) + * @see #setExactAndAllowWhileIdle + * @see #cancel + * @see android.content.Context#sendBroadcast + * @see android.content.Context#registerReceiver + * @see android.content.Intent#filterEquals + * @see #ELAPSED_REALTIME + * @see #ELAPSED_REALTIME_WAKEUP + * @see #RTC + * @see #RTC_WAKEUP + */ + public void setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { + setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, operation, + null, null); + } + + /** + * Like {@link #setExact(int, long, PendingIntent)}, but this alarm will be allowed to execute + * even when the system is in low-power idle modes. If you don't need exact scheduling of + * the alarm but still need to execute while idle, consider using + * {@link #setAndAllowWhileIdle}. This type of alarm must <b>only</b> + * be used for situations where it is actually required that the alarm go off while in + * idle -- a reasonable example would be for a calendar notification that should make a + * sound so the user is aware of it. These alarms can significantly impact the power use + * of the device when idle (and thus cause significant battery blame to the app scheduling + * them), so they should be used with care. + * + * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen + * out of order with any other alarms, even those from the same app. This will clearly happen + * when the device is idle (since this alarm can go off while idle, when any other alarms + * from the app will be held until later), but may also happen even when not idle. + * Note that the OS will allow itself more flexibility for scheduling these alarms than + * regular exact alarms, since the application has opted into this behavior. When the + * device is idle it may take even more liberties with scheduling in order to optimize + * for battery life.</p> + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. + * @param triggerAtMillis time in milliseconds that the alarm should go + * off, using the appropriate clock (depending on the alarm type). + * @param operation Action to perform when the alarm goes off; + * typically comes from {@link PendingIntent#getBroadcast + * IntentSender.getBroadcast()}. + * + * @see #set + * @see #setRepeating + * @see #setWindow + * @see #cancel + * @see android.content.Context#sendBroadcast + * @see android.content.Context#registerReceiver + * @see android.content.Intent#filterEquals + * @see #ELAPSED_REALTIME + * @see #ELAPSED_REALTIME_WAKEUP + * @see #RTC + * @see #RTC_WAKEUP + */ + public void setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { + setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation, + null, null); + } + /** * Remove any alarms with a matching {@link Intent}. * Any alarm, of any type, whose Intent matches this one (as defined by diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 07eee12..04f6430 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -446,18 +446,40 @@ final class ApplicationPackageManager extends PackageManager { } @Override - public void grantPermission(String packageName, String permissionName, UserHandle user) { + public void grantRuntimePermission(String packageName, String permissionName, + UserHandle user) { + try { + mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier()); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + + @Override + public void revokeRuntimePermission(String packageName, String permissionName, + UserHandle user) { + try { + mPM.revokeRuntimePermission(packageName, permissionName, user.getIdentifier()); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + + @Override + public int getPermissionFlags(String permissionName, String packageName, UserHandle user) { try { - mPM.grantPermission(packageName, permissionName, user.getIdentifier()); + return mPM.getPermissionFlags(permissionName, packageName, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } } @Override - public void revokePermission(String packageName, String permissionName, UserHandle user) { + public void updatePermissionFlags(String permissionName, String packageName, + int flagMask, int flagValues, UserHandle user) { try { - mPM.revokePermission(packageName, permissionName, user.getIdentifier()); + mPM.updatePermissionFlags(permissionName, packageName, flagMask, + flagValues, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index ae07206..3fb7059 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4253,11 +4253,7 @@ public class DevicePolicyManager { public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) { if (mService != null) { try { - if (policy != null) { - mService.setSystemUpdatePolicy(who, policy.getPolicyBundle()); - } else { - mService.setSystemUpdatePolicy(who, null); - } + mService.setSystemUpdatePolicy(who, policy); } catch (RemoteException re) { Log.w(TAG, "Error calling setSystemUpdatePolicy", re); } @@ -4272,12 +4268,7 @@ public class DevicePolicyManager { public SystemUpdatePolicy getSystemUpdatePolicy() { if (mService != null) { try { - PersistableBundle bundle = mService.getSystemUpdatePolicy(); - if (bundle != null) { - return new SystemUpdatePolicy(bundle); - } else { - return null; - } + return mService.getSystemUpdatePolicy(); } catch (RemoteException re) { Log.w(TAG, "Error calling getSystemUpdatePolicy", re); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e81e7c1..481ff62 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -17,6 +17,7 @@ package android.app.admin; +import android.app.admin.SystemUpdatePolicy; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -221,8 +222,8 @@ interface IDevicePolicyManager { void setUserIcon(in ComponentName admin, in Bitmap icon); void sendDeviceInitializerStatus(int statusCode, String description); - void setSystemUpdatePolicy(in ComponentName who, in PersistableBundle policy); - PersistableBundle getSystemUpdatePolicy(); + void setSystemUpdatePolicy(in ComponentName who, in SystemUpdatePolicy policy); + SystemUpdatePolicy getSystemUpdatePolicy(); boolean setKeyguardDisabled(in ComponentName admin, boolean disabled); boolean setStatusBarDisabled(in ComponentName who, boolean disabled); diff --git a/core/java/android/app/admin/SystemUpdatePolicy.aidl b/core/java/android/app/admin/SystemUpdatePolicy.aidl new file mode 100644 index 0000000..58e8d15 --- /dev/null +++ b/core/java/android/app/admin/SystemUpdatePolicy.aidl @@ -0,0 +1,20 @@ +/* +** +** Copyright 2015, 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.app.admin; + +parcelable SystemUpdatePolicy; diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java index de56cd0..20ddb77 100644 --- a/core/java/android/app/admin/SystemUpdatePolicy.java +++ b/core/java/android/app/admin/SystemUpdatePolicy.java @@ -17,8 +17,15 @@ package android.app.admin; import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; import android.os.PersistableBundle; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,7 +35,7 @@ import java.lang.annotation.RetentionPolicy; * @see DevicePolicyManager#setSystemUpdatePolicy * @see DevicePolicyManager#getSystemUpdatePolicy */ -public class SystemUpdatePolicy { +public class SystemUpdatePolicy implements Parcelable { /** @hide */ @IntDef({ @@ -39,6 +46,10 @@ public class SystemUpdatePolicy { @interface SystemUpdatePolicyType {} /** + * Unknown policy type, used only internally. + */ + private static final int TYPE_UNKNOWN = -1; + /** * Install system update automatically as soon as one is available. */ public static final int TYPE_INSTALL_AUTOMATIC = 1; @@ -63,45 +74,40 @@ public class SystemUpdatePolicy { private static final String KEY_POLICY_TYPE = "policy_type"; private static final String KEY_INSTALL_WINDOW_START = "install_window_start"; private static final String KEY_INSTALL_WINDOW_END = "install_window_end"; + /** + * The upper boundary of the daily maintenance window: 24 * 60 minutes. + */ + private static final int WINDOW_BOUNDARY = 24 * 60; - private PersistableBundle mPolicy; + @SystemUpdatePolicyType + private int mPolicyType; - public SystemUpdatePolicy() { - mPolicy = new PersistableBundle(); - } + private int mMaintenanceWindowStart; + private int mMaintenanceWindowEnd; - /** - * Construct an SystemUpdatePolicy object from a bundle. - * @hide - */ - public SystemUpdatePolicy(PersistableBundle in) { - mPolicy = new PersistableBundle(in); - } - /** - * Retrieve the underlying bundle where the policy is stored. - * @hide - */ - public PersistableBundle getPolicyBundle() { - return new PersistableBundle(mPolicy); + private SystemUpdatePolicy() { + mPolicyType = TYPE_UNKNOWN; } /** - * Set the policy to: install update automatically as soon as one is available. + * Create a policy object and set it to install update automatically as soon as one is + * available. * * @see #TYPE_INSTALL_AUTOMATIC */ - public void setAutomaticInstallPolicy() { - mPolicy.clear(); - mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_AUTOMATIC); + public static SystemUpdatePolicy createAutomaticInstallPolicy() { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = TYPE_INSTALL_AUTOMATIC; + return policy; } /** - * Set the policy to: new system update will only be installed automatically when the system - * clock is inside a daily maintenance window. If the start and end times are the same, the - * window is considered to include the WHOLE 24 hours, that is, updates can install at any time. - * If the given window in invalid, a {@link SystemUpdatePolicy.InvalidWindowException} will be - * thrown. If start time is later than end time, the window is considered spanning midnight, + * Create a policy object and set it to: new system update will only be installed automatically + * when the system clock is inside a daily maintenance window. If the start and end times are + * the same, the window is considered to include the WHOLE 24 hours, that is, updates can + * install at any time. If the given window in invalid, a {@link IllegalArgumentException} will + * be thrown. If start time is later than end time, the window is considered spanning midnight, * i.e. end time donates a time on the next day. The maintenance window will last for 30 days, * after which the system should revert back to its normal behavior as if no policy were set. * @@ -111,25 +117,29 @@ public class SystemUpdatePolicy { * midnight in the device's local time. Must be in the range of [0, 1440). * @see #TYPE_INSTALL_WINDOWED */ - public void setWindowedInstallPolicy(int startTime, int endTime) throws InvalidWindowException{ - if (startTime < 0 || startTime >= 1440 || endTime < 0 || endTime >= 1440) { - throw new InvalidWindowException("startTime and endTime must be inside [0, 1440)"); + public static SystemUpdatePolicy createWindowedInstallPolicy(int startTime, int endTime) { + if (startTime < 0 || startTime >= WINDOW_BOUNDARY + || endTime < 0 || endTime >= WINDOW_BOUNDARY) { + throw new IllegalArgumentException("startTime and endTime must be inside [0, 1440)"); } - mPolicy.clear(); - mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_WINDOWED); - mPolicy.putInt(KEY_INSTALL_WINDOW_START, startTime); - mPolicy.putInt(KEY_INSTALL_WINDOW_END, endTime); + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = TYPE_INSTALL_WINDOWED; + policy.mMaintenanceWindowStart = startTime; + policy.mMaintenanceWindowEnd = endTime; + return policy; } /** - * Set the policy to: block installation for a maximum period of 30 days. After expiration the - * system should revert back to its normal behavior as if no policy were set. + * Create a policy object and set it to block installation for a maximum period of 30 days. + * After expiration the system should revert back to its normal behavior as if no policy were + * set. * * @see #TYPE_POSTPONE */ - public void setPostponeInstallPolicy() { - mPolicy.clear(); - mPolicy.putInt(KEY_POLICY_TYPE, TYPE_POSTPONE); + public static SystemUpdatePolicy createPostponeInstallPolicy() { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = TYPE_POSTPONE; + return policy; } /** @@ -140,7 +150,7 @@ public class SystemUpdatePolicy { */ @SystemUpdatePolicyType public int getPolicyType() { - return mPolicy.getInt(KEY_POLICY_TYPE, -1); + return mPolicyType; } /** @@ -150,8 +160,8 @@ public class SystemUpdatePolicy { * or -1 if the policy does not have a maintenance window. */ public int getInstallWindowStart() { - if (getPolicyType() == TYPE_INSTALL_WINDOWED) { - return mPolicy.getInt(KEY_INSTALL_WINDOW_START, -1); + if (mPolicyType == TYPE_INSTALL_WINDOWED) { + return mMaintenanceWindowStart; } else { return -1; } @@ -164,26 +174,98 @@ public class SystemUpdatePolicy { * or -1 if the policy does not have a maintenance window. */ public int getInstallWindowEnd() { - if (getPolicyType() == TYPE_INSTALL_WINDOWED) { - return mPolicy.getInt(KEY_INSTALL_WINDOW_END, -1); + if (mPolicyType == TYPE_INSTALL_WINDOWED) { + return mMaintenanceWindowEnd; } else { return -1; } } + /** + * Return if this object represents a valid policy. + * @hide + */ + public boolean isValid() { + if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) { + return true; + } else if (mPolicyType == TYPE_INSTALL_WINDOWED) { + return mMaintenanceWindowStart >= 0 && mMaintenanceWindowStart < WINDOW_BOUNDARY + && mMaintenanceWindowEnd >= 0 && mMaintenanceWindowEnd < WINDOW_BOUNDARY; + } else { + return false; + } + } + @Override public String toString() { - return mPolicy.toString(); + return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d)", + mPolicyType, mMaintenanceWindowStart, mMaintenanceWindowEnd); + } + + @Override + public int describeContents() { + return 0; } + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mPolicyType); + dest.writeInt(mMaintenanceWindowStart); + dest.writeInt(mMaintenanceWindowEnd); + } + + public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR = + new Parcelable.Creator<SystemUpdatePolicy>() { + + @Override + public SystemUpdatePolicy createFromParcel(Parcel source) { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = source.readInt(); + policy.mMaintenanceWindowStart = source.readInt(); + policy.mMaintenanceWindowEnd = source.readInt(); + return policy; + } + + @Override + public SystemUpdatePolicy[] newArray(int size) { + return new SystemUpdatePolicy[size]; + } + }; + + /** - * Exception thrown by {@link SystemUpdatePolicy#setWindowedInstallPolicy(int, int)} in case the - * specified window is invalid. + * @hide */ - public static class InvalidWindowException extends Exception { - public InvalidWindowException(String reason) { - super(reason); + public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) { + try { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + String value = parser.getAttributeValue(null, KEY_POLICY_TYPE); + if (value != null) { + policy.mPolicyType = Integer.parseInt(value); + + value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_START); + if (value != null) { + policy.mMaintenanceWindowStart = Integer.parseInt(value); + } + value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_END); + if (value != null) { + policy.mMaintenanceWindowEnd = Integer.parseInt(value); + } + return policy; + } + } catch (NumberFormatException e) { + // Fail through } + return null; + } + + /** + * @hide + */ + public void saveToXml(XmlSerializer out) throws IOException { + out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType)); + out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart)); + out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd)); } } diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index abfc435..81c7422 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -46,6 +46,13 @@ public final class UsageStats implements Parcelable { public long mLastTimeUsed; /** + * Last time the package was used and the beginning of the idle countdown. + * This uses a different timebase that is about how much the device has been in use in general. + * {@hide} + */ + public long mBeginIdleTime; + + /** * {@hide} */ public long mTotalTimeInForeground; @@ -74,6 +81,7 @@ public final class UsageStats implements Parcelable { mTotalTimeInForeground = stats.mTotalTimeInForeground; mLaunchCount = stats.mLaunchCount; mLastEvent = stats.mLastEvent; + mBeginIdleTime = stats.mBeginIdleTime; } public String getPackageName() { @@ -110,6 +118,15 @@ public final class UsageStats implements Parcelable { } /** + * @hide + * Get the last time this package was active, measured in milliseconds. This timestamp + * uses a timebase that represents how much the device was used and not wallclock time. + */ + public long getBeginIdleTime() { + return mBeginIdleTime; + } + + /** * Get the total time this package spent in the foreground, measured in milliseconds. */ public long getTotalTimeInForeground() { @@ -133,6 +150,7 @@ public final class UsageStats implements Parcelable { mLastEvent = right.mLastEvent; mEndTimeStamp = right.mEndTimeStamp; mLastTimeUsed = right.mLastTimeUsed; + mBeginIdleTime = right.mBeginIdleTime; } mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp); mTotalTimeInForeground += right.mTotalTimeInForeground; @@ -153,6 +171,7 @@ public final class UsageStats implements Parcelable { dest.writeLong(mTotalTimeInForeground); dest.writeInt(mLaunchCount); dest.writeInt(mLastEvent); + dest.writeLong(mBeginIdleTime); } public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() { @@ -166,6 +185,7 @@ public final class UsageStats implements Parcelable { stats.mTotalTimeInForeground = in.readLong(); stats.mLaunchCount = in.readInt(); stats.mLastEvent = in.readInt(); + stats.mBeginIdleTime = in.readLong(); return stats; } diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 8b3fc2e..7bcc038 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -69,14 +69,6 @@ public abstract class UsageStatsManagerInternal { public abstract boolean isAppIdle(String packageName, int userId); /** - * Returns the most recent time that the specified package was active for the given user. - * @param packageName The package to search. - * @param userId The user id of the user of interest. - * @return The timestamp of when the package was last used, or -1 if it hasn't been used. - */ - public abstract long getLastPackageAccessTime(String packageName, int userId); - - /** * Sets up a listener for changes to packages being accessed. * @param listener A listener within the system process. */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 6f543a8..7d76760 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3365,14 +3365,6 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS"; /** - * A Parcelable[] of {@link android.service.chooser.ChooserTarget ChooserTarget} objects - * as set with {@link #putExtra(String, Parcelable[])} representing additional app-specific - * targets to place at the front of the list of choices. Shown to the user with - * {@link #ACTION_CHOOSER}. - */ - public static final String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS"; - - /** * A Bundle forming a mapping of potential target package names to different extras Bundles * to add to the default intent extras in {@link #EXTRA_INTENT} when used with * {@link #ACTION_CHOOSER}. Each key should be a package name. The package need not diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 94b0223..ddff782 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -96,9 +96,14 @@ interface IPackageManager { void removePermission(String name); - void grantPermission(String packageName, String permissionName, int userId); + void grantRuntimePermission(String packageName, String permissionName, int userId); - void revokePermission(String packageName, String permissionName, int userId); + void revokeRuntimePermission(String packageName, String permissionName, int userId); + + int getPermissionFlags(String permissionName, String packageName, int userId); + + void updatePermissionFlags(String permissionName, String packageName, int flagMask, + int flagValues, int userId); boolean isProtectedBroadcast(String actionName); diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java index 87b97aa..6827d7a 100644 --- a/core/java/android/content/pm/LauncherActivityInfo.java +++ b/core/java/android/content/pm/LauncherActivityInfo.java @@ -103,20 +103,30 @@ public class LauncherActivityInfo { * density DPI values from {@link DisplayMetrics}. * @see #getBadgedIcon(int) * @see DisplayMetrics - * @return The drawable associated with the activity + * @return The drawable associated with the activity. */ public Drawable getIcon(int density) { - int iconRes = mResolveInfo.getIconResource(); - Resources resources = null; - Drawable icon = null; - // Get the preferred density icon from the app's resources - if (density != 0 && iconRes != 0) { - try { - resources = mPm.getResourcesForApplication(mActivityInfo.applicationInfo); - icon = resources.getDrawableForDensity(iconRes, density); - } catch (NameNotFoundException | Resources.NotFoundException exc) { - } + final int iconRes = mResolveInfo.getIconResource(); + Drawable icon = getDrawableForDensity(iconRes, density); + // Get the default density icon + if (icon == null) { + icon = mResolveInfo.loadIcon(mPm); } + return icon; + } + + /** + * Returns the icon for this activity, without any badging for the profile. + * This function can get the icon no matter the icon needs to be badged or not. + * @param density The preferred density of the icon, zero for default density. Use + * density DPI values from {@link DisplayMetrics}. + * @see #getBadgedIcon(int) + * @see DisplayMetrics + * @return The drawable associated with the activity. + */ + private Drawable getOriginalIcon(int density) { + final int iconRes = mResolveInfo.getIconResourceInternal(); + Drawable icon = getDrawableForDensity(iconRes, density); // Get the default density icon if (icon == null) { icon = mResolveInfo.loadIcon(mPm); @@ -125,6 +135,27 @@ public class LauncherActivityInfo { } /** + * Returns the drawable for this activity, without any badging for the profile. + * @param resource id of the drawable. + * @param density The preferred density of the icon, zero for default density. Use + * density DPI values from {@link DisplayMetrics}. + * @see DisplayMetrics + * @return The drawable associated with the resource id. + */ + private Drawable getDrawableForDensity(int iconRes, int density) { + // Get the preferred density icon from the app's resources + if (density != 0 && iconRes != 0) { + try { + final Resources resources + = mPm.getResourcesForApplication(mActivityInfo.applicationInfo); + return resources.getDrawableForDensity(iconRes, density); + } catch (NameNotFoundException | Resources.NotFoundException exc) { + } + } + return null; + } + + /** * Returns the application flags from the ApplicationInfo of the activity. * * @return Application flags @@ -167,7 +198,7 @@ public class LauncherActivityInfo { * @return A badged icon for the activity. */ public Drawable getBadgedIcon(int density) { - Drawable originalIcon = getIcon(density); + Drawable originalIcon = getOriginalIcon(density); if (originalIcon instanceof BitmapDrawable) { return mPm.getUserBadgedIcon(originalIcon, mUser); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 51fa075..6401fe6 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1888,6 +1888,57 @@ public abstract class PackageManager { public static final String EXTRA_FAILURE_EXISTING_PERMISSION = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION"; + /** + * Permission flag: The permission is set in its current state + * by the user and apps can still request it at runtime. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_USER_SET = 1 << 0; + + /** + * Permission flag: The permission is set in its current state + * by the user and it is fixed, i.e. apps can no longer request + * this permission. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_USER_FIXED = 1 << 1; + + /** + * Permission flag: The permission is set in its current state + * by device policy and neither apps nor the user can change + * its state. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_POLICY_FIXED = 1 << 2; + + /** + * Permission flag: The permission is set in a granted state but + * access to resources it guards is restricted by other means to + * enable revoking a permission on legacy apps that do not support + * runtime permissions. If this permission is upgraded to runtime + * because the app was updated to support runtime permissions, the + * the permission will be revoked in the upgrade process. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 1 << 3; + + + /** + * Mask for all permission flags. + * + * @hide + */ + @SystemApi + public static final int MASK_PERMISSION_FLAGS = 0xF; + /** * Retrieve overall information about an application package that is * installed on the system. @@ -2374,6 +2425,20 @@ public abstract class PackageManager { */ public abstract void removePermission(String name); + + /** + * Permission flags set when granting or revoking a permission. + * + * @hide + */ + @SystemApi + @IntDef({FLAG_PERMISSION_USER_SET, + FLAG_PERMISSION_USER_FIXED, + FLAG_PERMISSION_POLICY_FIXED, + FLAG_PERMISSION_REVOKE_ON_UPGRADE}) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionFlags {} + /** * Grant a runtime permission to an application which the application does not * already have. The permission must have been requested by the application. @@ -2389,19 +2454,20 @@ public abstract class PackageManager { * @param permissionName The permission name to grant. * @param user The user for which to grant the permission. * - * @see #revokePermission(String, String, android.os.UserHandle) + * @see #revokeRuntimePermission(String, String, android.os.UserHandle) + * @see android.content.pm.PackageManager.PermissionFlags * * @hide */ @SystemApi - public abstract void grantPermission(@NonNull String packageName, + public abstract void grantRuntimePermission(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user); /** * Revoke a runtime permission that was previously granted by {@link - * #grantPermission(String, String, android.os.UserHandle)}. The permission - * must have been requested by and granted to the application. If the - * application is not allowed to hold the permission, a {@link + * #grantRuntimePermission(String, String, android.os.UserHandle)}. The + * permission must have been requested by and granted to the application. + * If the application is not allowed to hold the permission, a {@link * java.lang.SecurityException} is thrown. * <p> * <strong>Note: </strong>Using this API requires holding @@ -2413,15 +2479,47 @@ public abstract class PackageManager { * @param permissionName The permission name to revoke. * @param user The user for which to revoke the permission. * - * @see #grantPermission(String, String, android.os.UserHandle) + * @see #grantRuntimePermission(String, String, android.os.UserHandle) + * @see android.content.pm.PackageManager.PermissionFlags * * @hide */ @SystemApi - public abstract void revokePermission(@NonNull String packageName, + public abstract void revokeRuntimePermission(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user); /** + * Gets the state flags associated with a permission. + * + * @param permissionName The permission for which to get the flags. + * @param packageName The package name for which to get the flags. + * @param user The user for which to get permission flags. + * @return The permission flags. + * + * @hide + */ + @SystemApi + public abstract @PermissionFlags int getPermissionFlags(String permissionName, + String packageName, @NonNull UserHandle user); + + /** + * Updates the flags associated with a permission by replacing the flags in + * the specified mask with the provided flag values. + * + * @param permissionName The permission for which to update the flags. + * @param packageName The package name for which to update the flags. + * @param flagMask The flags which to replace. + * @param flagValues The flags with which to replace. + * @param user The user for which to update the permission flags. + * + * @hide + */ + @SystemApi + public abstract void updatePermissionFlags(String permissionName, + String packageName, @PermissionFlags int flagMask, int flagValues, + @NonNull UserHandle user); + + /** * Returns an {@link android.content.Intent} suitable for passing to * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)} * which prompts the user to grant permissions to this application. diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java index 05f5e90..649fdb4 100644 --- a/core/java/android/content/pm/ResolveInfo.java +++ b/core/java/android/content/pm/ResolveInfo.java @@ -221,16 +221,16 @@ public class ResolveInfo implements Parcelable { } return ci.loadIcon(pm); } - + /** * Return the icon resource identifier to use for this match. If the * match defines an icon, that is used; else if the activity defines * an icon, that is used; else, the application icon is used. - * + * This function does not check noResourceId flag. + * * @return The icon associated with this match. */ - public final int getIconResource() { - if (noResourceId) return 0; + final int getIconResourceInternal() { if (icon != 0) return icon; final ComponentInfo ci = getComponentInfo(); if (ci != null) { @@ -239,6 +239,18 @@ public class ResolveInfo implements Parcelable { return 0; } + /** + * Return the icon resource identifier to use for this match. If the + * match defines an icon, that is used; else if the activity defines + * an icon, that is used; else, the application icon is used. + * + * @return The icon associated with this match. + */ + public final int getIconResource() { + if (noResourceId) return 0; + return getIconResourceInternal(); + } + public void dump(Printer pw, String prefix) { if (filter != null) { pw.println(prefix + "Filter:"); diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl index 3cb29ff..602bfea 100644 --- a/core/java/android/os/IDeviceIdleController.aidl +++ b/core/java/android/os/IDeviceIdleController.aidl @@ -23,4 +23,5 @@ interface IDeviceIdleController { String[] getSystemPowerWhitelist(); String[] getFullPowerWhitelist(); int[] getAppIdWhitelist(); + boolean isPowerSaveWhitelistApp(String name); } diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 00ab262..9a0d0d0 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -134,4 +134,6 @@ public abstract class PowerManagerInternal { } public abstract void setDeviceIdleMode(boolean enabled); + + public abstract void setDeviceIdleWhitelist(int[] appids); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index dc70d7b..d3a5561 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5517,6 +5517,12 @@ public final class Settings { public static final String APP_IDLE_DURATION = "app_idle_duration"; /** + * Controls whether double tap to wake is enabled. + * @hide + */ + public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake"; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear @@ -5571,7 +5577,8 @@ public final class Settings { MOUNT_UMS_PROMPT, MOUNT_UMS_NOTIFY_ENABLED, UI_NIGHT_MODE, - SLEEP_TIMEOUT + SLEEP_TIMEOUT, + DOUBLE_TAP_TO_WAKE, }; /** diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java index f0ca276..4c94ee7 100644 --- a/core/java/android/service/chooser/ChooserTarget.java +++ b/core/java/android/service/chooser/ChooserTarget.java @@ -58,12 +58,6 @@ public final class ChooserTarget implements Parcelable { private IntentSender mIntentSender; /** - * A raw intent provided in lieu of an IntentSender. Will be filled in and sent - * by {@link #sendIntent(Context, Intent)}. - */ - private Intent mIntent; - - /** * The score given to this item. It can be normalized. */ private float mScore; @@ -146,43 +140,6 @@ public final class ChooserTarget implements Parcelable { mIntentSender = intentSender; } - /** - * Construct a deep link target for presentation by a chooser UI. - * - * <p>A target is composed of a title and an icon for presentation to the user. - * The UI presenting this target may truncate the title if it is too long to be presented - * in the available space, as well as crop, resize or overlay the supplied icon.</p> - * - * <p>The creator of a target may supply a ranking score. This score is assumed to be relative - * to the other targets supplied by the same - * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}. - * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match). - * Scores for a set of targets do not need to sum to 1.</p> - * - * <p>Before being sent, the Intent supplied will be - * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied - * to the chooser.</p> - * - * <p>Take care not to place custom {@link android.os.Parcelable} types into - * the Intent as extras, as the system will not be able to unparcel it to merge - * additional extras.</p> - * - * @param title title of this target that will be shown to a user - * @param icon icon to represent this target - * @param score ranking score for this target between 0.0f and 1.0f, inclusive - * @param intent Intent to fill in and send if the user chooses this target - */ - public ChooserTarget(CharSequence title, Bitmap icon, float score, Intent intent) { - mTitle = title; - mIcon = icon; - if (score > 1.f || score < 0.f) { - throw new IllegalArgumentException("Score " + score + " out of range; " - + "must be between 0.0f and 1.0f"); - } - mScore = score; - mIntent = intent; - } - ChooserTarget(Parcel in) { mTitle = in.readCharSequence(); if (in.readInt() != 0) { @@ -192,9 +149,6 @@ public final class ChooserTarget implements Parcelable { } mScore = in.readFloat(); mIntentSender = IntentSender.readIntentSenderOrNullFromParcel(in); - if (in.readInt() != 0) { - mIntent = Intent.CREATOR.createFromParcel(in); - } } /** @@ -241,18 +195,6 @@ public final class ChooserTarget implements Parcelable { } /** - * Returns the Intent supplied by the ChooserTarget's creator. - * This may be null if the creator specified an IntentSender or PendingIntent instead. - * - * <p>To fill in and send the intent, see {@link #sendIntent(Context, Intent)}.</p> - * - * @return the Intent supplied by the ChooserTarget's creator - */ - public Intent getIntent() { - return mIntent; - } - - /** * Fill in the IntentSender supplied by the ChooserTarget's creator and send it. * * @param context the sending Context; generally the Activity presenting the chooser UI @@ -272,91 +214,8 @@ public final class ChooserTarget implements Parcelable { Log.e(TAG, "sendIntent " + this + " failed", e); return false; } - } else if (mIntent != null) { - try { - final Intent toSend = new Intent(mIntent); - toSend.fillIn(fillInIntent, 0); - context.startActivity(toSend); - return true; - } catch (Exception e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } } else { - Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); - return false; - } - } - - /** - * Same as {@link #sendIntent(Context, Intent)}, but offers a userId field to use - * for launching the {@link #getIntent() intent} using - * {@link Activity#startActivityAsCaller(Intent, Bundle, int)} if the - * {@link #getIntentSender() IntentSender} is not present. If the IntentSender is present, - * it will be invoked as usual with its own calling identity. - * - * @hide internal use only. - */ - public boolean sendIntentAsCaller(Activity context, Intent fillInIntent, int userId) { - if (fillInIntent != null) { - fillInIntent.migrateExtraStreamToClipData(); - fillInIntent.prepareToLeaveProcess(); - } - if (mIntentSender != null) { - try { - mIntentSender.sendIntent(context, 0, fillInIntent, null, null); - return true; - } catch (IntentSender.SendIntentException e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } - } else if (mIntent != null) { - try { - final Intent toSend = new Intent(mIntent); - toSend.fillIn(fillInIntent, 0); - context.startActivityAsCaller(toSend, null, userId); - return true; - } catch (Exception e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } - } else { - Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); - return false; - } - } - - /** - * The UserHandle is only used if we're launching a raw intent. The IntentSender will be - * launched with its associated identity. - * - * @hide Internal use only - */ - public boolean sendIntentAsUser(Activity context, Intent fillInIntent, UserHandle user) { - if (fillInIntent != null) { - fillInIntent.migrateExtraStreamToClipData(); - fillInIntent.prepareToLeaveProcess(); - } - if (mIntentSender != null) { - try { - mIntentSender.sendIntent(context, 0, fillInIntent, null, null); - return true; - } catch (IntentSender.SendIntentException e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } - } else if (mIntent != null) { - try { - final Intent toSend = new Intent(mIntent); - toSend.fillIn(fillInIntent, 0); - context.startActivityAsUser(toSend, user); - return true; - } catch (Exception e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } - } else { - Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); + Log.e(TAG, "sendIntent " + this + " failed - no IntentSender to send"); return false; } } @@ -364,7 +223,7 @@ public final class ChooserTarget implements Parcelable { @Override public String toString() { return "ChooserTarget{" - + (mIntentSender != null ? mIntentSender.getCreatorPackage() : mIntent) + + (mIntentSender != null ? mIntentSender.getCreatorPackage() : null) + ", " + "'" + mTitle + "', " + mScore + "}"; @@ -386,10 +245,6 @@ public final class ChooserTarget implements Parcelable { } dest.writeFloat(mScore); IntentSender.writeIntentSenderOrNullToParcel(mIntentSender, dest); - dest.writeInt(mIntent != null ? 1 : 0); - if (mIntent != null) { - mIntent.writeToParcel(dest, 0); - } } public static final Creator<ChooserTarget> CREATOR diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 1ac3f45..f6ce353 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1817,9 +1817,7 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final boolean isWakeKey(int keyCode) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: - case KeyEvent.KEYCODE_POWER: case KeyEvent.KEYCODE_MENU: - case KeyEvent.KEYCODE_SLEEP: case KeyEvent.KEYCODE_WAKEUP: case KeyEvent.KEYCODE_PAIRING: return true; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 51c4760..b476e9b 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -586,6 +586,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mGroupFlags |= FLAG_CLIP_CHILDREN; mGroupFlags |= FLAG_CLIP_TO_PADDING; mGroupFlags |= FLAG_ANIMATION_DONE; + mGroupFlags |= FLAG_ANIMATION_CACHE; + mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index 6feb94b..bb4a948 100644 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -244,7 +244,7 @@ public class AppSecurityPermissions { @Override public void onClick(DialogInterface dialog, int which) { PackageManager pm = getContext().getPackageManager(); - pm.revokePermission(mPackageName, mPerm.name, + pm.revokeRuntimePermission(mPackageName, mPerm.name, new UserHandle(mContext.getUserId())); PermissionItemView.this.setVisibility(View.GONE); } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 4fd85b6..2e36cee 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3902,6 +3902,9 @@ public class Editor { @Override public void updatePosition(float x, float y) { positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false); + if (mSelectionActionMode != null) { + mSelectionActionMode.invalidate(); + } } @Override diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 62ca1f0..83fa967 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -24,9 +24,11 @@ import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; +import android.content.pm.LabeledIntent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.database.DataSetObserver; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -37,6 +39,7 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; +import android.os.UserManager; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; @@ -44,8 +47,16 @@ import android.service.chooser.IChooserTargetService; import android.text.TextUtils; import android.util.Log; import android.util.Slog; +import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.widget.AbsListView; +import android.widget.BaseAdapter; +import android.widget.LinearLayout; +import android.widget.ListView; +import com.android.internal.R; import java.util.ArrayList; import java.util.List; @@ -63,7 +74,7 @@ public class ChooserActivity extends ResolverActivity { private IntentSender mRefinementIntentSender; private RefinementResultReceiver mRefinementResultReceiver; - private ChooserTarget[] mCallerChooserTargets; + private ChooserListAdapter mChooserListAdapter; private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); @@ -84,8 +95,7 @@ public class ChooserActivity extends ResolverActivity { + " Have you considered returning results faster?"); break; } - final ChooserListAdapter cla = (ChooserListAdapter) getAdapter(); - cla.addServiceResults(sri.originalTarget, sri.resultTargets); + mChooserListAdapter.addServiceResults(sri.originalTarget, sri.resultTargets); unbindService(sri.connection); mServiceConnections.remove(sri.connection); break; @@ -166,20 +176,6 @@ public class ChooserActivity extends ResolverActivity { } } - pa = intent.getParcelableArrayExtra(Intent.EXTRA_CHOOSER_TARGETS); - if (pa != null) { - final ChooserTarget[] targets = new ChooserTarget[pa.length]; - for (int i = 0; i < pa.length; i++) { - if (!(pa[i] instanceof ChooserTarget)) { - Log.w(TAG, "Chooser target #" + i + " is not a ChooserTarget: " + pa[i]); - finish(); - super.onCreate(null); - return; - } - targets[i] = (ChooserTarget) pa[i]; - } - mCallerChooserTargets = targets; - } mChosenComponentSender = intent.getParcelableExtra( Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER); mRefinementIntentSender = intent.getParcelableExtra( @@ -233,8 +229,19 @@ public class ChooserActivity extends ResolverActivity { } @Override + void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, + boolean alwaysUseOption) { + final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; + mChooserListAdapter = (ChooserListAdapter) adapter; + adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter)); + if (listView != null) { + listView.setItemsCanFocus(true); + } + } + + @Override int getLayoutResource() { - return com.android.internal.R.layout.chooser_grid; + return R.layout.chooser_grid; } @Override @@ -413,10 +420,11 @@ public class ChooserActivity extends ResolverActivity { } @Override - ResolveListAdapter createAdapter(Context context, Intent[] initialIntents, - List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { - final ChooserListAdapter adapter = new ChooserListAdapter(context, initialIntents, rList, - launchedFromUid, filterLastUsed, mCallerChooserTargets); + ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, + boolean filterLastUsed) { + final ChooserListAdapter adapter = new ChooserListAdapter(context, payloadIntents, + initialIntents, rList, launchedFromUid, filterLastUsed); if (DEBUG) Log.d(TAG, "Adapter created; querying services"); queryTargetServices(adapter); return adapter; @@ -426,17 +434,23 @@ public class ChooserActivity extends ResolverActivity { private final DisplayResolveInfo mSourceInfo; private final ResolveInfo mBackupResolveInfo; private final ChooserTarget mChooserTarget; + private Drawable mBadgeIcon = null; private final Drawable mDisplayIcon; private final Intent mFillInIntent; private final int mFillInFlags; - public ChooserTargetInfo(ChooserTarget target) { - this(null, target); - } - public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget) { mSourceInfo = sourceInfo; mChooserTarget = chooserTarget; + if (sourceInfo != null) { + final ResolveInfo ri = sourceInfo.getResolveInfo(); + if (ri != null) { + final ActivityInfo ai = ri.activityInfo; + if (ai != null && ai.applicationInfo != null) { + mBadgeIcon = getPackageManager().getApplicationIcon(ai.applicationInfo); + } + } + } mDisplayIcon = new BitmapDrawable(getResources(), chooserTarget.getIcon()); if (sourceInfo != null) { @@ -453,6 +467,7 @@ public class ChooserActivity extends ResolverActivity { mSourceInfo = other.mSourceInfo; mBackupResolveInfo = other.mBackupResolveInfo; mChooserTarget = other.mChooserTarget; + mBadgeIcon = other.mBadgeIcon; mDisplayIcon = other.mDisplayIcon; mFillInIntent = fillInIntent; mFillInFlags = flags; @@ -460,10 +475,7 @@ public class ChooserActivity extends ResolverActivity { @Override public Intent getResolvedIntent() { - final Intent targetIntent = mChooserTarget.getIntent(); - if (targetIntent != null) { - return targetIntent; - } else if (mSourceInfo != null) { + if (mSourceInfo != null) { return mSourceInfo.getResolvedIntent(); } return getTargetIntent(); @@ -507,7 +519,8 @@ public class ChooserActivity extends ResolverActivity { if (intent == null) { return false; } - return mChooserTarget.sendIntentAsCaller(activity, intent, userId); + // ChooserTargets will launch with their IntentSender's identity + return mChooserTarget.sendIntent(activity, intent); } @Override @@ -516,7 +529,8 @@ public class ChooserActivity extends ResolverActivity { if (intent == null) { return false; } - return mChooserTarget.sendIntentAsUser(activity, intent, user); + // ChooserTargets will launch with their IntentSender's identity + return mChooserTarget.sendIntent(activity, intent); } @Override @@ -540,6 +554,11 @@ public class ChooserActivity extends ResolverActivity { } @Override + public Drawable getBadgeIcon() { + return mBadgeIcon; + } + + @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new ChooserTargetInfo(this, fillInIntent, flags); } @@ -556,16 +575,49 @@ public class ChooserActivity extends ResolverActivity { } public class ChooserListAdapter extends ResolveListAdapter { - private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); - private final List<ChooserTargetInfo> mCallerTargets = new ArrayList<>(); - - public ChooserListAdapter(Context context, Intent[] initialIntents, List<ResolveInfo> rList, - int launchedFromUid, boolean filterLastUsed, ChooserTarget[] callerChooserTargets) { - super(context, initialIntents, rList, launchedFromUid, filterLastUsed); + public static final int TARGET_BAD = -1; + public static final int TARGET_CALLER = 0; + public static final int TARGET_SERVICE = 1; + public static final int TARGET_STANDARD = 2; - if (callerChooserTargets != null) { - for (ChooserTarget target : callerChooserTargets) { - mCallerTargets.add(new ChooserTargetInfo(target)); + private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); + private final List<TargetInfo> mCallerTargets = new ArrayList<>(); + + public ChooserListAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, + boolean filterLastUsed) { + // Don't send the initial intents through the shared ResolverActivity path, + // we want to separate them into a different section. + super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed); + + if (initialIntents != null) { + final PackageManager pm = getPackageManager(); + for (int i = 0; i < initialIntents.length; i++) { + final Intent ii = initialIntents[i]; + if (ii == null) { + continue; + } + final ActivityInfo ai = ii.resolveActivityInfo(pm, 0); + if (ai == null) { + Log.w(TAG, "No activity found for " + ii); + continue; + } + ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = ai; + UserManager userManager = + (UserManager) getSystemService(Context.USER_SERVICE); + if (userManager.isManagedProfile()) { + ri.noResourceId = true; + } + if (ii instanceof LabeledIntent) { + LabeledIntent li = (LabeledIntent)ii; + ri.resolvePackageName = li.getSourcePackage(); + ri.labelRes = li.getLabelResource(); + ri.nonLocalizedLabel = li.getNonLocalizedLabel(); + ri.icon = li.getIconResource(); + } + mCallerTargets.add(new DisplayResolveInfo(ii, ri, + ri.loadLabel(pm), null, ii)); } } } @@ -578,7 +630,7 @@ public class ChooserActivity extends ResolverActivity { } @Override - public View createView(ViewGroup parent) { + public View onCreateView(ViewGroup parent) { return mInflater.inflate( com.android.internal.R.layout.resolve_grid_item, parent, false); } @@ -600,6 +652,41 @@ public class ChooserActivity extends ResolverActivity { return super.getCount() + mServiceTargets.size() + mCallerTargets.size(); } + public int getCallerTargetsCount() { + return mCallerTargets.size(); + } + + public int getServiceTargetsCount() { + return mServiceTargets.size(); + } + + public int getStandardTargetCount() { + return super.getCount(); + } + + public int getPositionTargetType(int position) { + int offset = 0; + + final int callerTargetCount = mCallerTargets.size(); + if (position < callerTargetCount) { + return TARGET_CALLER; + } + offset += callerTargetCount; + + final int serviceTargetCount = mServiceTargets.size(); + if (position - offset < serviceTargetCount) { + return TARGET_SERVICE; + } + offset += serviceTargetCount; + + final int standardTargetCount = super.getCount(); + if (position - offset < standardTargetCount) { + return TARGET_STANDARD; + } + + return TARGET_BAD; + } + @Override public TargetInfo getItem(int position) { int offset = 0; @@ -643,6 +730,133 @@ public class ChooserActivity extends ResolverActivity { } } + class ChooserRowAdapter extends BaseAdapter { + private ChooserListAdapter mChooserListAdapter; + private final LayoutInflater mLayoutInflater; + private final int mColumnCount = 4; + + public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) { + mChooserListAdapter = wrappedAdapter; + mLayoutInflater = LayoutInflater.from(ChooserActivity.this); + + wrappedAdapter.registerDataSetObserver(new DataSetObserver() { + @Override + public void onChanged() { + super.onChanged(); + notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + super.onInvalidated(); + notifyDataSetInvalidated(); + } + }); + } + + @Override + public int getCount() { + return (int) ( + Math.ceil((float) mChooserListAdapter.getCallerTargetsCount() / mColumnCount) + + Math.ceil((float) mChooserListAdapter.getServiceTargetsCount() / mColumnCount) + + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount) + ); + } + + @Override + public Object getItem(int position) { + // We have nothing useful to return here. + return position; + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final View[] holder; + if (convertView == null) { + holder = createViewHolder(parent); + } else { + holder = (View[]) convertView.getTag(); + } + bindViewHolder(position, holder); + + // We keep the actual list item view as the last item in the holder array + return holder[mColumnCount]; + } + + View[] createViewHolder(ViewGroup parent) { + final View[] holder = new View[mColumnCount + 1]; + + final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, + parent, false); + for (int i = 0; i < mColumnCount; i++) { + holder[i] = mChooserListAdapter.createView(row); + row.addView(holder[i]); + } + row.setTag(holder); + holder[mColumnCount] = row; + return holder; + } + + void bindViewHolder(int rowPosition, View[] holder) { + final int start = getFirstRowPosition(rowPosition); + final int startType = mChooserListAdapter.getPositionTargetType(start); + + int end = start + mColumnCount - 1; + while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) { + end--; + } + + final ViewGroup row = (ViewGroup) holder[mColumnCount]; + + if (startType == ChooserListAdapter.TARGET_SERVICE) { + row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color)); + } else { + row.setBackground(null); + } + + for (int i = 0; i < mColumnCount; i++) { + final View v = holder[i]; + if (start + i <= end) { + v.setVisibility(View.VISIBLE); + final int itemIndex = start + i; + mChooserListAdapter.bindView(itemIndex, v); + v.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startSelected(itemIndex, false, true); + } + }); + } else { + v.setVisibility(View.GONE); + } + } + } + + int getFirstRowPosition(int row) { + final int callerCount = mChooserListAdapter.getCallerTargetsCount(); + final int callerRows = (int) Math.ceil((float) callerCount / mColumnCount); + + if (row < callerRows) { + return row * mColumnCount; + } + + final int serviceCount = mChooserListAdapter.getServiceTargetsCount(); + final int serviceRows = (int) Math.ceil((float) serviceCount / mColumnCount); + + if (row < callerRows + serviceRows) { + return callerCount + (row - callerRows) * mColumnCount; + } + + return callerCount + serviceCount + + (row - callerRows - serviceRows) * mColumnCount; + } + } + class ChooserTargetServiceConnection implements ServiceConnection { private final DisplayResolveInfo mOriginalTarget; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 2048664..4696757 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -25,7 +25,6 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; import android.widget.AbsListView; -import android.widget.GridView; import com.android.internal.R; import com.android.internal.content.PackageMonitor; @@ -83,7 +82,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; * which there is more than one matching activity, allowing the user to decide * which to go to. It is not normally used directly by application developers. */ -public class ResolverActivity extends Activity implements AdapterView.OnItemClickListener { +public class ResolverActivity extends Activity { private static final String TAG = "ResolverActivity"; private static final boolean DEBUG = false; @@ -93,8 +92,6 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private boolean mSafeForwardingMode; private boolean mAlwaysUseOption; private AbsListView mAdapterView; - private ListView mListView; - private GridView mGridView; private Button mAlwaysButton; private Button mOnceButton; private View mProfileView; @@ -217,6 +214,13 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } catch (RemoteException e) { mLaunchedFromUid = -1; } + + if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) { + // Gulp! + finish(); + return; + } + mPm = getPackageManager(); mUsm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); @@ -229,67 +233,11 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); mIconDpi = am.getLauncherLargeIconDensity(); + // Add our initial intent as the first item, regardless of what else has already been added. mIntents.add(0, new Intent(intent)); - mAdapter = createAdapter(this, initialIntents, rList, mLaunchedFromUid, alwaysUseOption); - final int layoutId; - final boolean useHeader; - if (mAdapter.hasFilteredItem()) { - layoutId = R.layout.resolver_list_with_default; - alwaysUseOption = false; - useHeader = true; - } else { - useHeader = false; - layoutId = getLayoutResource(); - } - mAlwaysUseOption = alwaysUseOption; + configureContentView(mIntents, initialIntents, rList, alwaysUseOption); - if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) { - // Gulp! - finish(); - return; - } - - int count = mAdapter.mDisplayList.size(); - if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) { - setContentView(layoutId); - mAdapterView = (AbsListView) findViewById(R.id.resolver_list); - mAdapterView.setAdapter(mAdapter); - mAdapterView.setOnItemClickListener(this); - mAdapterView.setOnItemLongClickListener(new ItemLongClickListener()); - - // Initialize the different types of collection views we may have. Depending - // on which ones are initialized later we'll configure different properties. - if (mAdapterView instanceof ListView) { - mListView = (ListView) mAdapterView; - } - if (mAdapterView instanceof GridView) { - mGridView = (GridView) mAdapterView; - } - - if (alwaysUseOption) { - mAdapterView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); - } - - if (useHeader && mListView != null) { - mListView.addHeaderView(LayoutInflater.from(this).inflate( - R.layout.resolver_different_item_header, mListView, false)); - } - } else if (count == 1) { - safelyStartActivity(mAdapter.targetInfoForPosition(0, false)); - mPackageMonitor.unregister(); - mRegistered = false; - finish(); - return; - } else { - setContentView(R.layout.resolver_list); - - final TextView empty = (TextView) findViewById(R.id.empty); - empty.setVisibility(View.VISIBLE); - - mAdapterView = (AbsListView) findViewById(R.id.resolver_list); - mAdapterView.setVisibility(View.GONE); - } // Prevent the Resolver window from becoming the top fullscreen window and thus from taking // control of the system bars. getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR); @@ -548,29 +496,6 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - if (mListView != null) { - position -= mListView.getHeaderViewsCount(); - } - if (position < 0) { - // Header views don't count. - return; - } - final int checkedPos = mAdapterView.getCheckedItemPosition(); - final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION; - if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) { - setAlwaysButtonEnabled(hasValidSelection, checkedPos, true); - mOnceButton.setEnabled(hasValidSelection); - if (hasValidSelection) { - mAdapterView.smoothScrollToPosition(checkedPos); - } - mLastSelected = checkedPos; - } else { - startSelected(position, false, true); - } - } - private boolean hasManagedProfile() { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); if (userManager == null) { @@ -743,29 +668,37 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic if (r.match > bestMatch) bestMatch = r.match; } if (alwaysCheck) { - PackageManager pm = getPackageManager(); + final int userId = getUserId(); + final PackageManager pm = getPackageManager(); // Set the preferred Activity pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent()); - // Update Domain Verification status - int userId = getUserId(); - ComponentName cn = intent.getComponent(); - String packageName = cn.getPackageName(); - String dataScheme = (data != null) ? data.getScheme() : null; - - boolean isHttpOrHttps = (dataScheme != null) && - (dataScheme.equals(IntentFilter.SCHEME_HTTP) || - dataScheme.equals(IntentFilter.SCHEME_HTTPS)); - - boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW); - boolean hasCategoryBrowsable = (categories != null) && - categories.contains(Intent.CATEGORY_BROWSABLE); - - if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) { - pm.updateIntentVerificationStatus(packageName, - PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, - userId); + if (ri.handleAllWebDataURI) { + // Set default Browser if needed + final String packageName = pm.getDefaultBrowserPackageName(userId); + if (TextUtils.isEmpty(packageName)) { + pm.setDefaultBrowserPackageName(ri.activityInfo.packageName, userId); + } + } else { + // Update Domain Verification status + ComponentName cn = intent.getComponent(); + String packageName = cn.getPackageName(); + String dataScheme = (data != null) ? data.getScheme() : null; + + boolean isHttpOrHttps = (dataScheme != null) && + (dataScheme.equals(IntentFilter.SCHEME_HTTP) || + dataScheme.equals(IntentFilter.SCHEME_HTTPS)); + + boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW); + boolean hasCategoryBrowsable = (categories != null) && + categories.contains(Intent.CATEGORY_BROWSABLE); + + if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) { + pm.updateIntentVerificationStatus(packageName, + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, + userId); + } } } else { try { @@ -831,14 +764,68 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic startActivity(in); } - ResolveListAdapter createAdapter(Context context, Intent[] initialIntents, - List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { - return new ResolveListAdapter(context, initialIntents, rList, launchedFromUid, - filterLastUsed); + ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, + boolean filterLastUsed) { + return new ResolveListAdapter(context, payloadIntents, initialIntents, rList, + launchedFromUid, filterLastUsed); } - ResolveListAdapter getAdapter() { - return mAdapter; + void configureContentView(List<Intent> payloadIntents, Intent[] initialIntents, + List<ResolveInfo> rList, boolean alwaysUseOption) { + mAdapter = createAdapter(this, payloadIntents, initialIntents, rList, + mLaunchedFromUid, alwaysUseOption); + + final int layoutId; + if (mAdapter.hasFilteredItem()) { + layoutId = R.layout.resolver_list_with_default; + alwaysUseOption = false; + } else { + layoutId = getLayoutResource(); + } + mAlwaysUseOption = alwaysUseOption; + + int count = mAdapter.mDisplayList.size(); + if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) { + setContentView(layoutId); + mAdapterView = (AbsListView) findViewById(R.id.resolver_list); + onPrepareAdapterView(mAdapterView, mAdapter, alwaysUseOption); + } else if (count == 1) { + safelyStartActivity(mAdapter.targetInfoForPosition(0, false)); + mPackageMonitor.unregister(); + mRegistered = false; + finish(); + return; + } else { + setContentView(R.layout.resolver_list); + + final TextView empty = (TextView) findViewById(R.id.empty); + empty.setVisibility(View.VISIBLE); + + mAdapterView = (AbsListView) findViewById(R.id.resolver_list); + mAdapterView.setVisibility(View.GONE); + } + } + + void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, + boolean alwaysUseOption) { + final boolean useHeader = adapter.hasFilteredItem(); + final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; + + adapterView.setAdapter(mAdapter); + + final ItemClickListener listener = new ItemClickListener(); + adapterView.setOnItemClickListener(listener); + adapterView.setOnItemLongClickListener(listener); + + if (alwaysUseOption) { + listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); + } + + if (useHeader && listView != null) { + listView.addHeaderView(LayoutInflater.from(this).inflate( + R.layout.resolver_different_item_header, listView, false)); + } } final class DisplayResolveInfo implements TargetInfo { @@ -888,6 +875,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return mDisplayIcon; } + public Drawable getBadgeIcon() { + return null; + } + @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new DisplayResolveInfo(this, fillInIntent, flags); @@ -1024,6 +1015,11 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic public Drawable getDisplayIcon(); /** + * @return The (small) icon to badge the target with + */ + public Drawable getBadgeIcon(); + + /** * Clone this target with the given fill-in information. */ public TargetInfo cloneFilledIn(Intent fillInIntent, int flags); @@ -1035,6 +1031,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } class ResolveListAdapter extends BaseAdapter { + private final List<Intent> mIntents; private final Intent[] mInitialIntents; private final List<ResolveInfo> mBaseResolveList; private ResolveInfo mLastChosen; @@ -1050,8 +1047,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private int mLastChosenPosition = -1; private boolean mFilterLastUsed; - public ResolveListAdapter(Context context, Intent[] initialIntents, - List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { + public ResolveListAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, + boolean filterLastUsed) { + mIntents = payloadIntents; mInitialIntents = initialIntents; mBaseResolveList = rList; mLaunchedFromUid = launchedFromUid; @@ -1430,15 +1429,19 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic View view = convertView; if (view == null) { view = createView(parent); - - final ViewHolder holder = new ViewHolder(view); - view.setTag(holder); } - bindView(view, getItem(position)); + onBindView(view, getItem(position)); + return view; + } + + public final View createView(ViewGroup parent) { + final View view = onCreateView(parent); + final ViewHolder holder = new ViewHolder(view); + view.setTag(holder); return view; } - public View createView(ViewGroup parent) { + public View onCreateView(ViewGroup parent) { return mInflater.inflate( com.android.internal.R.layout.resolve_list_item, parent, false); } @@ -1447,7 +1450,11 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return !TextUtils.isEmpty(info.getExtendedInfo()); } - private final void bindView(View view, TargetInfo info) { + public final void bindView(int position, View view) { + onBindView(view, getItem(position)); + } + + private void onBindView(View view, TargetInfo info) { final ViewHolder holder = (ViewHolder) view.getTag(); holder.text.setText(info.getDisplayLabel()); if (showsExtendedInfo(info)) { @@ -1461,6 +1468,15 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic new LoadAdapterIconTask((DisplayResolveInfo) info).execute(); } holder.icon.setImageDrawable(info.getDisplayIcon()); + if (holder.badge != null) { + final Drawable badge = info.getBadgeIcon(); + if (badge != null) { + holder.badge.setImageDrawable(badge); + holder.badge.setVisibility(View.VISIBLE); + } else { + holder.badge.setVisibility(View.GONE); + } + } } } @@ -1514,20 +1530,47 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic public TextView text; public TextView text2; public ImageView icon; + public ImageView badge; public ViewHolder(View view) { text = (TextView) view.findViewById(com.android.internal.R.id.text1); text2 = (TextView) view.findViewById(com.android.internal.R.id.text2); icon = (ImageView) view.findViewById(R.id.icon); + badge = (ImageView) view.findViewById(R.id.target_badge); } } - class ItemLongClickListener implements AdapterView.OnItemLongClickListener { + class ItemClickListener implements AdapterView.OnItemClickListener, + AdapterView.OnItemLongClickListener { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + final ListView listView = parent instanceof ListView ? (ListView) parent : null; + if (listView != null) { + position -= listView.getHeaderViewsCount(); + } + if (position < 0) { + // Header views don't count. + return; + } + final int checkedPos = mAdapterView.getCheckedItemPosition(); + final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION; + if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) { + setAlwaysButtonEnabled(hasValidSelection, checkedPos, true); + mOnceButton.setEnabled(hasValidSelection); + if (hasValidSelection) { + mAdapterView.smoothScrollToPosition(checkedPos); + } + mLastSelected = checkedPos; + } else { + startSelected(position, false, true); + } + } @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { - if (mListView != null) { - position -= mListView.getHeaderViewsCount(); + final ListView listView = parent instanceof ListView ? (ListView) parent : null; + if (listView != null) { + position -= listView.getHeaderViewsCount(); } if (position < 0) { // Header views don't count. diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 0fa82eb..dcdfb6c 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -21,7 +21,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:maxWidth="@dimen/resolver_max_width" - android:maxCollapsedHeight="256dp" + android:maxCollapsedHeight="288dp" android:maxCollapsedHeightSmall="56dp" android:id="@id/contentPanel"> @@ -30,24 +30,25 @@ android:layout_height="wrap_content" android:layout_alwaysShow="true" android:elevation="8dp" - android:paddingStart="?attr/dialogPreferredPadding" + android:paddingStart="16dp" android:background="@color/white" > <ImageView android:id="@+id/title_icon" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="start|center_vertical" android:layout_marginEnd="16dp" + android:visibility="gone" android:scaleType="fitCenter" /> <TextView android:id="@+id/title" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:minHeight="56dp" android:textAppearance="?attr/textAppearanceMedium" + android:textSize="14sp" android:gravity="start|center_vertical" android:paddingEnd="?attr/dialogPreferredPadding" - android:paddingTop="8dp" - android:paddingBottom="8dp" /> + android:paddingTop="12dp" + android:paddingBottom="12dp" /> <LinearLayout android:id="@+id/profile_button" android:layout_width="wrap_content" android:layout_height="48dp" @@ -82,23 +83,24 @@ </LinearLayout> </LinearLayout> - <GridView + <ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/resolver_list" android:clipToPadding="false" - android:paddingStart="@dimen/chooser_grid_padding" - android:paddingEnd="@dimen/chooser_grid_padding" android:scrollbarStyle="outsideOverlay" android:background="@color/white" android:elevation="8dp" - android:numColumns="4" + android:listSelector="@color/transparent" + android:divider="@null" + android:scrollIndicators="top" android:nestedScrollingEnabled="true" /> <TextView android:id="@+id/empty" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alwaysShow="true" + android:background="@color/white" android:text="@string/noApplications" android:padding="32dp" android:gravity="center" diff --git a/core/res/res/layout/chooser_row.xml b/core/res/res/layout/chooser_row.xml new file mode 100644 index 0000000..9baa32c --- /dev/null +++ b/core/res/res/layout/chooser_row.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2015, 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" + android:minHeight="80dp" + android:gravity="start|top" + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:paddingStart="@dimen/chooser_grid_padding" + android:paddingEnd="@dimen/chooser_grid_padding" + android:weightSum="4"> + +</LinearLayout> + diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml index 664b02f..1c496f6 100644 --- a/core/res/res/layout/resolve_grid_item.xml +++ b/core/res/res/layout/resolve_grid_item.xml @@ -18,18 +18,31 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" android:minWidth="80dp" android:gravity="center" android:paddingTop="8dp" android:paddingBottom="8dp" - android:background="?attr/activatedBackgroundIndicator"> + android:background="?attr/selectableItemBackgroundBorderless"> - <!-- Activity icon when presenting dialog --> - <ImageView android:id="@+id/icon" - android:layout_width="48dp" - android:layout_height="48dp" - android:scaleType="fitCenter" /> + <FrameLayout android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <ImageView android:id="@+id/icon" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_marginLeft="3dp" + android:layout_marginRight="3dp" + android:layout_marginBottom="3dp" + android:scaleType="fitCenter" /> + <ImageView android:id="@+id/target_badge" + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_gravity="end|bottom" + android:visibility="gone" + android:scaleType="fitCenter" /> + </FrameLayout> <!-- Activity name --> <TextView android:id="@android:id/text1" @@ -40,21 +53,23 @@ android:layout_marginRight="4dp" android:textAppearance="?attr/textAppearanceSmall" android:textColor="?attr/textColorPrimary" + android:textSize="12sp" android:fontFamily="sans-serif-condensed" - android:gravity="center" + android:gravity="top|center_horizontal" android:minLines="2" android:maxLines="2" android:ellipsize="marquee" /> <!-- Extended activity info to distinguish between duplicate activity names --> <TextView android:id="@android:id/text2" android:textAppearance="?android:attr/textAppearanceSmall" + android:textSize="12sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_marginRight="4dp" android:minLines="2" android:maxLines="2" - android:gravity="center" + android:gravity="top|center_horizontal" android:ellipsize="marquee" /> </LinearLayout> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index b9825c5..7f8c460 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -174,4 +174,6 @@ <color name="Pink_800">#ffad1457</color> <color name="Red_700">#ffc53929</color> <color name="Red_800">#ffb93221</color> + + <color name="chooser_service_row_background_color">#fff5f5f5</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7270b2c..690fd28 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2141,4 +2141,7 @@ <!-- This config is ued to determine whether animations are allowed in low power mode. --> <bool name="config_allowAnimationsInLowPowerMode">false</bool> + + <!-- Whether device supports double tap to wake --> + <bool name="config_supportDoubleTapWake">false</bool> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index a3a6105..5b564e1 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -533,7 +533,7 @@ <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_contacts">Contacts</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgroupdesc_contacts">access and modify your contacts</string> + <string name="permgroupdesc_contacts">access your contacts</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_location">Location</string> @@ -548,12 +548,12 @@ <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_calendar">Calendar</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgroupdesc_calendar">access and modify your calendar</string> + <string name="permgroupdesc_calendar">access your calendar</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_sms">SMS</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgroupdesc_sms">access and modify SMS</string> + <string name="permgroupdesc_sms">view and manage SMS messages</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_dictionary">User Dictionary</string> @@ -568,22 +568,22 @@ <!-- Title of a category of application permissioncds, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_microphone">Microphone</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgroupdesc_microphone">use device microphone</string> + <string name="permgroupdesc_microphone">record audio</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_camera">Camera</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgroupdesc_camera">use device camera</string> + <string name="permgroupdesc_camera">take pictures and record video</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_phone">Phone</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgroupdesc_phone">use device telephony</string> + <string name="permgroupdesc_phone">make and manage phone calls</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_sensors">Sensors</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgroupdesc_sensors">access sensors and wearables</string> + <string name="permgroupdesc_sensors">access data from sensors and wearable devices</string> <!-- Title for the capability of an accessibility service to retrieve window content. --> <string name="capability_title_canRetrieveWindowContent">Retrieve window content</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 11583b3..8f949e1 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2257,4 +2257,9 @@ <java-symbol type="id" name="day_picker_view_pager" /> <java-symbol type="layout" name="day_picker_content_material" /> <java-symbol type="drawable" name="scroll_indicator_material" /> + + <java-symbol type="layout" name="chooser_row" /> + <java-symbol type="color" name="chooser_service_row_background_color" /> + <java-symbol type="id" name="target_badge" /> + <java-symbol type="bool" name="config_supportDoubleTapWake" /> </resources> diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd index 7cdf72f..a352f65 100644 --- a/docs/html/sdk/index.jd +++ b/docs/html/sdk/index.jd @@ -5,29 +5,28 @@ page.image=images/cards/android-studio_2x.png header.hide=1 page.metaDescription=Download the official Android IDE and developer tools to build apps for Android phones, tablets, wearables, TVs, and more. -studio.version=1.2.0.12 +studio.version=1.2.1.1 -studio.linux_bundle_download=android-studio-ide-141.1890965-linux.zip -studio.linux_bundle_bytes=259139652 -studio.linux_bundle_checksum=72149f65911ca18d8f0d360e6b7a1e2dc57e9935 +studio.linux_bundle_download=android-studio-ide-141.1903250-linux.zip +studio.linux_bundle_bytes=258634089 +studio.linux_bundle_checksum=61f576a24ac9aa00d498bb62942c028ef4a8905b -studio.mac_bundle_download=android-studio-ide-141.1890965-mac.dmg +studio.mac_bundle_download=android-studio-ide-141.1903250-mac.dmg studio.mac_bundle_bytes=260877150 -studio.mac_bundle_checksum=06fe5a2d9ab6c99bf42fb2014e519a073fc7e90d +studio.mac_bundle_checksum=a5a6ba50e3590de0973230a238d17726a1d9395c -studio.win_bundle_download=android-studio-ide-141.1890965-windows.zip -studio.win_bundle_bytes=261548058 -studio.win_bundle_checksum=29416e54ad074881a1e668e61e3d0c2316108dbe +studio.win_bundle_download=android-studio-ide-141.1903250-windows.zip +studio.win_bundle_bytes=261042465 +studio.win_bundle_checksum=ce924e0e4cff4b7f24df3f7ce0c1ce2379347d72 -studio.win_bundle_exe_download=android-studio-bundle-141.1890965-windows.exe -studio.win_bundle_exe_bytes=928285584 -studio.win_bundle_exe_checksum=47be67749409f0d710c05b9a8f22d9191c47a9d0 - -studio.win_notools_exe_download=android-studio-ide-141.1890965-windows.exe -studio.win_notools_exe_bytes=243621072 -studio.win_notools_exe_checksum=760d45212bea42f52adb1cbeab2390156d49c74d +studio.win_bundle_exe_download=android-studio-bundle-141.1903250-windows.exe +studio.win_bundle_exe_bytes=930462136 +studio.win_bundle_exe_checksum=680668b6b4a51c519efda814b96c2b61541a50f2 +studio.win_notools_exe_download=android-studio-ide-141.1903250-windows.exe +studio.win_notools_exe_bytes=243747312 +studio.win_notools_exe_checksum=d89917dd044e0559c87d6a05d49780ab110269f7 diff --git a/docs/html/tools/debugging/ddms.jd b/docs/html/tools/debugging/ddms.jd index e9c2877..28ad11e 100644 --- a/docs/html/tools/debugging/ddms.jd +++ b/docs/html/tools/debugging/ddms.jd @@ -204,6 +204,10 @@ parent.link=index.html <li>Click the <strong>Start Method Profiling</strong> button.</li> + <li>In Android 4.4 and later, choose either trace-based profiling or sample-based profiling + with a specified sampling interval. For earlier versions of Android, only trace-based profiling + is available.</li> + <li>Interact with your application to start the methods that you want to profile.</li> <li>Click the <strong>Stop Method Profiling</strong> button. DDMS stops profiling your diff --git a/docs/html/tools/debugging/debugging-tracing.jd b/docs/html/tools/debugging/debugging-tracing.jd index bd4afbc..fa5b4e1 100644 --- a/docs/html/tools/debugging/debugging-tracing.jd +++ b/docs/html/tools/debugging/debugging-tracing.jd @@ -127,7 +127,7 @@ parent.link=index.html {@link android.os.Debug#startMethodTracing() startMethodTracing()} methods. In the call, you specify a base name for the trace files that the system generates. To stop tracing, call {@link android.os.Debug#stopMethodTracing() stopMethodTracing()}. These methods start and stop method - tracing across the entire virtual machine. For example, you could call + tracing across the entire virtual machine. For example, you could call {@link android.os.Debug#startMethodTracing() startMethodTracing()} in your activity's {@link android.app.Activity#onCreate onCreate()} method, and call {@link android.os.Debug#stopMethodTracing() stopMethodTracing()} in that activity's @@ -157,6 +157,13 @@ parent.link=index.html times are only useful in relation to other profile output, so you can see if changes have made the code faster or slower relative to a previous profiling run.</p> + <p>In Android 4.4 and later, you can use sample-based profiling to profile with less runtime + performance impact. To enable sample profiling, call {@link + android.os.Debug#startMethodTracingSampling(java.lang.String, int, int) + startMethodTracingSampling()} with a specified sampling interval. The system will then gather + samples periodically until tracing is stopped via {@link android.os.Debug#stopMethodTracing() + stopMethodTracing()}.</p> + <h2 id="copyingfiles">Copying Trace Files to a Host Machine</h2> <p>After your application has run and the system has created your trace files @@ -277,7 +284,8 @@ dmtracedump [-ho] [-s sortable] [-d trace-base-name] [-g outfile] <trace-base Traceview logging does not handle threads well, resulting in these two problems: <ol> - <li>If a thread exits during profiling, the thread name is not emitted;</li> + <li>If a thread exits during profiling, the thread name is not emitted (fixed in Android 5.1 + and later);</li> <li>The VM reuses thread IDs. If a thread stops and another starts, they may get the same ID.</li> diff --git a/docs/html/tools/revisions/studio.jd b/docs/html/tools/revisions/studio.jd index f530a5f..7138efe 100644 --- a/docs/html/tools/revisions/studio.jd +++ b/docs/html/tools/revisions/studio.jd @@ -43,6 +43,21 @@ Android Studio, as denoted by revision number. </p> <div class="toggle-content opened"> <p><a href="#" onclick="return toggleContent(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" + alt=""/>Android Studio v1.2.1</a> <em>(May 2015)</em> + </p> + <div class="toggle-content-toggleme"> + <p>Various fixes and enhancements:</p> + <ul> + <li>Fixed minor performance and feature issues. </li> + </ul> + </div> +</div> + + + +<div class="toggle-content closed"> + <p><a href="#" onclick="return toggleContent(this)"> + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""/>Android Studio v1.2.0</a> <em>(April 2015)</em> </p> diff --git a/docs/html/training/testing/ui-testing/index.jd b/docs/html/training/testing/ui-testing/index.jd index 20422f7..d660c60 100644 --- a/docs/html/training/testing/ui-testing/index.jd +++ b/docs/html/training/testing/ui-testing/index.jd @@ -72,5 +72,5 @@ Testing UI for a Single App</a></strong></dt> <dd>Learn how to test UI in a single app by using the Espresso testing framework.</dd> <dt><strong><a href="uiautomator-testing.html"> Testing UI for Multiple Apps</a></strong></dt> - <dd>Learn how to test UI in multiple apps by using the UI Automator testing framework</dd> + <dd>Learn how to test UI in multiple apps by using the UI Automator testing framework.</dd> </dl>
\ No newline at end of file diff --git a/docs/html/training/testing/unit-testing/index.jd b/docs/html/training/testing/unit-testing/index.jd new file mode 100644 index 0000000..a35ba80 --- /dev/null +++ b/docs/html/training/testing/unit-testing/index.jd @@ -0,0 +1,63 @@ +page.title=Building Effective Unit Tests +page.tags=testing,androidjunitrunner,junit,unit test + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + <h2> + You should also read + </h2> + <ul> + <li> + <a href="{@docRoot}tools/testing-support-library/index.html">Testing Support Library</a> + </li> + </ul> +</div> +</div> + +<p>Unit tests are the fundamental tests in your app testing strategy. By creating and running unit +tests against your code, you can easily verify that the logic of individual units is correct. +Running unit tests after every build helps you to +quickly catch and fix software regressions introduced by code changes to your app. +</p> + +<p>A unit test generally exercises the functionality of the smallest possible unit of code (which +could be a method, class, or component) in a repeatable way. You should build unit tests when you +need to verify the logic of specific code in your app. For example, if you are unit testing a +class, your test might check that the class is in the right state. Typically, the unit of code +is tested in isolation; your test affects and monitors changes to that unit only. A +<a href="http://en.wikipedia.org/wiki/Mock_object" class="external-link">mocking framework</a> +can be used to isolate your unit from its dependencies.</p> + +<p class="note"><strong>Note:</strong> Unit tests are not suitable for testing +complex UI interaction events. Instead, you should use the UI testing frameworks, as described in +<a href="{@docRoot}training/testing/ui-testing/index.html">Automating UI Tests</a>.</p> + +<p>For testing Android apps, you typically create these types of automated unit tests:</p> + +<ul> +<li><strong>Local tests:</strong> Unit tests that run on your local machine only. These tests are +compiled to run locally on the Java Virtual Machine (JVM) to minimize execution time. Use this +approach to run unit tests that have no dependencies on the Android framework or have dependencies +that can be filled by using mock objects.</li> +<li><strong>Instrumented tests:</strong> Unit tests that run on an Android device or emulator. +These tests have access to instrumentation information, such as the +{@link android.content.Context} for the app under test. Use this approach to run unit tests that +have Android dependencies which cannot be easily filled by using mock objects.</li> +</ul> + +<p>The lessons in this class teach you how to build these types of automated unit tests.</p> + +<h2>Lessons</h2> +<dl> + <dt><strong><a href="local-unit-tests.html"> +Building Local Unit Tests</a></strong></dt> + <dd>Learn how to build unit tests that run on your local machine.</dd> + <dt><strong><a href="instrumented-unit-tests.html"> +Building Instrumented Unit Tests</a></strong></dt> + <dd>Learn how to build unit tests that run on an Android device or emulator.</dd> +</dl>
\ No newline at end of file diff --git a/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd new file mode 100644 index 0000000..07f0f73 --- /dev/null +++ b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd @@ -0,0 +1,250 @@ +page.title=Building Instrumented Unit Tests +page.tags=testing,androidjunitrunner,junit,unit test,mock,instrumentation +trainingnavtop=true + +@jd:body + +<!-- This is the training bar --> +<div id="tb-wrapper"> +<div id="tb"> + <h2>Dependencies and Prerequisites</h2> + + <ul> + <li>Android 2.2 (API level 8) or higher</li> + <li><a href="{@docRoot}tools/testing-support-library/index.html"> + Android Testing Support Library</a></li> + </ul> + + <h2>This lesson teaches you to</h2> + + <ol> + <li><a href="#setup">Set Up Your Testing Environment</a></li> + <li><a href="#build">Create a Instrumented Unit Test Class</a></li> + <li><a href="#run">Run Instrumented Unit Tests</a></li> + </ol> + + <h2>Try it out</h2> + + <ul> + <li> +<a href="https://github.com/googlesamples/android-testing/tree/master/unittesting/BasicUnitAndroidTest" +class="external-link">Instrumented Unit Tests Code Samples</a></li> + </ul> +</div> +</div> + +<p> +Instrumented unit tests are unit tests that run on physical devices and emulators, instead of +the Java Virtual Machine (JVM) on your local machine. You should create instrumented unit tests +if your tests need access to instrumentation information (such as the target app's +{@link android.content.Context}) or if they require the real implementation of an Android framework +component (such as a {@link android.os.Parcelable} or {@link android.content.SharedPreferences} +object). Using instrumented unit tests also helps to reduce the effort required to write and +maintain mock code. You are still free to use a mocking framework, if you choose, to simulate any +dependency relationships. Instrumented unit tests can take advantage of the Android framework APIs +and supporting APIs, such as the Android Testing Support Library. +</p> + +<h2 id="setup">Set Up Your Testing Environment</h2> +<p>Before building instrumented unit tests, you must:</p> + + <ul> + <li> + <strong>Install the Android Testing Support Library</strong>. The + <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html"> + {@code AndroidJUnitRunner}</a> API, located under the + {@code com.android.support.test.runner} package, allows you to + create and run instrumented unit tests. To learn how to install the + library, see <a href="{@docRoot}tools/testing-support-library/index.html#setup"> + Testing Support Library Setup</a>. + </li> + + <li> + <strong>Set up your project structure.</strong> In your Gradle project, the source code for + the target app that you want to test is typically placed under the {@code app/src/main/java} + folder. The source code for instrumentatation tests, including your unit tests, must be + placed under the <code>app/src/androidTest/java</code> folder. + To learn more about setting up your project directory, see + <a href="{@docRoot}tools/projects/index.html">Managing Projects</a>. + </li> + + <li> + <strong>Specify your Android testing dependencies</strong>. In order for the + <a href="{@docRoot}tools/building/plugin-for-gradle.html">Android Plug-in for Gradle</a> to + correctly build and run your instrumented unit tests, you must specify the following + libraries in the {@code build.gradle} file of your Android app module: + + <pre> +dependencies { + androidTestCompile 'com.android.support.test:runner:0.2' + androidTestCompile 'com.android.support.test:rules:0.2' + // Set this dependency if you want to use Hamcrest matching + androidTestCompile 'org.hamcrest:hamcrest-library:1.1' +} +</pre> + </li> + </ul> + +<h2 id="build">Create an Instrumented Unit Test Class</h2> +<p> +Your instrumented unit test class should be written as a JUnit 4 test class. To learn more about +creating JUnit 4 test classes and using JUnit 4 assertions and annotations, see +<a href="local-unit-tests.html#build">Create a Local Unit Test Class</a>. +</p> +<p>To create an instrumented JUnit 4 test class, add the {@code @RunWith(AndroidJUnit4.class)} +annotation at the beginning of your test class definition. You also need to specify the +<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html"> +{@code AndroidJUnitRunner}</a> class +provided in the Android Testing Support Library as your default test runner. This step is described +in more detail in <a href="#run">Run Instrumented Unit Tests</a>. +</p> + +<p>The following example shows how you might write an instrumented unit test to test that +the {@link android.os.Parcelable} interface is implemented correctly for the +{@code LogHistory} class:</p> + +<pre> +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.util.Pair; +import org.junit.Test; +import org.junit.runner.RunWith; +import java.util.List; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +@RunWith(AndroidJUnit4.class) +public class LogHistoryAndroidUnitTest { + + public static final String TEST_STRING = "This is a string"; + public static final long TEST_LONG = 12345678L; + private LogHistory mLogHistory; + + @Before + public void createLogHistory() { + mLogHistory = new LogHistory(); + } + + @Test + public void logHistory_ParcelableWriteRead() { + // Set up the Parcelable object to send and receive. + mLogHistory.addEntry(TEST_STRING, TEST_LONG); + + // Write the data. + Parcel parcel = Parcel.obtain(); + mLogHistory.writeToParcel(parcel, mLogHistory.describeContents()); + + // After you're done with writing, you need to reset the parcel for reading. + parcel.setDataPosition(0); + + // Read the data. + LogHistory createdFromParcel = LogHistory.CREATOR.createFromParcel(parcel); + List<Pair<String, Long>> createdFromParcelData = createdFromParcel.getData(); + + // Verify that the received data is correct. + assertThat(createdFromParcelData.size(), is(1)); + assertThat(createdFromParcelData.get(0).first, is(TEST_STRING)); + assertThat(createdFromParcelData.get(0).second, is(TEST_LONG)); + } +} +</pre> + +<h3 id="test-suites">Creating a test suite</h3> +<p> +To organize the execution of your instrumented unit tests, you can group a collection of test +classes in a <em>test suite</em> class and run these tests together. Test suites can be nested; +your test suite can group other test suites and run all their component test classes together. +</p> + +<p> +A test suite is contained in a test package, similar to the main application package. By +convention, the test suite package name usually ends with the {@code .suite} suffix (for example, +{@code com.example.android.testing.mysample.suite}). +</p> + +<p> +To create a test suite for your unit tests, import the JUnit +<a href="http://junit.sourceforge.net/javadoc/org/junit/runner/RunWith.html" +class="external-link">{@code RunWith}</a> and +<a href="http://junit.sourceforge.net/javadoc/org/junit/runners/Suite.html" +class="external-link">{@code Suite}</a> classes. In your test suite, add the +{@code @RunWith(Suite.class)} and the {@code @Suite.SuitClasses()} annotations. In +the {@code @Suite.SuiteClasses()} annotation, list the individual test classes or test +suites as arguments. +</p> + +<p> +The following example shows how you might implement a test suite called {@code UnitTestSuite} +that groups and runs the {@code CalculatorInstrumentationTest} and +{@code CalculatorAddParameterizedTest} test classes together. +</p> + +<pre> +import com.example.android.testing.mysample.CalculatorAddParameterizedTest; +import com.example.android.testing.mysample.CalculatorInstrumentationTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +// Runs all unit tests. +@RunWith(Suite.class) +@Suite.SuiteClasses({CalculatorInstrumentationTest.class, + CalculatorAddParameterizedTest.class}) +public class UnitTestSuite {} +</pre> + +<h2 id="run">Run Instrumented Unit Tests</h2> +<p> +The +<a href="https://developer.android.com/tools/building/plugin-for-gradle.html"> + Android Plug-in for Gradle</a> +provides a default directory ({@code src/androidTest/java}) for you to store the instrumented unit +and integration test classes and test suites that you want to run on a device. The plug-in compiles +the test code in that directory and then executes the test app using a test runner class. You must +set the +<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html"> +{@code AndroidJUnitRunner}</a> class provided in the +<a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a> +as your default test runner.</p> +</p> + +<p>To specify +<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html"> +{@code AndroidJUnitRunner}</a> as the default test instrumentation runner, add the following +setting in your {@code build.gradle} file:</p> +<pre> +android { + defaultConfig { + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } +} +</pre> + +<h3 id="run-from-Android-Studio">Running instrumented unit tests from Android Studio</h3> +<p> +To run instrumented unit tests in your Gradle project from Android Studio: +</p> +<ol> +<li>Open the <strong>Build Variants</strong> window by clicking the left-hand tab, then set the +test artifact to <em>Android Instrumentation Tests</em>. +</li> +<li>In the <strong>Project</strong> window, drill down to your unit test class or method, then + right-click and run it using the Android Test configuration. +</li> +</ol> + +<p>Android Studio displays the results of the unit test execution in the <strong>Run</strong> +window.</p> + +<h3 id="run-from-commandline">Running instrumented unit tests from the command-line</h3> + +<p>To run instrumented unit tests in your Gradle project from the command-line, call the + {@code connectedCheck} (or {@code cC}) task:</p> + +<pre> +./gradlew cC +</pre> + +<p>You can find the generated HTML test result reports in the +{@code <path_to_your_project>/app/build/outputs/reports/androidTests/connected/} directory, +and the corresponding XML files in the +{@code <path_to_your_project>/app/build/outputs/androidTest-results/connected/} directory.</p>
\ No newline at end of file diff --git a/docs/html/training/testing/unit-testing/local-unit-tests.jd b/docs/html/training/testing/unit-testing/local-unit-tests.jd new file mode 100644 index 0000000..421709b --- /dev/null +++ b/docs/html/training/testing/unit-testing/local-unit-tests.jd @@ -0,0 +1,302 @@ +page.title=Building Local Unit Tests +page.tags=testing,androidjunitrunner,junit,unit test,mock +trainingnavtop=true + +@jd:body + +<!-- This is the training bar --> +<div id="tb-wrapper"> +<div id="tb"> + <h2>Dependencies and Prerequisites</h2> + + <ul> + <li>Android Plug-in for Gradle 1.1.0 or higher</li> + </ul> + + <h2>This lesson teaches you to</h2> + + <ol> + <li><a href="#setup">Set Up Your Testing Environment</a></li> + <li><a href="#build">Create a Local Unit Test Class</a></li> + <li><a href="#run">Run Local Unit Tests</a></li> + </ol> + + <h2>Try it out</h2> + + <ul> + <li> +<a href="https://github.com/googlesamples/android-testing/tree/master/unittesting/BasicSample" +class="external-link">Local Unit Tests Code Samples</a></li> + </ul> +</div> +</div> + +<p>If your unit test has no dependencies or only has simple dependencies on Android, you should run +your test on a local development machine. This testing approach is efficient because it helps +you avoid the overhead of loading the target app and unit test code onto a physical device or +emulator every time your test is run. Consequently, the execution time for running your unit +test is greatly reduced. With this approach, you normally use a mocking framework, like +<a href="https://code.google.com/p/mockito/" class="external-link">Mockito</a>, to fulfill any +dependency relationships.</p> + +<p><a href="{@docRoot}tools/building/plugin-for-gradle.html">Android Plug-in for Gradle</a> +version 1.1.0 and higher allows you to create a source directory ({@code src/test/java}) in your +project to store JUnit tests that you want to run on a local machine. This feature improves your +project organization by letting you group your unit tests together into a single source set. You +can run the tests from Android Studio or the command-line, and the plugin executes them on the +local Java Virtual Machine (JVM) on your development machine. </p> + +<h2 id="setup">Set Up Your Testing Environment</h2> +<p>Before building local unit tests, you must:</p> + + <ul> + <li> + <strong>Set up your project structure.</strong> In your Gradle project, the source code for + the target app that you want to test is typically placed under the {@code app/src/main/java} + folder. The source code for your local unit tests must be placed under the + <code>app/src/test/java</code> folder. + To learn more about setting up your project directory, see + <a href="#run">Run Local Unit Tests</a> and + <a href="{@docRoot}tools/projects/index.html">Managing Projects</a>. + </li> + + <li> + <strong>Specify your Android testing dependencies</strong>. In order to use JUnit 4 and + Mockito with your local unit tests, specify the following libraries in + the {@code build.gradle} file of your Android app module: + + <pre> +dependencies { + // Unit testing dependencies + testCompile 'junit:junit:4.12' + // Set this dependency if you want to use Mockito + testCompile 'org.mockito:mockito-core:1.10.19' + // Set this dependency if you want to use Hamcrest matching + androidTestCompile 'org.hamcrest:hamcrest-library:1.1' +} +</pre> + </li> + </ul> + +<h2 id="build">Create a Local Unit Test Class</h2> +<p>Your local unit test class should be written as a JUnit 4 test class. +<a href="http://junit.org/" class="external-link">JUnit</a> is the most popular +and widely-used unit testing framework for Java. The latest version of this framework, JUnit 4, +allows you to write tests in a cleaner and more flexible way than its predecessor versions. Unlike +the previous approach to Android unit testing based on JUnit 3, with JUnit 4, you do not need to +extend the {@code junit.framework.TestCase} class. You also do not need to prefix your test method +name with the {@code ‘test’} keyword, or use any classes in the {@code junit.framework} or +{@code junit.extensions} package.</p> + +<p>To create a basic JUnit 4 test class, create a Java class that contains one or more test methods. +A test method begins with the {@code @Test} annotation and contains the code to exercise +and verify a single functionality in the component that you want to test.</p> + +<p>The following example shows how you might implement a local unit test class. The test method +{@code emailValidator_CorrectEmailSimple_ReturnsTrue} verifies that the {@code isValidEmail()} +method in the app under test returns the correct result.</p> + +<pre> +import org.junit.Test; +import java.util.regex.Pattern; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class EmailValidatorTest { + + @Test + public void emailValidator_CorrectEmailSimple_ReturnsTrue() { + assertThat(EmailValidator.isValidEmail("name@email.com"), is(true)); + } + ... +} +</pre> + +<p>To test that components in your app return the expected results, use the +<a href="http://junit.org/javadoc/latest/org/junit/Assert.html" class="external-link"> +junit.Assert</a> methods to perform validation checks (or <em>assertions</em>) to compare the state +of the component under test against some expected value. To make tests more readable, you +can use <a href="https://code.google.com/p/hamcrest/wiki/Tutorial" class="external-link"> +Hamcrest matchers</a> (such as the {@code is()} and {@code equalTo()} methods) to match the +returned result against the expected result.</p> + +<p>In your JUnit 4 test class, you can use annotations to call out sections in your test code for +special processing, such as:</p> + +<ul> +<li> +{@code @Before}: Use this annotation to specify a block of code with test setup operations. This +code block will be invoked before each test. You can have multiple {@code @Before} methods but +the order which these methods are called is not fixed. +</li> +<li> +{@code @After}: This annotation specifies a block of code with test tear-down operations. This +code block will be called after every test method. You can define multiple {@code @After} +operations in your test code. Use this annotation to release any resources from memory. +</li> +<li> +{@code @Test}: Use this annotation to mark a test method. A single test class can contain +multiple test methods, each prefixed with this annotation. +</li> +<li> +{@code @BeforeClass}: Use this annotation to specify static methods to be invoked only once per +test class. This testing step is useful for expensive operations such as connecting to a database. +</li> +<li> +{@code @AfterClass}: Use this annotation to specify static methods to be invoked only after all +tests in the class have been run. This testing step is useful for releasing any resources allocated +in the {@code @BeforeClass} block. +</li> +<li> +{@code @Test(timeout=<milliseconds>)}: Specifies a timeout period for the test. If the +test starts but does not complete within the given timeout period, it automatically fails. You must +specify the timeout period in milliseconds, for example: {@code @Test(timeout=5000)}. +</li> +</ul> + +<h3 id="mocking-dependencies">Mocking Android dependencies</h3> +<p> +By default, the <a href="{@docRoot}tools/building/plugin-for-gradle.html"> +Android Plug-in for Gradle</a> executes your local unit tests against a modified +version of the {@code android.jar} library, which does not contain any actual code. Instead, method +calls to Android classes from your unit test throw an exception. +</p> +<p> +You can use a mocking framework to stub out external dependencies in your code, to easily test that +your component interacts with a dependency in an expected way. By substituting Android dependencies +with mock objects, you can isolate your unit test from the rest of the Android system while +verifying that the correct methods in those dependencies are called. The +<a href="https://code.google.com/p/mockito/" class="external-link">Mockito</a> mocking framework +for Java (version 1.9.5 and higher) offers compatibility with Android unit testing. +With Mockito, you can configure mock objects to return some specific value when invoked.</p> + +<p>To add a mock object to your local unit test using this framework, follow this programming model: +</p> + +<ol> +<li> +Include the Mockito library dependency in your {@code build.gradle} file, as described in +<a href="#setup">Set Up Your Testing Environment</a>. +</li> +<li>At the beginning of your unit test class definition, add the +{@code @RunWith(MockitoJUnitRunner.class)} annotation. This annotation tells the Mockito test +runner to validate that your usage of the framework is correct and simplifies the initialization of +your mock objects. +</li> +<li>To create a mock object for an Android dependency, add the {@code @Mock} annotation before +the field declaration.</li> +<li>To stub the behavior of the dependency, you can specify a condition and return +value when the condition is met by using the {@code when()} and {@code thenReturn()} methods. +</li> +</ol> + +<p> +The following example shows how you might create a unit test that uses a mock +{@link android.content.Context} object. +</p> + +<pre> +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.*; +import static org.mockito.Mockito.*; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import android.content.SharedPreferences; + +@RunWith(MockitoJUnitRunner.class) +public class UnitTestSample { + + private static final String FAKE_STRING = "HELLO WORLD"; + + @Mock + Context mMockContext; + + @Test + public void readStringFromContext_LocalizedString() { + // Given a mocked Context injected into the object under test... + when(mMockContext.getString(R.string.hello_word)) + .thenReturn(FAKE_STRING); + ClassUnderTest myObjectUnderTest = new ClassUnderTest(mMockContext); + + // ...when the string is returned from the object under test... + String result = myObjectUnderTest.getHelloWorldString(); + + // ...then the result should be the expected one. + assertThat(result, is(FAKE_STRING)); + } +} +</pre> + +<p> +To learn more about using the Mockito framework, see the +<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html" +class="external-link">Mockito API reference</a> and the +{@code SharedPreferencesHelperTest} class in the +<a href="https://github.com/googlesamples/android-testing/tree/master/unittesting/BasicSample" +class="external-link">sample code</a>. +</p> + +<h2 id="run">Run Local Unit Tests</h2> +<p> +The Android Plug-in for Gradle provides a default directory ({@code src/test/java}) for you to +store unit test classes that you want to run on a local JVM. The plug-in compiles the test code in +that directory and then executes the test app locally using the default test runner class. +</p> +<p> +As with production code, you can create unit tests for a +<a href="http://developer.android.com/tools/building/configuring-gradle.html#workBuildVariants" +class="external-link">specific flavor or build type</a>. You should keep unit tests in a test +source tree location that corresponds to your production source tree, such as: + +<table> +<tr> +<th>Path to Production Class</th> +<th>Path to Local Unit Test Class</th> +</tr> +<tr> +<td>{@code src/main/java/Foo.java}</td> +<td>{@code src/test/java/FooTest.java}</td> +</tr> +<tr> +<td>{@code src/debug/java/Foo.java}</td> +<td>{@code src/testDebug/java/FooTest.java}</td> +</tr> +<tr> +<td>{@code src/myFlavor/java/Foo.java}</td> +<td>{@code src/testMyFlavor/java/FooTest.java}</td> +</tr> +</table> + +<h3 id="run-from-Android-Studio">Running local unit tests from Android Studio</h3> +<p> +To run local unit tests in your Gradle project from Android Studio: +</p> +<ol> +<li>In the <strong>Project</strong> window, right click on the project and synchronize your project. +</li> +<li>Open the <strong>Build Variants</strong> window by clicking the left-hand tab, then change the +test artifact to <em>Unit Tests</em>. +</li> +<li>In the <strong>Project</strong> window, drill down to your unit test class or method, then +right-click and run it. +</li> +</ol> + +<p>Android Studio displays the results of the unit test execution in the <strong>Run</strong> +window.</p> + +<h3 id="run-from-commandline">Running local unit tests from the command-line</h3> + +<p>To run local unit tests in your Gradle project from the command-line, call the {@code test} task +command with the {@code --continue} option.</p> + +<pre> +./gradlew test --continue +</pre> + +<p>If there are failing tests, the command will display links to HTML reports (one per build +variant). You can find the generated HTML test result reports in the +{@code <path_to_your_project>/app/build/reports/tests/} directory, and the corresponding XML +files in the {@code <path_to_your_project>/app/build/test-results/} directory.</p>
\ No newline at end of file diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 862663e..089739b 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -1875,6 +1875,24 @@ results." </ul> </li> </ul> + <ul> + <li class="nav-section"> + <div class="nav-section-header"><a href="<?cs var:toroot ?>training/testing/unit-testing/index.html" + description="How to build effective unit tests for Android apps."> + Building Effective Unit Tests + </a></div> + <ul> + <li><a href="<?cs var:toroot ?>training/testing/unit-testing/local-unit-tests.html"> + <span class="en">Building Local Unit Tests</span> + </a> + </li> + <li><a href="<?cs var:toroot ?>training/testing/unit-testing/instrumented-unit-tests.html"> + <span class="en">Building Instrumented Unit Tests</span> + </a> + </li> + </ul> + </li> + </ul> </li> <!-- End best Testing --> diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 1324311..2d8b0b2 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -1558,7 +1558,10 @@ public final class Bitmap implements Parcelable { * If other is null, return false. */ public boolean sameAs(Bitmap other) { - checkRecycled("Can't sameAs on a recycled bitmap!"); + checkRecycled("Can't call sameAs on a recycled bitmap!"); + if (other.isRecycled()) { + throw new IllegalArgumentException("Can't compare to a recycled bitmap!"); + } return this == other || (other != null && nativeSameAs(mFinalizer.mNativeBitmap, other.mFinalizer.mNativeBitmap)); } diff --git a/keystore/java/android/security/EcIesParameterSpec.java b/keystore/java/android/security/EcIesParameterSpec.java index a3e5aec..af93519 100644 --- a/keystore/java/android/security/EcIesParameterSpec.java +++ b/keystore/java/android/security/EcIesParameterSpec.java @@ -1,6 +1,8 @@ package android.security; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -50,7 +52,11 @@ import javax.crypto.Mac; public class EcIesParameterSpec implements AlgorithmParameterSpec { @Retention(RetentionPolicy.SOURCE) - @IntDef(value = {PointFormat.UNCOMPRESSED, PointFormat.COMPRESSED}) + @IntDef({ + PointFormat.UNSPECIFIED, + PointFormat.UNCOMPRESSED, + PointFormat.COMPRESSED, + }) public @interface PointFormatEnum {} /** @@ -127,6 +133,7 @@ public class EcIesParameterSpec implements AlgorithmParameterSpec { * Returns KEM KDF algorithm (e.g., {@code HKDFwithSHA256} or {@code KDF1withSHA1}) or * {@code null} if not specified. */ + @Nullable public String getKemKdfAlgorithm() { return mKemKdfAlgorithm; } @@ -138,6 +145,7 @@ public class EcIesParameterSpec implements AlgorithmParameterSpec { * @see Cipher#getInstance(String) * @see #getDemCipherKeySize() */ + @Nullable public String getDemCipherTransformation() { return mDemCipherTransformation; } @@ -158,6 +166,7 @@ public class EcIesParameterSpec implements AlgorithmParameterSpec { * @see Mac#getInstance(String) * @see #getDemMacKeySize() */ + @Nullable public String getDemMacAlgorithm() { return mDemMacAlgorithm; } @@ -194,7 +203,8 @@ public class EcIesParameterSpec implements AlgorithmParameterSpec { * Sets KEM KDF algorithm. For example, {@code HKDFwithSHA256}, {@code KDF2withSHA256}, or * {@code KDF1withSHA1}. */ - public Builder setKemKdfAlgorithm(String algorithm) { + @NonNull + public Builder setKemKdfAlgorithm(@Nullable String algorithm) { mKemKdfAlgorithm = algorithm; return this; } @@ -205,7 +215,8 @@ public class EcIesParameterSpec implements AlgorithmParameterSpec { * * @see Cipher#getInstance(String) */ - public Builder setDemCipherTransformation(String transformation) { + @NonNull + public Builder setDemCipherTransformation(@Nullable String transformation) { mDemCipherTransformation = transformation; return this; } @@ -217,6 +228,7 @@ public class EcIesParameterSpec implements AlgorithmParameterSpec { * * @see #setDemCipherTransformation(String) */ + @NonNull public Builder setDemCipherKeySize(int sizeBits) { mDemCipherKeySize = sizeBits; return this; @@ -227,7 +239,8 @@ public class EcIesParameterSpec implements AlgorithmParameterSpec { * * @see Mac#getInstance(String) */ - public Builder setDemMacAlgorithm(String algorithm) { + @NonNull + public Builder setDemMacAlgorithm(@Nullable String algorithm) { mDemMacAlgorithm = algorithm; return this; } @@ -239,6 +252,7 @@ public class EcIesParameterSpec implements AlgorithmParameterSpec { * * @see #setDemCipherKeySize(int) */ + @NonNull public Builder setDemMacKeySize(int sizeBits) { mDemMacKeySize = sizeBits; return this; @@ -247,6 +261,7 @@ public class EcIesParameterSpec implements AlgorithmParameterSpec { /** * Returns a new {@link EcIesParameterSpec} based on the current state of this builder. */ + @NonNull public EcIesParameterSpec build() { int demMacKeySize = (mDemMacKeySize != -1) ? mDemMacKeySize : mDemCipherKeySize; return new EcIesParameterSpec( diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 8e27dc3..d3dbebf 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -15,6 +15,8 @@ */ package android.security; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Activity; import android.app.PendingIntent; import android.content.ComponentName; @@ -217,6 +219,7 @@ public final class KeyChain { * successfully installed, otherwise {@link * Activity#RESULT_CANCELED} will be returned. */ + @NonNull public static Intent createInstallIntent() { Intent intent = new Intent(ACTION_INSTALL); intent.setClassName(CERT_INSTALLER_PACKAGE, @@ -261,9 +264,10 @@ public final class KeyChain { * @param alias The alias to preselect if available, or null if * unavailable. */ - public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response, + public static void choosePrivateKeyAlias(@NonNull Activity activity, + @NonNull KeyChainAliasCallback response, @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers, - String host, int port, String alias) { + @Nullable String host, int port, @Nullable String alias) { choosePrivateKeyAlias(activity, response, keyTypes, issuers, host, port, null, alias); } @@ -306,9 +310,10 @@ public final class KeyChain { * @param alias The alias to preselect if available, or null if * unavailable. */ - public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response, + public static void choosePrivateKeyAlias(@NonNull Activity activity, + @NonNull KeyChainAliasCallback response, @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers, - String host, int port, String url, String alias) { + @Nullable String host, int port, @Nullable String url, @Nullable String alias) { /* * TODO currently keyTypes, issuers are unused. They are meant * to follow the semantics and purpose of X509KeyManager @@ -361,7 +366,8 @@ public final class KeyChain { * returned via {@link KeyChainAliasCallback#alias}. * @throws KeyChainException if the alias was valid but there was some problem accessing it. */ - public static PrivateKey getPrivateKey(Context context, String alias) + @Nullable + public static PrivateKey getPrivateKey(@NonNull Context context, @NonNull String alias) throws KeyChainException, InterruptedException { if (alias == null) { throw new NullPointerException("alias == null"); @@ -396,8 +402,9 @@ public final class KeyChain { * returned via {@link KeyChainAliasCallback#alias}. * @throws KeyChainException if the alias was valid but there was some problem accessing it. */ - public static X509Certificate[] getCertificateChain(Context context, String alias) - throws KeyChainException, InterruptedException { + @Nullable + public static X509Certificate[] getCertificateChain(@NonNull Context context, + @NonNull String alias) throws KeyChainException, InterruptedException { if (alias == null) { throw new NullPointerException("alias == null"); } @@ -432,7 +439,7 @@ public final class KeyChain { * "RSA"). */ public static boolean isKeyAlgorithmSupported( - @KeyStoreKeyProperties.AlgorithmEnum String algorithm) { + @NonNull @KeyStoreKeyProperties.AlgorithmEnum String algorithm) { final String algUpper = algorithm.toUpperCase(Locale.US); return KeyStoreKeyProperties.Algorithm.EC.equals(algUpper) || KeyStoreKeyProperties.Algorithm.RSA.equals(algUpper); @@ -446,7 +453,7 @@ public final class KeyChain { * that makes it non-exportable. */ public static boolean isBoundKeyAlgorithm( - @KeyStoreKeyProperties.AlgorithmEnum String algorithm) { + @NonNull @KeyStoreKeyProperties.AlgorithmEnum String algorithm) { if (!isKeyAlgorithmSupported(algorithm)) { return false; } @@ -455,7 +462,8 @@ public final class KeyChain { } /** @hide */ - public static X509Certificate toCertificate(byte[] bytes) { + @NonNull + public static X509Certificate toCertificate(@NonNull byte[] bytes) { if (bytes == null) { throw new IllegalArgumentException("bytes == null"); } @@ -496,14 +504,14 @@ public final class KeyChain { * * Caller should call unbindService on the result when finished. */ - public static KeyChainConnection bind(Context context) throws InterruptedException { + public static KeyChainConnection bind(@NonNull Context context) throws InterruptedException { return bindAsUser(context, Process.myUserHandle()); } /** * @hide */ - public static KeyChainConnection bindAsUser(Context context, UserHandle user) + public static KeyChainConnection bindAsUser(@NonNull Context context, UserHandle user) throws InterruptedException { if (context == null) { throw new NullPointerException("context == null"); @@ -537,7 +545,7 @@ public final class KeyChain { return new KeyChainConnection(context, keyChainServiceConnection, q.take()); } - private static void ensureNotOnMainThread(Context context) { + private static void ensureNotOnMainThread(@NonNull Context context) { Looper looper = Looper.myLooper(); if (looper != null && looper == context.getMainLooper()) { throw new IllegalStateException( diff --git a/keystore/java/android/security/KeyChainAliasCallback.java b/keystore/java/android/security/KeyChainAliasCallback.java index 2500863..8e41377 100644 --- a/keystore/java/android/security/KeyChainAliasCallback.java +++ b/keystore/java/android/security/KeyChainAliasCallback.java @@ -15,6 +15,8 @@ */ package android.security; +import android.annotation.Nullable; + /** * The KeyChainAliasCallback is the callback for {@link * KeyChain#choosePrivateKeyAlias}. @@ -25,5 +27,5 @@ public interface KeyChainAliasCallback { * Called with the alias of the certificate chosen by the user, or * null if no value was chosen. */ - public void alias(String alias); + public void alias(@Nullable String alias); } diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java index 97e3a67..404f939 100644 --- a/keystore/java/android/security/KeyGeneratorSpec.java +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -16,6 +16,9 @@ package android.security; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.KeyguardManager; import android.content.Context; import android.text.TextUtils; @@ -163,6 +166,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityStart() { return mKeyValidityStart; } @@ -172,6 +176,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityForConsumptionEnd() { return mKeyValidityForConsumptionEnd; } @@ -181,6 +186,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityForOriginationEnd() { return mKeyValidityForOriginationEnd; } @@ -195,6 +201,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. */ + @NonNull public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() { return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } @@ -202,6 +209,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of block modes with which the key can be used. */ + @NonNull public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() { return ArrayUtils.cloneIfNotEmpty(mBlockModes); } @@ -269,7 +277,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * {@code context} passed in may be used to pop up some UI to ask the user to unlock or * initialize the Android KeyStore facility. */ - public Builder(Context context) { + public Builder(@NonNull Context context) { if (context == null) { throw new NullPointerException("context == null"); } @@ -282,7 +290,8 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * <p>The alias must be provided. There is no default. */ - public Builder setAlias(String alias) { + @NonNull + public Builder setAlias(@NonNull String alias) { if (alias == null) { throw new NullPointerException("alias == null"); } @@ -296,6 +305,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * <p>By default, the key size will be determines based on the key algorithm. For example, * for {@code HmacSHA256}, the key size will default to {@code 256}. */ + @NonNull public Builder setKeySize(int keySize) { mKeySize = keySize; return this; @@ -313,6 +323,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @see KeyguardManager#isDeviceSecure() */ + @NonNull public Builder setEncryptionRequired() { mFlags |= KeyStore.FLAG_ENCRYPTED; return this; @@ -325,6 +336,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @see #setKeyValidityEnd(Date) */ + @NonNull public Builder setKeyValidityStart(Date startDate) { mKeyValidityStart = startDate; return this; @@ -339,6 +351,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * @see #setKeyValidityForConsumptionEnd(Date) * @see #setKeyValidityForOriginationEnd(Date) */ + @NonNull public Builder setKeyValidityEnd(Date endDate) { setKeyValidityForOriginationEnd(endDate); setKeyValidityForConsumptionEnd(endDate); @@ -352,6 +365,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @see #setKeyValidityForConsumptionEnd(Date) */ + @NonNull public Builder setKeyValidityForOriginationEnd(Date endDate) { mKeyValidityForOriginationEnd = endDate; return this; @@ -365,6 +379,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @see #setKeyValidityForOriginationEnd(Date) */ + @NonNull public Builder setKeyValidityForConsumptionEnd(Date endDate) { mKeyValidityForConsumptionEnd = endDate; return this; @@ -375,6 +390,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * <p>This must be specified for all keys. There is no default. */ + @NonNull public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; return this; @@ -387,6 +403,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * <p>This must be specified for keys which are used for encryption/decryption. */ + @NonNull public Builder setEncryptionPaddings( @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) { mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); @@ -399,6 +416,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * <p>This must be specified for encryption/decryption keys. */ + @NonNull public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) { mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; @@ -436,6 +454,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * ciphertext.</li> * </ul> */ + @NonNull public Builder setRandomizedEncryptionRequired(boolean required) { mRandomizedEncryptionRequired = required; return this; @@ -456,6 +475,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @see #setUserAuthenticationValidityDurationSeconds(int) */ + @NonNull public Builder setUserAuthenticationRequired(boolean required) { mUserAuthenticationRequired = required; return this; @@ -472,7 +492,9 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @see #setUserAuthenticationRequired(boolean) */ - public Builder setUserAuthenticationValidityDurationSeconds(int seconds) { + @NonNull + public Builder setUserAuthenticationValidityDurationSeconds( + @IntRange(from = -1) int seconds) { mUserAuthenticationValidityDurationSeconds = seconds; return this; } @@ -482,6 +504,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * * @throws IllegalArgumentException if a required field is missing or violates a constraint. */ + @NonNull public KeyGeneratorSpec build() { return new KeyGeneratorSpec(mContext, mKeystoreAlias, diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index 7fd5cb5..2086ccb 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -17,6 +17,9 @@ package android.security; import android.app.KeyguardManager; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.text.TextUtils; @@ -286,6 +289,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Returns the key type (e.g., "EC", "RSA") specified by this parameter. */ + @Nullable public @KeyStoreKeyProperties.AlgorithmEnum String getKeyType() { return mKeyType; } @@ -303,6 +307,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Returns the {@link AlgorithmParameterSpec} that will be used for creation * of the key pair. */ + @NonNull public AlgorithmParameterSpec getAlgorithmParameterSpec() { return mSpec; } @@ -311,6 +316,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Gets the subject distinguished name to be used on the X.509 certificate * that will be put in the {@link java.security.KeyStore}. */ + @NonNull public X500Principal getSubjectDN() { return mSubjectDN; } @@ -319,6 +325,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Gets the serial number to be used on the X.509 certificate that will be * put in the {@link java.security.KeyStore}. */ + @NonNull public BigInteger getSerialNumber() { return mSerialNumber; } @@ -327,6 +334,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Gets the start date to be used on the X.509 certificate that will be put * in the {@link java.security.KeyStore}. */ + @NonNull public Date getStartDate() { return mStartDate; } @@ -335,6 +343,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Gets the end date to be used on the X.509 certificate that will be put in * the {@link java.security.KeyStore}. */ + @NonNull public Date getEndDate() { return mEndDate; } @@ -359,6 +368,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityStart() { return mKeyValidityStart; } @@ -369,6 +379,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityForConsumptionEnd() { return mKeyValidityForConsumptionEnd; } @@ -378,6 +389,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityForOriginationEnd() { return mKeyValidityForOriginationEnd; } @@ -392,6 +404,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of digest algorithms with which the key can be used. */ + @NonNull public @KeyStoreKeyProperties.DigestEnum String[] getDigests() { return ArrayUtils.cloneIfNotEmpty(mDigests); } @@ -399,6 +412,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. */ + @NonNull public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() { return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } @@ -406,6 +420,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of padding schemes with which the key can be used when signing/verifying. */ + @NonNull public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() { return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } @@ -413,6 +428,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Gets the set of block modes with which the key can be used. */ + @NonNull public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() { return ArrayUtils.cloneIfNotEmpty(mBlockModes); } @@ -528,7 +544,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * some UI to ask the user to unlock or initialize the Android KeyStore * facility. */ - public Builder(Context context) { + public Builder(@NonNull Context context) { if (context == null) { throw new NullPointerException("context == null"); } @@ -540,7 +556,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * {@link java.security.KeyStore} instance using the * {@code AndroidKeyStore} provider. */ - public Builder setAlias(String alias) { + @NonNull + public Builder setAlias(@NonNull String alias) { if (alias == null) { throw new NullPointerException("alias == null"); } @@ -551,7 +568,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * Sets the key type (e.g., EC, RSA) of the keypair to be created. */ - public Builder setKeyType(@KeyStoreKeyProperties.AlgorithmEnum String keyType) + @NonNull + public Builder setKeyType(@NonNull @KeyStoreKeyProperties.AlgorithmEnum String keyType) throws NoSuchAlgorithmException { if (keyType == null) { throw new NullPointerException("keyType == null"); @@ -569,6 +587,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * key type of RSA this will set the modulus size and for a key type of * EC it will select a curve with a matching field size. */ + @NonNull public Builder setKeySize(int keySize) { if (keySize < 0) { throw new IllegalArgumentException("keySize < 0"); @@ -581,7 +600,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Sets the algorithm-specific key generation parameters. For example, for RSA keys * this may be an instance of {@link java.security.spec.RSAKeyGenParameterSpec}. */ - public Builder setAlgorithmParameterSpec(AlgorithmParameterSpec spec) { + public Builder setAlgorithmParameterSpec(@NonNull AlgorithmParameterSpec spec) { if (spec == null) { throw new NullPointerException("spec == null"); } @@ -597,7 +616,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On * newer platforms the subject defaults to {@code CN=fake} if not specified. */ - public Builder setSubject(X500Principal subject) { + @NonNull + public Builder setSubject(@NonNull X500Principal subject) { if (subject == null) { throw new NullPointerException("subject == null"); } @@ -613,7 +633,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On * newer platforms the serial number defaults to {@code 1} if not specified. */ - public Builder setSerialNumber(BigInteger serialNumber) { + @NonNull + public Builder setSerialNumber(@NonNull BigInteger serialNumber) { if (serialNumber == null) { throw new NullPointerException("serialNumber == null"); } @@ -629,7 +650,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On * newer platforms the date defaults to {@code Jan 1 1970} if not specified. */ - public Builder setStartDate(Date startDate) { + @NonNull + public Builder setStartDate(@NonNull Date startDate) { if (startDate == null) { throw new NullPointerException("startDate == null"); } @@ -645,7 +667,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On * newer platforms the date defaults to {@code Jan 1 2048} if not specified. */ - public Builder setEndDate(Date endDate) { + @NonNull + public Builder setEndDate(@NonNull Date endDate) { if (endDate == null) { throw new NullPointerException("endDate == null"); } @@ -665,6 +688,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @see KeyguardManager#isDeviceSecure() */ + @NonNull public Builder setEncryptionRequired() { mFlags |= KeyStore.FLAG_ENCRYPTED; return this; @@ -679,6 +703,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @see #setKeyValidityEnd(Date) */ + @NonNull public Builder setKeyValidityStart(Date startDate) { mKeyValidityStart = startDate; return this; @@ -695,6 +720,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * @see #setKeyValidityForConsumptionEnd(Date) * @see #setKeyValidityForOriginationEnd(Date) */ + @NonNull public Builder setKeyValidityEnd(Date endDate) { setKeyValidityForOriginationEnd(endDate); setKeyValidityForConsumptionEnd(endDate); @@ -710,6 +736,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @see #setKeyValidityForConsumptionEnd(Date) */ + @NonNull public Builder setKeyValidityForOriginationEnd(Date endDate) { mKeyValidityForOriginationEnd = endDate; return this; @@ -725,6 +752,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @see #setKeyValidityForOriginationEnd(Date) */ + @NonNull public Builder setKeyValidityForConsumptionEnd(Date endDate) { mKeyValidityForConsumptionEnd = endDate; return this; @@ -743,6 +771,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * <p><b>NOTE: This has currently no effect. */ + @NonNull public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; return this; @@ -756,6 +785,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * <p><b>NOTE: This has currently no effect. */ + @NonNull public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) { mDigests = ArrayUtils.cloneIfNotEmpty(digests); return this; @@ -770,6 +800,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * <p><b>NOTE: This has currently no effect. */ + @NonNull public Builder setEncryptionPaddings( @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) { mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); @@ -785,6 +816,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * <p><b>NOTE: This has currently no effect. */ + @NonNull public Builder setSignaturePaddings( @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) { mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings); @@ -799,6 +831,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * <p><b>NOTE: This has currently no effect. */ + @NonNull public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) { mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; @@ -826,6 +859,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * <p><b>NOTE: This has currently no effect. */ + @NonNull public Builder setRandomizedEncryptionRequired(boolean required) { mRandomizedEncryptionRequired = required; return this; @@ -851,6 +885,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @see #setUserAuthenticationValidityDurationSeconds(int) */ + @NonNull public Builder setUserAuthenticationRequired(boolean required) { mUserAuthenticationRequired = required; return this; @@ -872,7 +907,9 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @see #setUserAuthenticationRequired(boolean) */ - public Builder setUserAuthenticationValidityDurationSeconds(int seconds) { + @NonNull + public Builder setUserAuthenticationValidityDurationSeconds( + @IntRange(from = -1) int seconds) { mUserAuthenticationValidityDurationSeconds = seconds; return this; } @@ -883,6 +920,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * @throws IllegalArgumentException if a required field is missing * @return built instance of {@code KeyPairGeneratorSpec} */ + @NonNull public KeyPairGeneratorSpec build() { return new KeyPairGeneratorSpec(mContext, mKeystoreAlias, diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java index 1cf6a7a..d1b0e5b 100644 --- a/keystore/java/android/security/KeyStoreKeyProperties.java +++ b/keystore/java/android/security/KeyStoreKeyProperties.java @@ -17,6 +17,8 @@ package android.security; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.StringDef; import android.security.keymaster.KeymasterDefs; @@ -111,6 +113,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ + @NonNull public static int[] allToKeymaster(@PurposeEnum int purposes) { int[] result = getSetFlags(purposes); for (int i = 0; i < result.length; i++) { @@ -122,7 +125,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) { + public static @PurposeEnum int allFromKeymaster(@NonNull Collection<Integer> purposes) { @PurposeEnum int result = 0; for (int keymasterPurpose : purposes) { result |= fromKeymaster(keymasterPurpose); @@ -182,7 +185,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static int toKeymasterSecretKeyAlgorithm(@AlgorithmEnum String algorithm) { + static int toKeymasterSecretKeyAlgorithm(@NonNull @AlgorithmEnum String algorithm) { if (AES.equalsIgnoreCase(algorithm)) { return KeymasterDefs.KM_ALGORITHM_AES; } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) { @@ -196,6 +199,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ + @NonNull static @AlgorithmEnum String fromKeymasterSecretKeyAlgorithm( int keymasterAlgorithm, int keymasterDigest) { switch (keymasterAlgorithm) { @@ -232,7 +236,7 @@ public abstract class KeyStoreKeyProperties { * * @return keymaster digest or {@code -1} if the algorithm does not involve a digest. */ - static int toKeymasterDigest(@AlgorithmEnum String algorithm) { + static int toKeymasterDigest(@NonNull @AlgorithmEnum String algorithm) { String algorithmUpper = algorithm.toUpperCase(Locale.US); if (algorithmUpper.startsWith("HMAC")) { String digestUpper = algorithmUpper.substring("HMAC".length()); @@ -287,7 +291,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static int toKeymaster(@BlockModeEnum String blockMode) { + static int toKeymaster(@NonNull @BlockModeEnum String blockMode) { if (ECB.equalsIgnoreCase(blockMode)) { return KeymasterDefs.KM_MODE_ECB; } else if (CBC.equalsIgnoreCase(blockMode)) { @@ -304,6 +308,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ + @NonNull static @BlockModeEnum String fromKeymaster(int blockMode) { switch (blockMode) { case KeymasterDefs.KM_MODE_ECB: @@ -322,7 +327,8 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static @BlockModeEnum String[] allFromKeymaster(Collection<Integer> blockModes) { + @NonNull + static @BlockModeEnum String[] allFromKeymaster(@NonNull Collection<Integer> blockModes) { if ((blockModes == null) || (blockModes.isEmpty())) { return EmptyArray.STRING; } @@ -338,7 +344,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static int[] allToKeymaster(@BlockModeEnum String[] blockModes) { + static int[] allToKeymaster(@Nullable @BlockModeEnum String[] blockModes) { if ((blockModes == null) || (blockModes.length == 0)) { return EmptyArray.INT; } @@ -388,7 +394,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static int toKeymaster(@EncryptionPaddingEnum String padding) { + static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) { if (NONE.equalsIgnoreCase(padding)) { return KeymasterDefs.KM_PAD_NONE; } else if (PKCS7.equalsIgnoreCase(padding)) { @@ -406,6 +412,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ + @NonNull static @EncryptionPaddingEnum String fromKeymaster(int padding) { switch (padding) { case KeymasterDefs.KM_PAD_NONE: @@ -425,7 +432,8 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static int[] allToKeymaster(@EncryptionPaddingEnum String[] paddings) { + @NonNull + static int[] allToKeymaster(@Nullable @EncryptionPaddingEnum String[] paddings) { if ((paddings == null) || (paddings.length == 0)) { return EmptyArray.INT; } @@ -463,7 +471,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static int toKeymaster(@SignaturePaddingEnum String padding) { + static int toKeymaster(@NonNull @SignaturePaddingEnum String padding) { switch (padding.toUpperCase(Locale.US)) { case RSA_PKCS1: return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN; @@ -478,6 +486,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ + @NonNull static @SignaturePaddingEnum String fromKeymaster(int padding) { switch (padding) { case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN: @@ -492,7 +501,8 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static int[] allToKeymaster(@SignaturePaddingEnum String[] paddings) { + @NonNull + static int[] allToKeymaster(@Nullable @SignaturePaddingEnum String[] paddings) { if ((paddings == null) || (paddings.length == 0)) { return EmptyArray.INT; } @@ -561,7 +571,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static int toKeymaster(@DigestEnum String digest) { + static int toKeymaster(@NonNull @DigestEnum String digest) { switch (digest.toUpperCase(Locale.US)) { case SHA1: return KeymasterDefs.KM_DIGEST_SHA1; @@ -585,6 +595,7 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ + @NonNull static @DigestEnum String fromKeymaster(int digest) { switch (digest) { case KeymasterDefs.KM_DIGEST_NONE: @@ -609,7 +620,8 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static @DigestEnum String[] allFromKeymaster(Collection<Integer> digests) { + @NonNull + static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) { if (digests.isEmpty()) { return EmptyArray.STRING; } @@ -625,7 +637,8 @@ public abstract class KeyStoreKeyProperties { /** * @hide */ - static int[] allToKeymaster(@DigestEnum String[] digests) { + @NonNull + static int[] allToKeymaster(@Nullable @DigestEnum String[] digests) { if ((digests == null) || (digests.length == 0)) { return EmptyArray.INT; } diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java index 0a9acbb..81a19bb 100644 --- a/keystore/java/android/security/KeyStoreKeySpec.java +++ b/keystore/java/android/security/KeyStoreKeySpec.java @@ -16,6 +16,9 @@ package android.security; +import android.annotation.NonNull; +import android.annotation.Nullable; + import java.security.PrivateKey; import java.security.spec.KeySpec; import java.util.Date; @@ -150,6 +153,7 @@ public class KeyStoreKeySpec implements KeySpec { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityStart() { return mKeyValidityStart; } @@ -159,6 +163,7 @@ public class KeyStoreKeySpec implements KeySpec { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityForConsumptionEnd() { return mKeyValidityForConsumptionEnd; } @@ -168,6 +173,7 @@ public class KeyStoreKeySpec implements KeySpec { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityForOriginationEnd() { return mKeyValidityForOriginationEnd; } @@ -182,6 +188,7 @@ public class KeyStoreKeySpec implements KeySpec { /** * Gets the set of block modes with which the key can be used. */ + @NonNull public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() { return ArrayUtils.cloneIfNotEmpty(mBlockModes); } @@ -189,6 +196,7 @@ public class KeyStoreKeySpec implements KeySpec { /** * Gets the set of padding modes with which the key can be used when encrypting/decrypting. */ + @NonNull public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() { return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } @@ -196,6 +204,7 @@ public class KeyStoreKeySpec implements KeySpec { /** * Gets the set of padding modes with which the key can be used when signing/verifying. */ + @NonNull public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() { return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } @@ -203,6 +212,7 @@ public class KeyStoreKeySpec implements KeySpec { /** * Gets the set of digest algorithms with which the key can be used. */ + @NonNull public @KeyStoreKeyProperties.DigestEnum String[] getDigests() { return ArrayUtils.cloneIfNotEmpty(mDigests); } diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java index 7332332..4a736c3 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -16,6 +16,9 @@ package android.security; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.KeyguardManager; import android.content.Context; @@ -182,6 +185,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityStart() { return mKeyValidityStart; } @@ -191,6 +195,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityForConsumptionEnd() { return mKeyValidityForConsumptionEnd; } @@ -200,6 +205,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @return instant or {@code null} if not restricted. */ + @Nullable public Date getKeyValidityForOriginationEnd() { return mKeyValidityForOriginationEnd; } @@ -214,6 +220,7 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Gets the set of padding schemes with which the key can be used when encrypting/decrypting. */ + @NonNull public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() { return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings); } @@ -222,6 +229,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * Gets the set of padding schemes with which the key can be used when signing or verifying * signatures. */ + @NonNull public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() { return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings); } @@ -233,6 +241,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @see #isDigestsSpecified() */ + @NonNull public @KeyStoreKeyProperties.DigestEnum String[] getDigests() { if (mDigests == null) { throw new IllegalStateException("Digests not specified"); @@ -246,6 +255,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @see #getDigests() */ + @NonNull public boolean isDigestsSpecified() { return mDigests != null; } @@ -253,6 +263,7 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * Gets the set of block modes with which the key can be used. */ + @NonNull public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() { return ArrayUtils.cloneIfNotEmpty(mBlockModes); } @@ -330,7 +341,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * some UI to ask the user to unlock or initialize the Android KeyStore * facility. */ - public Builder(Context context) { + public Builder(@NonNull Context context) { if (context == null) { throw new NullPointerException("context == null"); } @@ -350,6 +361,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @see KeyguardManager#isDeviceSecure() */ + @NonNull public Builder setEncryptionRequired(boolean required) { if (required) { mFlags |= KeyStore.FLAG_ENCRYPTED; @@ -368,6 +380,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @see #setKeyValidityEnd(Date) */ + @NonNull public Builder setKeyValidityStart(Date startDate) { mKeyValidityStart = startDate; return this; @@ -384,6 +397,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * @see #setKeyValidityForConsumptionEnd(Date) * @see #setKeyValidityForOriginationEnd(Date) */ + @NonNull public Builder setKeyValidityEnd(Date endDate) { setKeyValidityForOriginationEnd(endDate); setKeyValidityForConsumptionEnd(endDate); @@ -399,6 +413,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @see #setKeyValidityForConsumptionEnd(Date) */ + @NonNull public Builder setKeyValidityForOriginationEnd(Date endDate) { mKeyValidityForOriginationEnd = endDate; return this; @@ -414,6 +429,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @see #setKeyValidityForOriginationEnd(Date) */ + @NonNull public Builder setKeyValidityForConsumptionEnd(Date endDate) { mKeyValidityForConsumptionEnd = endDate; return this; @@ -426,6 +442,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ + @NonNull public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) { mPurposes = purposes; return this; @@ -440,6 +457,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ + @NonNull public Builder setEncryptionPaddings( @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) { mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings); @@ -455,6 +473,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ + @NonNull public Builder setSignaturePaddings( @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) { mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings); @@ -471,6 +490,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ + @NonNull public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) { mDigests = ArrayUtils.cloneIfNotEmpty(digests); return this; @@ -484,6 +504,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ + @NonNull public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) { mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes); return this; @@ -525,6 +546,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <p><b>NOTE: This has currently no effect on asymmetric key pairs. */ + @NonNull public Builder setRandomizedEncryptionRequired(boolean required) { mRandomizedEncryptionRequired = required; return this; @@ -547,6 +569,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @see #setUserAuthenticationValidityDurationSeconds(int) */ + @NonNull public Builder setUserAuthenticationRequired(boolean required) { mUserAuthenticationRequired = required; return this; @@ -565,7 +588,9 @@ public final class KeyStoreParameter implements ProtectionParameter { * * @see #setUserAuthenticationRequired(boolean) */ - public Builder setUserAuthenticationValidityDurationSeconds(int seconds) { + @NonNull + public Builder setUserAuthenticationValidityDurationSeconds( + @IntRange(from = -1) int seconds) { mUserAuthenticationValidityDurationSeconds = seconds; return this; } @@ -576,6 +601,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * @throws IllegalArgumentException if a required field is missing * @return built instance of {@code KeyStoreParameter} */ + @NonNull public KeyStoreParameter build() { return new KeyStoreParameter( mContext, diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java index f261079..d138c24 100644 --- a/keystore/tests/src/android/security/KeyStoreTest.java +++ b/keystore/tests/src/android/security/KeyStoreTest.java @@ -817,6 +817,9 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { KeymasterArguments out = new KeymasterArguments(); args = new KeymasterArguments(); + args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM); + args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, null, out); IBinder token = result.token; @@ -881,14 +884,18 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { hexToBytes("591ccb10d410ed26dc5ba74a31362870"), hexToBytes("b6ed21b99ca6f4f9f153e7b1beafed1d"), hexToBytes("23304b7a39f9f3ff067d8d8f9e24ecc7")}; + KeymasterArguments beginArgs = new KeymasterArguments(); + beginArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); + beginArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB); + beginArgs.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); for (int i = 0; i < testVectors.length; i++) { byte[] cipherText = doOperation(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, testVectors[i], - new KeymasterArguments()); + beginArgs); MoreAsserts.assertEquals(cipherVectors[i], cipherText); } for (int i = 0; i < testVectors.length; i++) { byte[] plainText = doOperation(name, KeymasterDefs.KM_PURPOSE_DECRYPT, - cipherVectors[i], new KeymasterArguments()); + cipherVectors[i], beginArgs); MoreAsserts.assertEquals(testVectors[i], plainText); } } @@ -912,6 +919,9 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> { KeymasterArguments out = new KeymasterArguments(); args = new KeymasterArguments(); + args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES); + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR); + args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, null, out); assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode); diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index af3d9b3..93b1d56 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -68,6 +68,7 @@ ZipFileRO::~ZipFileRO() { const int32_t error = OpenArchive(zipFileName, &handle); if (error) { ALOGW("Error opening archive %s: %s", zipFileName, ErrorCodeString(error)); + CloseArchive(handle); return NULL; } diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 216d9b9..c0bc6d6 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -416,7 +416,8 @@ public class AudioRecord * <br>If the audio format is not specified or is incomplete, its sample rate will be the * default output sample rate of the device (see * {@link AudioManager#PROPERTY_OUTPUT_SAMPLE_RATE}), its channel configuration will be - * {@link AudioFormat#CHANNEL_IN_DEFAULT}. + * {@link AudioFormat#CHANNEL_IN_MONO}, and the encoding will be + * {@link AudioFormat#ENCODING_PCM_16BIT}. * <br>If the buffer size is not specified with {@link #setBufferSizeInBytes(int)}, * the minimum buffer size for the source is used. */ @@ -533,7 +534,22 @@ public class AudioRecord */ public AudioRecord build() throws UnsupportedOperationException { if (mFormat == null) { - mFormat = new AudioFormat.Builder().build(); + mFormat = new AudioFormat.Builder() + .setEncoding(AudioFormat.ENCODING_PCM_16BIT) + .setChannelMask(AudioFormat.CHANNEL_IN_MONO) + .build(); + } else { + if (mFormat.getEncoding() == AudioFormat.ENCODING_INVALID) { + mFormat = new AudioFormat.Builder(mFormat) + .setEncoding(AudioFormat.ENCODING_PCM_16BIT) + .build(); + } + if (mFormat.getChannelMask() == AudioFormat.CHANNEL_INVALID + && mFormat.getChannelIndexMask() == AudioFormat.CHANNEL_INVALID) { + mFormat = new AudioFormat.Builder(mFormat) + .setChannelMask(AudioFormat.CHANNEL_IN_MONO) + .build(); + } } if (mAttributes == null) { mAttributes = new AudioAttributes.Builder() diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index b6491d8..078fb2f 100644 --- a/media/java/android/media/tv/ITvInputManager.aidl +++ b/media/java/android/media/tv/ITvInputManager.aidl @@ -72,7 +72,7 @@ interface ITvInputManager { void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId); void removeOverlayView(in IBinder sessionToken, int userId); - void requestUnblockContent(in IBinder sessionToken, in String unblockedRating, int userId); + void unblockContent(in IBinder sessionToken, in String unblockedRating, int userId); void timeShiftPause(in IBinder sessionToken, int userId); void timeShiftResume(in IBinder sessionToken, int userId); diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl index a054200..17f3984 100644 --- a/media/java/android/media/tv/ITvInputSession.aidl +++ b/media/java/android/media/tv/ITvInputSession.aidl @@ -45,7 +45,7 @@ oneway interface ITvInputSession { void relayoutOverlayView(in Rect frame); void removeOverlayView(); - void requestUnblockContent(in String unblockedRating); + void unblockContent(in String unblockedRating); void timeShiftPause(); void timeShiftResume(); diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java index 95aaa7f..0191652 100644 --- a/media/java/android/media/tv/ITvInputSessionWrapper.java +++ b/media/java/android/media/tv/ITvInputSessionWrapper.java @@ -57,7 +57,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand private static final int DO_CREATE_OVERLAY_VIEW = 10; private static final int DO_RELAYOUT_OVERLAY_VIEW = 11; private static final int DO_REMOVE_OVERLAY_VIEW = 12; - private static final int DO_REQUEST_UNBLOCK_CONTENT = 13; + private static final int DO_UNBLOCK_CONTENT = 13; private static final int DO_TIME_SHIFT_PAUSE = 14; private static final int DO_TIME_SHIFT_RESUME = 15; private static final int DO_TIME_SHIFT_SEEK_TO = 16; @@ -155,7 +155,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand mTvInputSessionImpl.removeOverlayView(true); break; } - case DO_REQUEST_UNBLOCK_CONTENT: { + case DO_UNBLOCK_CONTENT: { mTvInputSessionImpl.unblockContent((String) msg.obj); break; } @@ -267,9 +267,9 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand } @Override - public void requestUnblockContent(String unblockedRating) { + public void unblockContent(String unblockedRating) { mCaller.executeOrSendMessage(mCaller.obtainMessageO( - DO_REQUEST_UNBLOCK_CONTENT, unblockedRating)); + DO_UNBLOCK_CONTENT, unblockedRating)); } @Override diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index dca0631..705aa3d6 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -1367,12 +1367,12 @@ public final class TvInputManager { } /** - * Notifies of any structural changes (format or size) of the {@link Surface} - * passed by {@link #setSurface}. + * Notifies of any structural changes (format or size) of the surface passed in + * {@link #setSurface}. * - * @param format The new PixelFormat of the {@link Surface}. - * @param width The new width of the {@link Surface}. - * @param height The new height of the {@link Surface}. + * @param format The new PixelFormat of the surface. + * @param width The new width of the surface. + * @param height The new height of the surface. * @hide */ @SystemApi @@ -1820,14 +1820,14 @@ public final class TvInputManager { /** * Requests to unblock content blocked by parental controls. */ - void requestUnblockContent(@NonNull TvContentRating unblockedRating) { + void unblockContent(@NonNull TvContentRating unblockedRating) { Preconditions.checkNotNull(unblockedRating); if (mToken == null) { Log.w(TAG, "The session has been already released"); return; } try { - mService.requestUnblockContent(mToken, unblockedRating.flattenToString(), mUserId); + mService.unblockContent(mToken, unblockedRating.flattenToString(), mUserId); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index c1035b0..8b0472a 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -281,8 +281,15 @@ public abstract class TvInputService extends Service { } /** - * Enables or disables the overlay view. By default, the overlay view is disabled. Must be - * called explicitly after the session is created to enable the overlay view. + * Enables or disables the overlay view. + * + * <p>By default, the overlay view is disabled. Must be called explicitly after the + * session is created to enable the overlay view. + * + * <p>The TV input service can disable its overlay view when the size of the overlay view is + * insufficient to display the whole information, such as when used in Picture-in-picture. + * Override {@link #onOverlayViewSizeChanged} to get the size of the overlay view, which + * then can be used to determine whether to enable/disable the overlay view. * * @param enable {@code true} if you want to enable the overlay view. {@code false} * otherwise. @@ -637,17 +644,15 @@ public abstract class TvInputService extends Service { } /** - * Assigns a position of the {@link Surface} passed by {@link #onSetSurface}. The position - * is relative to an overlay view. + * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position + * is relative to the overlay view that sits on top of this surface. * * @param left Left position in pixels, relative to the overlay view. * @param top Top position in pixels, relative to the overlay view. * @param right Right position in pixels, relative to the overlay view. * @param bottom Bottom position in pixels, relative to the overlay view. * @see #onOverlayViewSizeChanged - * @hide */ - @SystemApi public void layoutSurface(final int left, final int top, final int right, final int bottom) { if (left > right || top > bottom) { @@ -701,41 +706,40 @@ public abstract class TvInputService extends Service { } /** - * Sets the {@link Surface} for the current input session on which the TV input renders - * video. + * Called when the application sets the surface. * - * <p>When {@code setSurface(null)} is called, the implementation should stop using the - * Surface object previously given and release any references to it. + * <p>The TV input service should render video onto the given surface. When called with + * {@code null}, the input service should immediately release any references to the + * currently set surface and stop using it. * - * @param surface possibly {@code null} {@link Surface} the application passes to this TV - * input session. - * @return {@code true} if the surface was set, {@code false} otherwise. + * @param surface The surface to be used for video rendering. Can be {@code null}. + * @return {@code true} if the surface was set successfully, {@code false} otherwise. */ public abstract boolean onSetSurface(@Nullable Surface surface); /** - * Called after any structural changes (format or size) have been made to the - * {@link Surface} passed by {@link #onSetSurface}. This method is always called - * at least once, after {@link #onSetSurface} with non-null {@link Surface} is called. + * Called after any structural changes (format or size) have been made to the surface passed + * in {@link #onSetSurface}. This method is always called at least once, after + * {@link #onSetSurface} is called with non-null surface. * - * @param format The new PixelFormat of the {@link Surface}. - * @param width The new width of the {@link Surface}. - * @param height The new height of the {@link Surface}. + * @param format The new PixelFormat of the surface. + * @param width The new width of the surface. + * @param height The new height of the surface. */ public void onSurfaceChanged(int format, int width, int height) { } /** - * Called when a size of an overlay view is changed by the application. Even when the - * overlay view is disabled by {@link #setOverlayViewEnabled}, this is called. The size is - * same as the size of {@link Surface} in general. Once {@link #layoutSurface} is called, - * the sizes of {@link Surface} and the overlay view can be different. + * Called when the size of the overlay view is changed by the application. + * + * <p>This is always called at least once when the session is created regardless of whether + * the overlay view is enabled or not. The overlay view size is the same as the containing + * {@link TvView}. Note that the size of the underlying surface can be different if the + * surface was changed by calling {@link #layoutSurface}. * * @param width The width of the overlay view. * @param height The height of the overlay view. - * @hide */ - @SystemApi public void onOverlayViewSizeChanged(int width, int height) { } diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 7e64b17..ebe281f 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -350,11 +350,27 @@ public class TvView extends ViewGroup { * @param unblockedRating A TvContentRating to unblock. * @see TvInputService.Session#notifyContentBlocked(TvContentRating) * @hide + * @deprecated Use {@link #unblockContent} instead. */ + @Deprecated @SystemApi public void requestUnblockContent(TvContentRating unblockedRating) { + unblockContent(unblockedRating); + } + + /** + * Requests to unblock TV content according to the given rating. + * + * <p>This notifies TV input that blocked content is now OK to play. + * + * @param unblockedRating A TvContentRating to unblock. + * @see TvInputService.Session#notifyContentBlocked(TvContentRating) + * @hide + */ + @SystemApi + public void unblockContent(TvContentRating unblockedRating) { if (mSession != null) { - mSession.requestUnblockContent(unblockedRating); + mSession.unblockContent(unblockedRating); } } diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 16c6075..3f0d71c 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -207,4 +207,7 @@ <!-- Default for Settings.Global.GUEST_USER_ENABLED --> <bool name="def_guest_user_enabled">true</bool> + <!-- Default state of tap to wake --> + <bool name="def_double_tap_to_wake">true</bool> + </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 1953e75..ad710a6 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -35,6 +35,7 @@ import android.database.MatrixCursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.hardware.camera2.utils.ArrayUtils; +import android.media.AudioManager; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -52,9 +53,11 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; + import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; + import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -1788,7 +1791,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 118; + private static final int SETTINGS_VERSION = 120; private final int mUserId; @@ -1891,6 +1894,31 @@ public class SettingsProvider extends ContentProvider { int currentVersion = oldVersion; + // v119: Reset zen + ringer mode. + if (currentVersion == 118) { + if (userId == UserHandle.USER_OWNER) { + final SettingsState globalSettings = getGlobalSettingsLocked(); + globalSettings.updateSettingLocked(Settings.Global.ZEN_MODE, + Integer.toString(Settings.Global.ZEN_MODE_OFF), + SettingsState.SYSTEM_PACKAGE_NAME); + globalSettings.updateSettingLocked(Settings.Global.MODE_RINGER, + Integer.toString(AudioManager.RINGER_MODE_NORMAL), + SettingsState.SYSTEM_PACKAGE_NAME); + } + currentVersion = 119; + } + + // v120: Add double tap to wake setting. + if (currentVersion == 119) { + SettingsState secureSettings = getSecureSettingsLocked(userId); + secureSettings.insertSettingLocked(Settings.Secure.DOUBLE_TAP_TO_WAKE, + getContext().getResources().getBoolean( + R.bool.def_double_tap_to_wake) ? "1" : "0", + SettingsState.SYSTEM_PACKAGE_NAME); + + currentVersion = 120; + } + // vXXX: Add new settings above this point. // Return the current version. diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 24f6931..209a233 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -182,6 +182,20 @@ android:excludeFromRecents="true"> </activity> + <activity android:name=".tuner.TunerActivity" + android:enabled="false" + android:icon="@drawable/icon" + android:theme="@android:style/Theme.Material.Settings" + android:label="@string/system_ui_tuner" + android:exported="true"> + <intent-filter> + <action android:name="com.android.settings.action.EXTRA_SETTINGS" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + <meta-data android:name="com.android.settings.category" + android:value="com.android.settings.category.device" /> + </activity> + <!-- Alternate Recents --> <activity android:name=".recents.RecentsActivity" android:label="@string/accessibility_desc_recent_apps" diff --git a/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml b/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml new file mode 100644 index 0000000..4875974 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml @@ -0,0 +1,29 @@ +<!-- + Copyright (C) 2015 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0" + android:width="24dp" > + + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0s10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.5c-4.7,0.0 -8.5,-3.8 -8.5,-8.5S7.3,3.5 12.0,3.5s8.5,3.8 8.5,8.5S16.7,20.5 12.0,20.5z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.0,6.0c-3.3,0.0 -6.0,2.7 -6.0,6.0c0.0,3.3 2.7,6.0 6.0,6.0s6.0,-2.7 6.0,-6.0C18.0,8.7 15.4,6.0 12.0,6.0zM15.0,13.0L9.0,13.0l0.0,-2.0l6.0,0.0L15.0,13.0z"/> + +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml b/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml index 7617ec4..f4c20a9 100644 --- a/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml +++ b/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml @@ -1,7 +1,7 @@ <!-- -Copyright (C) 2015 The Android Open Source Project + Copyright (C) 2015 The Android Open Source Project - Licensed under the Apache License, Version 2.0 (the "License"); + 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 diff --git a/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml b/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml new file mode 100644 index 0000000..fb26c09 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml @@ -0,0 +1,29 @@ +<!-- + Copyright (C) 2015 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="64dp" + android:height="64dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0s10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.5c-4.7,0.0 -8.5,-3.8 -8.5,-8.5S7.3,3.5 12.0,3.5s8.5,3.8 8.5,8.5S16.7,20.5 12.0,20.5z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.0,6.0c-3.3,0.0 -6.0,2.7 -6.0,6.0c0.0,3.3 2.7,6.0 6.0,6.0s6.0,-2.7 6.0,-6.0C18.0,8.7 15.4,6.0 12.0,6.0zM15.0,13.0L9.0,13.0l0.0,-2.0l6.0,0.0L15.0,13.0z"/> + +</vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_dnd.xml b/packages/SystemUI/res/drawable/stat_sys_dnd.xml index 9361bc0..3bf5e98 100644 --- a/packages/SystemUI/res/drawable/stat_sys_dnd.xml +++ b/packages/SystemUI/res/drawable/stat_sys_dnd.xml @@ -1,7 +1,7 @@ <!-- -Copyright (C) 2015 The Android Open Source Project + Copyright (C) 2015 The Android Open Source Project - Licensed under the Apache License, Version 2.0 (the "License"); + 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 diff --git a/packages/SystemUI/res/drawable/stat_sys_dnd_total_silence.xml b/packages/SystemUI/res/drawable/stat_sys_dnd_total_silence.xml new file mode 100644 index 0000000..82e25ca --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_dnd_total_silence.xml @@ -0,0 +1,31 @@ +<!-- + Copyright (C) 2015 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. +--> +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:insetLeft="2.5dp" + android:insetRight="2.5dp"> + <vector + android:width="17dp" + android:height="17dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0s10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.5c-4.7,0.0 -8.5,-3.8 -8.5,-8.5S7.3,3.5 12.0,3.5s8.5,3.8 8.5,8.5S16.7,20.5 12.0,20.5z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12.0,6.0c-3.3,0.0 -6.0,2.7 -6.0,6.0c0.0,3.3 2.7,6.0 6.0,6.0s6.0,-2.7 6.0,-6.0C18.0,8.7 15.4,6.0 12.0,6.0zM15.0,13.0L9.0,13.0l0.0,-2.0l6.0,0.0L15.0,13.0z"/> + </vector> +</inset> diff --git a/packages/SystemUI/res/layout/volume_zen_footer.xml b/packages/SystemUI/res/layout/volume_zen_footer.xml index b780f7d..998741c 100644 --- a/packages/SystemUI/res/layout/volume_zen_footer.xml +++ b/packages/SystemUI/res/layout/volume_zen_footer.xml @@ -41,8 +41,7 @@ android:layout_width="@dimen/volume_button_size" android:layout_height="@dimen/volume_button_size" android:layout_marginEnd="7dp" - android:scaleType="center" - android:src="@drawable/ic_dnd" /> + android:scaleType="center" /> <LinearLayout android:layout_width="0dp" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8606a59..c74633c 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -213,7 +213,7 @@ <string name="unlock_label">unlock</string> <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] --> <string name="phone_label">open phone</string> - <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] --> + <!-- Click action label for accessibility for the voice assist button. This is not shown on-screen and is an accessibility label for the icon which launches the voice assist from the lock screen.[CHAR LIMIT=NONE] --> <string name="voice_assist_label">open voice assist</string> <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] --> <string name="camera_label">open camera</string> @@ -1021,4 +1021,7 @@ <string name="volume_stream_muted_dnd" translatable="false">%s silent — Total silence</string> <string name="volume_stream_limited_dnd" translatable="false">%s — Priority only</string> <string name="volume_stream_vibrate_dnd" translatable="false">%s vibrate — Priority only</string> + + <!-- Name of special SystemUI debug settings --> + <string name="system_ui_tuner">SystemUI Tuner</string> </resources> diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml new file mode 100644 index 0000000..263260e --- /dev/null +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 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. +--> + +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + android:title="@string/system_ui_tuner"> + + <!-- Tuner prefs go here --> + +</PreferenceScreen> diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index d1f8963..08659e9 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -204,7 +204,8 @@ public class AssistManager { private boolean getVoiceInteractorSupportsAssistGesture() { try { - return mVoiceInteractionManagerService.activeServiceSupportsAssist(); + return mVoiceInteractionManagerService != null + && mVoiceInteractionManagerService.activeServiceSupportsAssist(); } catch (RemoteException e) { Log.w(TAG, "Failed to call activeServiceSupportsAssistGesture", e); return false; @@ -213,7 +214,8 @@ public class AssistManager { public boolean canVoiceAssistBeLaunchedFromKeyguard() { try { - return mVoiceInteractionManagerService.activeServiceSupportsLaunchFromKeyguard(); + return mVoiceInteractionManagerService != null + && mVoiceInteractionManagerService.activeServiceSupportsLaunchFromKeyguard(); } catch (RemoteException e) { Log.w(TAG, "Failed to call activeServiceSupportsLaunchFromKeyguard", e); return false; @@ -231,7 +233,8 @@ public class AssistManager { private boolean isVoiceSessionRunning() { try { - return mVoiceInteractionManagerService.isSessionRunning(); + return mVoiceInteractionManagerService != null + && mVoiceInteractionManagerService.isSessionRunning(); } catch (RemoteException e) { Log.w(TAG, "Failed to call isSessionRunning", e); return false; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 8205798..5f24619 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -112,7 +112,7 @@ public class DndTile extends QSTile<QSTile.BooleanState> { R.string.accessibility_quick_settings_dnd_priority_on); break; case Global.ZEN_MODE_NO_INTERRUPTIONS: - state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on); + state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on_total_silence); state.label = mContext.getString(R.string.quick_settings_dnd_none_label); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_dnd_none_on); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index fe7bc97..3eb6b13 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -75,7 +75,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken { mInitialTouchY = y; mInitialTouchX = x; setTrackingHeadsUp(false); - ExpandableView child = mStackScroller.getChildAtPosition(x, y); + ExpandableView child = mStackScroller.getChildAtRawPosition(x, y); mTouchingHeadsUpView = false; if (child instanceof ExpandableNotificationRow) { mPickedChild = (ExpandableNotificationRow) child; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index da65a01..8ccd222 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -619,7 +619,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, R.color.notification_panel_solid_background))); } - mHeadsUpManager = new HeadsUpManager(context, mNotificationPanel.getViewTreeObserver()); + mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow); mHeadsUpManager.setBar(this); mHeadsUpManager.addListener(this); mHeadsUpManager.addListener(mNotificationPanel); @@ -1869,21 +1869,34 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { if (inPinnedMode) { + // We need to ensure that the touchable region is updated before the window will be + // resized, in order to not catch any touches. A layout will ensure that + // onComputeInternalInsets will be called and after that we can resize the layout. Let's + // make sure that the window stays small for one frame until the touchableRegion is set. + mNotificationPanel.requestLayout(); mStatusBarWindowManager.setHeadsUpShowing(true); mStatusBarWindowManager.setForceStatusBarVisible(true); - } else { - Runnable endRunnable = new Runnable() { + mStatusBarWindowManager.setForceWindowCollapsed(true); + mNotificationPanel.post(new Runnable() { @Override public void run() { - if (!mHeadsUpManager.hasPinnedHeadsUp()) { - mStatusBarWindowManager.setHeadsUpShowing(false); - } + mStatusBarWindowManager.setForceWindowCollapsed(false); } - }; + }); + } else { if (!mNotificationPanel.isFullyCollapsed()) { - endRunnable.run(); + mStatusBarWindowManager.setHeadsUpShowing(false); } else { - mStackScroller.runAfterAnimationFinished(endRunnable); + mHeadsUpManager.setHeadsUpGoingAway(true); + mStackScroller.runAfterAnimationFinished(new Runnable() { + @Override + public void run() { + if (!mHeadsUpManager.hasPinnedHeadsUp()) { + mStatusBarWindowManager.setHeadsUpShowing(false); + mHeadsUpManager.setHeadsUpGoingAway(false); + } + } + }); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 0e8e844..5942b46 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -219,7 +219,8 @@ public class PhoneStatusBarPolicy { if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) { zenVisible = mZen != Global.ZEN_MODE_OFF; - zenIconId = R.drawable.stat_sys_dnd; + zenIconId = mZen == Global.ZEN_MODE_NO_INTERRUPTIONS + ? R.drawable.stat_sys_dnd_total_silence : R.drawable.stat_sys_dnd; zenDescription = mContext.getString(R.string.quick_settings_dnd_label); } else if (mZen == Global.ZEN_MODE_NO_INTERRUPTIONS) { zenVisible = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index e6edbea..acf2f57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -378,8 +378,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, if (previousAnimator != null) { if (animate || alpha == mCurrentHeadsUpAlpha) { previousAnimator.cancel(); + } else { + animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_END_ALPHA); } - animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_START_ALPHA); } if (alpha != mCurrentHeadsUpAlpha && alpha != animEndValue) { if (animate) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java index e7e4384..422d868 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -129,8 +129,9 @@ public class StatusBarWindowManager { } private void applyHeight(State state) { - boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded - || state.keyguardFadingAway || state.bouncerShowing || state.headsUpShowing; + boolean expanded = !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded() + || state.statusBarExpanded || state.keyguardFadingAway || state.bouncerShowing + || state.headsUpShowing); if (expanded) { mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT; } else { @@ -256,6 +257,16 @@ public class StatusBarWindowManager { apply(mCurrentState); } + /** + * Force the window to be collapsed, even if it should theoretically be expanded. + * Used for when a heads-up comes in but we still need to wait for the touchable regions to + * be computed. + */ + public void setForceWindowCollapsed(boolean force) { + mCurrentState.forceCollapsed = force; + apply(mCurrentState); + } + private static class State { boolean keyguardShowing; boolean keyguardOccluded; @@ -267,6 +278,7 @@ public class StatusBarWindowManager { boolean qsExpanded; boolean headsUpShowing; boolean forceStatusBarVisible; + boolean forceCollapsed; /** * The {@link BaseStatusBar} state from the status bar. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index 0db9221..98822a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -25,6 +25,7 @@ import android.provider.Settings; import android.util.ArrayMap; import android.util.Log; import android.util.Pools; +import android.view.View; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; @@ -78,6 +79,8 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL } }; + private final View mStatusBarWindowView; + private final int mStatusBarHeight; private PhoneStatusBar mBar; private int mSnoozeLengthMs; private ContentObserver mSettingsObserver; @@ -92,8 +95,11 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL private boolean mIsExpanded; private boolean mHasPinnedNotification; private int[] mTmpTwoArray = new int[2]; + private boolean mHeadsUpGoingAway; + private boolean mWaitingOnCollapseWhenGoingAway; + private boolean mIsObserving; - public HeadsUpManager(final Context context, ViewTreeObserver observer) { + public HeadsUpManager(final Context context, View statusBarWindowView) { Resources resources = context.getResources(); mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay); mSnoozedPackages = new ArrayMap<>(); @@ -119,7 +125,24 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL context.getContentResolver().registerContentObserver( Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false, mSettingsObserver); - observer.addOnComputeInternalInsetsListener(this); + mStatusBarWindowView = statusBarWindowView; + mStatusBarHeight = resources.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); + } + + private void updateTouchableRegionListener() { + boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway + || mWaitingOnCollapseWhenGoingAway; + if (shouldObserve == mIsObserving) { + return; + } + if (shouldObserve) { + mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); + mStatusBarWindowView.requestLayout(); + } else { + mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this); + } + mIsObserving = shouldObserve; } public void setBar(PhoneStatusBar bar) { @@ -207,6 +230,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL return; } mHasPinnedNotification = hasPinnedNotification; + updateTouchableRegionListener(); for (OnHeadsUpChangedListener listener : mListeners) { listener.onHeadsUpPinnedModeChanged(hasPinnedNotification); } @@ -326,7 +350,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL } public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { - if (!mIsExpanded && mHasPinnedNotification) { + if (mHasPinnedNotification) { int minX = Integer.MAX_VALUE; int maxX = 0; int minY = Integer.MAX_VALUE; @@ -344,6 +368,9 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); info.touchableRegion.set(minX, minY, maxX, maxY); + } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) { + info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); } } @@ -419,6 +446,10 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL mIsExpanded = isExpanded; if (isExpanded) { unpinAll(); + // make sure our state is sane + mWaitingOnCollapseWhenGoingAway = false; + mHeadsUpGoingAway = false; + updateTouchableRegionListener(); } } } @@ -443,6 +474,40 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL return aEntry.compareTo(bEntry); } + /** + * Set that we are exiting the headsUp pinned mode, but some notifications might still be + * animating out. This is used to keep the touchable regions in a sane state. + */ + public void setHeadsUpGoingAway(boolean headsUpGoingAway) { + if (headsUpGoingAway != mHeadsUpGoingAway) { + mHeadsUpGoingAway = headsUpGoingAway; + if (!headsUpGoingAway) { + waitForStatusBarLayout(); + } + updateTouchableRegionListener(); + } + } + + /** + * We need to wait on the whole panel to collapse, before we can remove the touchable region + * listener. + */ + private void waitForStatusBarLayout() { + mWaitingOnCollapseWhenGoingAway = true; + mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, + int oldTop, int oldRight, int oldBottom) { + if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) { + mStatusBarWindowView.removeOnLayoutChangeListener(this); + mWaitingOnCollapseWhenGoingAway = false; + updateTouchableRegionListener(); + } + } + }); + } + /** * This represents a notification and how long it is in a heads up mode. It also manages its diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java new file mode 100644 index 0000000..c84f618 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.tuner; + +import android.app.Activity; +import android.os.Bundle; + +public class TunerActivity extends Activity { + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getFragmentManager().beginTransaction().replace(android.R.id.content, new TunerFragment()) + .commit(); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java new file mode 100644 index 0000000..df1b0d0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.tuner; + +import android.os.Bundle; +import android.preference.PreferenceFragment; +import android.view.MenuItem; + +import com.android.systemui.R; + +public class TunerFragment extends PreferenceFragment { + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.tuner_prefs); + getActivity().getActionBar().setDisplayHomeAsUpEnabled(true); + setHasOptionsMenu(true); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + getActivity().finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java index ccb2b5a..66c4993 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java +++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java @@ -21,6 +21,7 @@ import android.provider.Settings.Global; import android.service.notification.ZenModeConfig; import android.util.AttributeSet; import android.view.View; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -38,6 +39,7 @@ public class ZenFooter extends LinearLayout { private final Context mContext; private final SpTexts mSpTexts; + private ImageView mIcon; private TextView mSummaryLine1; private TextView mSummaryLine2; private TextView mEndNowButton; @@ -55,6 +57,7 @@ public class ZenFooter extends LinearLayout { @Override protected void onFinishInflate() { super.onFinishInflate(); + mIcon = (ImageView) findViewById(R.id.volume_zen_icon); mSummaryLine1 = (TextView) findViewById(R.id.volume_zen_summary_line_1); mSummaryLine2 = (TextView) findViewById(R.id.volume_zen_summary_line_2); mEndNowButton = (TextView) findViewById(R.id.volume_zen_end_now); @@ -115,6 +118,7 @@ public class ZenFooter extends LinearLayout { } public void update() { + mIcon.setImageResource(isZenNone() ? R.drawable.ic_dnd_total_silence : R.drawable.ic_dnd); final String line1 = isZenPriority() ? mContext.getString(R.string.interruption_level_priority) : isZenAlarms() ? mContext.getString(R.string.interruption_level_alarms) diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java index 3b61f9d..70a5821 100644 --- a/rs/java/android/renderscript/Allocation.java +++ b/rs/java/android/renderscript/Allocation.java @@ -1422,6 +1422,8 @@ public class Allocation extends BaseObj { } /** + * @hide + * * This is only intended to be used by auto-generated code reflected from * the RenderScript script files and should not be used by developers. * diff --git a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java index 5e28d3f..f7e81b0 100644 --- a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java +++ b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java @@ -1039,14 +1039,8 @@ public final class ScriptIntrinsicBLAS extends ScriptIntrinsic { if (cM != cN) { throw new RSRuntimeException("Matrix C is not symmetric"); } - if (TransA != NO_TRANSPOSE) { - if (aN != cM) { - throw new RSRuntimeException("Called BLAS with invalid dimensions"); - } - } else { - if (aM != cM) { - throw new RSRuntimeException("Called BLAS with invalid dimensions"); - } + if (aM != cM) { + throw new RSRuntimeException("Called BLAS with invalid dimensions"); } } else if (A != null && B != null) { // A and B only diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index a49fb76..1833a1c 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -43,9 +43,6 @@ //#define LOG_API ALOGE static constexpr bool kLogApi = false; -static constexpr size_t kMaxNumberArgsAndBindings = 1000; -static constexpr size_t kMaxNumberClosuresInScriptGroup = 1000000; -static constexpr size_t kMaxNumberKernelArguments = 256; using namespace android; @@ -371,7 +368,7 @@ nClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong kernelID, goto exit; } - if (numValues > kMaxNumberArgsAndBindings) { + if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) { ALOGE("Too many arguments or globals in closure creation"); goto exit; } @@ -456,7 +453,7 @@ nInvokeClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong invokeID, numValues = (size_t) fieldIDs_length; - if (numValues > kMaxNumberArgsAndBindings) { + if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) { ALOGE("Too many arguments or globals in closure creation"); goto exit; } @@ -521,7 +518,7 @@ nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name, RsClosure* closures; - if (numClosures > (jsize) kMaxNumberClosuresInScriptGroup) { + if (numClosures > (jsize) RS_SCRIPT_GROUP_MAX_NUMBER_CLOSURES) { ALOGE("Too many closures in script group"); goto exit; } @@ -1867,7 +1864,7 @@ nScriptForEach(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, if (ains != nullptr) { in_len = _env->GetArrayLength(ains); - if (in_len > (jint)kMaxNumberKernelArguments) { + if (in_len > (jint)RS_KERNEL_MAX_ARGUMENTS) { ALOGE("Too many arguments in kernel launch."); // TODO (b/20758983): Report back to Java and throw an exception return; diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index b7bc0f0..0b33812 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -142,6 +142,7 @@ public class DeviceIdleController extends SystemService { private PendingIntent mAlarmIntent; private Intent mIdleIntent; private Display mCurDisplay; + private boolean mIdleDisabled; private boolean mScreenOn; private boolean mCharging; private boolean mSigMotionActive; @@ -187,10 +188,16 @@ public class DeviceIdleController extends SystemService { private final ArrayMap<String, Integer> mPowerSaveWhitelistUserApps = new ArrayMap<>(); /** - * UIDs that have been white-listed to opt out of power save restrictions. + * App IDs that have been white-listed to opt out of power save restrictions. */ private final SparseBooleanArray mPowerSaveWhitelistAppIds = new SparseBooleanArray(); + /** + * Current app IDs that are in the complete power save white list. This array can + * be shared with others because it will not be modified once set. + */ + private int[] mPowerSaveWhitelistAppIdArray = new int[0]; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { @@ -306,6 +313,10 @@ public class DeviceIdleController extends SystemService { return getAppIdWhitelistInternal(); } + @Override public boolean isPowerSaveWhitelistApp(String name) { + return isPowerSaveWhitelistAppInternal(name); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { DeviceIdleController.this.dump(fd, pw, args); } @@ -381,6 +392,8 @@ public class DeviceIdleController extends SystemService { filter.addAction(ACTION_STEP_IDLE_STATE); getContext().registerReceiver(mReceiver, filter); + mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAppIdArray); + mDisplayManager.registerDisplayListener(mDisplayListener, null); updateDisplayLocked(); } @@ -443,14 +456,16 @@ public class DeviceIdleController extends SystemService { } } + public boolean isPowerSaveWhitelistAppInternal(String packageName) { + synchronized (this) { + return mPowerSaveWhitelistApps.containsKey(packageName) + || mPowerSaveWhitelistUserApps.containsKey(packageName); + } + } + public int[] getAppIdWhitelistInternal() { synchronized (this) { - int size = mPowerSaveWhitelistAppIds.size(); - int[] appids = new int[size]; - for (int i = 0; i < size; i++) { - appids[i] = mPowerSaveWhitelistAppIds.keyAt(i); - } - return appids; + return mPowerSaveWhitelistAppIdArray; } } @@ -499,7 +514,7 @@ public class DeviceIdleController extends SystemService { } void becomeInactiveIfAppropriateLocked() { - if (!mScreenOn && !mCharging && mState == STATE_ACTIVE) { + if (!mScreenOn && !mCharging && !mIdleDisabled && mState == STATE_ACTIVE) { // Screen has turned off; we are now going to become inactive and start // waiting to see if we will ultimately go idle. mState = STATE_INACTIVE; @@ -625,6 +640,15 @@ public class DeviceIdleController extends SystemService { for (int i=0; i<mPowerSaveWhitelistUserApps.size(); i++) { mPowerSaveWhitelistAppIds.put(mPowerSaveWhitelistUserApps.valueAt(i), true); } + int size = mPowerSaveWhitelistAppIds.size(); + int[] appids = new int[size]; + for (int i = 0; i < size; i++) { + appids[i] = mPowerSaveWhitelistAppIds.keyAt(i); + } + mPowerSaveWhitelistAppIdArray = appids; + if (mLocalPowerManager != null) { + mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAppIdArray); + } } private void reportPowerSaveWhitelistChangedLocked() { @@ -763,6 +787,10 @@ public class DeviceIdleController extends SystemService { pw.println("Commands:"); pw.println(" step"); pw.println(" Immediately step to next state, without waiting for alarm."); + pw.println(" disable"); + pw.println(" Completely disable device idle mode."); + pw.println(" enable"); + pw.println(" Re-enable device idle mode after it had previously been disabled."); pw.println(" whitelist"); pw.println(" Add (prefix with +) or remove (prefix with -) packages."); } @@ -782,12 +810,32 @@ public class DeviceIdleController extends SystemService { if ("-h".equals(arg)) { dumpHelp(pw); return; + } else if ("-a".equals(arg)) { + // Ignore, we always dump all. } else if ("step".equals(arg)) { synchronized (this) { stepIdleStateLocked(); pw.print("Stepped to: "); pw.println(stateToString(mState)); } return; + } else if ("disable".equals(arg)) { + synchronized (this) { + if (!mIdleDisabled) { + mIdleDisabled = true; + becomeActiveLocked("disabled"); + pw.println("Idle mode disabled"); + } + } + return; + } else if ("enable".equals(arg)) { + synchronized (this) { + if (mIdleDisabled) { + mIdleDisabled = false; + becomeInactiveIfAppropriateLocked(); + pw.println("Idle mode enabled"); + } + } + return; } else if ("whitelist".equals(arg)) { i++; while (i < args.length) { @@ -853,6 +901,7 @@ public class DeviceIdleController extends SystemService { } pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor); pw.print(" mCurDisplay="); pw.println(mCurDisplay); + pw.print(" mIdleDisabled="); pw.println(mIdleDisabled); pw.print(" mScreenOn="); pw.println(mScreenOn); pw.print(" mCharging="); pw.println(mCharging); pw.print(" mSigMotionActive="); pw.println(mSigMotionActive); diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java index c79fdfc..3fdef1d 100644 --- a/services/core/java/com/android/server/GraphicsStatsService.java +++ b/services/core/java/com/android/server/GraphicsStatsService.java @@ -52,9 +52,9 @@ import java.util.ArrayList; * 2) ASHMEM_SIZE (for scratch space used during dumping) * 3) ASHMEM_SIZE * HISTORY_SIZE * - * Currently ASHMEM_SIZE is 256 bytes and HISTORY_SIZE is 10. Assuming + * Currently ASHMEM_SIZE is 256 bytes and HISTORY_SIZE is 20. Assuming * the system then also has 10 active rendering processes in the worst case - * this would end up using under 10KiB (8KiB for the buffers, plus some overhead + * this would end up using under 14KiB (12KiB for the buffers, plus some overhead * for userId, pid, package name, and a couple other objects) * * @hide */ @@ -63,7 +63,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub { private static final String TAG = "GraphicsStatsService"; private static final int ASHMEM_SIZE = 256; - private static final int HISTORY_SIZE = 10; + private static final int HISTORY_SIZE = 20; private final Context mContext; private final Object mLock = new Object(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 10855e2..d035c92 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19490,7 +19490,7 @@ public final class ActivityManagerService extends ActivityManagerNative mStackSupervisor.resumeTopActivitiesLocked(); } EventLogTags.writeAmSwitchUser(newUserId); - getUserManagerLocked().userForeground(newUserId); + getUserManagerLocked().onUserForeground(newUserId); sendUserSwitchBroadcastsLocked(oldUserId, newUserId); } @@ -19956,7 +19956,7 @@ public final class ActivityManagerService extends ActivityManagerNative public ComponentName getHomeActivityForUser(int userId) { synchronized (ActivityManagerService.this) { ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId); - return homeActivity.realActivity; + return homeActivity == null ? null : homeActivity.realActivity; } } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index aa365ea..2149b7a 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.REMOTE_AUDIO_PLAYBACK; import static android.media.AudioManager.RINGER_MODE_NORMAL; import static android.media.AudioManager.RINGER_MODE_SILENT; import static android.media.AudioManager.RINGER_MODE_VIBRATE; +import static android.os.Process.FIRST_APPLICATION_UID; import android.Manifest; import android.app.ActivityManager; @@ -5024,6 +5025,10 @@ public class AudioService extends IAudioService.Stub { } for (int j = packages.size() - 1; j >= 0; j--) { PackageInfo pkg = packages.get(j); + // Skip system processes + if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) { + continue; + } if (homeActivityName != null && pkg.packageName.equals(homeActivityName.getPackageName()) && pkg.applicationInfo.isSystemApp()) { diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index ed8519a..0faccc6 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -16,10 +16,12 @@ package com.android.server.fingerprint; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.ContentResolver; import android.content.Context; import android.os.Binder; +import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -38,6 +40,7 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver; import static android.Manifest.permission.MANAGE_FINGERPRINT; import static android.Manifest.permission.USE_FINGERPRINT; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -116,6 +119,7 @@ public class FingerprintService extends SystemService { static native int nativeCloseHal(); static native void nativeInit(MessageQueue queue, FingerprintService service); static native long nativeGetAuthenticatorId(); + static native int nativeSetActiveGroup(int gid, byte[] storePath); static final class FpHalMsg { int type; // Type of the message. One of the constants in fingerprint.h @@ -628,6 +632,11 @@ public class FingerprintService extends SystemService { public void onStart() { publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper()); mHalDeviceId = nativeOpenHal(); + if (mHalDeviceId != 0) { + int userId = ActivityManager.getCurrentUser(); + File path = Environment.getUserSystemDirectory(userId); + nativeSetActiveGroup(0, path.getAbsolutePath().getBytes()); + } if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId); } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 65949bf..8086461 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -765,8 +765,9 @@ public class MediaSessionService extends SystemService implements Monitor { // If we don't have a media button receiver to fall back on // include non-playing sessions for dispatching UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser()); - boolean useNotPlayingSessions = ur.mLastMediaButtonReceiver == null - && ur.mRestoredMediaButtonReceiver == null; + boolean useNotPlayingSessions = (ur == null) || + (ur.mLastMediaButtonReceiver == null + && ur.mRestoredMediaButtonReceiver == null); MediaSessionRecord session = mPriorityStack .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions); if (isVoiceKey(keyEvent.getKeyCode())) { diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java index 30f8b37..18407c9 100644 --- a/services/core/java/com/android/server/pm/BasePermission.java +++ b/services/core/java/com/android/server/pm/BasePermission.java @@ -20,8 +20,6 @@ import android.content.pm.PackageParser; import android.content.pm.PermissionInfo; import android.os.UserHandle; -import com.android.internal.util.ArrayUtils; - final class BasePermission { final static int TYPE_NORMAL = 0; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a120c1f..477af72 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -206,6 +206,7 @@ import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.Watchdog; import com.android.server.pm.Settings.DatabaseVersion; +import com.android.server.pm.PermissionsState.PermissionState; import com.android.server.storage.DeviceStorageMonitorInternal; import org.xmlpull.v1.XmlPullParser; @@ -304,6 +305,8 @@ public class PackageManagerService extends IPackageManager.Stub { static final int REMOVE_CHATTY = 1<<16; + private static final int[] EMPTY_INT_ARRAY = new int[0]; + /** * Timeout (in milliseconds) after which the watchdog should declare that * our handler thread is wedged. The usual default for such things is one @@ -3139,18 +3142,26 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private static void enforceOnlySystemUpdatesPermissionPolicyFlags(int flagMask, int flagValues) { + if (((flagMask & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0 + || (flagValues & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) + && getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system can modify policy flags"); + } + } + @Override - public void grantPermission(String packageName, String name, int userId) { + public void grantRuntimePermission(String packageName, String name, int userId) { if (!sUserManager.exists(userId)) { return; } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, - "grantPermission"); + "grantRuntimePermission"); enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, - "grantPermission"); + "grantRuntimePermission"); boolean gidsChanged = false; final SettingBase sb; @@ -3197,17 +3208,17 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public void revokePermission(String packageName, String name, int userId) { + public void revokeRuntimePermission(String packageName, String name, int userId) { if (!sUserManager.exists(userId)) { return; } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, - "revokePermission"); + "revokeRuntimePermission"); enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, - "revokePermission"); + "revokeRuntimePermission"); final SettingBase sb; @@ -3236,7 +3247,7 @@ public class PackageManagerService extends IPackageManager.Stub { return; } - // Critical, after this call all should never have the permission. + // Critical, after this call app should never have the permission. mSettings.writeRuntimePermissionsForUserLPr(userId, true); } @@ -3244,6 +3255,86 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + public int getPermissionFlags(String name, String packageName, int userId) { + if (!sUserManager.exists(userId)) { + return 0; + } + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, + "getPermissionFlags"); + + enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, + "getPermissionFlags"); + + synchronized (mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + final BasePermission bp = mSettings.mPermissions.get(name); + if (bp == null) { + throw new IllegalArgumentException("Unknown permission: " + name); + } + + SettingBase sb = (SettingBase) pkg.mExtras; + if (sb == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + PermissionsState permissionsState = sb.getPermissionsState(); + return permissionsState.getPermissionFlags(name, userId); + } + } + + @Override + public void updatePermissionFlags(String name, String packageName, int flagMask, + int flagValues, int userId) { + if (!sUserManager.exists(userId)) { + return; + } + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, + "updatePermissionFlags"); + + enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, + "updatePermissionFlags"); + + enforceOnlySystemUpdatesPermissionPolicyFlags(flagMask, flagValues); + + synchronized (mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + final BasePermission bp = mSettings.mPermissions.get(name); + if (bp == null) { + throw new IllegalArgumentException("Unknown permission: " + name); + } + + SettingBase sb = (SettingBase) pkg.mExtras; + if (sb == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + PermissionsState permissionsState = sb.getPermissionsState(); + + if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) { + // Install and runtime permissions are stored in different places, + // so figure out what permission changed and persist the change. + if (permissionsState.getInstallPermissionState(name) != null) { + scheduleWriteSettingsLocked(); + } else if (permissionsState.getRuntimePermissionState(name, userId) != null) { + mSettings.writeRuntimePermissionsForUserLPr(userId, false); + } + } + } + } + + @Override public boolean isProtectedBroadcast(String actionName) { synchronized (mPackages) { return mProtectedBroadcasts.contains(actionName); @@ -7530,8 +7621,8 @@ public class PackageManagerService extends IPackageManager.Stub { final int[] currentUserIds = UserManagerService.getInstance().getUserIds(); - int[] upgradeUserIds = PermissionsState.USERS_NONE; - int[] changedRuntimePermissionUserIds = PermissionsState.USERS_NONE; + int[] upgradeUserIds = EMPTY_INT_ARRAY; + int[] changedRuntimePermissionUserIds = EMPTY_INT_ARRAY; boolean changedInstallPermission = false; @@ -7657,11 +7748,18 @@ public class PackageManagerService extends IPackageManager.Stub { // Grant previously granted runtime permissions. for (int userId : UserManagerService.getInstance().getUserIds()) { if (origPermissions.hasRuntimePermission(bp.name, userId)) { + PermissionState permissionState = origPermissions + .getRuntimePermissionState(bp.name, userId); + final int flags = permissionState.getFlags(); if (permissionsState.grantRuntimePermission(bp, userId) == PermissionsState.PERMISSION_OPERATION_FAILURE) { // If we cannot put the permission as it was, we have to write. changedRuntimePermissionUserIds = ArrayUtils.appendInt( changedRuntimePermissionUserIds, userId); + } else { + // Propagate the permission flags. + permissionsState.updatePermissionFlags(bp, userId, + flags, flags); } } } @@ -7669,13 +7767,28 @@ public class PackageManagerService extends IPackageManager.Stub { case GRANT_UPGRADE: { // Grant runtime permissions for a previously held install permission. - permissionsState.revokeInstallPermission(bp); - for (int userId : upgradeUserIds) { - if (permissionsState.grantRuntimePermission(bp, userId) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { - // If we granted the permission, we have to write. - changedRuntimePermissionUserIds = ArrayUtils.appendInt( - changedRuntimePermissionUserIds, userId); + PermissionState permissionState = origPermissions + .getInstallPermissionState(bp.name); + final int flags = permissionState != null ? permissionState.getFlags() : 0; + + origPermissions.revokeInstallPermission(bp); + // We will be transferring the permission flags, so clear them. + origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, 0); + + // If the permission is not to be promoted to runtime we ignore it and + // also its other flags as they are not applicable to install permissions. + if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) { + for (int userId : upgradeUserIds) { + if (permissionsState.grantRuntimePermission(bp, userId) != + PermissionsState.PERMISSION_OPERATION_FAILURE) { + // Transfer the permission flags. + permissionsState.updatePermissionFlags(bp, userId, + flags, flags); + // If we granted the permission, we have to write. + changedRuntimePermissionUserIds = ArrayUtils.appendInt( + changedRuntimePermissionUserIds, userId); + } } } } break; @@ -7692,6 +7805,9 @@ public class PackageManagerService extends IPackageManager.Stub { } else { if (permissionsState.revokeInstallPermission(bp) != PermissionsState.PERMISSION_OPERATION_FAILURE) { + // Also drop the permission flags. + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, 0); changedInstallPermission = true; Slog.i(TAG, "Un-granting permission " + perm + " from package " + pkg.packageName @@ -9101,7 +9217,9 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId); } - scheduleWritePackageRestrictionsLocked(userId); + if (result) { + scheduleWritePackageRestrictionsLocked(userId); + } return result; } @@ -9138,9 +9256,11 @@ public class PackageManagerService extends IPackageManager.Stub { public boolean setDefaultBrowserPackageName(String packageName, int userId) { synchronized (mPackages) { boolean result = mSettings.setDefaultBrowserPackageNameLPr(packageName, userId); - result |= updateIntentVerificationStatus(packageName, - PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, - UserHandle.myUserId()); + if (packageName != null) { + result |= updateIntentVerificationStatus(packageName, + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, + UserHandle.myUserId()); + } return result; } } @@ -11854,6 +11974,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (deletedPs != null) { if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) { clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL); + clearDefaultBrowserIfNeeded(packageName); if (outInfo != null) { mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName); outInfo.removedAppId = mSettings.removePackageLPw(packageName); @@ -12388,7 +12509,7 @@ public class PackageManagerService extends IPackageManager.Stub { succeded = deleteApplicationCacheFilesLI(packageName, userId); } clearExternalStorageDataSync(packageName, userId, false); - if(observer != null) { + if (observer != null) { try { observer.onRemoveCompleted(packageName, succeded); } catch (RemoteException e) { @@ -12757,6 +12878,17 @@ public class PackageManagerService extends IPackageManager.Stub { } } + + void clearDefaultBrowserIfNeeded(String packageName) { + for (int oneUserId : sUserManager.getUserIds()) { + String defaultBrowserPackageName = getDefaultBrowserPackageName(oneUserId); + if (TextUtils.isEmpty(defaultBrowserPackageName)) continue; + if (packageName.equals(defaultBrowserPackageName)) { + setDefaultBrowserPackageName(null, oneUserId); + } + } + } + @Override public void resetPreferredActivities(int userId) { /* TODO: Actually use userId. Why is it being passed in? */ diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java index 3749957..171a50d 100644 --- a/services/core/java/com/android/server/pm/PermissionsState.java +++ b/services/core/java/com/android/server/pm/PermissionsState.java @@ -20,10 +20,13 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.SparseArray; import com.android.internal.util.ArrayUtils; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Set; /** @@ -55,15 +58,8 @@ public final class PermissionsState { /** The permission operation failed. */ public static final int PERMISSION_OPERATION_FAILURE = 3; - public static final int[] USERS_ALL = {UserHandle.USER_ALL}; - - public static final int[] USERS_NONE = {}; - private static final int[] NO_GIDS = {}; - private static final int FLAG_INSTALL_PERMISSIONS = 1 << 0; - private static final int FLAG_RUNTIME_PERMISSIONS = 1 << 1; - private ArrayMap<String, PermissionData> mPermissions; private int[] mGlobalGids = NO_GIDS; @@ -147,14 +143,16 @@ public final class PermissionsState { } /** - * Grant a runtime permission. + * Grant a runtime permission for a given device user. * * @param permission The permission to grant. + * @param userId The device user id. * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link * #PERMISSION_OPERATION_FAILURE}. */ public int grantRuntimePermission(BasePermission permission, int userId) { + enforceValidUserId(userId); if (userId == UserHandle.USER_ALL) { return PERMISSION_OPERATION_FAILURE; } @@ -162,15 +160,18 @@ public final class PermissionsState { } /** - * Revoke a runtime permission for a given device user. + * Revoke a runtime permission for a given device user. * * @param permission The permission to revoke. * @param userId The device user id. * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link * #PERMISSION_OPERATION_FAILURE}. + * + * @see android.content.pm.PackageManager.PermissionFlags */ public int revokeRuntimePermission(BasePermission permission, int userId) { + enforceValidUserId(userId); if (userId == UserHandle.USER_ALL) { return PERMISSION_OPERATION_FAILURE; } @@ -178,17 +179,6 @@ public final class PermissionsState { } /** - * Gets whether this state has a given permission, regardless if - * it is install time or runtime one. - * - * @param name The permission name. - * @return Whether this state has the permission. - */ - public boolean hasPermission(String name) { - return mPermissions != null && mPermissions.get(name) != null; - } - - /** * Gets whether this state has a given runtime permission for a * given device user id. * @@ -197,6 +187,7 @@ public final class PermissionsState { * @return Whether this state has the permission. */ public boolean hasRuntimePermission(String name, int userId) { + enforceValidUserId(userId); return !hasInstallPermission(name) && hasPermission(name, userId); } @@ -211,36 +202,6 @@ public final class PermissionsState { } /** - * Revokes a permission for all users regardless if it is an install or - * a runtime permission. - * - * @param permission The permission to revoke. - * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, - * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link - * #PERMISSION_OPERATION_FAILURE}. - */ - public int revokePermission(BasePermission permission) { - if (!hasPermission(permission.name)) { - return PERMISSION_OPERATION_FAILURE; - } - - int result = PERMISSION_OPERATION_SUCCESS; - - PermissionData permissionData = mPermissions.get(permission.name); - for (int userId : permissionData.getUserIds()) { - if (revokePermission(permission, userId) - == PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - result = PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; - break; - } - } - - mPermissions.remove(permission.name); - - return result; - } - - /** * Gets whether the state has a given permission for the specified * user, regardless if this is an install or a runtime permission. * @@ -256,49 +217,133 @@ public final class PermissionsState { } PermissionData permissionData = mPermissions.get(name); - return permissionData != null && permissionData.hasUserId(userId); + return permissionData != null && permissionData.isGranted(userId); } /** - * Gets all permissions regardless if they are install or runtime. + * Gets all permissions for a given device user id regardless if they + * are install time or runtime permissions. * + * @param userId The device user id. * @return The permissions or an empty set. */ - public Set<String> getPermissions() { - if (mPermissions != null) { - return mPermissions.keySet(); + public Set<String> getPermissions(int userId) { + enforceValidUserId(userId); + + if (mPermissions == null) { + return Collections.emptySet(); } - return Collections.emptySet(); + Set<String> permissions = new ArraySet<>(); + + final int permissionCount = mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + String permission = mPermissions.keyAt(i); + + if (hasInstallPermission(permission)) { + permissions.add(permission); + } + + if (userId != UserHandle.USER_ALL) { + if (hasRuntimePermission(permission, userId)) { + permissions.add(permission); + } + } + } + + return permissions; } /** - * Gets all permissions for a given device user id regardless if they - * are install time or runtime permissions. + * Gets the state for an install permission or null if no such. * + * @param name The permission name. + * @return The permission state. + */ + public PermissionState getInstallPermissionState(String name) { + return getPermissionState(name, UserHandle.USER_ALL); + } + + /** + * Gets the state for a runtime permission or null if no such. + * + * @param name The permission name. * @param userId The device user id. - * @return The permissions or an empty set. + * @return The permission state. */ - public Set<String> getPermissions(int userId) { - return getPermissionsInternal(FLAG_INSTALL_PERMISSIONS | FLAG_RUNTIME_PERMISSIONS, userId); + public PermissionState getRuntimePermissionState(String name, int userId) { + enforceValidUserId(userId); + return getPermissionState(name, userId); } /** - * Gets all runtime permissions. + * Gets all install permission states. * - * @return The permissions or an empty set. + * @return The permission states or an empty set. */ - public Set<String> getRuntimePermissions(int userId) { - return getPermissionsInternal(FLAG_RUNTIME_PERMISSIONS, userId); + public List<PermissionState> getInstallPermissionStates() { + return getPermissionStatesInternal(UserHandle.USER_ALL); } /** - * Gets all install permissions. + * Gets all runtime permission states. * - * @return The permissions or an empty set. + * @return The permission states or an empty set. + */ + public List<PermissionState> getRuntimePermissionStates(int userId) { + enforceValidUserId(userId); + return getPermissionStatesInternal(userId); + } + + /** + * Gets the flags for a permission regardless if it is install or + * runtime permission. + * + * @param name The permission name. + * @return The permission state or null if no such. */ - public Set<String> getInstallPermissions() { - return getPermissionsInternal(FLAG_INSTALL_PERMISSIONS, UserHandle.USER_ALL); + public int getPermissionFlags(String name, int userId) { + PermissionState installPermState = getInstallPermissionState(name); + if (installPermState != null) { + return installPermState.getFlags(); + } + PermissionState runtimePermState = getRuntimePermissionState(name, userId); + if (runtimePermState != null) { + return runtimePermState.getFlags(); + } + return 0; + } + + /** + * Update the flags associated with a given permission. + * @param permission The permission whose flags to update. + * @param userId The user for which to update. + * @param flagMask Mask for which flags to change. + * @param flagValues New values for the mask flags. + * @return Whether the permission flags changed. + */ + public boolean updatePermissionFlags(BasePermission permission, int userId, + int flagMask, int flagValues) { + enforceValidUserId(userId); + + final boolean mayChangeFlags = flagValues != 0 || flagMask != 0; + + if (mPermissions == null) { + if (!mayChangeFlags) { + return false; + } + ensurePermissionData(permission); + } + + PermissionData permissionData = mPermissions.get(permission.name); + if (permissionData == null) { + if (!mayChangeFlags) { + return false; + } + permissionData = ensurePermissionData(permission); + } + + return permissionData.updateFlags(userId, flagMask, flagValues); } /** @@ -357,36 +402,37 @@ public final class PermissionsState { mPermissions = null; } - private Set<String> getPermissionsInternal(int flags, int userId) { - enforceValidUserId(userId); - + private PermissionState getPermissionState(String name, int userId) { if (mPermissions == null) { - return Collections.emptySet(); + return null; } + PermissionData permissionData = mPermissions.get(name); + if (permissionData == null) { + return null; + } + return permissionData.getPermissionState(userId); + } - if (userId == UserHandle.USER_ALL) { - flags = FLAG_INSTALL_PERMISSIONS; + private List<PermissionState> getPermissionStatesInternal(int userId) { + enforceValidUserId(userId); + + if (mPermissions == null) { + return Collections.emptyList(); } - Set<String> permissions = new ArraySet<>(); + List<PermissionState> permissionStates = new ArrayList<>(); final int permissionCount = mPermissions.size(); for (int i = 0; i < permissionCount; i++) { - String permission = mPermissions.keyAt(i); + PermissionData permissionData = mPermissions.valueAt(i); - if ((flags & FLAG_INSTALL_PERMISSIONS) != 0) { - if (hasInstallPermission(permission)) { - permissions.add(permission); - } - } - if ((flags & FLAG_RUNTIME_PERMISSIONS) != 0) { - if (hasRuntimePermission(permission, userId)) { - permissions.add(permission); - } + PermissionState permissionState = permissionData.getPermissionState(userId); + if (permissionState != null) { + permissionStates.add(permissionState); } } - return permissions; + return permissionStates; } private int grantPermission(BasePermission permission, int userId) { @@ -397,17 +443,9 @@ public final class PermissionsState { final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId)); final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS; - if (mPermissions == null) { - mPermissions = new ArrayMap<>(); - } - - PermissionData permissionData = mPermissions.get(permission.name); - if (permissionData == null) { - permissionData = new PermissionData(permission); - mPermissions.put(permission.name, permissionData); - } + PermissionData permissionData = ensurePermissionData(permission); - if (!permissionData.addUserId(userId)) { + if (!permissionData.grant(userId)) { return PERMISSION_OPERATION_FAILURE; } @@ -431,16 +469,12 @@ public final class PermissionsState { PermissionData permissionData = mPermissions.get(permission.name); - if (!permissionData.removeUserId(userId)) { + if (!permissionData.revoke(userId)) { return PERMISSION_OPERATION_FAILURE; } - if (permissionData.getUserIds() == USERS_NONE) { - mPermissions.remove(permission.name); - } - - if (mPermissions.isEmpty()) { - mPermissions = null; + if (permissionData.isDefault()) { + ensureNoPermissionData(permission.name); } if (hasGids) { @@ -468,9 +502,31 @@ public final class PermissionsState { } } + private PermissionData ensurePermissionData(BasePermission permission) { + if (mPermissions == null) { + mPermissions = new ArrayMap<>(); + } + PermissionData permissionData = mPermissions.get(permission.name); + if (permissionData == null) { + permissionData = new PermissionData(permission); + mPermissions.put(permission.name, permissionData); + } + return permissionData; + } + + private void ensureNoPermissionData(String name) { + if (mPermissions == null) { + return; + } + mPermissions.remove(name); + if (mPermissions.isEmpty()) { + mPermissions = null; + } + } + private static final class PermissionData { private final BasePermission mPerm; - private int[] mUserIds = USERS_NONE; + private SparseArray<PermissionState> mUserStates = new SparseArray<>(); public PermissionData(BasePermission perm) { mPerm = perm; @@ -478,11 +534,11 @@ public final class PermissionsState { public PermissionData(PermissionData other) { this(other.mPerm); - - if (other.mUserIds == USERS_ALL || other.mUserIds == USERS_NONE) { - mUserIds = other.mUserIds; - } else { - mUserIds = Arrays.copyOf(other.mUserIds, other.mUserIds.length); + final int otherStateCount = other.mUserStates.size(); + for (int i = 0; i < otherStateCount; i++) { + final int otherUserId = other.mUserStates.keyAt(i); + PermissionState otherState = other.mUserStates.valueAt(i); + mUserStates.put(otherUserId, new PermissionState(otherState)); } } @@ -490,53 +546,146 @@ public final class PermissionsState { return mPerm.computeGids(userId); } - public int[] getUserIds() { - return mUserIds; - } - - public boolean hasUserId(int userId) { - if (mUserIds == USERS_ALL) { - return true; + public boolean isGranted(int userId) { + if (isInstallPermission()) { + userId = UserHandle.USER_ALL; } - if (userId != UserHandle.USER_ALL) { - return ArrayUtils.contains(mUserIds, userId); + PermissionState userState = mUserStates.get(userId); + if (userState == null) { + return false; } - return false; + return userState.mGranted; } - public boolean addUserId(int userId) { - if (hasUserId(userId)) { + public boolean grant(int userId) { + if (!isCompatibleUserId(userId)) { return false; } - if (userId == UserHandle.USER_ALL) { - mUserIds = USERS_ALL; - return true; + if (isGranted(userId)) { + return false; } - mUserIds = ArrayUtils.appendInt(mUserIds, userId); + PermissionState userState = mUserStates.get(userId); + if (userState == null) { + userState = new PermissionState(mPerm.name); + mUserStates.put(userId, userState); + } + + userState.mGranted = true; return true; } - public boolean removeUserId(int userId) { - if (!hasUserId(userId)) { + public boolean revoke(int userId) { + if (!isCompatibleUserId(userId)) { return false; } - if (mUserIds == USERS_ALL) { - mUserIds = UserManagerService.getInstance().getUserIds(); + if (!isGranted(userId)) { + return false; } - mUserIds = ArrayUtils.removeInt(mUserIds, userId); + PermissionState userState = mUserStates.get(userId); + userState.mGranted = false; - if (mUserIds.length == 0) { - mUserIds = USERS_NONE; + if (userState.isDefault()) { + mUserStates.remove(userId); } return true; } + + public PermissionState getPermissionState(int userId) { + return mUserStates.get(userId); + } + + public int getFlags(int userId) { + PermissionState userState = mUserStates.get(userId); + if (userState != null) { + return userState.mFlags; + } + return 0; + } + + public boolean isDefault() { + return mUserStates.size() <= 0; + } + + public static boolean isInstallPermissionKey(int userId) { + return userId == UserHandle.USER_ALL; + } + + public boolean updateFlags(int userId, int flagMask, int flagValues) { + if (isInstallPermission()) { + userId = UserHandle.USER_ALL; + } + + if (!isCompatibleUserId(userId)) { + return false; + } + + final int newFlags = flagValues & flagMask; + + PermissionState userState = mUserStates.get(userId); + if (userState != null) { + final int oldFlags = userState.mFlags; + userState.mFlags = (userState.mFlags & ~flagMask) | newFlags; + if (userState.isDefault()) { + mUserStates.remove(userId); + } + return userState.mFlags != oldFlags; + } else if (newFlags != 0) { + userState = new PermissionState(mPerm.name); + userState.mFlags = newFlags; + mUserStates.put(userId, userState); + return true; + } + + return false; + } + + private boolean isCompatibleUserId(int userId) { + return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId)); + } + + private boolean isInstallPermission() { + return mUserStates.size() == 1 + && mUserStates.get(UserHandle.USER_ALL) != null; + } + } + + public static final class PermissionState { + private final String mName; + private boolean mGranted; + private int mFlags; + + public PermissionState(String name) { + mName = name; + } + + public PermissionState(PermissionState other) { + mName = other.mName; + mGranted = other.mGranted; + mFlags = other.mFlags; + } + + public boolean isDefault() { + return !mGranted && mFlags == 0; + } + + public String getName() { + return mName; + } + + public boolean isGranted() { + return mGranted; + } + + public int getFlags() { + return mFlags; + } } } diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index 0c7f79d..c35258a 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -21,11 +21,13 @@ import android.content.pm.ApplicationInfo; import java.util.Arrays; abstract class SettingBase { + private static final int[] USERS_NONE = new int[0]; + int pkgFlags; int pkgPrivateFlags; protected final PermissionsState mPermissionsState; - private int[] mPermissionsUpdatedForUserIds = PermissionsState.USERS_NONE; + private int[] mPermissionsUpdatedForUserIds = USERS_NONE; SettingBase(int pkgFlags, int pkgPrivateFlags) { setFlags(pkgFlags); @@ -53,7 +55,7 @@ abstract class SettingBase { return; } - if (userIds == PermissionsState.USERS_NONE || userIds == PermissionsState.USERS_ALL) { + if (userIds == USERS_NONE) { mPermissionsUpdatedForUserIds = userIds; } else { mPermissionsUpdatedForUserIds = Arrays.copyOf(userIds, userIds.length); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index fd70ce1..2e9656a 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -60,6 +60,7 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.backup.PreferredActivityBackupHelper; import com.android.server.pm.PackageManagerService.DumpState; +import com.android.server.pm.PermissionsState.PermissionState; import java.io.FileNotFoundException; import java.util.Collection; @@ -178,6 +179,8 @@ final class Settings { private static final String ATTR_CODE = "code"; private static final String ATTR_NOT_LAUNCHED = "nl"; private static final String ATTR_ENABLED = "enabled"; + private static final String ATTR_GRANTED = "granted"; + private static final String ATTR_FLAGS = "flags"; private static final String ATTR_ENABLED_CALLER = "enabledCaller"; private static final String ATTR_STOPPED = "stopped"; // Legacy, here for reading older versions of the package-restrictions. @@ -820,14 +823,20 @@ final class Settings { } if (!used) { + PermissionsState permissionsState = sus.getPermissionsState(); + // Try to revoke as an install permission which is for all users. - if (sus.getPermissionsState().revokeInstallPermission(bp) == + // The package is gone - no need to keep flags for applying policy. + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, 0); + + if (permissionsState.revokeInstallPermission(bp) == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { return UserHandle.USER_ALL; } // Try to revoke as an install permission which is per user. - if (sus.getPermissionsState().revokeRuntimePermission(bp, userId) == + if (permissionsState.revokeRuntimePermission(bp, userId) == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { return userId; } @@ -1724,10 +1733,32 @@ final class Settings { continue; } - if (permissionsState.grantInstallPermission(bp) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { - Slog.w(PackageManagerService.TAG, "Permission already added: " + name); - XmlUtils.skipCurrentTag(parser); + String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED); + final boolean granted = grantedStr == null + || Boolean.parseBoolean(grantedStr); + + String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS); + final int flags = (flagsStr != null) + ? Integer.parseInt(flagsStr, 16) : 0; + + if (granted) { + if (permissionsState.grantInstallPermission(bp) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + Slog.w(PackageManagerService.TAG, "Permission already added: " + name); + XmlUtils.skipCurrentTag(parser); + } else { + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, flags); + } + } else { + if (permissionsState.revokeInstallPermission(bp) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + Slog.w(PackageManagerService.TAG, "Permission already added: " + name); + XmlUtils.skipCurrentTag(parser); + } else { + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, flags); + } } } else { Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: " @@ -1737,17 +1768,19 @@ final class Settings { } } - void writePermissionsLPr(XmlSerializer serializer, Set<String> permissions) + void writePermissionsLPr(XmlSerializer serializer, List<PermissionState> permissionStates) throws IOException { - if (permissions.isEmpty()) { + if (permissionStates.isEmpty()) { return; } serializer.startTag(null, TAG_PERMISSIONS); - for (String permission : permissions) { + for (PermissionState permissionState : permissionStates) { serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, permission); + serializer.attribute(null, ATTR_NAME, permissionState.getName()); + serializer.attribute(null, ATTR_GRANTED, String.valueOf(permissionState.isGranted())); + serializer.attribute(null, ATTR_FLAGS, Integer.toHexString(permissionState.getFlags())); serializer.endTag(null, TAG_ITEM); } @@ -1945,7 +1978,8 @@ final class Settings { serializer.attribute(null, "userId", Integer.toString(usr.userId)); usr.signatures.writeXml(serializer, "sigs", mPastSignatures); - writePermissionsLPr(serializer, usr.getPermissionsState().getInstallPermissions()); + writePermissionsLPr(serializer, usr.getPermissionsState() + .getInstallPermissionStates()); serializer.endTag(null, "shared-user"); } @@ -2120,7 +2154,8 @@ final class Settings { // If this is a shared user, the permissions will be written there. if (pkg.sharedUser == null) { - writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions()); + writePermissionsLPr(serializer, pkg.getPermissionsState() + .getInstallPermissionStates()); } serializer.endTag(null, "updated-package"); @@ -2175,9 +2210,9 @@ final class Settings { serializer.attribute(null, "volumeUuid", pkg.volumeUuid); } pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); - if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { - writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions()); - } + + writePermissionsLPr(serializer, pkg.getPermissionsState() + .getInstallPermissionStates()); writeSigningKeySetLPr(serializer, pkg.keySetData); writeUpgradeKeySetsLPr(serializer, pkg.keySetData); @@ -3922,7 +3957,7 @@ final class Settings { PermissionsState permissionsState = ps.getPermissionsState(); dumpGidsLPr(pw, prefix + " ", permissionsState.computeGids(user.id)); dumpRuntimePermissionsLPr(pw, prefix + " ", permissionsState - .getRuntimePermissions(user.id)); + .getRuntimePermissionStates(user.id)); } ArraySet<String> cmp = ps.getDisabledComponents(user.id); @@ -4071,7 +4106,8 @@ final class Settings { for (int userId : UserManagerService.getInstance().getUserIds()) { final int[] gids = permissionsState.computeGids(userId); - Set<String> permissions = permissionsState.getRuntimePermissions(userId); + List<PermissionState> permissions = permissionsState + .getRuntimePermissionStates(userId); if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) { pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": "); dumpGidsLPr(pw, prefix + " ", gids); @@ -4120,22 +4156,29 @@ final class Settings { } } - void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, Set<String> permissions) { - if (!permissions.isEmpty()) { + void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, + List<PermissionState> permissionStates) { + if (!permissionStates.isEmpty()) { pw.print(prefix); pw.println("runtime permissions:"); - for (String permission : permissions) { - pw.print(prefix); pw.print(" "); pw.println(permission); + for (PermissionState permissionState : permissionStates) { + pw.print(prefix); pw.print(" "); pw.print(permissionState.getName()); + pw.print(", granted="); pw.print(permissionState.isGranted()); + pw.print(", flags=0x"); pw.println(Integer.toHexString( + permissionState.getFlags())); } } } void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, PermissionsState permissionsState) { - Set<String> permissions = permissionsState.getInstallPermissions(); - if (!permissions.isEmpty()) { + List<PermissionState> permissionStates = permissionsState.getInstallPermissionStates(); + if (!permissionStates.isEmpty()) { pw.print(prefix); pw.println("install permissions:"); - for (String permission : permissions) { - pw.print(prefix); pw.print(" "); pw.println(permission); + for (PermissionState permissionState : permissionStates) { + pw.print(prefix); pw.print(" "); pw.print(permissionState.getName()); + pw.print(", granted="); pw.print(permissionState.isGranted()); + pw.print(", flags=0x"); pw.println(Integer.toHexString( + permissionState.getFlags())); } } } @@ -4207,8 +4250,8 @@ final class Settings { private void writePermissionsSync(int userId) { AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId)); - ArrayMap<String, Set<String>> permissionsForPackage = new ArrayMap<>(); - ArrayMap<String, Set<String>> permissionsForSharedUser = new ArrayMap<>(); + ArrayMap<String, List<PermissionState>> permissionsForPackage = new ArrayMap<>(); + ArrayMap<String, List<PermissionState>> permissionsForSharedUser = new ArrayMap<>(); synchronized (mLock) { mWriteScheduled.delete(userId); @@ -4219,9 +4262,10 @@ final class Settings { PackageSetting packageSetting = mPackages.valueAt(i); if (packageSetting.sharedUser == null) { PermissionsState permissionsState = packageSetting.getPermissionsState(); - Set<String> permissions = permissionsState.getRuntimePermissions(userId); - if (!permissions.isEmpty()) { - permissionsForPackage.put(packageName, permissions); + List<PermissionState> permissionsStates = permissionsState + .getRuntimePermissionStates(userId); + if (!permissionsStates.isEmpty()) { + permissionsForPackage.put(packageName, permissionsStates); } } } @@ -4231,9 +4275,10 @@ final class Settings { String sharedUserName = mSharedUsers.keyAt(i); SharedUserSetting sharedUser = mSharedUsers.valueAt(i); PermissionsState permissionsState = sharedUser.getPermissionsState(); - Set<String> permissions = permissionsState.getRuntimePermissions(userId); - if (!permissions.isEmpty()) { - permissionsForSharedUser.put(sharedUserName, permissions); + List<PermissionState> permissionsStates = permissionsState + .getRuntimePermissionStates(userId); + if (!permissionsStates.isEmpty()) { + permissionsForSharedUser.put(sharedUserName, permissionsStates); } } } @@ -4252,20 +4297,20 @@ final class Settings { final int packageCount = permissionsForPackage.size(); for (int i = 0; i < packageCount; i++) { String packageName = permissionsForPackage.keyAt(i); - Set<String> permissions = permissionsForPackage.valueAt(i); + List<PermissionState> permissionStates = permissionsForPackage.valueAt(i); serializer.startTag(null, TAG_PACKAGE); serializer.attribute(null, ATTR_NAME, packageName); - writePermissions(serializer, permissions); + writePermissions(serializer, permissionStates); serializer.endTag(null, TAG_PACKAGE); } final int sharedUserCount = permissionsForSharedUser.size(); for (int i = 0; i < sharedUserCount; i++) { String packageName = permissionsForSharedUser.keyAt(i); - Set<String> permissions = permissionsForSharedUser.valueAt(i); + List<PermissionState> permissionStates = permissionsForSharedUser.valueAt(i); serializer.startTag(null, TAG_SHARED_USER); serializer.attribute(null, ATTR_NAME, packageName); - writePermissions(serializer, permissions); + writePermissions(serializer, permissionStates); serializer.endTag(null, TAG_SHARED_USER); } @@ -4290,20 +4335,23 @@ final class Settings { mHandler.removeMessages(userId); for (SettingBase sb : mPackages.values()) { - revokeRuntimePermissions(sb, userId); + revokeRuntimePermissionsAndClearFlags(sb, userId); } for (SettingBase sb : mSharedUsers.values()) { - revokeRuntimePermissions(sb, userId); + revokeRuntimePermissionsAndClearFlags(sb, userId); } } - private void revokeRuntimePermissions(SettingBase sb, int userId) { + private void revokeRuntimePermissionsAndClearFlags(SettingBase sb, int userId) { PermissionsState permissionsState = sb.getPermissionsState(); - for (String permission : permissionsState.getRuntimePermissions(userId)) { - BasePermission bp = mPermissions.get(permission); + for (PermissionState permissionState + : permissionsState.getRuntimePermissionStates(userId)) { + BasePermission bp = mPermissions.get(permissionState.getName()); if (bp != null) { permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, 0); } } } @@ -4391,20 +4439,47 @@ final class Settings { continue; } - if (permissionsState.grantRuntimePermission(bp, userId) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { - Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name); + String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED); + final boolean granted = grantedStr == null + || Boolean.parseBoolean(grantedStr); + + String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS); + final int flags = (flagsStr != null) + ? Integer.parseInt(flagsStr, 16) : 0; + + if (granted) { + if (permissionsState.grantRuntimePermission(bp, userId) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name); + } else { + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, flags); + + } + } else { + if (permissionsState.revokeRuntimePermission(bp, userId) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name); + } else { + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, flags); + } } + } break; } } } - private void writePermissions(XmlSerializer serializer, Set<String> permissions) - throws IOException { - for (String permission : permissions) { + private void writePermissions(XmlSerializer serializer, + List<PermissionState> permissionStates) throws IOException { + for (PermissionState permissionState : permissionStates) { serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, permission); + serializer.attribute(null, ATTR_NAME,permissionState.getName()); + serializer.attribute(null, ATTR_GRANTED, + String.valueOf(permissionState.isGranted())); + serializer.attribute(null, ATTR_FLAGS, + Integer.toHexString(permissionState.getFlags())); serializer.endTag(null, TAG_ITEM); } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 8dccfdf..e463fad 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -241,7 +241,7 @@ public class UserManagerService extends IUserManager.Stub { } } } - userForeground(UserHandle.USER_OWNER); + onUserForeground(UserHandle.USER_OWNER); mAppOpsService = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); for (int i = 0; i < mUserIds.length; ++i) { @@ -1784,7 +1784,7 @@ public class UserManagerService extends IUserManager.Stub { * Make a note of the last started time of a user and do some cleanup. * @param userId the user that was just foregrounded */ - public void userForeground(int userId) { + public void onUserForeground(int userId) { synchronized (mPackagesLock) { UserInfo user = mUsers.get(userId); long now = System.currentTimeMillis(); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index f790f75..74df0a0 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -69,6 +69,7 @@ import android.view.WindowManagerPolicy; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import libcore.util.Objects; @@ -149,6 +150,12 @@ public final class PowerManagerService extends SystemService private static final int POWER_HINT_INTERACTION = 2; private static final int POWER_HINT_LOW_POWER = 5; + // Power features defined in hardware/libhardware/include/hardware/power.h. + private static final int POWER_FEATURE_DOUBLE_TAP_TO_WAKE = 1; + + // Default setting for double tap to wake. + private static final int DEFAULT_DOUBLE_TAP_TO_WAKE = 0; + private final Context mContext; private final ServiceThread mHandlerThread; private final PowerManagerHandler mHandler; @@ -338,6 +345,9 @@ public final class PowerManagerService extends SystemService // Otherwise the user won't get much screen on time before dimming occurs. private float mMaximumScreenDimRatioConfig; + // Whether device supports double tap to wake. + private boolean mSupportsDoubleTapWakeConfig; + // The screen off timeout setting value in milliseconds. private int mScreenOffTimeoutSetting; @@ -423,9 +433,15 @@ public final class PowerManagerService extends SystemService // True if we are currently in device idle mode. private boolean mDeviceIdleMode; + // Set of app ids that we will always respect the wake locks for. + int[] mDeviceIdleWhitelist = new int[0]; + // True if theater mode is enabled private boolean mTheaterModeEnabled; + // True if double tap to wake is enabled + private boolean mDoubleTapWakeEnabled; + private final ArrayList<PowerManagerInternal.LowPowerModeListener> mLowPowerModeListeners = new ArrayList<PowerManagerInternal.LowPowerModeListener>(); @@ -436,6 +452,7 @@ public final class PowerManagerService extends SystemService private static native void nativeSetInteractive(boolean enable); private static native void nativeSetAutoSuspend(boolean enable); private static native void nativeSendPowerHint(int hintId, int data); + private static native void nativeSetFeature(int featureId, int data); public PowerManagerService(Context context) { super(context); @@ -458,6 +475,7 @@ public final class PowerManagerService extends SystemService nativeInit(); nativeSetAutoSuspend(false); nativeSetInteractive(true); + nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0); } } @@ -576,6 +594,9 @@ public final class PowerManagerService extends SystemService resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.THEATER_MODE_ON), false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.DOUBLE_TAP_TO_WAKE), + false, mSettingsObserver, UserHandle.USER_ALL); // Go. readConfigurationLocked(); updateSettingsLocked(); @@ -621,6 +642,8 @@ public final class PowerManagerService extends SystemService com.android.internal.R.integer.config_maximumScreenDimDuration); mMaximumScreenDimRatioConfig = resources.getFraction( com.android.internal.R.fraction.config_maximumScreenDimRatio, 1, 1); + mSupportsDoubleTapWakeConfig = resources.getBoolean( + com.android.internal.R.bool.config_supportDoubleTapWake); } private void updateSettingsLocked() { @@ -649,6 +672,16 @@ public final class PowerManagerService extends SystemService mTheaterModeEnabled = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) == 1; + if (mSupportsDoubleTapWakeConfig) { + boolean doubleTapWakeEnabled = Settings.Secure.getIntForUser(resolver, + Settings.Secure.DOUBLE_TAP_TO_WAKE, DEFAULT_DOUBLE_TAP_TO_WAKE, + UserHandle.USER_CURRENT) != 0; + if (doubleTapWakeEnabled != mDoubleTapWakeEnabled) { + mDoubleTapWakeEnabled = doubleTapWakeEnabled; + nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0); + } + } + final int oldScreenBrightnessSetting = mScreenBrightnessSetting; mScreenBrightnessSetting = Settings.System.getIntForUser(resolver, Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessSettingDefault, @@ -758,6 +791,7 @@ public final class PowerManagerService extends SystemService throw new IllegalArgumentException("Wake lock is already dead."); } mWakeLocks.add(wakeLock); + setWakeLockDisabledStateLocked(wakeLock); notifyAcquire = true; } @@ -894,7 +928,7 @@ public final class PowerManagerService extends SystemService } private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) { - if (mSystemReady) { + if (mSystemReady && !wakeLock.mDisabled) { wakeLock.mNotifiedAcquired = true; mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource, @@ -1388,7 +1422,10 @@ public final class PowerManagerService extends SystemService final WakeLock wakeLock = mWakeLocks.get(i); switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { case PowerManager.PARTIAL_WAKE_LOCK: - mWakeLockSummary |= WAKE_LOCK_CPU; + if (!wakeLock.mDisabled) { + // We only respect this if the wake lock is not disabled. + mWakeLockSummary |= WAKE_LOCK_CPU; + } break; case PowerManager.FULL_WAKE_LOCK: mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT; @@ -2248,12 +2285,12 @@ public final class PowerManagerService extends SystemService } } - private void setStayOnSettingInternal(int val) { + void setStayOnSettingInternal(int val) { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.STAY_ON_WHILE_PLUGGED_IN, val); } - private void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) { + void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) { synchronized (mLock) { mMaximumScreenOffTimeoutFromDeviceAdmin = timeMs; mDirty |= DIRTY_SETTINGS; @@ -2261,6 +2298,69 @@ public final class PowerManagerService extends SystemService } } + void setDeviceIdleModeInternal(boolean enabled) { + synchronized (mLock) { + if (mDeviceIdleMode != enabled) { + mDeviceIdleMode = enabled; + updateWakeLockDisabledStatesLocked(); + } + } + } + + void setDeviceIdleWhitelistInternal(int[] appids) { + synchronized (mLock) { + mDeviceIdleWhitelist = appids; + if (mDeviceIdleMode) { + updateWakeLockDisabledStatesLocked(); + } + } + } + + private void updateWakeLockDisabledStatesLocked() { + boolean changed = false; + final int numWakeLocks = mWakeLocks.size(); + for (int i = 0; i < numWakeLocks; i++) { + final WakeLock wakeLock = mWakeLocks.get(i); + if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) + == PowerManager.PARTIAL_WAKE_LOCK) { + if (setWakeLockDisabledStateLocked(wakeLock)) { + changed = true; + if (wakeLock.mDisabled) { + // This wake lock is no longer being respected. + notifyWakeLockReleasedLocked(wakeLock); + } else { + notifyWakeLockAcquiredLocked(wakeLock); + } + } + } + } + if (changed) { + mDirty |= DIRTY_WAKE_LOCKS; + updatePowerStateLocked(); + } + } + + private boolean setWakeLockDisabledStateLocked(WakeLock wakeLock) { + if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) + == PowerManager.PARTIAL_WAKE_LOCK) { + boolean disabled = false; + if (mDeviceIdleMode) { + final int appid = UserHandle.getAppId(wakeLock.mOwnerUid); + // If we are in idle mode, we will ignore all partial wake locks that are + // for application uids that are not whitelisted. + if (appid >= Process.FIRST_APPLICATION_UID && + Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0) { + disabled = true; + } + } + if (wakeLock.mDisabled != disabled) { + wakeLock.mDisabled = disabled; + return true; + } + } + return false; + } + private boolean isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() { return mMaximumScreenOffTimeoutFromDeviceAdmin >= 0 && mMaximumScreenOffTimeoutFromDeviceAdmin < Integer.MAX_VALUE; @@ -2459,6 +2559,8 @@ public final class PowerManagerService extends SystemService pw.println(" mSandmanSummoned=" + mSandmanSummoned); pw.println(" mLowPowerModeEnabled=" + mLowPowerModeEnabled); pw.println(" mBatteryLevelLow=" + mBatteryLevelLow); + pw.println(" mDeviceIdleMode=" + mDeviceIdleMode); + pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist)); pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime)); pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime)); pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime)); @@ -2537,6 +2639,7 @@ public final class PowerManagerService extends SystemService pw.println(" mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum); pw.println(" mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum); pw.println(" mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault); + pw.println(" mDoubleTapWakeEnabled=" + mDoubleTapWakeEnabled); final int sleepTimeout = getSleepTimeoutLocked(); final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout); @@ -2671,6 +2774,7 @@ public final class PowerManagerService extends SystemService public final int mOwnerUid; public final int mOwnerPid; public boolean mNotifiedAcquired; + public boolean mDisabled; public WakeLock(IBinder lock, int flags, String tag, String packageName, WorkSource workSource, String historyTag, int ownerUid, int ownerPid) { @@ -2729,7 +2833,7 @@ public final class PowerManagerService extends SystemService @Override public String toString() { return getLockLevelString() - + " '" + mTag + "'" + getLockFlagsString() + + " '" + mTag + "'" + getLockFlagsString() + (mDisabled ? " DISABLED" : "") + " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")"; } @@ -3340,9 +3444,12 @@ public final class PowerManagerService extends SystemService @Override public void setDeviceIdleMode(boolean enabled) { - synchronized (mLock) { - mDeviceIdleMode = enabled; - } + setDeviceIdleModeInternal(enabled); + } + + @Override + public void setDeviceIdleWhitelist(int[] appids) { + setDeviceIdleWhitelistInternal(appids); } } } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 56816f9..e649e48 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -1206,7 +1206,7 @@ public final class TvInputManagerService extends SystemService { } @Override - public void requestUnblockContent( + public void unblockContent( IBinder sessionToken, String unblockedRating, int userId) { final int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, @@ -1216,9 +1216,9 @@ public final class TvInputManagerService extends SystemService { synchronized (mLock) { try { getSessionLocked(sessionToken, callingUid, resolvedUserId) - .requestUnblockContent(unblockedRating); + .unblockContent(unblockedRating); } catch (RemoteException | SessionNotFoundException e) { - Slog.e(TAG, "error in requestUnblockContent", e); + Slog.e(TAG, "error in unblockContent", e); } } } finally { diff --git a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp index 7dbfaf6..39474ec 100644 --- a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp +++ b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp @@ -181,6 +181,21 @@ static jlong nativeGetAuthenticatorId(JNIEnv *, jobject clazz) { return gContext.device->get_authenticator_id(gContext.device); } +static jint nativeSetActiveGroup(JNIEnv *env, jobject clazz, jint gid, jbyteArray path) { + const int pathSize = env->GetArrayLength(path); + jbyte* pathData = env->GetByteArrayElements(path, 0); + if (pathSize >= PATH_MAX) { + ALOGE("Path name is too long\n"); + return -1; + } + char path_name[PATH_MAX] = {0}; + memcpy(path_name, pathData, pathSize); + ALOG(LOG_VERBOSE, LOG_TAG, "nativeSetActiveGroup() path: %s, gid: %d\n", path_name, gid); + int result = gContext.device->set_active_group(gContext.device, gid, path_name); + env->ReleaseByteArrayElements(path, pathData, 0); + return result; +} + static jint nativeOpenHal(JNIEnv* env, jobject clazz) { ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n"); int err; @@ -242,6 +257,7 @@ static const JNINativeMethod g_methods[] = { { "nativeAuthenticate", "(JI)I", (void*)nativeAuthenticate }, { "nativeStopAuthentication", "()I", (void*)nativeStopAuthentication }, { "nativeEnroll", "([BII)I", (void*)nativeEnroll }, + { "nativeSetActiveGroup", "(I[B)I", (void*)nativeSetActiveGroup }, { "nativePreEnroll", "()J", (void*)nativePreEnroll }, { "nativeStopEnrollment", "()I", (void*)nativeStopEnrollment }, { "nativeRemove", "(II)I", (void*)nativeRemove }, diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp index 6dcdd9d..1662755 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -156,6 +156,14 @@ static void nativeSendPowerHint(JNIEnv *env, jclass clazz, jint hintId, jint dat } } +static void nativeSetFeature(JNIEnv *env, jclass clazz, jint featureId, jint data) { + int data_param = data; + + if (gPowerModule && gPowerModule->setFeature) { + gPowerModule->setFeature(gPowerModule, (feature_t)featureId, data_param); + } +} + // ---------------------------------------------------------------------------- static JNINativeMethod gPowerManagerServiceMethods[] = { @@ -172,6 +180,8 @@ static JNINativeMethod gPowerManagerServiceMethods[] = { (void*) nativeSetAutoSuspend }, { "nativeSendPowerHint", "(II)V", (void*) nativeSendPowerHint }, + { "nativeSetFeature", "(II)V", + (void*) nativeSetFeature }, }; #define FIND_CLASS(var, className) \ diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java index 28ffc57..88bf54e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java @@ -17,6 +17,7 @@ package com.android.server.devicepolicy; import android.app.AppGlobals; +import android.app.admin.SystemUpdatePolicy; import android.content.ComponentName; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -78,7 +79,7 @@ class DeviceOwner { private final HashMap<Integer, OwnerInfo> mProfileOwners = new HashMap<Integer, OwnerInfo>(); // Local system update policy controllable by device owner. - private PersistableBundle mSystemUpdatePolicy; + private SystemUpdatePolicy mSystemUpdatePolicy; // Private default constructor. private DeviceOwner() { @@ -192,11 +193,11 @@ class DeviceOwner { return mProfileOwners.keySet(); } - PersistableBundle getSystemUpdatePolicy() { + SystemUpdatePolicy getSystemUpdatePolicy() { return mSystemUpdatePolicy; } - void setSystemUpdatePolicy(PersistableBundle systemUpdatePolicy) { + void setSystemUpdatePolicy(SystemUpdatePolicy systemUpdatePolicy) { mSystemUpdatePolicy = systemUpdatePolicy; } @@ -291,7 +292,7 @@ class DeviceOwner { } mProfileOwners.put(userId, profileOwnerInfo); } else if (TAG_SYSTEM_UPDATE_POLICY.equals(tag)) { - mSystemUpdatePolicy = PersistableBundle.restoreFromXml(parser); + mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser); } else { throw new XmlPullParserException( "Unexpected tag in device owner file: " + tag); @@ -361,11 +362,7 @@ class DeviceOwner { // Write system update policy tag if (mSystemUpdatePolicy != null) { out.startTag(null, TAG_SYSTEM_UPDATE_POLICY); - try { - mSystemUpdatePolicy.saveToXml(out); - } catch (XmlPullParserException e) { - Slog.e(TAG, "Failed to save system update policy", e); - } + mSystemUpdatePolicy.saveToXml(out); out.endTag(null, TAG_SYSTEM_UPDATE_POLICY); } out.endDocument(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 67c198f..ba5d666 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -39,6 +39,7 @@ import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.IDevicePolicyManager; +import android.app.admin.SystemUpdatePolicy; import android.app.backup.IBackupManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -6238,7 +6239,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public void setSystemUpdatePolicy(ComponentName who, PersistableBundle policy) { + public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) { + if (policy != null && !policy.isValid()) { + throw new IllegalArgumentException("Invalid system update policy."); + } synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); if (policy == null) { @@ -6254,9 +6258,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public PersistableBundle getSystemUpdatePolicy() { + public SystemUpdatePolicy getSystemUpdatePolicy() { synchronized (this) { - return mDeviceOwner.getSystemUpdatePolicy(); + SystemUpdatePolicy policy = mDeviceOwner.getSystemUpdatePolicy(); + if (policy != null && !policy.isValid()) { + Slog.w(LOG_TAG, "Stored system update policy is invalid, return null instead."); + return null; + } + return policy; } } @@ -6344,10 +6353,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long ident = Binder.clearCallingIdentity(); try { + PackageManager packageManager = mContext.getPackageManager(); if (granted) { - mContext.getPackageManager().grantPermission(packageName, permission, user); + packageManager.grantRuntimePermission(packageName, permission, user); + packageManager.updatePermissionFlags(permission, packageName, + PackageManager.FLAG_PERMISSION_POLICY_FIXED, + PackageManager.FLAG_PERMISSION_POLICY_FIXED, user); } else { - mContext.getPackageManager().revokePermission(packageName, permission, user); + packageManager.revokeRuntimePermission(packageName, + permission, user); + packageManager.updatePermissionFlags(permission, packageName, + PackageManager.FLAG_PERMISSION_POLICY_FIXED, + PackageManager.FLAG_PERMISSION_POLICY_FIXED, user); } return true; } catch (SecurityException se) { diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index 869d6e1..d6ff475 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -48,6 +48,7 @@ class IntervalStats { usageStats.mPackageName = getCachedStringRef(packageName); usageStats.mBeginTimeStamp = beginTime; usageStats.mEndTimeStamp = endTime; + usageStats.mBeginIdleTime = endTime; packageStats.put(usageStats.mPackageName, usageStats); } return usageStats; @@ -119,6 +120,17 @@ class IntervalStats { endTime = timeStamp; } + /** + * Updates the last active time for the package. The timestamp uses a timebase that + * tracks the device usage time. + * @param packageName + * @param timeStamp + */ + void updateBeginIdleTime(String packageName, long timeStamp) { + UsageStats usageStats = getOrCreateUsageStats(packageName); + usageStats.mBeginIdleTime = timeStamp; + } + void updateConfigurationStats(Configuration config, long timeStamp) { if (activeConfiguration != null) { ConfigurationStats activeStats = configurations.get(activeConfiguration); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 48b127a..197daed 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -40,31 +40,39 @@ import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.database.ContentObserver; +import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.BatteryManager; import android.os.Binder; import android.os.Environment; import android.os.Handler; +import android.os.IDeviceIdleController; import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.util.ArraySet; +import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; +import android.view.Display; -import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; -import com.android.server.SystemConfig; +import com.android.server.DeviceIdleController; import com.android.server.SystemService; +import java.io.BufferedReader; import java.io.File; import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -76,6 +84,7 @@ import java.util.List; */ public class UsageStatsService extends SystemService implements UserUsageStatsService.StatsUpdatedListener { + static final String TAG = "UsageStatsService"; static final boolean DEBUG = false; @@ -91,7 +100,7 @@ public class UsageStatsService extends SystemService implements static final int MSG_FLUSH_TO_DISK = 1; static final int MSG_REMOVE_USER = 2; static final int MSG_INFORM_LISTENERS = 3; - static final int MSG_RESET_LAST_TIMESTAMP = 4; + static final int MSG_FORCE_IDLE_STATE = 4; static final int MSG_CHECK_IDLE_STATES = 5; private final Object mLock = new Object(); @@ -99,16 +108,20 @@ public class UsageStatsService extends SystemService implements AppOpsManager mAppOps; UserManager mUserManager; AppWidgetManager mAppWidgetManager; + IDeviceIdleController mDeviceIdleController; + private DisplayManager mDisplayManager; private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>(); private File mUsageStatsDir; long mRealTimeSnapshot; long mSystemTimeSnapshot; boolean mAppIdleParoled; + private boolean mScreenOn; long mAppIdleDurationMillis; - long mCheckIdleIntervalMillis = DEFAULT_CHECK_IDLE_INTERVAL; + long mScreenOnTime; + long mScreenOnSystemTimeSnapshot; private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener> mPackageAccessListeners = new ArrayList<>(); @@ -162,6 +175,18 @@ public class UsageStatsService extends SystemService implements // Observe changes to the threshold new SettingsObserver(mHandler).registerObserver(); mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class); + mDeviceIdleController = IDeviceIdleController.Stub.asInterface( + ServiceManager.getService(DeviceIdleController.SERVICE_NAME)); + mDisplayManager = (DisplayManager) getContext().getSystemService( + Context.DISPLAY_SERVICE); + mScreenOnSystemTimeSnapshot = System.currentTimeMillis(); + synchronized (this) { + mScreenOnTime = readScreenOnTimeLocked(); + } + mDisplayManager.registerDisplayListener(mDisplayListener, null); + synchronized (this) { + updateDisplayLocked(); + } } else if (phase == PHASE_BOOT_COMPLETED) { setAppIdleParoled(getContext().getSystemService(BatteryManager.class).isCharging()); } @@ -195,6 +220,24 @@ public class UsageStatsService extends SystemService implements } } + private final DisplayManager.DisplayListener mDisplayListener + = new DisplayManager.DisplayListener() { + + @Override public void onDisplayAdded(int displayId) { + } + + @Override public void onDisplayRemoved(int displayId) { + } + + @Override public void onDisplayChanged(int displayId) { + if (displayId == Display.DEFAULT_DISPLAY) { + synchronized (UsageStatsService.this.mLock) { + updateDisplayLocked(); + } + } + } + }; + @Override public void onStatsUpdated() { mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL); @@ -261,7 +304,7 @@ public class UsageStatsService extends SystemService implements final int packageCount = packages.size(); for (int p = 0; p < packageCount; p++) { final String packageName = packages.get(p).packageName; - final boolean isIdle = isAppIdle(packageName, userId); + final boolean isIdle = isAppIdleFiltered(packageName, userId); mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, isIdle ? 1 : 0, packageName)); } @@ -270,6 +313,61 @@ public class UsageStatsService extends SystemService implements mHandler.sendEmptyMessageDelayed(MSG_CHECK_IDLE_STATES, mCheckIdleIntervalMillis); } + void updateDisplayLocked() { + boolean screenOn = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState() + != Display.STATE_OFF; + if (screenOn == mScreenOn) return; + + mScreenOn = screenOn; + long now = System.currentTimeMillis(); + if (mScreenOn) { + mScreenOnSystemTimeSnapshot = now; + } else { + mScreenOnTime += now - mScreenOnSystemTimeSnapshot; + writeScreenOnTimeLocked(mScreenOnTime); + } + } + + private long getScreenOnTimeLocked(long now) { + if (mScreenOn) { + return now - mScreenOnSystemTimeSnapshot + mScreenOnTime; + } else { + return mScreenOnTime; + } + } + + private File getScreenOnTimeFile() { + return new File(mUsageStatsDir, UserHandle.USER_OWNER + "/screen_on_time"); + } + + private long readScreenOnTimeLocked() { + long screenOnTime = 0; + File screenOnTimeFile = getScreenOnTimeFile(); + if (screenOnTimeFile.exists()) { + try { + BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile)); + screenOnTime = Long.parseLong(reader.readLine()); + reader.close(); + } catch (IOException | NumberFormatException e) { + } + } else { + writeScreenOnTimeLocked(screenOnTime); + } + return screenOnTime; + } + + private void writeScreenOnTimeLocked(long screenOnTime) { + AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile()); + FileOutputStream fos = null; + try { + fos = screenOnTimeFile.startWrite(); + fos.write(Long.toString(screenOnTime).getBytes()); + screenOnTimeFile.finishWrite(fos); + } catch (IOException ioe) { + screenOnTimeFile.failWrite(fos); + } + } + private static void deleteRecursively(File f) { File[] files = f.listFiles(); if (files != null) { @@ -289,7 +387,7 @@ public class UsageStatsService extends SystemService implements if (service == null) { service = new UserUsageStatsService(getContext(), userId, new File(mUsageStatsDir, Integer.toString(userId)), this); - service.init(currentTimeMillis); + service.init(currentTimeMillis, getScreenOnTimeLocked(currentTimeMillis)); mUserState.put(userId, service); } return service; @@ -343,9 +441,10 @@ public class UsageStatsService extends SystemService implements final UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId, timeNow); - final long lastUsed = service.getLastPackageAccessTime(event.mPackage); - final boolean previouslyIdle = hasPassedIdleDuration(lastUsed); - service.reportEvent(event); + final long lastUsed = service.getBeginIdleTime(event.mPackage); + final long screenOnTime = getScreenOnTimeLocked(timeNow); + final boolean previouslyIdle = hasPassedIdleTimeout(lastUsed, screenOnTime); + service.reportEvent(event, screenOnTime); // Inform listeners if necessary if ((event.mEventType == Event.MOVE_TO_FOREGROUND || event.mEventType == Event.MOVE_TO_BACKGROUND @@ -360,19 +459,21 @@ public class UsageStatsService extends SystemService implements } /** - * Forces the app's timestamp to reflect idle or active. If idle, then it rolls back the - * last used timestamp to a point in time thats behind the threshold for idle. + * Forces the app's beginIdleTime to reflect idle or active. If idle, then it rolls back the + * beginIdleTime to a point in time thats behind the threshold for idle. */ - void resetLastTimestamp(String packageName, int userId, boolean idle) { + void forceIdleState(String packageName, int userId, boolean idle) { synchronized (mLock) { final long timeNow = checkAndGetTimeLocked(); - final long lastTimestamp = timeNow - (idle ? mAppIdleDurationMillis : 0) - 5000; + final long screenOnTime = getScreenOnTimeLocked(timeNow); + final long deviceUsageTime = screenOnTime - (idle ? mAppIdleDurationMillis : 0) - 5000; final UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId, timeNow); - final long lastUsed = service.getLastPackageAccessTime(packageName); - final boolean previouslyIdle = hasPassedIdleDuration(lastUsed); - service.setLastTimestamp(packageName, lastTimestamp); + final long lastUsed = service.getBeginIdleTime(packageName); + final boolean previouslyIdle = hasPassedIdleTimeout(lastUsed, + getScreenOnTimeLocked(timeNow)); + service.setBeginIdleTime(packageName, deviceUsageTime); // Inform listeners if necessary if (previouslyIdle != idle) { // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage); @@ -451,23 +552,25 @@ public class UsageStatsService extends SystemService implements } } - /** - * Called by LocalService stub. - */ - long getLastPackageAccessTime(String packageName, int userId) { + private boolean isAppIdleUnfiltered(String packageName, int userId) { synchronized (mLock) { final long timeNow = checkAndGetTimeLocked(); - // android package is always considered non-idle. - // TODO: Add a generic whitelisting mechanism - if (packageName.equals("android")) { - return timeNow; - } final UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId, timeNow); - return service.getLastPackageAccessTime(packageName); + long beginIdleTime = service.getBeginIdleTime(packageName); + return hasPassedIdleTimeout(beginIdleTime, getScreenOnTimeLocked(timeNow)); } } + /** + * @param timestamp when the app was last used in device usage timebase + * @param currentTime current time in device usage timebase + * @return whether it's been used far enough in the past to be considered inactive + */ + boolean hasPassedIdleTimeout(long timestamp, long currentTime) { + return timestamp <= currentTime - mAppIdleDurationMillis; + } + void addListener(AppIdleStateChangeListener listener) { synchronized (mLock) { if (!mPackageAccessListeners.contains(listener)) { @@ -482,12 +585,13 @@ public class UsageStatsService extends SystemService implements } } - private boolean hasPassedIdleDuration(long lastUsed) { - final long now = System.currentTimeMillis(); - return lastUsed <= now - mAppIdleDurationMillis; - } - - boolean isAppIdle(String packageName, int userId) { + /** + * Checks if an app has been idle for a while and filters out apps that are excluded. + * It returns false if the current system state allows all apps to be considered active. + * This happens if the device is plugged in or temporarily allowed to make exceptions. + * Called by interface impls. + */ + boolean isAppIdleFiltered(String packageName, int userId) { if (packageName == null) return false; synchronized (mLock) { // Temporary exemption, probably due to device charging or occasional allowance to @@ -496,8 +600,12 @@ public class UsageStatsService extends SystemService implements return false; } } - if (SystemConfig.getInstance().getAllowInPowerSave().contains(packageName)) { - return false; + if (packageName.equals("android")) return false; + try { + if (mDeviceIdleController.isPowerSaveWhitelistApp(packageName)) { + return false; + } + } catch (RemoteException re) { } // TODO: Optimize this check if (isActiveDeviceAdmin(packageName, userId)) { @@ -509,14 +617,13 @@ public class UsageStatsService extends SystemService implements return false; } - final long lastUsed = getLastPackageAccessTime(packageName, userId); - return hasPassedIdleDuration(lastUsed); + return isAppIdleUnfiltered(packageName, userId); } void setAppIdle(String packageName, boolean idle, int userId) { if (packageName == null) return; - mHandler.obtainMessage(MSG_RESET_LAST_TIMESTAMP, userId, idle ? 1 : 0, packageName) + mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName) .sendToTarget(); } @@ -559,6 +666,7 @@ public class UsageStatsService extends SystemService implements */ void dump(String[] args, PrintWriter pw) { synchronized (mLock) { + final long screenOnTime = getScreenOnTimeLocked(checkAndGetTimeLocked()); IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " "); ArraySet<String> argSet = new ArraySet<>(); argSet.addAll(Arrays.asList(args)); @@ -569,12 +677,13 @@ public class UsageStatsService extends SystemService implements idpw.println(); idpw.increaseIndent(); if (argSet.contains("--checkin")) { - mUserState.valueAt(i).checkin(idpw); + mUserState.valueAt(i).checkin(idpw, screenOnTime); } else { - mUserState.valueAt(i).dump(idpw); + mUserState.valueAt(i).dump(idpw, screenOnTime); } idpw.decreaseIndent(); } + pw.write("Screen On Timestamp:" + mScreenOnTime + "\n"); } } @@ -602,8 +711,8 @@ public class UsageStatsService extends SystemService implements informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1); break; - case MSG_RESET_LAST_TIMESTAMP: - resetLastTimestamp((String) msg.obj, msg.arg1, msg.arg2 == 1); + case MSG_FORCE_IDLE_STATE: + forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1); break; case MSG_CHECK_IDLE_STATES: @@ -727,7 +836,7 @@ public class UsageStatsService extends SystemService implements } final long token = Binder.clearCallingIdentity(); try { - return UsageStatsService.this.isAppIdle(packageName, userId); + return UsageStatsService.this.isAppIdleFiltered(packageName, userId); } finally { Binder.restoreCallingIdentity(token); } @@ -832,12 +941,7 @@ public class UsageStatsService extends SystemService implements @Override public boolean isAppIdle(String packageName, int userId) { - return UsageStatsService.this.isAppIdle(packageName, userId); - } - - @Override - public long getLastPackageAccessTime(String packageName, int userId) { - return UsageStatsService.this.getLastPackageAccessTime(packageName, userId); + return UsageStatsService.this.isAppIdleFiltered(packageName, userId); } @Override diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index bfb71c5..0111201 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -26,6 +26,7 @@ import android.app.usage.TimeSparseArray; import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.content.res.Configuration; +import android.text.TextUtils; import java.io.IOException; import java.net.ProtocolException; @@ -54,6 +55,7 @@ final class UsageStatsXmlV1 { // Time attributes stored as an offset of the beginTime. private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive"; + private static final String BEGIN_IDLE_TIME_ATTR = "beginIdleTime"; private static final String END_TIME_ATTR = "endTime"; private static final String TIME_ATTR = "time"; @@ -69,7 +71,10 @@ final class UsageStatsXmlV1 { // Apply the offset to the beginTime to find the absolute time. stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute( parser, LAST_TIME_ACTIVE_ATTR); - + final String beginIdleTime = parser.getAttributeValue(null, BEGIN_IDLE_TIME_ATTR); + if (!TextUtils.isEmpty(beginIdleTime)) { + stats.mBeginIdleTime = Long.parseLong(beginIdleTime); + } stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR); stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR); } @@ -129,6 +134,7 @@ final class UsageStatsXmlV1 { XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName); XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground); XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent); + XmlUtils.writeLongAttribute(xml, BEGIN_IDLE_TIME_ATTR, usageStats.mBeginIdleTime); xml.endTag(null, PACKAGE_TAG); } diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index d94759d..b638c8e 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -80,7 +80,7 @@ class UserUsageStatsService { mUserId = userId; } - void init(final long currentTimeMillis) { + void init(final long currentTimeMillis, final long deviceUsageTime) { mDatabase.init(currentTimeMillis); int nullCount = 0; @@ -135,7 +135,8 @@ class UserUsageStatsService { } if (mDatabase.isNewUpdate()) { - initializeDefaultsForApps(currentTimeMillis, mDatabase.isFirstUpdate()); + initializeDefaultsForApps(currentTimeMillis, deviceUsageTime, + mDatabase.isFirstUpdate()); } } @@ -145,7 +146,8 @@ class UserUsageStatsService { * @param firstUpdate if it is the first update, touch all installed apps, otherwise only * touch the system apps */ - private void initializeDefaultsForApps(long currentTimeMillis, boolean firstUpdate) { + private void initializeDefaultsForApps(long currentTimeMillis, long deviceUsageTime, + boolean firstUpdate) { PackageManager pm = mContext.getPackageManager(); List<PackageInfo> packages = pm.getInstalledPackages(0, mUserId); final int packageCount = packages.size(); @@ -153,9 +155,10 @@ class UserUsageStatsService { final PackageInfo pi = packages.get(i); String packageName = pi.packageName; if (pi.applicationInfo != null && (firstUpdate || pi.applicationInfo.isSystemApp()) - && getLastPackageAccessTime(packageName) == -1) { + && getBeginIdleTime(packageName) == -1) { for (IntervalStats stats : mCurrentStats) { stats.update(packageName, currentTimeMillis, Event.INTERACTION); + stats.updateBeginIdleTime(packageName, deviceUsageTime); mStatsChanged = true; } } @@ -170,7 +173,7 @@ class UserUsageStatsService { loadActiveStats(newTime, true); } - void reportEvent(UsageEvents.Event event) { + void reportEvent(UsageEvents.Event event, long deviceUsageTime) { if (DEBUG) { Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage + "[" + event.mTimeStamp + "]: " @@ -205,6 +208,7 @@ class UserUsageStatsService { stats.updateConfigurationStats(newFullConfig, event.mTimeStamp); } else { stats.update(event.mPackage, event.mTimeStamp, event.mEventType); + stats.updateBeginIdleTime(event.mPackage, deviceUsageTime); } } @@ -215,9 +219,9 @@ class UserUsageStatsService { * Sets the last timestamp for each of the intervals. * @param lastTimestamp */ - void setLastTimestamp(String packageName, long lastTimestamp) { + void setBeginIdleTime(String packageName, long deviceUsageTime) { for (IntervalStats stats : mCurrentStats) { - stats.update(packageName, lastTimestamp, UsageEvents.Event.NONE); + stats.updateBeginIdleTime(packageName, deviceUsageTime); } notifyStatsChanged(); } @@ -376,13 +380,13 @@ class UserUsageStatsService { return new UsageEvents(results, table); } - long getLastPackageAccessTime(String packageName) { + long getBeginIdleTime(String packageName) { final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY]; UsageStats packageUsage; if ((packageUsage = yearly.packageStats.get(packageName)) == null) { return -1; } else { - return packageUsage.getLastTimeUsed(); + return packageUsage.getBeginIdleTime(); } } @@ -506,23 +510,23 @@ class UserUsageStatsService { // -- DUMP related methods -- // - void checkin(final IndentingPrintWriter pw) { + void checkin(final IndentingPrintWriter pw, final long screenOnTime) { mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() { @Override public boolean checkin(IntervalStats stats) { - printIntervalStats(pw, stats, false); + printIntervalStats(pw, stats, screenOnTime, false); return true; } }); } - void dump(IndentingPrintWriter pw) { + void dump(IndentingPrintWriter pw, final long screenOnTime) { // This is not a check-in, only dump in-memory stats. for (int interval = 0; interval < mCurrentStats.length; interval++) { pw.print("In-memory "); pw.print(intervalToString(interval)); pw.println(" stats"); - printIntervalStats(pw, mCurrentStats[interval], true); + printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true); } } @@ -540,7 +544,8 @@ class UserUsageStatsService { return Long.toString(elapsedTime); } - void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, boolean prettyDates) { + void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, long screenOnTime, + boolean prettyDates) { if (prettyDates) { pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext, stats.beginTime, stats.endTime, sDateFormatFlags) + "\""); @@ -559,6 +564,8 @@ class UserUsageStatsService { pw.printPair("package", usageStats.mPackageName); pw.printPair("totalTime", formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates)); pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates)); + pw.printPair("inactiveTime", + formatElapsedTime(screenOnTime - usageStats.mBeginIdleTime, prettyDates)); pw.println(); } pw.decreaseIndent(); diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 9efea0d..bf1ea4d 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -44,14 +44,12 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.VerificationParams; import android.content.pm.VerifierDeviceIdentity; -import android.content.pm.PackageManager.MoveCallback; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; -import android.os.ResultReceiver; import android.os.UserHandle; import android.os.storage.VolumeInfo; @@ -195,13 +193,28 @@ public class MockPackageManager extends PackageManager { /** @hide */ @Override - public void grantPermission(String packageName, String permissionName, UserHandle user) { + public void grantRuntimePermission(String packageName, String permissionName, + UserHandle user) { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override + public void revokeRuntimePermission(String packageName, String permissionName, + UserHandle user) { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override + public int getPermissionFlags(String permissionName, String packageName, UserHandle user) { throw new UnsupportedOperationException(); } /** @hide */ @Override - public void revokePermission(String packageName, String permissionName, UserHandle user) { + public void updatePermissionFlags(String permissionName, String packageName, + int flagMask, int flagValues, UserHandle user) { throw new UnsupportedOperationException(); } |