diff options
261 files changed, 9152 insertions, 3330 deletions
diff --git a/Android.bp b/Android.bp index a603006d119d..faad6f32dec4 100644 --- a/Android.bp +++ b/Android.bp @@ -77,6 +77,7 @@ java_defaults { "core/java/android/app/ISearchManager.aidl", "core/java/android/app/ISearchManagerCallback.aidl", "core/java/android/app/IServiceConnection.aidl", + "core/java/android/app/ISmsAppService.aidl", "core/java/android/app/IStopUserCallback.aidl", "core/java/android/app/job/IJobCallback.aidl", "core/java/android/app/job/IJobScheduler.aidl", @@ -515,11 +516,14 @@ java_defaults { "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl", "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl", "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl", + "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl", "telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl", "telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl", "telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl", + "telephony/java/android/telephony/mbms/IGroupCallCallback.aidl", "telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl", "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl", + "telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl", "telephony/java/android/telephony/INetworkService.aidl", "telephony/java/android/telephony/INetworkServiceCallback.aidl", "telephony/java/com/android/ims/internal/IImsCallSession.aidl", @@ -703,7 +707,6 @@ java_defaults { // Loaded with System.loadLibrary by android.view.textclassifier required: [ - "libtextclassifier", "libmedia2_jni", ], @@ -807,12 +810,16 @@ gensrcs { java_library { name: "ext", installable: true, - no_framework_libs: true, + sdk_version: "core_current", static_libs: [ "libphonenumber-platform", "nist-sip", "tagsoup", "rappor", + "libtextclassifier-java", + ], + required: [ + "libtextclassifier", ], dxflags: ["--core-library"], } @@ -1282,6 +1289,9 @@ droiddoc { } droiddoc { + // Please sync with android-api-council@ before making any changes for the name property below. + // Since there's cron jobs that fetch offline-sdk-referenceonly-docs-docs.zip periodically. + // See b/116221385 for reference. name: "offline-sdk-referenceonly-docs", defaults: ["framework-docs-default"], srcs: [ @@ -1298,6 +1308,9 @@ droiddoc { } droiddoc { + // Please sync with android-api-council@ before making any changes for the name property below. + // Since there's cron jobs that fetch offline-system-sdk-referenceonly-docs-docs.zip periodically. + // See b/116221385 for reference. name: "offline-system-sdk-referenceonly-docs", defaults: ["framework-docs-default"], srcs: [ diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index de83f3e01074..ff40f7543c9d 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -8,6 +8,11 @@ checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPL services/print/ services/usb/ telephony/ + tests/ActivityViewTest/ + tests/LotsOfApps/ + tests/NativeProcessesMemoryTest/ + tests/OdmApps/ + tests/SystemMemoryTest/ wifi/ api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREUPLOAD_COMMIT} diff --git a/api/current.txt b/api/current.txt index 210f956ba882..1b0971000f3a 100755 --- a/api/current.txt +++ b/api/current.txt @@ -37,6 +37,7 @@ package android { field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final java.lang.String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; + field public static final java.lang.String BIND_SMS_APP_SERVICE = "android.permission.BIND_SMS_APP_SERVICE"; field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT"; @@ -4296,6 +4297,20 @@ package android.app { method public abstract void onActivityCreated(android.app.Activity, android.os.Bundle); method public abstract void onActivityDestroyed(android.app.Activity); method public abstract void onActivityPaused(android.app.Activity); + method public default void onActivityPostCreated(android.app.Activity, android.os.Bundle); + method public default void onActivityPostDestroyed(android.app.Activity); + method public default void onActivityPostPaused(android.app.Activity); + method public default void onActivityPostResumed(android.app.Activity); + method public default void onActivityPostSaveInstanceState(android.app.Activity, android.os.Bundle); + method public default void onActivityPostStarted(android.app.Activity); + method public default void onActivityPostStopped(android.app.Activity); + method public default void onActivityPreCreated(android.app.Activity, android.os.Bundle); + method public default void onActivityPreDestroyed(android.app.Activity); + method public default void onActivityPrePaused(android.app.Activity); + method public default void onActivityPreResumed(android.app.Activity); + method public default void onActivityPreSaveInstanceState(android.app.Activity, android.os.Bundle); + method public default void onActivityPreStarted(android.app.Activity); + method public default void onActivityPreStopped(android.app.Activity); method public abstract void onActivityResumed(android.app.Activity); method public abstract void onActivitySaveInstanceState(android.app.Activity, android.os.Bundle); method public abstract void onActivityStarted(android.app.Activity); @@ -6095,6 +6110,11 @@ package android.app { method public abstract void onSharedElementsReady(); } + public class SmsAppService extends android.app.Service { + ctor public SmsAppService(); + method public final android.os.IBinder onBind(android.content.Intent); + } + public deprecated class TabActivity extends android.app.ActivityGroup { ctor public TabActivity(); method public android.widget.TabHost getTabHost(); @@ -14007,6 +14027,10 @@ package android.graphics { method public float getRunAdvance(char[], int, int, int, int, boolean, int); method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int); method public android.graphics.Shader getShader(); + method public int getShadowLayerColor(); + method public float getShadowLayerDx(); + method public float getShadowLayerDy(); + method public float getShadowLayerRadius(); method public float getStrikeThruPosition(); method public float getStrikeThruThickness(); method public android.graphics.Paint.Cap getStrokeCap(); @@ -15310,7 +15334,7 @@ package android.graphics.fonts { method public static java.lang.String toFontVariationSettings(android.graphics.fonts.FontVariationAxis[]); } - public class SystemFonts { + public final class SystemFonts { method public static java.util.Set<android.graphics.fonts.Font> getAvailableFonts(); } @@ -40106,6 +40130,7 @@ package android.service.voice { field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10 field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8 field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4 + field public static final int SHOW_SOURCE_NOTIFICATION = 64; // 0x40 field public static final int SHOW_SOURCE_PUSH_TO_TALK = 32; // 0x20 field public static final int SHOW_WITH_ASSIST = 1; // 0x1 field public static final int SHOW_WITH_SCREENSHOT = 2; // 0x2 @@ -42265,6 +42290,7 @@ package android.telephony { field public static final deprecated java.lang.String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool"; field public static final java.lang.String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool"; field public static final java.lang.String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool"; + field public static final java.lang.String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool"; field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool"; field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool"; field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool"; @@ -42526,6 +42552,13 @@ package android.telephony { field public static final int STATUS_UNKNOWN = 0; // 0x0 } + public class MbmsGroupCallSession implements java.lang.AutoCloseable { + method public void close(); + method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback); + method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback); + method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback); + } + public class MbmsStreamingSession implements java.lang.AutoCloseable { method public void close(); method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback); @@ -42907,6 +42940,7 @@ package android.telephony { method public static int getSlotIndex(int); method public static int[] getSubscriptionIds(int); method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int); + method public boolean isActiveSubscriptionId(int); method public boolean isNetworkRoaming(int); method public static boolean isValidSubscriptionId(int); method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); @@ -43059,6 +43093,7 @@ package android.telephony { field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE"; field public static final java.lang.String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE"; field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION"; + field public static final java.lang.String ACTION_SMS_APP_SERVICE = "android.telephony.action.SMS_APP_SERVICE"; field public static final java.lang.String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED"; field public static final int APPTYPE_CSIM = 4; // 0x4 field public static final int APPTYPE_ISIM = 5; // 0x5 @@ -43475,6 +43510,29 @@ package android.telephony.mbms { field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR; } + public class GroupCall implements java.lang.AutoCloseable { + method public void close(); + method public long getTmgi(); + method public void updateGroupCall(int[], int[]); + field public static final int REASON_BY_USER_REQUEST = 1; // 0x1 + field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3 + field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6 + field public static final int REASON_NONE = 0; // 0x0 + field public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5; // 0x5 + field public static final int REASON_OUT_OF_MEMORY = 4; // 0x4 + field public static final int STATE_STALLED = 3; // 0x3 + field public static final int STATE_STARTED = 2; // 0x2 + field public static final int STATE_STOPPED = 1; // 0x1 + } + + public class GroupCallCallback { + ctor public GroupCallCallback(); + method public void onBroadcastSignalStrengthUpdated(int); + method public void onError(int, java.lang.String); + method public void onGroupCallStateChanged(int, int); + field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff + } + public class MbmsDownloadReceiver extends android.content.BroadcastReceiver { ctor public MbmsDownloadReceiver(); method public void onReceive(android.content.Context, android.content.Intent); @@ -43523,6 +43581,14 @@ package android.telephony.mbms { field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e } + public class MbmsGroupCallSessionCallback { + ctor public MbmsGroupCallSessionCallback(); + method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>); + method public void onError(int, java.lang.String); + method public void onMiddlewareReady(); + method public void onServiceInterfaceAvailable(java.lang.String, int); + } + public class MbmsStreamingSessionCallback { ctor public MbmsStreamingSessionCallback(); method public void onError(int, java.lang.String); @@ -44157,6 +44223,8 @@ package android.text { field public float density; field public int[] drawableState; field public int linkColor; + field public int underlineColor; + field public float underlineThickness; } public class TextUtils { @@ -51361,7 +51429,7 @@ package android.webkit { } public abstract class CookieManager { - ctor public CookieManager(); + ctor public deprecated CookieManager(); method public abstract boolean acceptCookie(); method public abstract boolean acceptThirdPartyCookies(android.webkit.WebView); method public static boolean allowFileSchemeCookies(); @@ -51461,13 +51529,13 @@ package android.webkit { } public abstract class RenderProcessGoneDetail { - ctor public RenderProcessGoneDetail(); + ctor public deprecated RenderProcessGoneDetail(); method public abstract boolean didCrash(); method public abstract int rendererPriorityAtExit(); } public abstract class SafeBrowsingResponse { - ctor public SafeBrowsingResponse(); + ctor public deprecated SafeBrowsingResponse(); method public abstract void backToSafety(boolean); method public abstract void proceed(boolean); method public abstract void showInterstitial(boolean); @@ -51479,7 +51547,7 @@ package android.webkit { } public abstract class ServiceWorkerController { - ctor public ServiceWorkerController(); + ctor public deprecated ServiceWorkerController(); method public static android.webkit.ServiceWorkerController getInstance(); method public abstract android.webkit.ServiceWorkerWebSettings getServiceWorkerWebSettings(); method public abstract void setServiceWorkerClient(android.webkit.ServiceWorkerClient); @@ -51528,7 +51596,7 @@ package android.webkit { } public abstract class TracingController { - ctor public TracingController(); + ctor public deprecated TracingController(); method public static android.webkit.TracingController getInstance(); method public abstract boolean isTracing(); method public abstract void start(android.webkit.TracingConfig); @@ -52068,7 +52136,7 @@ package android.webkit { } public abstract class WebViewDatabase { - ctor public WebViewDatabase(); + ctor public deprecated WebViewDatabase(); method public abstract deprecated void clearFormData(); method public abstract void clearHttpAuthUsernamePassword(); method public abstract deprecated void clearUsernamePassword(); @@ -53348,7 +53416,7 @@ package android.widget { } public final class Magnifier { - ctor public Magnifier(android.view.View); + ctor public deprecated Magnifier(android.view.View); method public void dismiss(); method public float getCornerRadius(); method public int getDefaultHorizontalSourceToMagnifierOffset(); diff --git a/api/system-current.txt b/api/system-current.txt index 1009b672cf65..5785e4aa3969 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -950,6 +950,7 @@ package android.content { field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control"; field public static final java.lang.String NETWORK_SCORE_SERVICE = "network_score"; field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock"; + field public static final java.lang.String PERMISSION_SERVICE = "permission"; field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block"; field public static final java.lang.String SECURE_ELEMENT_SERVICE = "secure_element"; field public static final java.lang.String STATS_MANAGER = "stats"; @@ -4192,6 +4193,20 @@ package android.os.storage { } +package android.permission { + + public final class PermissionManager { + method public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions(); + } + + public static final class PermissionManager.SplitPermissionInfo { + method public java.lang.String[] getNewPermissions(); + method public java.lang.String getRootPermission(); + method public int getTargetSdk(); + } + +} + package android.permissionpresenterservice { public abstract class RuntimePermissionPresenterService extends android.app.Service { @@ -5257,6 +5272,10 @@ package android.telephony { field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload"; } + public class MbmsGroupCallSession implements java.lang.AutoCloseable { + field public static final java.lang.String MBMS_GROUP_CALL_SERVICE_ACTION = "android.telephony.action.EmbmsGroupCall"; + } + public class MbmsStreamingSession implements java.lang.AutoCloseable { field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming"; } @@ -5420,6 +5439,7 @@ package android.telephony { method public boolean disableDataConnectivity(); method public boolean enableDataConnectivity(); method public void enableVideoCalling(boolean); + method public java.lang.String getAidForAppType(int); method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int); @@ -5434,6 +5454,8 @@ package android.telephony { method public deprecated boolean getDataEnabled(); method public deprecated boolean getDataEnabled(int); method public boolean getEmergencyCallbackMode(); + method public java.lang.String getIsimDomain(); + method public int getPreferredNetworkType(int); method public int getSimApplicationState(); method public int getSimCardState(); method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms(); @@ -5443,10 +5465,7 @@ package android.telephony { method public boolean handlePinMmi(java.lang.String); method public boolean handlePinMmiForSubscriber(int, java.lang.String); method public boolean isDataConnectivityPossible(); - method public deprecated boolean isIdle(); - method public deprecated boolean isOffhook(); method public deprecated boolean isRadioOn(); - method public deprecated boolean isRinging(); method public boolean isVideoCallingEnabled(); method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle); method public boolean needsOtaServiceProvisioning(); @@ -5460,7 +5479,6 @@ package android.telephony { method public void setSimPowerStateForSlot(int, int); method public deprecated void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean); method public void setVoiceActivationState(int); - method public deprecated void silenceRinger(); method public boolean supplyPin(java.lang.String); method public int[] supplyPinReportResult(java.lang.String); method public boolean supplyPuk(java.lang.String, java.lang.String); @@ -5478,6 +5496,29 @@ package android.telephony { field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE"; field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL"; field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING"; + field public static final int NETWORK_MODE_CDMA_EVDO = 4; // 0x4 + field public static final int NETWORK_MODE_CDMA_NO_EVDO = 5; // 0x5 + field public static final int NETWORK_MODE_EVDO_NO_CDMA = 6; // 0x6 + field public static final int NETWORK_MODE_GLOBAL = 7; // 0x7 + field public static final int NETWORK_MODE_GSM_ONLY = 1; // 0x1 + field public static final int NETWORK_MODE_GSM_UMTS = 3; // 0x3 + field public static final int NETWORK_MODE_LTE_CDMA_EVDO = 8; // 0x8 + field public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; // 0xa + field public static final int NETWORK_MODE_LTE_GSM_WCDMA = 9; // 0x9 + field public static final int NETWORK_MODE_LTE_ONLY = 11; // 0xb + field public static final int NETWORK_MODE_LTE_TDSCDMA = 15; // 0xf + field public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; // 0x16 + field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; // 0x11 + field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; // 0x14 + field public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; // 0x13 + field public static final int NETWORK_MODE_LTE_WCDMA = 12; // 0xc + field public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; // 0x15 + field public static final int NETWORK_MODE_TDSCDMA_GSM = 16; // 0x10 + field public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; // 0x12 + field public static final int NETWORK_MODE_TDSCDMA_ONLY = 13; // 0xd + field public static final int NETWORK_MODE_TDSCDMA_WCDMA = 14; // 0xe + field public static final int NETWORK_MODE_WCDMA_ONLY = 2; // 0x2 + field public static final int NETWORK_MODE_WCDMA_PREF = 0; // 0x0 field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2 field public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; // 0x1 field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3 @@ -6558,6 +6599,17 @@ package android.telephony.mbms.vendor { method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException; } + public class MbmsGroupCallServiceBase extends android.app.Service { + ctor public MbmsGroupCallServiceBase(); + method public void dispose(int) throws android.os.RemoteException; + method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException; + method public void onAppCallbackDied(int, int); + method public android.os.IBinder onBind(android.content.Intent); + method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback); + method public void stopGroupCall(int, long); + method public void updateGroupCall(int, long, int[], int[]); + } + public class MbmsStreamingServiceBase extends android.os.Binder { ctor public MbmsStreamingServiceBase(); method public void dispose(int) throws android.os.RemoteException; diff --git a/api/system-removed.txt b/api/system-removed.txt index 9012c3315cfe..22465621e693 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -148,6 +148,10 @@ package android.telephony { public class TelephonyManager { method public deprecated void answerRingingCall(); method public deprecated boolean endCall(); + method public deprecated boolean isIdle(); + method public deprecated boolean isOffhook(); + method public deprecated boolean isRinging(); + method public deprecated void silenceRinger(); } } diff --git a/api/test-current.txt b/api/test-current.txt index f4d7cbcab6dc..956761615254 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1188,6 +1188,10 @@ package android.telephony { field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override"; } + public class MbmsGroupCallSession implements java.lang.AutoCloseable { + field public static final java.lang.String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA = "mbms-group-call-service-override"; + } + public class MbmsStreamingSession implements java.lang.AutoCloseable { field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override"; } @@ -1257,6 +1261,17 @@ package android.telephony.mbms.vendor { method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException; } + public class MbmsGroupCallServiceBase extends android.app.Service { + ctor public MbmsGroupCallServiceBase(); + method public void dispose(int) throws android.os.RemoteException; + method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException; + method public void onAppCallbackDied(int, int); + method public android.os.IBinder onBind(android.content.Intent); + method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback); + method public void stopGroupCall(int, long); + method public void updateGroupCall(int, long, int[], int[]); + } + public class MbmsStreamingServiceBase extends android.os.Binder { ctor public MbmsStreamingServiceBase(); method public void dispose(int) throws android.os.RemoteException; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 485b91fb8ece..1e9c354d26bf 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -80,7 +80,8 @@ message Atom { BatteryLevelChanged battery_level_changed = 30; ChargingStateChanged charging_state_changed = 31; PluggedStateChanged plugged_state_changed = 32; - // 33 - 34 are available + InteractiveStateChanged interactive_state_changed = 33; + // 34 is available WakeupAlarmOccurred wakeup_alarm_occurred = 35; KernelWakeupReported kernel_wakeup_reported = 36; WifiLockStateChanged wifi_lock_state_changed = 37; @@ -136,6 +137,7 @@ message Atom { FingerprintAcquired fingerprint_acquired = 87; FingerprintAuthenticated fingerprint_authenticated = 88; FingerprintErrorOccurred fingerprint_error_occurred = 89; + Notification notification = 90; } // Pulled events will start at field 10000. @@ -664,6 +666,20 @@ message LongPartialWakelockStateChanged { } /** + * Logs when the device is interactive, according to the PowerManager Notifier. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/power/Notifier.java + */ +message InteractiveStateChanged { + enum State { + OFF = 0; + ON = 1; + } + optional State state = 1; +} + +/** * Logs Battery Saver state change. * * Logged from: @@ -1891,6 +1907,69 @@ message FingerprintErrorOccurred { // The type of error. optional Error error = 3; } + +message Notification { + + // Type of notification event. + enum Type { + TYPE_UNKNOWN = 0; + // Notification became visible to the user. + TYPE_OPEN = 1; + // Notification became hidden. + TYPE_CLOSE = 2; + // Notification switched to detail mode. + TYPE_DETAIL = 3; + // Notification was clicked. + TYPE_ACTION = 4; + // Notification was dismissed. + TYPE_DISMISS = 5; + // Notification switched to summary mode. The enum value of 14 is to + // match that of metrics_constants. + TYPE_COLLAPSE = 14; + } + optional Type type = 1; + + // Package name associated with the notification. + optional string package_name = 2; + + // Tag associated with notification. + optional string tag = 3; + + // Application-supplied ID associated with the notification. + optional int32 id = 4; + + // Index of notification in the notification panel. + optional int32 shade_index = 5; + + // The number of notifications in the notification panel. + optional int32 shade_count = 6; + + // Importance for the notification. + optional int32 importance = 7; + + // ID for the notification channel. + optional int32 channel_id = 8; + + // Importance for the notification channel. + optional int32 channel_importance = 9; + + // Whether notification was a group summary. + optional bool group_summary = 10; + + // Time since notification was created in milliseconds. + optional int64 since_create_millis = 11; + + // Time since notification was interrupted in milliseconds. + optional int64 since_interruption_millis = 12; + + // Time since notification was updated in milliseconds. + optional int64 since_update_millis = 13; + + // Time since notification was visible in milliseconds. + optional int64 since_visible_millis = 14; +} + + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// @@ -2156,6 +2235,11 @@ message ProcessMemoryState { // SWAP optional int64 swap_in_bytes = 8; + + // RSS high watermark. + // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or + // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups. + optional int64 rss_high_watermark_in_bytes = 9; } /* diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 5a0172b22301..66392f80f1fe 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -169,7 +169,7 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}}, // process_memory_state {android::util::PROCESS_MEMORY_STATE, - {{4, 5, 6, 7, 8}, + {{4, 5, 6, 7, 8, 9}, {2, 3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}}, diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 04724616420b..ac16fd311c32 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -2076,27 +2076,20 @@ Lcom/android/internal/telephony/ISub;->getDefaultDataSubId()I Lcom/android/internal/telephony/ISub;->getDefaultSubId()I Lcom/android/internal/telephony/ISub;->setDefaultDataSubId(I)V Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCall()Z -Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCallForSubscriber(I)Z Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String; Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->isRadioOn(Ljava/lang/String;)Z Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->mRemote:Landroid/os/IBinder; Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony; Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String; -Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_answerRingingCall:I Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I -Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_endCall:I Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I -Lcom/android/internal/telephony/ITelephony;->answerRingingCall()V Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V Lcom/android/internal/telephony/ITelephony;->dial(Ljava/lang/String;)V Lcom/android/internal/telephony/ITelephony;->disableDataConnectivity()Z Lcom/android/internal/telephony/ITelephony;->disableLocationUpdates()V Lcom/android/internal/telephony/ITelephony;->enableDataConnectivity()Z Lcom/android/internal/telephony/ITelephony;->enableLocationUpdates()V -Lcom/android/internal/telephony/ITelephony;->endCall()Z -Lcom/android/internal/telephony/ITelephony;->endCallForSubscriber(I)Z Lcom/android/internal/telephony/ITelephony;->getActivePhoneType()I Lcom/android/internal/telephony/ITelephony;->getCallState()I Lcom/android/internal/telephony/ITelephony;->getDataActivity()I @@ -2108,12 +2101,8 @@ Lcom/android/internal/telephony/ITelephony;->handlePinMmiForSubscriber(ILjava/la Lcom/android/internal/telephony/ITelephony;->hasIccCard()Z Lcom/android/internal/telephony/ITelephony;->iccCloseLogicalChannel(II)Z Lcom/android/internal/telephony/ITelephony;->iccTransmitApduLogicalChannel(IIIIIIILjava/lang/String;)Ljava/lang/String; -Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z -Lcom/android/internal/telephony/ITelephony;->isIdleForSubscriber(ILjava/lang/String;)Z Lcom/android/internal/telephony/ITelephony;->isRadioOnForSubscriber(ILjava/lang/String;)Z -Lcom/android/internal/telephony/ITelephony;->isRinging(Ljava/lang/String;)Z Lcom/android/internal/telephony/ITelephony;->setRadio(Z)Z -Lcom/android/internal/telephony/ITelephony;->silenceRinger()V Lcom/android/internal/telephony/ITelephony;->supplyPin(Ljava/lang/String;)Z Lcom/android/internal/telephony/ITelephony;->toggleRadioOnOff()V Lcom/android/internal/telephony/ITelephony;->updateServiceLocation()V diff --git a/config/preloaded-classes b/config/preloaded-classes index d93befdf5143..56ca98ff9888 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -1022,6 +1022,7 @@ android.graphics.-$$Lambda$ColorSpace$Rgb$b9VGKuNnse0bbguR9jbOM_wK2Ac android.graphics.-$$Lambda$ColorSpace$Rgb$bWzafC8vMHNuVmRuTUPEFUMlfuY android.graphics.-$$Lambda$ColorSpace$S2rlqJvkXGTpUF6mZhvkElds8JE android.graphics.BaseCanvas +android.graphics.BaseRecordingCanvas android.graphics.Bitmap android.graphics.Bitmap$1 android.graphics.Bitmap$2 @@ -3303,7 +3304,6 @@ android.view.OrientationEventListener android.view.OrientationEventListener$SensorEventListenerImpl android.view.PointerIcon android.view.PointerIcon$1 -android.view.RecordingCanvas android.view.RenderNode android.view.RenderNode$NoImagePreloadHolder android.view.RenderNodeAnimator diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 2ee266de4c77..2acae1cf8bac 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1588,11 +1588,13 @@ public class Activity extends ContextThemeWrapper * @param outState The bundle to save the state to. */ final void performSaveInstanceState(@NonNull Bundle outState) { + getApplication().dispatchActivityPreSaveInstanceState(this, outState); onSaveInstanceState(outState); saveManagedDialogs(outState); mActivityTransitionState.saveState(outState); storeHasCurrentPermissionRequest(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState); + getApplication().dispatchActivityPostSaveInstanceState(this, outState); } /** @@ -1606,11 +1608,13 @@ public class Activity extends ContextThemeWrapper */ final void performSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) { + getApplication().dispatchActivityPreSaveInstanceState(this, outState); onSaveInstanceState(outState, outPersistentState); saveManagedDialogs(outState); storeHasCurrentPermissionRequest(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState + ", " + outPersistentState); + getApplication().dispatchActivityPostSaveInstanceState(this, outState); } /** @@ -7195,6 +7199,7 @@ public class Activity extends ContextThemeWrapper @UnsupportedAppUsage final void performCreate(Bundle icicle, PersistableBundle persistentState) { + getApplication().dispatchActivityPreCreated(this, icicle); mCanEnterPictureInPicture = true; restoreHasCurrentPermissionRequest(icicle); if (persistentState != null) { @@ -7209,6 +7214,7 @@ public class Activity extends ContextThemeWrapper com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); + getApplication().dispatchActivityPostCreated(this, icicle); } final void performNewIntent(@NonNull Intent intent) { @@ -7217,6 +7223,7 @@ public class Activity extends ContextThemeWrapper } final void performStart(String reason) { + getApplication().dispatchActivityPreStarted(this); mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); mFragments.noteStateNotSaved(); mCalled = false; @@ -7284,6 +7291,7 @@ public class Activity extends ContextThemeWrapper } mActivityTransitionState.enterReady(this); + getApplication().dispatchActivityPostStarted(this); } /** @@ -7338,6 +7346,7 @@ public class Activity extends ContextThemeWrapper } final void performResume(boolean followedByPause, String reason) { + getApplication().dispatchActivityPreResumed(this); performRestart(true /* start */, reason); mFragments.execPendingActions(); @@ -7387,9 +7396,11 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPostResume()"); } + getApplication().dispatchActivityPostResumed(this); } final void performPause() { + getApplication().dispatchActivityPrePaused(this); mDoReportFullyDrawn = false; mFragments.dispatchPause(); mCalled = false; @@ -7402,6 +7413,7 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onPause()"); } + getApplication().dispatchActivityPostPaused(this); } final void performUserLeaving() { @@ -7417,6 +7429,7 @@ public class Activity extends ContextThemeWrapper mCanEnterPictureInPicture = false; if (!mStopped) { + getApplication().dispatchActivityPreStopped(this); if (mWindow != null) { mWindow.closeAllPanels(); } @@ -7451,11 +7464,13 @@ public class Activity extends ContextThemeWrapper } mStopped = true; + getApplication().dispatchActivityPostStopped(this); } mResumed = false; } final void performDestroy() { + getApplication().dispatchActivityPreDestroyed(this); mDestroyed = true; mWindow.destroy(); mFragments.dispatchDestroy(); @@ -7465,6 +7480,7 @@ public class Activity extends ContextThemeWrapper if (mVoiceInteractor != null) { mVoiceInteractor.detachActivity(); } + getApplication().dispatchActivityPostDestroyed(this); } final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode, diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index be1f2dbc8e4f..294a3ec73efd 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -19,11 +19,14 @@ package android.app; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; +import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.UserInfo; +import android.os.Bundle; import android.os.IBinder; +import android.os.TransactionTooLargeException; import android.view.RemoteAnimationAdapter; import java.util.ArrayList; @@ -232,4 +235,14 @@ public abstract class ActivityManagerInternal { public abstract void setBooted(boolean booted); public abstract boolean isBooted(); public abstract void finishBooting(); + + public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid, + long duration, String tag); + public abstract int broadcastIntentInPackage(String packageName, int uid, Intent intent, + String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, + Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized, + boolean sticky, int userId); + public abstract ComponentName startServiceInPackage(int uid, Intent service, + String resolvedType, boolean fgRequired, String callingPackage, int userId) + throws TransactionTooLargeException; } diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index 68b745d54bc0..e12942f248d4 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -65,13 +65,144 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { public LoadedApk mLoadedApk; public interface ActivityLifecycleCallbacks { + + /** + * Called as the first step of the Activity being created. This is always called before + * {@link Activity#onCreate}. + */ + default void onActivityPreCreated(@NonNull Activity activity, + @Nullable Bundle savedInstanceState) { + } + + /** + * Called when the Activity calls {@link Activity#onCreate super.onCreate()}. + */ void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState); + + /** + * Called as the last step of the Activity being created. This is always called after + * {@link Activity#onCreate}. + */ + default void onActivityPostCreated(@NonNull Activity activity, + @Nullable Bundle savedInstanceState) { + } + + /** + * Called as the first step of the Activity being started. This is always called before + * {@link Activity#onStart}. + */ + default void onActivityPreStarted(@NonNull Activity activity) { + } + + /** + * Called when the Activity calls {@link Activity#onStart super.onStart()}. + */ void onActivityStarted(@NonNull Activity activity); + + /** + * Called as the last step of the Activity being started. This is always called after + * {@link Activity#onStart}. + */ + default void onActivityPostStarted(@NonNull Activity activity) { + } + + /** + * Called as the first step of the Activity being resumed. This is always called before + * {@link Activity#onResume}. + */ + default void onActivityPreResumed(@NonNull Activity activity) { + } + + /** + * Called when the Activity calls {@link Activity#onResume super.onResume()}. + */ void onActivityResumed(@NonNull Activity activity); + + /** + * Called as the last step of the Activity being resumed. This is always called after + * {@link Activity#onResume} and {@link Activity#onPostResume}. + */ + default void onActivityPostResumed(@NonNull Activity activity) { + } + + /** + * Called as the first step of the Activity being paused. This is always called before + * {@link Activity#onPause}. + */ + default void onActivityPrePaused(@NonNull Activity activity) { + } + + /** + * Called when the Activity calls {@link Activity#onPause super.onPause()}. + */ void onActivityPaused(@NonNull Activity activity); + + /** + * Called as the last step of the Activity being paused. This is always called after + * {@link Activity#onPause}. + */ + default void onActivityPostPaused(@NonNull Activity activity) { + } + + /** + * Called as the first step of the Activity being stopped. This is always called before + * {@link Activity#onStop}. + */ + default void onActivityPreStopped(@NonNull Activity activity) { + } + + /** + * Called when the Activity calls {@link Activity#onStop super.onStop()}. + */ void onActivityStopped(@NonNull Activity activity); + + /** + * Called as the last step of the Activity being stopped. This is always called after + * {@link Activity#onStop}. + */ + default void onActivityPostStopped(@NonNull Activity activity) { + } + + /** + * Called as the first step of the Activity saving its instance state. This is always + * called before {@link Activity#onSaveInstanceState}. + */ + default void onActivityPreSaveInstanceState(@NonNull Activity activity, + @NonNull Bundle outState) { + } + + /** + * Called when the Activity calls + * {@link Activity#onSaveInstanceState super.onSaveInstanceState()}. + */ void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState); + + /** + * Called as the last step of the Activity saving its instance state. This is always + * called after{@link Activity#onSaveInstanceState}. + */ + default void onActivityPostSaveInstanceState(@NonNull Activity activity, + @NonNull Bundle outState) { + } + + /** + * Called as the first step of the Activity being destroyed. This is always called before + * {@link Activity#onDestroy}. + */ + default void onActivityPreDestroyed(@NonNull Activity activity) { + } + + /** + * Called when the Activity calls {@link Activity#onDestroy super.onDestroy()}. + */ void onActivityDestroyed(@NonNull Activity activity); + + /** + * Called as the last step of the Activity being destroyed. This is always called after + * {@link Activity#onDestroy}. + */ + default void onActivityPostDestroyed(@NonNull Activity activity) { + } } /** @@ -222,6 +353,18 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } @UnsupportedAppUsage + /* package */ void dispatchActivityPreCreated(@NonNull Activity activity, + @Nullable Bundle savedInstanceState) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreCreated(activity, + savedInstanceState); + } + } + } + + @UnsupportedAppUsage /* package */ void dispatchActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { Object[] callbacks = collectActivityLifecycleCallbacks(); @@ -234,6 +377,28 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } @UnsupportedAppUsage + /* package */ void dispatchActivityPostCreated(@NonNull Activity activity, + @Nullable Bundle savedInstanceState) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostCreated(activity, + savedInstanceState); + } + } + } + + @UnsupportedAppUsage + /* package */ void dispatchActivityPreStarted(@NonNull Activity activity) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStarted(activity); + } + } + } + + @UnsupportedAppUsage /* package */ void dispatchActivityStarted(@NonNull Activity activity) { Object[] callbacks = collectActivityLifecycleCallbacks(); if (callbacks != null) { @@ -244,6 +409,26 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } @UnsupportedAppUsage + /* package */ void dispatchActivityPostStarted(@NonNull Activity activity) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostStarted(activity); + } + } + } + + @UnsupportedAppUsage + /* package */ void dispatchActivityPreResumed(@NonNull Activity activity) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreResumed(activity); + } + } + } + + @UnsupportedAppUsage /* package */ void dispatchActivityResumed(@NonNull Activity activity) { Object[] callbacks = collectActivityLifecycleCallbacks(); if (callbacks != null) { @@ -254,6 +439,26 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } @UnsupportedAppUsage + /* package */ void dispatchActivityPostResumed(@NonNull Activity activity) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostResumed(activity); + } + } + } + + @UnsupportedAppUsage + /* package */ void dispatchActivityPrePaused(@NonNull Activity activity) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPrePaused(activity); + } + } + } + + @UnsupportedAppUsage /* package */ void dispatchActivityPaused(@NonNull Activity activity) { Object[] callbacks = collectActivityLifecycleCallbacks(); if (callbacks != null) { @@ -264,6 +469,26 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } @UnsupportedAppUsage + /* package */ void dispatchActivityPostPaused(@NonNull Activity activity) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostPaused(activity); + } + } + } + + @UnsupportedAppUsage + /* package */ void dispatchActivityPreStopped(@NonNull Activity activity) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStopped(activity); + } + } + } + + @UnsupportedAppUsage /* package */ void dispatchActivityStopped(@NonNull Activity activity) { Object[] callbacks = collectActivityLifecycleCallbacks(); if (callbacks != null) { @@ -274,6 +499,28 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } @UnsupportedAppUsage + /* package */ void dispatchActivityPostStopped(@NonNull Activity activity) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostStopped(activity); + } + } + } + + @UnsupportedAppUsage + /* package */ void dispatchActivityPreSaveInstanceState(@NonNull Activity activity, + @NonNull Bundle outState) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreSaveInstanceState( + activity, outState); + } + } + } + + @UnsupportedAppUsage /* package */ void dispatchActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { Object[] callbacks = collectActivityLifecycleCallbacks(); @@ -286,6 +533,28 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } @UnsupportedAppUsage + /* package */ void dispatchActivityPostSaveInstanceState(@NonNull Activity activity, + @NonNull Bundle outState) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostSaveInstanceState( + activity, outState); + } + } + } + + @UnsupportedAppUsage + /* package */ void dispatchActivityPreDestroyed(@NonNull Activity activity) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPreDestroyed(activity); + } + } + } + + @UnsupportedAppUsage /* package */ void dispatchActivityDestroyed(@NonNull Activity activity) { Object[] callbacks = collectActivityLifecycleCallbacks(); if (callbacks != null) { @@ -295,6 +564,16 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } } + @UnsupportedAppUsage + /* package */ void dispatchActivityPostDestroyed(@NonNull Activity activity) { + Object[] callbacks = collectActivityLifecycleCallbacks(); + if (callbacks != null) { + for (int i = 0; i < callbacks.length; i++) { + ((ActivityLifecycleCallbacks) callbacks[i]).onActivityPostDestroyed(activity); + } + } + } + private Object[] collectComponentCallbacks() { Object[] callbacks = null; synchronized (mComponentCallbacks) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java b/core/java/android/app/ISmsAppService.aidl index c50d6d62d6e9..1ac2ec6b1c41 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java +++ b/core/java/android/app/ISmsAppService.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2018 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. @@ -11,15 +11,13 @@ * 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 + * limitations under the License. */ -package com.android.systemui.stackdivider.events; - -import com.android.systemui.recents.events.EventBus; +package android.app; /** - * Sent when the divider isn't draging anymore. + * @hide */ -public class StoppedDragingEvent extends EventBus.Event { +interface ISmsAppService { } diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java index 39db16d10575..9bfdae0d8c03 100644 --- a/core/java/android/app/ProcessMemoryState.java +++ b/core/java/android/app/ProcessMemoryState.java @@ -23,19 +23,20 @@ import android.os.Parcelable; * The memory stats for a process. * {@hide} */ -public class ProcessMemoryState implements Parcelable { - public int uid; - public String processName; - public int oomScore; - public long pgfault; - public long pgmajfault; - public long rssInBytes; - public long cacheInBytes; - public long swapInBytes; +public final class ProcessMemoryState implements Parcelable { + public final int uid; + public final String processName; + public final int oomScore; + public final long pgfault; + public final long pgmajfault; + public final long rssInBytes; + public final long cacheInBytes; + public final long swapInBytes; + public final long rssHighWatermarkInBytes; public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault, long pgmajfault, long rssInBytes, long cacheInBytes, - long swapInBytes) { + long swapInBytes, long rssHighWatermarkInBytes) { this.uid = uid; this.processName = processName; this.oomScore = oomScore; @@ -44,6 +45,7 @@ public class ProcessMemoryState implements Parcelable { this.rssInBytes = rssInBytes; this.cacheInBytes = cacheInBytes; this.swapInBytes = swapInBytes; + this.rssHighWatermarkInBytes = rssHighWatermarkInBytes; } private ProcessMemoryState(Parcel in) { @@ -55,6 +57,7 @@ public class ProcessMemoryState implements Parcelable { rssInBytes = in.readLong(); cacheInBytes = in.readLong(); swapInBytes = in.readLong(); + rssHighWatermarkInBytes = in.readLong(); } public static final Creator<ProcessMemoryState> CREATOR = new Creator<ProcessMemoryState>() { @@ -84,5 +87,6 @@ public class ProcessMemoryState implements Parcelable { parcel.writeLong(rssInBytes); parcel.writeLong(cacheInBytes); parcel.writeLong(swapInBytes); + parcel.writeLong(rssHighWatermarkInBytes); } } diff --git a/core/java/android/app/SmsAppService.java b/core/java/android/app/SmsAppService.java new file mode 100644 index 000000000000..3f2b025016df --- /dev/null +++ b/core/java/android/app/SmsAppService.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 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; + +import android.content.ComponentName; +import android.content.Intent; +import android.os.IBinder; + +/** + * If the default SMS app has a service that extends this class, the system always tries to bind + * it so that the process is always running, which allows the app to have a persistent connection + * to the server. + * + * <p>The service must have {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE} + * action in the intent handler, and be protected with + * {@link android.Manifest.permission#BIND_SMS_APP_SERVICE}. However the service does not have to + * be exported. + * + * <p>Apps can use + * {@link android.content.pm.PackageManager#setComponentEnabledSetting(ComponentName, int, int)} + * to disable/enable the service. Apps should use it to disable the service when it no longer needs + * to be running. + * + * <p>When the owner process crashes, the service will be re-bound automatically after a + * back-off. + * + * <p>Note the process may still be killed if the system is under heavy memory pressure, in which + * case the process will be re-started later. + */ +public class SmsAppService extends Service { + private final ISmsAppService mImpl; + + public SmsAppService() { + mImpl = new ISmsAppServiceImpl(); + } + + @Override + public final IBinder onBind(Intent intent) { + return mImpl.asBinder(); + } + + private class ISmsAppServiceImpl extends ISmsAppService.Stub { + } +} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 41dec8f10937..0044005c51f2 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -134,6 +134,7 @@ import android.os.UserManager; import android.os.Vibrator; import android.os.health.SystemHealthManager; import android.os.storage.StorageManager; +import android.permission.PermissionManager; import android.print.IPrintManager; import android.print.PrintManager; import android.service.oemlock.IOemLockService; @@ -1064,6 +1065,13 @@ final class SystemServiceRegistry { throws ServiceNotFoundException { return new TimeZoneDetector(); }}); + + registerService(Context.PERMISSION_SERVICE, PermissionManager.class, + new CachedServiceFetcher<PermissionManager>() { + @Override + public PermissionManager createService(ContextImpl ctx) { + return new PermissionManager(ctx.getOuterContext()); + }}); } /** diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java index 183be5f38bd1..559a59b68b4e 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -73,6 +73,8 @@ public final class BluetoothMapClient implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; + private static final int UPLOADING_FEATURE_BITMASK = 0x08; + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { public void onBluetoothStateChange(boolean up) { @@ -395,6 +397,23 @@ public final class BluetoothMapClient implements BluetoothProfile { return false; } + /** + * Returns the "Uploading" feature bit value from the SDP record's + * MapSupportedFeatures field (see Bluetooth MAP 1.4 spec, page 114). + * @param device The Bluetooth device to get this value for. + * @return Returns true if the Uploading bit value in SDP record's + * MapSupportedFeatures field is set. False is returned otherwise. + */ + public boolean isUploadingSupported(BluetoothDevice device) { + try { + return (mService != null && isEnabled() && isValidDevice(device)) + && ((mService.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage()); + } + return false; + } + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index bd1e6a462805..a64eead04c6f 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -51,6 +51,8 @@ import android.os.storage.StorageManager; import android.text.TextUtils; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -58,6 +60,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Objects; /** * Content providers are one of the primary building blocks of Android applications, providing @@ -99,6 +102,7 @@ import java.util.Arrays; * <p>For more information about using content providers, read the * <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> * developer guide.</p> + * </div> */ public abstract class ContentProvider implements ComponentCallbacks2 { @@ -220,7 +224,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) { - validateIncomingUri(uri); + uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { // The caller has no access to the data, so return an empty cursor with @@ -268,7 +272,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public String getType(Uri uri) { // getCallingPackage() isn't available in getType(), as the javadoc states. - validateIncomingUri(uri); + uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); Trace.traceBegin(TRACE_TAG_DATABASE, "getType"); try { @@ -280,7 +284,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) { - validateIncomingUri(uri); + uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); uri = maybeGetUriWithoutUserId(uri); if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { @@ -303,7 +307,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) { - validateIncomingUri(uri); + uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { return 0; @@ -327,11 +331,12 @@ public abstract class ContentProvider implements ComponentCallbacks2 { for (int i = 0; i < numOperations; i++) { ContentProviderOperation operation = operations.get(i); Uri uri = operation.getUri(); - validateIncomingUri(uri); userIds[i] = getUserIdFromUri(uri); - if (userIds[i] != UserHandle.USER_CURRENT) { - // Removing the user id from the uri. - operation = new ContentProviderOperation(operation, true); + uri = validateIncomingUri(uri); + uri = maybeGetUriWithoutUserId(uri); + // Rebuild operation if we changed the Uri above + if (!Objects.equals(operation.getUri(), uri)) { + operation = new ContentProviderOperation(operation, uri); operations.set(i, operation); } if (operation.isReadOperation()) { @@ -368,7 +373,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) { - validateIncomingUri(uri); + uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { return 0; @@ -386,7 +391,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public int update(String callingPkg, Uri uri, ContentValues values, String selection, String[] selectionArgs) { - validateIncomingUri(uri); + uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { return 0; @@ -405,7 +410,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { public ParcelFileDescriptor openFile( String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken) throws FileNotFoundException { - validateIncomingUri(uri); + uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); enforceFilePermission(callingPkg, uri, mode, callerToken); Trace.traceBegin(TRACE_TAG_DATABASE, "openFile"); @@ -423,7 +428,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { public AssetFileDescriptor openAssetFile( String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal) throws FileNotFoundException { - validateIncomingUri(uri); + uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); enforceFilePermission(callingPkg, uri, mode, null); Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile"); @@ -454,7 +459,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public String[] getStreamTypes(Uri uri, String mimeTypeFilter) { // getCallingPackage() isn't available in getType(), as the javadoc states. - validateIncomingUri(uri); + uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); Trace.traceBegin(TRACE_TAG_DATABASE, "getStreamTypes"); try { @@ -468,7 +473,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType, Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException { Bundle.setDefusable(opts, true); - validateIncomingUri(uri); + uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); enforceFilePermission(callingPkg, uri, "r", null); Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile"); @@ -489,7 +494,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public Uri canonicalize(String callingPkg, Uri uri) { - validateIncomingUri(uri); + uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); uri = getUriWithoutUserId(uri); if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { @@ -507,7 +512,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public Uri uncanonicalize(String callingPkg, Uri uri) { - validateIncomingUri(uri); + uri = validateIncomingUri(uri); int userId = getUserIdFromUri(uri); uri = getUriWithoutUserId(uri); if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { @@ -526,7 +531,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public boolean refresh(String callingPkg, Uri uri, Bundle args, ICancellationSignal cancellationSignal) throws RemoteException { - validateIncomingUri(uri); + uri = validateIncomingUri(uri); uri = getUriWithoutUserId(uri); if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { return false; @@ -1965,7 +1970,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { */ if (mContext == null) { mContext = context; - if (context != null) { + if (context != null && mTransport != null) { mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService( Context.APP_OPS_SERVICE); } @@ -2074,7 +2079,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } /** @hide */ - private void validateIncomingUri(Uri uri) throws SecurityException { + @VisibleForTesting + public Uri validateIncomingUri(Uri uri) throws SecurityException { String auth = uri.getAuthority(); if (!mSingleUser) { int userId = getUserIdFromAuthority(auth, UserHandle.USER_CURRENT); @@ -2093,6 +2099,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } throw new SecurityException(message); } + + // Normalize the path by removing any empty path segments, which can be + // a source of security issues. + final String encodedPath = uri.getEncodedPath(); + if (encodedPath != null && encodedPath.indexOf("//") != -1) { + return uri.buildUpon().encodedPath(encodedPath.replaceAll("//+", "/")).build(); + } else { + return uri; + } } /** @hide */ diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java index e3d9b1931faa..7dc45776715c 100644 --- a/core/java/android/content/ContentProviderOperation.java +++ b/core/java/android/content/ContentProviderOperation.java @@ -101,13 +101,9 @@ public class ContentProviderOperation implements Parcelable { } /** @hide */ - public ContentProviderOperation(ContentProviderOperation cpo, boolean removeUserIdFromUri) { + public ContentProviderOperation(ContentProviderOperation cpo, Uri withUri) { mType = cpo.mType; - if (removeUserIdFromUri) { - mUri = ContentProvider.getUriWithoutUserId(cpo.mUri); - } else { - mUri = cpo.mUri; - } + mUri = withUri; mValues = cpo.mValues; mSelection = cpo.mSelection; mSelectionArgs = cpo.mSelectionArgs; @@ -117,14 +113,6 @@ public class ContentProviderOperation implements Parcelable { mYieldAllowed = cpo.mYieldAllowed; } - /** @hide */ - public ContentProviderOperation getWithoutUserIdInUri() { - if (ContentProvider.uriHasUserId(mUri)) { - return new ContentProviderOperation(this, true); - } - return this; - } - public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); Uri.writeToParcel(dest, mUri); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 981be833f3c0..d71157459fc8 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3088,6 +3088,7 @@ public abstract class Context { //@hide: SYSTEM_UPDATE_SERVICE, //@hide: TIME_DETECTOR_SERVICE, //@hide: TIME_ZONE_DETECTOR_SERVICE, + PERMISSION_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -3860,6 +3861,14 @@ public abstract class Context { */ public static final String SOUND_TRIGGER_SERVICE = "soundtrigger"; + /** + * Official published name of the (internal) permission service. + * + * @see #getSystemService(String) + * @hide + */ + @SystemApi + public static final String PERMISSION_SERVICE = "permission"; /** * Use with {@link #getSystemService(String)} to retrieve an @@ -4302,6 +4311,12 @@ public abstract class Context { public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector"; /** + * Binder service name for {@link AppBindingService}. + * @hide + */ + public static final String APP_BINDING_SERVICE = "app_binding"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 03a3d1f438ff..1fa5190ef8df 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -40,7 +40,6 @@ import static android.os.Build.VERSION_CODES.O; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; -import android.Manifest; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -73,6 +72,7 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.storage.StorageManager; +import android.permission.PermissionManager; import android.system.ErrnoException; import android.system.OsConstants; import android.system.StructStat; @@ -258,19 +258,6 @@ public class PackageParser { } } - /** @hide */ - public static class SplitPermissionInfo { - public final String rootPerm; - public final String[] newPerms; - public final int targetSdk; - - public SplitPermissionInfo(String rootPerm, String[] newPerms, int targetSdk) { - this.rootPerm = rootPerm; - this.newPerms = newPerms; - this.targetSdk = targetSdk; - } - } - /** * List of new permissions that have been added since 1.0. * NOTE: These must be declared in SDK version order, with permissions @@ -290,34 +277,6 @@ public class PackageParser { }; /** - * List of permissions that have been split into more granular or dependent - * permissions. - * @hide - */ - public static final PackageParser.SplitPermissionInfo SPLIT_PERMISSIONS[] = - new PackageParser.SplitPermissionInfo[] { - // READ_EXTERNAL_STORAGE is always required when an app requests - // WRITE_EXTERNAL_STORAGE, because we can't have an app that has - // write access without read access. The hack here with the target - // target SDK version ensures that this grant is always done. - new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, - new String[] { android.Manifest.permission.READ_EXTERNAL_STORAGE }, - android.os.Build.VERSION_CODES.CUR_DEVELOPMENT+1), - new PackageParser.SplitPermissionInfo(android.Manifest.permission.READ_CONTACTS, - new String[] { android.Manifest.permission.READ_CALL_LOG }, - android.os.Build.VERSION_CODES.JELLY_BEAN), - new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_CONTACTS, - new String[] { android.Manifest.permission.WRITE_CALL_LOG }, - android.os.Build.VERSION_CODES.JELLY_BEAN), - new PackageParser.SplitPermissionInfo(Manifest.permission.ACCESS_FINE_LOCATION, - new String[] { android.Manifest.permission.ACCESS_BACKGROUND_LOCATION }, - android.os.Build.VERSION_CODES.P0), - new PackageParser.SplitPermissionInfo(Manifest.permission.ACCESS_COARSE_LOCATION, - new String[] { android.Manifest.permission.ACCESS_BACKGROUND_LOCATION }, - android.os.Build.VERSION_CODES.P0), - }; - - /** * @deprecated callers should move to explicitly passing around source path. */ @Deprecated @@ -2474,16 +2433,18 @@ public class PackageParser { Slog.i(TAG, implicitPerms.toString()); } - final int NS = PackageParser.SPLIT_PERMISSIONS.length; + + final int NS = PermissionManager.SPLIT_PERMISSIONS.length; for (int is=0; is<NS; is++) { - final PackageParser.SplitPermissionInfo spi - = PackageParser.SPLIT_PERMISSIONS[is]; - if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk - || !pkg.requestedPermissions.contains(spi.rootPerm)) { + final PermissionManager.SplitPermissionInfo spi = + PermissionManager.SPLIT_PERMISSIONS[is]; + if (pkg.applicationInfo.targetSdkVersion >= spi.getTargetSdk() + || !pkg.requestedPermissions.contains(spi.getRootPermission())) { continue; } - for (int in=0; in<spi.newPerms.length; in++) { - final String perm = spi.newPerms[in]; + final String[] newPerms = spi.getNewPermissions(); + for (int in = 0; in < newPerms.length; in++) { + final String perm = newPerms[in]; if (!pkg.requestedPermissions.contains(perm)) { pkg.requestedPermissions.add(perm); } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index d967fbaf610c..7810e6c19abe 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -1889,7 +1889,7 @@ public class CameraDeviceImpl extends CameraDevice final long ident = Binder.clearCallingIdentity(); try { CameraDeviceImpl.this.mDeviceExecutor.execute(obtainRunnable( - CameraDeviceCallbacks::notifyError, this, code)); + CameraDeviceCallbacks::notifyError, this, code).recycleOnUse()); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index b94840279cea..ae12f93285a8 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -410,14 +410,6 @@ public class InputMethodService extends AbstractInputMethodService { @GuardedBy("mLock") private boolean mNotifyUserActionSent; - /** - * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we - * have not shown our own window yet. In this situation, the previous inset continues to be - * shown as an empty region until it is explicitly updated. Basically we can trigger the update - * by calling 1) {@code mWindow.show()} or 2) {@link #clearInsetOfPreviousIme()}. - */ - boolean mShouldClearInsetOfPreviousIme; - @UnsupportedAppUsage final Insets mTmpInsets = new Insets(); final int[] mTmpLocation = new int[2]; @@ -581,7 +573,6 @@ public class InputMethodService extends AbstractInputMethodService { mShowInputFlags = 0; mShowInputRequested = false; doHideWindow(); - clearInsetOfPreviousIme(); if (resultReceiver != null) { resultReceiver.send(wasVis != isInputViewShown() ? InputMethodManager.RESULT_HIDDEN @@ -601,7 +592,6 @@ public class InputMethodService extends AbstractInputMethodService { if (dispatchOnShowInputRequested(flags, false)) { showWindow(true); } - clearInsetOfPreviousIme(); // If user uses hard keyboard, IME button should always be shown. setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition); @@ -946,9 +936,6 @@ public class InputMethodService extends AbstractInputMethodService { super.onCreate(); mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); mSettingsObserver = SettingsObserver.createAndRegister(this); - // If the previous IME has occupied non-empty inset in the screen, we need to decide whether - // we continue to use the same size of the inset or update it - mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0); // TODO(b/111364446) Need to address context lifecycle issue if need to re-create // for update resources & configuration correctly when show soft input // in non-default display. @@ -1882,9 +1869,6 @@ public class InputMethodService extends AbstractInputMethodService { if (DEBUG) Log.v(TAG, "showWindow: showing!"); onWindowShown(); mWindow.show(); - // Put here rather than in onWindowShown() in case people forget to call - // super.onWindowShown(). - mShouldClearInsetOfPreviousIme = false; } } @@ -1934,32 +1918,6 @@ public class InputMethodService extends AbstractInputMethodService { } /** - * Reset the inset occupied the previous IME when and only when - * {@link #mShouldClearInsetOfPreviousIme} is {@code true}. - */ - private void clearInsetOfPreviousIme() { - if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme() " - + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme); - if (!mShouldClearInsetOfPreviousIme) return; - - clearLastInputMethodWindowForTransition(); - mShouldClearInsetOfPreviousIme = false; - } - - /** - * Tells the system that the IME decided to not show a window and the system no longer needs to - * use the previous IME's inset. - * - * <p>Caveat: {@link android.inputmethodservice.InputMethodService#clearInsetOfPreviousIme()} - * is the only expected caller of this method. Do not depend on this anywhere else.</p> - * - * <p>TODO: We probably need to reconsider how IME should be handled.</p> - */ - private void clearLastInputMethodWindowForTransition() { - mPrivOps.clearLastInputMethodWindowForTransition(); - } - - /** * Called when a new client has bound to the input method. This * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)} * and {@link #onFinishInput()} calls as the user navigates through its @@ -2980,7 +2938,6 @@ public class InputMethodService extends AbstractInputMethodService { + " visibleTopInsets=" + mTmpInsets.visibleTopInsets + " touchableInsets=" + mTmpInsets.touchableInsets + " touchableRegion=" + mTmpInsets.touchableRegion); - p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme); p.println(" mSettingsObserver=" + mSettingsObserver); } } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java new file mode 100644 index 000000000000..aa44eb7e3a26 --- /dev/null +++ b/core/java/android/permission/PermissionManager.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2018 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.permission; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; + +import com.android.internal.annotations.Immutable; + +import java.util.Arrays; +import java.util.List; + +/** + * System level service for accessing the permission capabilities of the platform. + * + * @hide + */ +@SystemApi +@SystemService(Context.PERMISSION_SERVICE) +public final class PermissionManager { + /** + * {@link android.content.pm.PackageParser} needs access without having a {@link Context}. + * + * @hide + */ + public static final SplitPermissionInfo[] SPLIT_PERMISSIONS = new SplitPermissionInfo[]{ + // READ_EXTERNAL_STORAGE is always required when an app requests + // WRITE_EXTERNAL_STORAGE, because we can't have an app that has + // write access without read access. The hack here with the target + // target SDK version ensures that this grant is always done. + new SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, + new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE}, + android.os.Build.VERSION_CODES.CUR_DEVELOPMENT + 1), + new SplitPermissionInfo(android.Manifest.permission.READ_CONTACTS, + new String[]{android.Manifest.permission.READ_CALL_LOG}, + android.os.Build.VERSION_CODES.JELLY_BEAN), + new SplitPermissionInfo(android.Manifest.permission.WRITE_CONTACTS, + new String[]{android.Manifest.permission.WRITE_CALL_LOG}, + android.os.Build.VERSION_CODES.JELLY_BEAN), + new SplitPermissionInfo(Manifest.permission.ACCESS_FINE_LOCATION, + new String[]{android.Manifest.permission.ACCESS_BACKGROUND_LOCATION}, + android.os.Build.VERSION_CODES.P0), + new SplitPermissionInfo(Manifest.permission.ACCESS_COARSE_LOCATION, + new String[]{android.Manifest.permission.ACCESS_BACKGROUND_LOCATION}, + android.os.Build.VERSION_CODES.P0)}; + + private final @NonNull Context mContext; + + /** + * Creates a new instance. + * + * @param context The current context in which to operate. + * @hide + */ + public PermissionManager(@NonNull Context context) { + mContext = context; + } + + /** + * Get list of permissions that have been split into more granular or dependent permissions. + * + * <p>E.g. before {@link android.os.Build.VERSION_CODES#P0} an app that was granted + * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access he location while it was in + * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#P0} + * the location permission only grants location access while the app is in foreground. This + * would break apps that target before {@link android.os.Build.VERSION_CODES#P0}. Hence whenever + * such an old app asks for a location permission (i.e. the + * {@link SplitPermissionInfo#getRootPermission()}), then the + * {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside + * {@{@link SplitPermissionInfo#getNewPermissions}) is added. + * + * <p>Note: Regular apps do not have to worry about this. The platform and permission controller + * automatically add the new permissions where needed. + * + * @return All permissions that are split. + */ + public @NonNull List<SplitPermissionInfo> getSplitPermissions() { + return Arrays.asList(SPLIT_PERMISSIONS); + } + + /** + * A permission that was added in a previous API level might have split into several + * permissions. This object describes one such split. + */ + @Immutable + public static final class SplitPermissionInfo { + private final @NonNull String mRootPerm; + private final @NonNull String[] mNewPerms; + private final int mTargetSdk; + + /** + * Get the permission that is split. + */ + public @NonNull String getRootPermission() { + return mRootPerm; + } + + /** + * Get the permissions that are added. + */ + public @NonNull String[] getNewPermissions() { + return mNewPerms; + } + + /** + * Get the target API level when the permission was split. + */ + public int getTargetSdk() { + return mTargetSdk; + } + + private SplitPermissionInfo(@NonNull String rootPerm, @NonNull String[] newPerms, + int targetSdk) { + mRootPerm = rootPerm; + mNewPerms = newPerms; + mTargetSdk = targetSdk; + } + } +} diff --git a/core/java/android/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java index 119f5d0de0a0..4f4d62af679c 100644 --- a/core/java/android/security/net/config/DirectoryCertificateSource.java +++ b/core/java/android/security/net/config/DirectoryCertificateSource.java @@ -16,26 +16,23 @@ package android.security.net.config; -import android.os.Environment; -import android.os.UserHandle; import android.util.ArraySet; import android.util.Log; -import android.util.Pair; + +import libcore.io.IoUtils; + import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; -import java.io.InputStream; import java.io.IOException; -import java.security.cert.Certificate; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Collections; import java.util.Set; -import libcore.io.IoUtils; - -import com.android.org.conscrypt.Hex; -import com.android.org.conscrypt.NativeCrypto; import javax.security.auth.x500.X500Principal; @@ -192,8 +189,36 @@ abstract class DirectoryCertificateSource implements CertificateSource { } private String getHash(X500Principal name) { - int hash = NativeCrypto.X509_NAME_hash_old(name); - return Hex.intToHexString(hash, 8); + int hash = hashName(name); + return intToHexString(hash, 8); + } + + private static final char[] DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + private static String intToHexString(int i, int minWidth) { + int bufLen = 8; // Max number of hex digits in an int + char[] buf = new char[bufLen]; + int cursor = bufLen; + + do { + buf[--cursor] = DIGITS[i & 0xf]; + } while ((i >>>= 4) != 0 || (bufLen - cursor < minWidth)); + + return new String(buf, cursor, bufLen - cursor); + } + + // This code matches the code in X509_NAME_hash_old() in Conscrypt, which in turn matches + // the names of certificate files. It must be kept in sync. + private static int hashName(X500Principal principal) { + try { + byte[] digest = MessageDigest.getInstance("MD5").digest(principal.getEncoded()); + int offset = 0; + return (((digest[offset++] & 0xff) << 0) | ((digest[offset++] & 0xff) << 8) + | ((digest[offset++] & 0xff) << 16) | ((digest[offset] & 0xff) << 24)); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } } private X509Certificate readCertificate(String file) { diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 26223f7bc6cd..163e3d5fab56 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -115,6 +115,12 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall */ public static final int SHOW_SOURCE_PUSH_TO_TALK = 1 << 5; + /** + * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked + * from a notification. + */ + public static final int SHOW_SOURCE_NOTIFICATION = 1 << 6; + final Context mContext; final HandlerCaller mHandlerCaller; diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index b49a949a36e7..44dfd115f7f2 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -1230,7 +1230,7 @@ public class TextLine { // with the next chunk. So we just save the TextPaint for future comparisons // and use. activePaint.set(wp); - } else if (!wp.hasEqualAttributes(activePaint)) { + } else if (!equalAttributes(wp, activePaint)) { // The style of the present chunk of text is substantially different from the // style of the previous chunk. We need to handle the active piece of text // and restart with the present chunk. @@ -1335,4 +1335,42 @@ public class TextLine { } private static final int TAB_INCREMENT = 20; + + private static boolean equalAttributes(@NonNull TextPaint lp, @NonNull TextPaint rp) { + return lp.getColorFilter() == rp.getColorFilter() + && lp.getMaskFilter() == rp.getMaskFilter() + && lp.getShader() == rp.getShader() + && lp.getTypeface() == rp.getTypeface() + && lp.getXfermode() == rp.getXfermode() + && lp.getTextLocales().equals(rp.getTextLocales()) + && TextUtils.equals(lp.getFontFeatureSettings(), rp.getFontFeatureSettings()) + && TextUtils.equals(lp.getFontVariationSettings(), rp.getFontVariationSettings()) + && lp.getShadowLayerRadius() == rp.getShadowLayerRadius() + && lp.getShadowLayerDx() == rp.getShadowLayerDx() + && lp.getShadowLayerDy() == rp.getShadowLayerDy() + && lp.getShadowLayerColor() == rp.getShadowLayerColor() + && lp.getFlags() == rp.getFlags() + && lp.getHinting() == rp.getHinting() + && lp.getStyle() == rp.getStyle() + && lp.getColor() == rp.getColor() + && lp.getStrokeWidth() == rp.getStrokeWidth() + && lp.getStrokeMiter() == rp.getStrokeMiter() + && lp.getStrokeCap() == rp.getStrokeCap() + && lp.getStrokeJoin() == rp.getStrokeJoin() + && lp.getTextAlign() == rp.getTextAlign() + && lp.isElegantTextHeight() == rp.isElegantTextHeight() + && lp.getTextSize() == rp.getTextSize() + && lp.getTextScaleX() == rp.getTextScaleX() + && lp.getTextSkewX() == rp.getTextSkewX() + && lp.getLetterSpacing() == rp.getLetterSpacing() + && lp.getWordSpacing() == rp.getWordSpacing() + && lp.getHyphenEdit() == rp.getHyphenEdit() + && lp.bgColor == rp.bgColor + && lp.baselineShift == rp.baselineShift + && lp.linkColor == rp.linkColor + && lp.drawableState == rp.drawableState + && lp.density == rp.density + && lp.underlineColor == rp.underlineColor + && lp.underlineThickness == rp.underlineThickness; + } } diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java index 7bcc6859b8fc..d5aad33a85e7 100644 --- a/core/java/android/text/TextPaint.java +++ b/core/java/android/text/TextPaint.java @@ -17,7 +17,7 @@ package android.text; import android.annotation.ColorInt; -import android.annotation.NonNull; +import android.annotation.Px; import android.annotation.UnsupportedAppUsage; import android.graphics.Paint; @@ -37,17 +37,14 @@ public class TextPaint extends Paint { public float density = 1.0f; /** * Special value 0 means no custom underline - * @hide */ @ColorInt - @UnsupportedAppUsage public int underlineColor = 0; + /** * Thickness of the underline, in pixels. - * @hide */ - @UnsupportedAppUsage - public float underlineThickness; + public @Px float underlineThickness; public TextPaint() { super(); @@ -78,24 +75,6 @@ public class TextPaint extends Paint { } /** - * Returns true if all attributes, including the attributes inherited from Paint, are equal. - * - * The caller is expected to have checked the trivial cases, like the pointers being equal, - * the objects having different classes, or the parameter being null. - * @hide - */ - public boolean hasEqualAttributes(@NonNull TextPaint other) { - return bgColor == other.bgColor - && baselineShift == other.baselineShift - && linkColor == other.linkColor - && drawableState == other.drawableState - && density == other.density - && underlineColor == other.underlineColor - && underlineThickness == other.underlineThickness - && super.hasEqualAttributes((Paint) other); - } - - /** * Defines a custom underline for this Paint. * @param color underline solid color * @param thickness underline thickness diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index f8e8762ef592..c4ef77a3e7ae 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -189,6 +189,8 @@ public class TransitionManager { final ViewGroup sceneRoot = scene.getSceneRoot(); if (!sPendingTransitions.contains(sceneRoot)) { if (transition == null) { + exitPreviousScene(sceneRoot); + scene.enter(); } else { sPendingTransitions.add(sceneRoot); @@ -210,6 +212,14 @@ public class TransitionManager { } } + private static void exitPreviousScene(final ViewGroup sceneRoot) { + // Notify previous scene that it is being exited + final Scene previousScene = Scene.getCurrentScene(sceneRoot); + if (previousScene != null) { + previousScene.exit(); + } + } + @UnsupportedAppUsage private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() { WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions = @@ -339,11 +349,7 @@ public class TransitionManager { transition.captureValues(sceneRoot, true); } - // Notify previous scene that it is being exited - Scene previousScene = Scene.getCurrentScene(sceneRoot); - if (previousScene != null) { - previousScene.exit(); - } + exitPreviousScene(sceneRoot); } /** diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java index 553511d73670..edd09f8f73c4 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/ApkVerityBuilder.java @@ -40,7 +40,7 @@ import java.util.ArrayList; * * @hide */ -abstract class ApkVerityBuilder { +public abstract class ApkVerityBuilder { private ApkVerityBuilder() {} private static final int CHUNK_SIZE_BYTES = 4096; // Typical Linux block size @@ -51,12 +51,18 @@ abstract class ApkVerityBuilder { private static final String JCA_DIGEST_ALGORITHM = "SHA-256"; private static final byte[] DEFAULT_SALT = new byte[8]; - static class ApkVerityResult { + /** Result generated by the builder. */ + public static class ApkVerityResult { + /** Raw fs-verity metadata and Merkle tree ready to be deployed on disk. */ public final ByteBuffer verityData; + + /** Size of the Merkle tree in {@code verityData}. */ public final int merkleTreeSize; + + /** Root hash of the Merkle tree. */ public final byte[] rootHash; - ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) { + private ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) { this.verityData = verityData; this.merkleTreeSize = merkleTreeSize; this.rootHash = rootHash; @@ -65,19 +71,47 @@ abstract class ApkVerityBuilder { /** * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link - * ByteBuffer} created by the {@link ByteBufferFactory}. The Merkle tree is suitable to be used - * as the on-disk format for apk-verity. + * ByteBuffer} created by the {@link ByteBufferFactory}. The output is suitable to be used as + * the on-disk format for fs-verity to use. * * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the * front, the tree size, and the calculated root hash. */ @NonNull - static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk, + public static ApkVerityResult generateFsVerityTree(@NonNull RandomAccessFile apk, + @NonNull ByteBufferFactory bufferFactory) + throws IOException, SecurityException, NoSuchAlgorithmException, DigestException { + return generateVerityTree(apk, bufferFactory, null /* signatureInfo */, + false /* skipSigningBlock */); + } + + /** + * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link + * ByteBuffer} created by the {@link ByteBufferFactory}. The Merkle tree does not cover Signing + * Block specificed in {@code signatureInfo}. The output is suitable to be used as the on-disk + * format for fs-verity to use (with elide and patch extensions). + * + * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the + * front, the tree size, and the calculated root hash. + */ + @NonNull + public static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk, @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory) throws IOException, SecurityException, NoSuchAlgorithmException, DigestException { - long signingBlockSize = - signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset; - long dataSize = apk.length() - signingBlockSize; + return generateVerityTree(apk, bufferFactory, signatureInfo, true /* skipSigningBlock */); + } + + @NonNull + private static ApkVerityResult generateVerityTree(@NonNull RandomAccessFile apk, + @NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo, + boolean skipSigningBlock) + throws IOException, SecurityException, NoSuchAlgorithmException, DigestException { + long dataSize = apk.length(); + if (skipSigningBlock) { + long signingBlockSize = + signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset; + dataSize -= signingBlockSize; + } int[] levelOffset = calculateVerityLevelOffset(dataSize); int merkleTreeSize = levelOffset[levelOffset.length - 1]; @@ -85,10 +119,11 @@ abstract class ApkVerityBuilder { merkleTreeSize + CHUNK_SIZE_BYTES); // maximum size of apk-verity metadata output.order(ByteOrder.LITTLE_ENDIAN); - ByteBuffer tree = slice(output, 0, merkleTreeSize); - byte[] apkRootHash = generateApkVerityTreeInternal(apk, signatureInfo, DEFAULT_SALT, - levelOffset, tree); + // Only use default salt in legacy case. + byte[] salt = skipSigningBlock ? DEFAULT_SALT : null; + byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, salt, levelOffset, + tree, skipSigningBlock); return new ApkVerityResult(output, merkleTreeSize, apkRootHash); } @@ -138,7 +173,8 @@ abstract class ApkVerityBuilder { throws IOException, SignatureNotFoundException, SecurityException, DigestException, NoSuchAlgorithmException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { - ApkVerityResult result = generateApkVerityTree(apk, signatureInfo, bufferFactory); + ApkVerityResult result = generateVerityTree(apk, bufferFactory, signatureInfo, + true /* skipSigningBlock */); ByteBuffer footer = slice(result.verityData, result.merkleTreeSize, result.verityData.limit()); generateApkVerityFooter(apk, signatureInfo, footer); @@ -170,11 +206,14 @@ abstract class ApkVerityBuilder { private final byte[] mDigestBuffer = new byte[DIGEST_SIZE_BYTES]; private final byte[] mSalt; - private BufferedDigester(byte[] salt, ByteBuffer output) throws NoSuchAlgorithmException { + private BufferedDigester(@Nullable byte[] salt, @NonNull ByteBuffer output) + throws NoSuchAlgorithmException { mSalt = salt; mOutput = output.slice(); mMd = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM); - mMd.update(mSalt); + if (mSalt != null) { + mMd.update(mSalt); + } mBytesDigestedSinceReset = 0; } @@ -201,7 +240,9 @@ abstract class ApkVerityBuilder { mMd.digest(mDigestBuffer, 0, mDigestBuffer.length); mOutput.put(mDigestBuffer); // After digest, MessageDigest resets automatically, so no need to reset again. - mMd.update(mSalt); + if (mSalt != null) { + mMd.update(mSalt); + } mBytesDigestedSinceReset = 0; } } @@ -242,6 +283,26 @@ abstract class ApkVerityBuilder { // thus the syscall overhead is not too big. private static final int MMAP_REGION_SIZE_BYTES = 1024 * 1024; + private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file, ByteBuffer output) + throws IOException, NoSuchAlgorithmException, DigestException { + BufferedDigester digester = new BufferedDigester(null /* salt */, output); + + // 1. Digest the whole file by chunks. + consumeByChunk(digester, + new MemoryMappedFileDataSource(file.getFD(), 0, file.length()), + MMAP_REGION_SIZE_BYTES); + + // 2. Pad 0s up to the nearest 4096-byte block before hashing. + int lastIncompleteChunkSize = (int) (file.length() % CHUNK_SIZE_BYTES); + if (lastIncompleteChunkSize != 0) { + digester.consume(ByteBuffer.allocate(CHUNK_SIZE_BYTES - lastIncompleteChunkSize)); + } + digester.assertEmptyBuffer(); + + // 3. Fill up the rest of buffer with 0s. + digester.fillUpLastOutputChunk(); + } + private static void generateApkVerityDigestAtLeafLevel(RandomAccessFile apk, SignatureInfo signatureInfo, byte[] salt, ByteBuffer output) throws IOException, NoSuchAlgorithmException, DigestException { @@ -288,15 +349,19 @@ abstract class ApkVerityBuilder { } @NonNull - private static byte[] generateApkVerityTreeInternal(@NonNull RandomAccessFile apk, - @Nullable SignatureInfo signatureInfo, @NonNull byte[] salt, - @NonNull int[] levelOffset, @NonNull ByteBuffer output) + private static byte[] generateVerityTreeInternal(@NonNull RandomAccessFile apk, + @Nullable SignatureInfo signatureInfo, @Nullable byte[] salt, + @NonNull int[] levelOffset, @NonNull ByteBuffer output, boolean skipSigningBlock) throws IOException, NoSuchAlgorithmException, DigestException { - assertSigningBlockAlignedAndHasFullPages(signatureInfo); - // 1. Digest the apk to generate the leaf level hashes. - generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output, - levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1])); + if (skipSigningBlock) { + assertSigningBlockAlignedAndHasFullPages(signatureInfo); + generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output, + levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1])); + } else { + generateFsVerityDigestAtLeafLevel(apk, slice(output, + levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1])); + } // 2. Digest the lower level hashes bottom up. for (int level = levelOffset.length - 3; level >= 0; level--) { @@ -434,10 +499,11 @@ abstract class ApkVerityBuilder { return levelOffset; } - private static void assertSigningBlockAlignedAndHasFullPages(SignatureInfo signatureInfo) { + private static void assertSigningBlockAlignedAndHasFullPages( + @NonNull SignatureInfo signatureInfo) { if (signatureInfo.apkSigningBlockOffset % CHUNK_SIZE_BYTES != 0) { throw new IllegalArgumentException( - "APK Signing Block does not start at the page boundary: " + "APK Signing Block does not start at the page boundary: " + signatureInfo.apkSigningBlockOffset); } diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java index 4b946d7f430f..667fab5537c9 100644 --- a/core/java/android/view/DisplayListCanvas.java +++ b/core/java/android/view/DisplayListCanvas.java @@ -19,6 +19,7 @@ package android.view; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; +import android.graphics.BaseRecordingCanvas; import android.graphics.Bitmap; import android.graphics.CanvasProperty; import android.graphics.Paint; @@ -35,7 +36,7 @@ import dalvik.annotation.optimization.FastNative; * * @hide */ -public final class DisplayListCanvas extends RecordingCanvas { +public final class DisplayListCanvas extends BaseRecordingCanvas { // The recording canvas pool should be large enough to handle a deeply nested // view hierarchy because display lists are generated recursively. private static final int POOL_LIMIT = 25; diff --git a/core/java/android/view/NativeVectorDrawableAnimator.java b/core/java/android/view/NativeVectorDrawableAnimator.java new file mode 100644 index 000000000000..b0556a3f8a91 --- /dev/null +++ b/core/java/android/view/NativeVectorDrawableAnimator.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 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.view; + +/** + * Exists just to allow for android.graphics & android.view package separation + * + * TODO: Get off of this coupling more cleanly somehow + * + * @hide + */ +public interface NativeVectorDrawableAnimator { + /** @hide */ + long getAnimatorNativePtr(); +} diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 982e5c2a6924..8ae912762fdb 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -24,7 +24,6 @@ import android.graphics.Matrix; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.drawable.AnimatedVectorDrawable; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; @@ -148,12 +147,12 @@ public class RenderNode { * @hide */ final long mNativeRenderNode; - private final View mOwningView; + private final AnimationHost mAnimationHost; - private RenderNode(String name, View owningView) { + private RenderNode(String name, AnimationHost animationHost) { mNativeRenderNode = nCreate(name); NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode); - mOwningView = owningView; + mAnimationHost = animationHost; } /** @@ -162,7 +161,7 @@ public class RenderNode { private RenderNode(long nativePtr) { mNativeRenderNode = nativePtr; NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode); - mOwningView = null; + mAnimationHost = null; } /** @@ -174,8 +173,8 @@ public class RenderNode { * @return A new RenderNode. */ @UnsupportedAppUsage - public static RenderNode create(String name, @Nullable View owningView) { - return new RenderNode(name, owningView); + public static RenderNode create(String name, @Nullable AnimationHost animationHost) { + return new RenderNode(name, animationHost); } /** @@ -189,10 +188,37 @@ public class RenderNode { } /** + * Listens for RenderNode position updates for synchronous window movement. + * + * This is not suitable for generic position listening, it is only designed & intended + * for use by things which require external position events like SurfaceView, PopupWindow, etc.. + * + * @hide + */ + interface PositionUpdateListener { + + /** + * Called by native by a Rendering Worker thread to update window position + * + * @hide + */ + void positionChanged(long frameNumber, int left, int top, int right, int bottom); + + /** + * Called by native on RenderThread to notify that the view is no longer in the + * draw tree. UI thread is blocked at this point. + * + * @hide + */ + void positionLost(long frameNumber); + + } + + /** * Enable callbacks for position changes. */ - public void requestPositionUpdates(SurfaceView view) { - nRequestPositionUpdates(mNativeRenderNode, view); + public void requestPositionUpdates(PositionUpdateListener listener) { + nRequestPositionUpdates(mNativeRenderNode, listener); } @@ -873,26 +899,42 @@ public class RenderNode { // Animations /////////////////////////////////////////////////////////////////////////// + /** + * TODO: Figure out if this can be eliminated/refactored away + * + * For now this interface exists to de-couple RenderNode from anything View-specific in a + * bit of a kludge. + * + * @hide */ + interface AnimationHost { + void registerAnimatingRenderNode(RenderNode animator); + void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator); + boolean isAttached(); + } + + /** @hide */ public void addAnimator(RenderNodeAnimator animator) { - if (mOwningView == null || mOwningView.mAttachInfo == null) { + if (!isAttached()) { throw new IllegalStateException("Cannot start this animator on a detached view!"); } nAddAnimator(mNativeRenderNode, animator.getNativeAnimator()); - mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this); + mAnimationHost.registerAnimatingRenderNode(this); } + /** @hide */ public boolean isAttached() { - return mOwningView != null && mOwningView.mAttachInfo != null; + return mAnimationHost != null && mAnimationHost.isAttached(); } - public void registerVectorDrawableAnimator( - AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) { - if (mOwningView == null || mOwningView.mAttachInfo == null) { + /** @hide */ + public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animatorSet) { + if (!isAttached()) { throw new IllegalStateException("Cannot start this animator on a detached view!"); } - mOwningView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animatorSet); + mAnimationHost.registerVectorDrawableAnimator(animatorSet); } + /** @hide */ public void endAllAnimators() { nEndAllAnimators(mNativeRenderNode); } @@ -906,7 +948,8 @@ public class RenderNode { private static native long nGetNativeFinalizer(); private static native void nOutput(long renderNode); private static native int nGetDebugSize(long renderNode); - private static native void nRequestPositionUpdates(long renderNode, SurfaceView callback); + private static native void nRequestPositionUpdates(long renderNode, + PositionUpdateListener callback); // Animations diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 514a11ebf713..e71182c33c12 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -209,7 +209,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mRenderNode.requestPositionUpdates(this); + mRenderNode.requestPositionUpdates(mPositionListener); setWillNotDraw(true); } @@ -826,81 +826,80 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb private Rect mRTLastReportedPosition = new Rect(); - /** - * Called by native by a Rendering Worker thread to update the window position - * @hide - */ - @UnsupportedAppUsage - public final void updateSurfacePosition_renderWorker(long frameNumber, - int left, int top, int right, int bottom) { - if (mSurfaceControl == null) { - return; - } + private RenderNode.PositionUpdateListener mPositionListener = + new RenderNode.PositionUpdateListener() { - // TODO: This is teensy bit racey in that a brand new SurfaceView moving on - // its 2nd frame if RenderThread is running slowly could potentially see - // this as false, enter the branch, get pre-empted, then this comes along - // and reports a new position, then the UI thread resumes and reports - // its position. This could therefore be de-sync'd in that interval, but - // the synchronization would violate the rule that RT must never block - // on the UI thread which would open up potential deadlocks. The risk of - // a single-frame desync is therefore preferable for now. - mRtHandlingPositionUpdates = true; - if (mRTLastReportedPosition.left == left - && mRTLastReportedPosition.top == top - && mRTLastReportedPosition.right == right - && mRTLastReportedPosition.bottom == bottom) { - return; - } - try { - if (DEBUG) { - Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " + - "postion = [%d, %d, %d, %d]", System.identityHashCode(this), - frameNumber, left, top, right, bottom)); + @Override + public void positionChanged(long frameNumber, int left, int top, int right, int bottom) { + if (mSurfaceControl == null) { + return; } - mRTLastReportedPosition.set(left, top, right, bottom); - setParentSpaceRectangle(mRTLastReportedPosition, frameNumber); - // Now overwrite mRTLastReportedPosition with our values - } catch (Exception ex) { - Log.e(TAG, "Exception from repositionChild", ex); - } - } - /** - * Called by native on RenderThread to notify that the view is no longer in the - * draw tree. UI thread is blocked at this point. - * @hide - */ - @UnsupportedAppUsage - public final void surfacePositionLost_uiRtSync(long frameNumber) { - if (DEBUG) { - Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", - System.identityHashCode(this), frameNumber)); + // TODO: This is teensy bit racey in that a brand new SurfaceView moving on + // its 2nd frame if RenderThread is running slowly could potentially see + // this as false, enter the branch, get pre-empted, then this comes along + // and reports a new position, then the UI thread resumes and reports + // its position. This could therefore be de-sync'd in that interval, but + // the synchronization would violate the rule that RT must never block + // on the UI thread which would open up potential deadlocks. The risk of + // a single-frame desync is therefore preferable for now. + mRtHandlingPositionUpdates = true; + if (mRTLastReportedPosition.left == left + && mRTLastReportedPosition.top == top + && mRTLastReportedPosition.right == right + && mRTLastReportedPosition.bottom == bottom) { + return; + } + try { + if (DEBUG) { + Log.d(TAG, String.format( + "%d updateSurfacePosition RenderWorker, frameNr = %d, " + + "postion = [%d, %d, %d, %d]", + System.identityHashCode(this), frameNumber, + left, top, right, bottom)); + } + mRTLastReportedPosition.set(left, top, right, bottom); + setParentSpaceRectangle(mRTLastReportedPosition, frameNumber); + // Now overwrite mRTLastReportedPosition with our values + } catch (Exception ex) { + Log.e(TAG, "Exception from repositionChild", ex); + } } - mRTLastReportedPosition.setEmpty(); - if (mSurfaceControl == null) { - return; - } - if (mRtHandlingPositionUpdates) { - mRtHandlingPositionUpdates = false; - // This callback will happen while the UI thread is blocked, so we can - // safely access other member variables at this time. - // So do what the UI thread would have done if RT wasn't handling position - // updates. - if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) { - try { - if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " + - "postion = [%d, %d, %d, %d]", System.identityHashCode(this), - mScreenRect.left, mScreenRect.top, - mScreenRect.right, mScreenRect.bottom)); - setParentSpaceRectangle(mScreenRect, frameNumber); - } catch (Exception ex) { - Log.e(TAG, "Exception configuring surface", ex); + @Override + public void positionLost(long frameNumber) { + if (DEBUG) { + Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", + System.identityHashCode(this), frameNumber)); + } + mRTLastReportedPosition.setEmpty(); + + if (mSurfaceControl == null) { + return; + } + if (mRtHandlingPositionUpdates) { + mRtHandlingPositionUpdates = false; + // This callback will happen while the UI thread is blocked, so we can + // safely access other member variables at this time. + // So do what the UI thread would have done if RT wasn't handling position + // updates. + if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) { + try { + if (DEBUG) { + Log.d(TAG, String.format("%d updateSurfacePosition, " + + "postion = [%d, %d, %d, %d]", + System.identityHashCode(this), + mScreenRect.left, mScreenRect.top, + mScreenRect.right, mScreenRect.bottom)); + } + setParentSpaceRectangle(mScreenRect, frameNumber); + } catch (Exception ex) { + Log.e(TAG, "Exception configuring surface", ex); + } } } } - } + }; private SurfaceHolder.Callback[] getSurfaceCallbacks() { SurfaceHolder.Callback callbacks[]; diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 6737839993ed..0986b89849cf 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -25,7 +25,6 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.AnimatedVectorDrawable; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -914,8 +913,7 @@ public final class ThreadedRenderer { nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode); } - void registerVectorDrawableAnimator( - AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) { + void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) { nRegisterVectorDrawableAnimator(mRootNode.mNativeRenderNode, animator.getAnimatorNativePtr()); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f4be9f2d7949..1eb35c546c99 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4864,7 +4864,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS); mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; - mRenderNode = RenderNode.create(getClass().getName(), this); + mRenderNode = RenderNode.create(getClass().getName(), new ViewAnimationHostBridge(this)); if (!sCompatibilityDone && context != null) { final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; @@ -5732,7 +5732,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @UnsupportedAppUsage View() { mResources = null; - mRenderNode = RenderNode.create(getClass().getName(), this); + mRenderNode = RenderNode.create(getClass().getName(), new ViewAnimationHostBridge(this)); } final boolean debugDraw() { @@ -20600,7 +20600,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) { if (renderNode == null) { - renderNode = RenderNode.create(drawable.getClass().getName(), this); + renderNode = RenderNode.create(drawable.getClass().getName(), + new ViewAnimationHostBridge(this)); renderNode.setUsageHint(RenderNode.USAGE_BACKGROUND); } diff --git a/core/java/android/view/ViewAnimationHostBridge.java b/core/java/android/view/ViewAnimationHostBridge.java new file mode 100644 index 000000000000..58f555dfa305 --- /dev/null +++ b/core/java/android/view/ViewAnimationHostBridge.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 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.view; + +/** + * Maps a View to a RenderNode's AnimationHost + * + * @hide + */ +public class ViewAnimationHostBridge implements RenderNode.AnimationHost { + private final View mView; + + /** + * @param view the View to bridge to an AnimationHost + */ + public ViewAnimationHostBridge(View view) { + mView = view; + } + + @Override + public void registerAnimatingRenderNode(RenderNode animator) { + mView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(animator); + } + + @Override + public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) { + mView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animator); + } + + @Override + public boolean isAttached() { + return mView.mAttachInfo != null; + } +} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2ee7ab939bd9..5bc44ce2bd49 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -52,7 +52,6 @@ import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; -import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; @@ -239,6 +238,12 @@ public final class ViewRootImpl implements ViewParent, final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>(); @UnsupportedAppUsage final Context mContext; + /** + * TODO(b/116349163): Check if we can merge this into {@link #mContext}. + */ + @NonNull + private Context mDisplayContext; + @UnsupportedAppUsage final IWindowSession mWindowSession; @NonNull Display mDisplay; @@ -532,6 +537,7 @@ public final class ViewRootImpl implements ViewParent, public ViewRootImpl(Context context, Display display) { mContext = context; + mDisplayContext = context.createDisplayContext(display); mWindowSession = WindowManagerGlobal.getWindowSession(); mDisplay = display; mBasePackageName = context.getBasePackageName(); @@ -984,6 +990,9 @@ public final class ViewRootImpl implements ViewParent, ThreadedRenderer.invokeFunctor(functor, waitForCompletion); } + /** + * @param animator animator to register with the hardware renderer + */ public void registerAnimatingRenderNode(RenderNode animator) { if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mThreadedRenderer.registerAnimatingRenderNode(animator); @@ -995,8 +1004,10 @@ public final class ViewRootImpl implements ViewParent, } } - public void registerVectorDrawableAnimator( - AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) { + /** + * @param animator animator to register with the hardware renderer + */ + public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) { if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mThreadedRenderer.registerVectorDrawableAnimator(animator); } @@ -1249,6 +1260,7 @@ public final class ViewRootImpl implements ViewParent, } else { mDisplay = preferredDisplay; } + mDisplayContext = mContext.createDisplayContext(mDisplay); } void pokeDrawLockIfNeeded() { @@ -2579,7 +2591,7 @@ public final class ViewRootImpl implements ViewParent, .mayUseInputMethod(mWindowAttributes.flags); if (imTarget != mLastWasImTarget) { mLastWasImTarget = imTarget; - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); + InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class); if (imm != null && imTarget) { imm.onPreWindowFocus(mView, hasWindowFocus); imm.onPostWindowFocus(mView, mView.findFocus(), @@ -2695,7 +2707,7 @@ public final class ViewRootImpl implements ViewParent, mLastWasImTarget = WindowManager.LayoutParams .mayUseInputMethod(mWindowAttributes.flags); - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); + InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class); if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) { imm.onPreWindowFocus(mView, hasWindowFocus); } @@ -4329,7 +4341,8 @@ public final class ViewRootImpl implements ViewParent, enqueueInputEvent(event, null, 0, true); } break; case MSG_CHECK_FOCUS: { - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); + InputMethodManager imm = + mDisplayContext.getSystemService(InputMethodManager.class); if (imm != null) { imm.checkFocus(); } @@ -4871,7 +4884,7 @@ public final class ViewRootImpl implements ViewParent, @Override protected int onProcess(QueuedInputEvent q) { if (mLastWasImTarget && !isInLocalFocusMode()) { - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); + InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class); if (imm != null) { final InputEvent event = q.mEvent; if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event); diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 29339e915ce0..3e240cfdb69f 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -44,6 +44,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; +import com.google.android.textclassifier.AnnotatorModel; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -92,7 +94,7 @@ public final class TextClassifierImpl implements TextClassifier { @GuardedBy("mLock") // Do not access outside this lock. private ModelFile mModel; @GuardedBy("mLock") // Do not access outside this lock. - private TextClassifierImplNative mNative; + private AnnotatorModel mNative; private final Object mLoggerLock = new Object(); @GuardedBy("mLoggerLock") // Do not access outside this lock. @@ -125,7 +127,7 @@ public final class TextClassifierImpl implements TextClassifier { && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) { final String localesString = concatenateLocales(request.getDefaultLocales()); final ZonedDateTime refTime = ZonedDateTime.now(); - final TextClassifierImplNative nativeImpl = getNative(request.getDefaultLocales()); + final AnnotatorModel nativeImpl = getNative(request.getDefaultLocales()); final int start; final int end; if (mSettings.isModelDarkLaunchEnabled() && !request.isDarkLaunchAllowed()) { @@ -134,7 +136,7 @@ public final class TextClassifierImpl implements TextClassifier { } else { final int[] startEnd = nativeImpl.suggestSelection( string, request.getStartIndex(), request.getEndIndex(), - new TextClassifierImplNative.SelectionOptions(localesString)); + new AnnotatorModel.SelectionOptions(localesString)); start = startEnd[0]; end = startEnd[1]; } @@ -142,10 +144,10 @@ public final class TextClassifierImpl implements TextClassifier { && start >= 0 && end <= string.length() && start <= request.getStartIndex() && end >= request.getEndIndex()) { final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end); - final TextClassifierImplNative.ClassificationResult[] results = + final AnnotatorModel.ClassificationResult[] results = nativeImpl.classifyText( string, start, end, - new TextClassifierImplNative.ClassificationOptions( + new AnnotatorModel.ClassificationOptions( refTime.toInstant().toEpochMilli(), refTime.getZone().getId(), localesString)); @@ -184,11 +186,11 @@ public final class TextClassifierImpl implements TextClassifier { final String localesString = concatenateLocales(request.getDefaultLocales()); final ZonedDateTime refTime = request.getReferenceTime() != null ? request.getReferenceTime() : ZonedDateTime.now(); - final TextClassifierImplNative.ClassificationResult[] results = + final AnnotatorModel.ClassificationResult[] results = getNative(request.getDefaultLocales()) .classifyText( string, request.getStartIndex(), request.getEndIndex(), - new TextClassifierImplNative.ClassificationOptions( + new AnnotatorModel.ClassificationOptions( refTime.toInstant().toEpochMilli(), refTime.getZone().getId(), localesString)); @@ -228,17 +230,17 @@ public final class TextClassifierImpl implements TextClassifier { ? request.getEntityConfig().resolveEntityListModifications( getEntitiesForHints(request.getEntityConfig().getHints())) : mSettings.getEntityListDefault(); - final TextClassifierImplNative nativeImpl = + final AnnotatorModel nativeImpl = getNative(request.getDefaultLocales()); - final TextClassifierImplNative.AnnotatedSpan[] annotations = + final AnnotatorModel.AnnotatedSpan[] annotations = nativeImpl.annotate( textString, - new TextClassifierImplNative.AnnotationOptions( + new AnnotatorModel.AnnotationOptions( refTime.toInstant().toEpochMilli(), refTime.getZone().getId(), concatenateLocales(request.getDefaultLocales()))); - for (TextClassifierImplNative.AnnotatedSpan span : annotations) { - final TextClassifierImplNative.ClassificationResult[] results = + for (AnnotatorModel.AnnotatedSpan span : annotations) { + final AnnotatorModel.ClassificationResult[] results = span.getClassification(); if (results.length == 0 || !entitiesToIdentify.contains(results[0].getCollection())) { @@ -297,7 +299,7 @@ public final class TextClassifierImpl implements TextClassifier { } } - private TextClassifierImplNative getNative(LocaleList localeList) + private AnnotatorModel getNative(LocaleList localeList) throws FileNotFoundException { synchronized (mLock) { localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList; @@ -310,7 +312,7 @@ public final class TextClassifierImpl implements TextClassifier { destroyNativeIfExistsLocked(); final ParcelFileDescriptor fd = ParcelFileDescriptor.open( new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY); - mNative = new TextClassifierImplNative(fd.getFd()); + mNative = new AnnotatorModel(fd.getFd()); closeAndLogError(fd); mModel = bestModel; } @@ -398,14 +400,14 @@ public final class TextClassifierImpl implements TextClassifier { } private TextClassification createClassificationResult( - TextClassifierImplNative.ClassificationResult[] classifications, + AnnotatorModel.ClassificationResult[] classifications, String text, int start, int end, @Nullable Instant referenceTime) { final String classifiedText = text.substring(start, end); final TextClassification.Builder builder = new TextClassification.Builder() .setText(classifiedText); final int size = classifications.length; - TextClassifierImplNative.ClassificationResult highestScoringResult = null; + AnnotatorModel.ClassificationResult highestScoringResult = null; float highestScore = Float.MIN_VALUE; for (int i = 0; i < size; i++) { builder.setEntityType(classifications[i].getCollection(), @@ -486,9 +488,9 @@ public final class TextClassifierImpl implements TextClassifier { try { final ParcelFileDescriptor modelFd = ParcelFileDescriptor.open( file, ParcelFileDescriptor.MODE_READ_ONLY); - final int version = TextClassifierImplNative.getVersion(modelFd.getFd()); + final int version = AnnotatorModel.getVersion(modelFd.getFd()); final String supportedLocalesStr = - TextClassifierImplNative.getLocales(modelFd.getFd()); + AnnotatorModel.getLocales(modelFd.getFd()); if (supportedLocalesStr.isEmpty()) { Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath()); return null; @@ -676,7 +678,7 @@ public final class TextClassifierImpl implements TextClassifier { public static List<LabeledIntent> create( Context context, @Nullable Instant referenceTime, - TextClassifierImplNative.ClassificationResult classification, + AnnotatorModel.ClassificationResult classification, String text) { final String type = classification.getCollection().trim().toLowerCase(Locale.ENGLISH); text = text.trim(); diff --git a/core/java/android/view/textclassifier/TextClassifierImplNative.java b/core/java/android/view/textclassifier/TextClassifierImplNative.java deleted file mode 100644 index 3d4c8f2863ea..000000000000 --- a/core/java/android/view/textclassifier/TextClassifierImplNative.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (C) 2017 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.view.textclassifier; - -import android.content.res.AssetFileDescriptor; - -/** - * Java wrapper for TextClassifier native library interface. This library is used for detecting - * entities in text. - */ -final class TextClassifierImplNative { - - static { - System.loadLibrary("textclassifier"); - } - - private final long mModelPtr; - - /** - * Creates a new instance of TextClassifierImplNative, using the provided model image, given as - * a file descriptor. - */ - TextClassifierImplNative(int fd) { - mModelPtr = nativeNew(fd); - if (mModelPtr == 0L) { - throw new IllegalArgumentException("Couldn't initialize TC from file descriptor."); - } - } - - /** - * Creates a new instance of TextClassifierImplNative, using the provided model image, given as - * a file path. - */ - TextClassifierImplNative(String path) { - mModelPtr = nativeNewFromPath(path); - if (mModelPtr == 0L) { - throw new IllegalArgumentException("Couldn't initialize TC from given file."); - } - } - - /** - * Creates a new instance of TextClassifierImplNative, using the provided model image, given as - * an AssetFileDescriptor. - */ - TextClassifierImplNative(AssetFileDescriptor afd) { - mModelPtr = nativeNewFromAssetFileDescriptor(afd, afd.getStartOffset(), afd.getLength()); - if (mModelPtr == 0L) { - throw new IllegalArgumentException( - "Couldn't initialize TC from given AssetFileDescriptor"); - } - } - - /** - * Given a string context and current selection, computes the SmartSelection suggestion. - * - * <p>The begin and end are character indices into the context UTF8 string. selectionBegin is - * the character index where the selection begins, and selectionEnd is the index of one - * character past the selection span. - * - * <p>The return value is an array of two ints: suggested selection beginning and end, with the - * same semantics as the input selectionBeginning and selectionEnd. - */ - public int[] suggestSelection( - String context, int selectionBegin, int selectionEnd, SelectionOptions options) { - return nativeSuggestSelection(mModelPtr, context, selectionBegin, selectionEnd, options); - } - - /** - * Given a string context and current selection, classifies the type of the selected text. - * - * <p>The begin and end params are character indices in the context string. - * - * <p>Returns an array of ClassificationResult objects with the probability scores for different - * collections. - */ - public ClassificationResult[] classifyText( - String context, int selectionBegin, int selectionEnd, ClassificationOptions options) { - return nativeClassifyText(mModelPtr, context, selectionBegin, selectionEnd, options); - } - - /** - * Annotates given input text. The annotations should cover the whole input context except for - * whitespaces, and are sorted by their position in the context string. - */ - public AnnotatedSpan[] annotate(String text, AnnotationOptions options) { - return nativeAnnotate(mModelPtr, text, options); - } - - /** Frees up the allocated memory. */ - public void close() { - nativeClose(mModelPtr); - } - - /** Returns a comma separated list of locales supported by the model as BCP 47 tags. */ - public static String getLocales(int fd) { - return nativeGetLocales(fd); - } - - /** Returns the version of the model. */ - public static int getVersion(int fd) { - return nativeGetVersion(fd); - } - - /** Represents a datetime parsing result from classifyText calls. */ - public static final class DatetimeResult { - static final int GRANULARITY_YEAR = 0; - static final int GRANULARITY_MONTH = 1; - static final int GRANULARITY_WEEK = 2; - static final int GRANULARITY_DAY = 3; - static final int GRANULARITY_HOUR = 4; - static final int GRANULARITY_MINUTE = 5; - static final int GRANULARITY_SECOND = 6; - - private final long mTimeMsUtc; - private final int mGranularity; - - DatetimeResult(long timeMsUtc, int granularity) { - mGranularity = granularity; - mTimeMsUtc = timeMsUtc; - } - - public long getTimeMsUtc() { - return mTimeMsUtc; - } - - public int getGranularity() { - return mGranularity; - } - } - - /** Represents a result of classifyText method call. */ - public static final class ClassificationResult { - private final String mCollection; - private final float mScore; - private final DatetimeResult mDatetimeResult; - - ClassificationResult( - String collection, float score, DatetimeResult datetimeResult) { - mCollection = collection; - mScore = score; - mDatetimeResult = datetimeResult; - } - - public String getCollection() { - if (mCollection.equals(TextClassifier.TYPE_DATE) && mDatetimeResult != null) { - switch (mDatetimeResult.getGranularity()) { - case DatetimeResult.GRANULARITY_HOUR: - // fall through - case DatetimeResult.GRANULARITY_MINUTE: - // fall through - case DatetimeResult.GRANULARITY_SECOND: - return TextClassifier.TYPE_DATE_TIME; - default: - return TextClassifier.TYPE_DATE; - } - } - return mCollection; - } - - public float getScore() { - return mScore; - } - - public DatetimeResult getDatetimeResult() { - return mDatetimeResult; - } - } - - /** Represents a result of Annotate call. */ - public static final class AnnotatedSpan { - private final int mStartIndex; - private final int mEndIndex; - private final ClassificationResult[] mClassification; - - AnnotatedSpan( - int startIndex, int endIndex, ClassificationResult[] classification) { - mStartIndex = startIndex; - mEndIndex = endIndex; - mClassification = classification; - } - - public int getStartIndex() { - return mStartIndex; - } - - public int getEndIndex() { - return mEndIndex; - } - - public ClassificationResult[] getClassification() { - return mClassification; - } - } - - /** Represents options for the suggestSelection call. */ - public static final class SelectionOptions { - private final String mLocales; - - SelectionOptions(String locales) { - mLocales = locales; - } - - public String getLocales() { - return mLocales; - } - } - - /** Represents options for the classifyText call. */ - public static final class ClassificationOptions { - private final long mReferenceTimeMsUtc; - private final String mReferenceTimezone; - private final String mLocales; - - ClassificationOptions(long referenceTimeMsUtc, String referenceTimezone, String locale) { - mReferenceTimeMsUtc = referenceTimeMsUtc; - mReferenceTimezone = referenceTimezone; - mLocales = locale; - } - - public long getReferenceTimeMsUtc() { - return mReferenceTimeMsUtc; - } - - public String getReferenceTimezone() { - return mReferenceTimezone; - } - - public String getLocale() { - return mLocales; - } - } - - /** Represents options for the Annotate call. */ - public static final class AnnotationOptions { - private final long mReferenceTimeMsUtc; - private final String mReferenceTimezone; - private final String mLocales; - - AnnotationOptions(long referenceTimeMsUtc, String referenceTimezone, String locale) { - mReferenceTimeMsUtc = referenceTimeMsUtc; - mReferenceTimezone = referenceTimezone; - mLocales = locale; - } - - public long getReferenceTimeMsUtc() { - return mReferenceTimeMsUtc; - } - - public String getReferenceTimezone() { - return mReferenceTimezone; - } - - public String getLocale() { - return mLocales; - } - } - - private static native long nativeNew(int fd); - - private static native long nativeNewFromPath(String path); - - private static native long nativeNewFromAssetFileDescriptor( - AssetFileDescriptor afd, long offset, long size); - - private static native int[] nativeSuggestSelection( - long context, - String text, - int selectionBegin, - int selectionEnd, - SelectionOptions options); - - private static native ClassificationResult[] nativeClassifyText( - long context, - String text, - int selectionBegin, - int selectionEnd, - ClassificationOptions options); - - private static native AnnotatedSpan[] nativeAnnotate( - long context, String text, AnnotationOptions options); - - private static native void nativeClose(long context); - - private static native String nativeGetLocales(int fd); - - private static native int nativeGetVersion(int fd); -} diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index ae6a2fd787d7..23d12374453f 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -25,6 +25,13 @@ import android.net.WebAddress; * Cookies are manipulated according to RFC2109. */ public abstract class CookieManager { + /** + * @deprecated This class should not be constructed by applications, use {@link #getInstance} + * instead to fetch the singleton instance. + */ + // TODO(ntfschr): mark this as @SystemApi after a year. + @Deprecated + public CookieManager() {} @Override protected Object clone() throws CloneNotSupportedException { diff --git a/core/java/android/webkit/RenderProcessGoneDetail.java b/core/java/android/webkit/RenderProcessGoneDetail.java index 0843e26ea19c..9beeaa5c1524 100644 --- a/core/java/android/webkit/RenderProcessGoneDetail.java +++ b/core/java/android/webkit/RenderProcessGoneDetail.java @@ -22,6 +22,13 @@ package android.webkit; **/ public abstract class RenderProcessGoneDetail { /** + * @deprecated This class should not be constructed by applications. + */ + // TODO(ntfschr): mark this as @SystemApi after a year. + @Deprecated + public RenderProcessGoneDetail() {} + + /** * Indicates whether the render process was observed to crash, or whether * it was killed by the system. * diff --git a/core/java/android/webkit/SafeBrowsingResponse.java b/core/java/android/webkit/SafeBrowsingResponse.java index 7839a00eff69..ca33a0c659af 100644 --- a/core/java/android/webkit/SafeBrowsingResponse.java +++ b/core/java/android/webkit/SafeBrowsingResponse.java @@ -27,6 +27,12 @@ package android.webkit; * {@link android.webkit.WebView#getSafeBrowsingPrivacyPolicyUrl()}. */ public abstract class SafeBrowsingResponse { + /** + * @deprecated This class should not be constructed by applications. + */ + // TODO(ntfschr): mark this as @SystemApi after a year. + @Deprecated + public SafeBrowsingResponse() {} /** * Display the default interstitial. diff --git a/core/java/android/webkit/ServiceWorkerController.java b/core/java/android/webkit/ServiceWorkerController.java index 3517c74b680e..d7e0715a3770 100644 --- a/core/java/android/webkit/ServiceWorkerController.java +++ b/core/java/android/webkit/ServiceWorkerController.java @@ -38,6 +38,14 @@ import android.annotation.Nullable; public abstract class ServiceWorkerController { /** + * @deprecated This class should not be constructed by applications, use {@link #getInstance()} + * instead to fetch the singleton instance. + */ + // TODO(ntfschr): mark this as @SystemApi after a year. + @Deprecated + public ServiceWorkerController() {} + + /** * Returns the default ServiceWorkerController instance. At present there is * only one ServiceWorkerController instance for all WebView instances, * however this restriction may be relaxed in the future. diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java index 30f465cb42c6..99081827658a 100644 --- a/core/java/android/webkit/TracingController.java +++ b/core/java/android/webkit/TracingController.java @@ -43,6 +43,13 @@ import java.util.concurrent.Executor; * </pre></p> */ public abstract class TracingController { + /** + * @deprecated This class should not be constructed by applications, use {@link #getInstance} + * instead to fetch the singleton instance. + */ + // TODO(ntfschr): mark this as @SystemApi after a year. + @Deprecated + public TracingController() {} /** * Returns the default TracingController instance. At present there is diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index f6166c58a4c9..f346c602c3b6 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -31,6 +31,14 @@ import android.content.Context; */ public abstract class WebViewDatabase { /** + * @deprecated This class should not be constructed by applications, use {@link + * #getInstance(Context)} instead to fetch the singleton instance. + */ + // TODO(ntfschr): mark this as @SystemApi after a year. + @Deprecated + public WebViewDatabase() {} + + /** * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} */ protected static final String LOGTAG = "webviewdatabase"; diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 8dd30f6abe24..9d74c98a04ba 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -388,7 +388,8 @@ public class Editor { com.android.internal.R.bool.config_enableHapticTextHandle); if (FLAG_USE_MAGNIFIER) { - mMagnifierAnimator = new MagnifierMotionAnimator(new Magnifier(mTextView)); + final Magnifier magnifier = new Magnifier.Builder(mTextView).build(); + mMagnifierAnimator = new MagnifierMotionAnimator(magnifier); } } diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 8e2786d51a43..16ddd0fc8247 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -119,8 +119,9 @@ public final class Magnifier { * * @param view the view for which this magnifier is attached * - * @see Builder + * @deprecated Please use {@link Builder} instead */ + @Deprecated public Magnifier(@NonNull View view) { this(new Builder(view)); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index bbfac440d414..f95b3ce98264 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -321,6 +321,12 @@ import java.util.function.Supplier; * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize * @attr ref android.R.styleable#TextView_autoSizeStepGranularity * @attr ref android.R.styleable#TextView_autoSizePresetSizes + * @attr ref android.R.styleable#TextView_textCursorDrawable + * @attr ref android.R.styleable#TextView_textSelectHandle + * @attr ref android.R.styleable#TextView_textSelectHandleLeft + * @attr ref android.R.styleable#TextView_textSelectHandleRight + * @attr ref android.R.styleable#TextView_allowUndo + * @attr ref android.R.styleable#TextView_enabled */ @RemoteView public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { diff --git a/core/java/com/android/internal/app/procstats/IProcessStats.aidl b/core/java/com/android/internal/app/procstats/IProcessStats.aidl index 44867c7719cd..0c203ab76346 100644 --- a/core/java/com/android/internal/app/procstats/IProcessStats.aidl +++ b/core/java/com/android/internal/app/procstats/IProcessStats.aidl @@ -24,4 +24,14 @@ interface IProcessStats { byte[] getCurrentStats(out List<ParcelFileDescriptor> historic); ParcelFileDescriptor getStatsOverTime(long minTime); int getCurrentMemoryState(); + + /** + * Get stats committed after highWaterMarkMs + * @param highWaterMarkMs Report stats committed after this time. + * @param section Integer mask to indicate which sections to include in the stats. + * @param doAggregate Whether to aggregate the stats or keep them separated. + * @param List of Files of individual commits in protobuf binary or one that is merged from them. + */ + long getCommittedStats(long highWaterMarkMs, int section, boolean doAggregate, + out List<ParcelFileDescriptor> committedStats); } diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java index f6e99ba90b86..e7ac5664c3ee 100644 --- a/core/java/com/android/internal/app/procstats/ProcessStats.java +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -158,6 +158,25 @@ public final class ProcessStats implements Parcelable { STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY }; + // Should report process stats. + public static final int REPORT_PROC_STATS = 0x01; + // Should report package process stats. + public static final int REPORT_PKG_PROC_STATS = 0x02; + // Should report package service stats. + public static final int REPORT_PKG_SVC_STATS = 0x04; + // Should report package association stats. + public static final int REPORT_PKG_ASC_STATS = 0x08; + // Should report package stats. + public static final int REPORT_PKG_STATS = 0x0E; + // Should report all stats. + public static final int REPORT_ALL = 0x0F; + + public static final int[] OPTIONS = + {REPORT_PROC_STATS, REPORT_PKG_PROC_STATS, REPORT_PKG_SVC_STATS, REPORT_PKG_ASC_STATS, + REPORT_PKG_STATS, REPORT_ALL}; + public static final String[] OPTIONS_STR = + {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"}; + // Current version of the parcel format. private static final int PARCEL_VERSION = 34; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data @@ -1412,7 +1431,7 @@ public final class ProcessStats implements Parcelable { } public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary, - boolean dumpDetails, boolean dumpAll, boolean activeOnly) { + boolean dumpDetails, boolean dumpAll, boolean activeOnly, int section) { long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, mStartTime, now); boolean sepNeeded = false; @@ -1421,176 +1440,205 @@ public final class ProcessStats implements Parcelable { mSysMemUsage.dump(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ); sepNeeded = true; } - ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = mPackages.getMap(); boolean printedHeader = false; - for (int ip=0; ip<pkgMap.size(); ip++) { - final String pkgName = pkgMap.keyAt(ip); - final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - final int uid = uids.keyAt(iu); - final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu); - for (int iv=0; iv<vpkgs.size(); iv++) { - final long vers = vpkgs.keyAt(iv); - final PackageState pkgState = vpkgs.valueAt(iv); - final int NPROCS = pkgState.mProcesses.size(); - final int NSRVS = pkgState.mServices.size(); - final int NASCS = pkgState.mAssociations.size(); - final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName); - if (!pkgMatch) { - boolean procMatch = false; - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - if (reqPackage.equals(proc.getName())) { - procMatch = true; - break; - } - } - if (!procMatch) { - continue; - } - } - if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) { - if (!printedHeader) { - if (sepNeeded) pw.println(); - pw.println("Per-Package Stats:"); - printedHeader = true; - sepNeeded = true; - } - pw.print(" * "); pw.print(pkgName); pw.print(" / "); - UserHandle.formatUid(pw, uid); pw.print(" / v"); - pw.print(vers); pw.println(":"); - } - if (!dumpSummary || dumpAll) { - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - if (!pkgMatch && !reqPackage.equals(proc.getName())) { - continue; + if ((section & REPORT_PKG_STATS) != 0) { + ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = + mPackages.getMap(); + for (int ip = 0; ip < pkgMap.size(); ip++) { + final String pkgName = pkgMap.keyAt(ip); + final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); + for (int iu = 0; iu < uids.size(); iu++) { + final int uid = uids.keyAt(iu); + final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu); + for (int iv = 0; iv < vpkgs.size(); iv++) { + final long vers = vpkgs.keyAt(iv); + final PackageState pkgState = vpkgs.valueAt(iv); + final int NPROCS = pkgState.mProcesses.size(); + final int NSRVS = pkgState.mServices.size(); + final int NASCS = pkgState.mAssociations.size(); + final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName); + if (!pkgMatch) { + boolean procMatch = false; + for (int iproc = 0; iproc < NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (reqPackage.equals(proc.getName())) { + procMatch = true; + break; + } } - if (activeOnly && !proc.isInUse()) { - pw.print(" (Not active: "); - pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")"); + if (!procMatch) { continue; } - pw.print(" Process "); - pw.print(pkgState.mProcesses.keyAt(iproc)); - if (proc.getCommonProcess().isMultiPackage()) { - pw.print(" (multi, "); - } else { - pw.print(" (unique, "); + } + if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) { + if (!printedHeader) { + if (sepNeeded) pw.println(); + pw.println("Per-Package Stats:"); + printedHeader = true; + sepNeeded = true; } - pw.print(proc.getDurationsBucketCount()); - pw.print(" entries)"); + pw.print(" * "); + pw.print(pkgName); + pw.print(" / "); + UserHandle.formatUid(pw, uid); + pw.print(" / v"); + pw.print(vers); pw.println(":"); - proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES, now); - proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES, now); - proc.dumpInternalLocked(pw, " ", dumpAll); } - } else { - ArrayList<ProcessState> procs = new ArrayList<ProcessState>(); - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - if (!pkgMatch && !reqPackage.equals(proc.getName())) { - continue; - } - if (activeOnly && !proc.isInUse()) { - continue; + if ((section & REPORT_PKG_PROC_STATS) != 0) { + if (!dumpSummary || dumpAll) { + for (int iproc = 0; iproc < NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (!pkgMatch && !reqPackage.equals(proc.getName())) { + continue; + } + if (activeOnly && !proc.isInUse()) { + pw.print(" (Not active: "); + pw.print(pkgState.mProcesses.keyAt(iproc)); + pw.println(")"); + continue; + } + pw.print(" Process "); + pw.print(pkgState.mProcesses.keyAt(iproc)); + if (proc.getCommonProcess().isMultiPackage()) { + pw.print(" (multi, "); + } else { + pw.print(" (unique, "); + } + pw.print(proc.getDurationsBucketCount()); + pw.print(" entries)"); + pw.println(":"); + proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, + ALL_MEM_ADJ, + ALL_PROC_STATES, now); + proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + proc.dumpInternalLocked(pw, " ", dumpAll); + } + } else { + ArrayList<ProcessState> procs = new ArrayList<ProcessState>(); + for (int iproc = 0; iproc < NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (!pkgMatch && !reqPackage.equals(proc.getName())) { + continue; + } + if (activeOnly && !proc.isInUse()) { + continue; + } + procs.add(proc); + } + DumpUtils.dumpProcessSummaryLocked(pw, " ", "Prc ", procs, + ALL_SCREEN_ADJ, ALL_MEM_ADJ, NON_CACHED_PROC_STATES, + now, totalTime); } - procs.add(proc); - } - DumpUtils.dumpProcessSummaryLocked(pw, " ", "Prc ", procs, - ALL_SCREEN_ADJ, ALL_MEM_ADJ, NON_CACHED_PROC_STATES, - now, totalTime); - } - for (int isvc=0; isvc<NSRVS; isvc++) { - ServiceState svc = pkgState.mServices.valueAt(isvc); - if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) { - continue; } - if (activeOnly && !svc.isInUse()) { - pw.print(" (Not active service: "); - pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")"); - continue; - } - if (dumpAll) { - pw.print(" Service "); - } else { - pw.print(" * Svc "); - } - pw.print(pkgState.mServices.keyAt(isvc)); - pw.println(":"); - pw.print(" Process: "); pw.println(svc.getProcessName()); - svc.dumpStats(pw, " ", " ", " ", - now, totalTime, dumpSummary, dumpAll); - } - for (int iasc=0; iasc<NASCS; iasc++) { - AssociationState asc = pkgState.mAssociations.valueAt(iasc); - if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) { - continue; - } - if (activeOnly && !asc.isInUse()) { - pw.print(" (Not active association: "); - pw.print(pkgState.mAssociations.keyAt(iasc)); pw.println(")"); - continue; + if ((section & REPORT_PKG_SVC_STATS) != 0) { + for (int isvc = 0; isvc < NSRVS; isvc++) { + ServiceState svc = pkgState.mServices.valueAt(isvc); + if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) { + continue; + } + if (activeOnly && !svc.isInUse()) { + pw.print(" (Not active service: "); + pw.print(pkgState.mServices.keyAt(isvc)); + pw.println(")"); + continue; + } + if (dumpAll) { + pw.print(" Service "); + } else { + pw.print(" * Svc "); + } + pw.print(pkgState.mServices.keyAt(isvc)); + pw.println(":"); + pw.print(" Process: "); + pw.println(svc.getProcessName()); + svc.dumpStats(pw, " ", " ", " ", + now, totalTime, dumpSummary, dumpAll); + } } - if (dumpAll) { - pw.print(" Association "); - } else { - pw.print(" * Asc "); + if ((section & REPORT_PKG_ASC_STATS) != 0) { + for (int iasc = 0; iasc < NASCS; iasc++) { + AssociationState asc = pkgState.mAssociations.valueAt(iasc); + if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) { + continue; + } + if (activeOnly && !asc.isInUse()) { + pw.print(" (Not active association: "); + pw.print(pkgState.mAssociations.keyAt(iasc)); + pw.println(")"); + continue; + } + if (dumpAll) { + pw.print(" Association "); + } else { + pw.print(" * Asc "); + } + pw.print(pkgState.mAssociations.keyAt(iasc)); + pw.println(":"); + pw.print(" Process: "); + pw.println(asc.getProcessName()); + asc.dumpStats(pw, " ", " ", " ", + now, totalTime, dumpDetails, dumpAll); + } } - pw.print(pkgState.mAssociations.keyAt(iasc)); - pw.println(":"); - pw.print(" Process: "); pw.println(asc.getProcessName()); - asc.dumpStats(pw, " ", " ", " ", - now, totalTime, dumpDetails, dumpAll); } } } } - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - printedHeader = false; - int numShownProcs = 0, numTotalProcs = 0; - for (int ip=0; ip<procMap.size(); ip++) { - String procName = procMap.keyAt(ip); - SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - int uid = uids.keyAt(iu); - numTotalProcs++; - final ProcessState proc = uids.valueAt(iu); - if (!proc.hasAnyData()) { - continue; - } - if (!proc.isMultiPackage()) { - continue; - } - if (reqPackage != null && !reqPackage.equals(procName) - && !reqPackage.equals(proc.getPackage())) { - continue; - } - numShownProcs++; - if (sepNeeded) { - pw.println(); - } - sepNeeded = true; - if (!printedHeader) { - pw.println("Multi-Package Common Processes:"); - printedHeader = true; - } - if (activeOnly && !proc.isInUse()) { - pw.print(" (Not active: "); pw.print(procName); pw.println(")"); - continue; + if ((section & REPORT_PROC_STATS) != 0) { + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + printedHeader = false; + int numShownProcs = 0, numTotalProcs = 0; + for (int ip = 0; ip < procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu = 0; iu < uids.size(); iu++) { + int uid = uids.keyAt(iu); + numTotalProcs++; + final ProcessState proc = uids.valueAt(iu); + if (!proc.hasAnyData()) { + continue; + } + if (!proc.isMultiPackage()) { + continue; + } + if (reqPackage != null && !reqPackage.equals(procName) + && !reqPackage.equals(proc.getPackage())) { + continue; + } + numShownProcs++; + if (sepNeeded) { + pw.println(); + } + sepNeeded = true; + if (!printedHeader) { + pw.println("Multi-Package Common Processes:"); + printedHeader = true; + } + if (activeOnly && !proc.isInUse()) { + pw.print(" (Not active: "); + pw.print(procName); + pw.println(")"); + continue; + } + pw.print(" * "); + pw.print(procName); + pw.print(" / "); + UserHandle.formatUid(pw, uid); + pw.print(" ("); + pw.print(proc.getDurationsBucketCount()); + pw.print(" entries)"); + pw.println(":"); + proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now); + proc.dumpInternalLocked(pw, " ", dumpAll); } - pw.print(" * "); pw.print(procName); pw.print(" / "); - UserHandle.formatUid(pw, uid); - pw.print(" ("); pw.print(proc.getDurationsBucketCount()); - pw.print(" entries)"); pw.println(":"); - proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES, now); - proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now); - proc.dumpInternalLocked(pw, " ", dumpAll); } + pw.print(" Total procs: "); pw.print(numShownProcs); + pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total"); } if (dumpAll) { @@ -1598,8 +1646,7 @@ public final class ProcessStats implements Parcelable { pw.println(); } sepNeeded = true; - pw.print(" Total procs: "); pw.print(numShownProcs); - pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total"); + if (mTrackingAssociations.size() > 0) { pw.println(); pw.println("Tracking associations:"); @@ -1866,7 +1913,10 @@ public final class ProcessStats implements Parcelable { return outProcs; } - public void dumpCheckinLocked(PrintWriter pw, String reqPackage) { + /** + * Prints checkin style stats dump. + */ + public void dumpCheckinLocked(PrintWriter pw, String reqPackage, int section) { final long now = SystemClock.uptimeMillis(); final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = mPackages.getMap(); @@ -1895,50 +1945,61 @@ public final class ProcessStats implements Parcelable { } pw.println(); pw.print("config,"); pw.println(mRuntime); - for (int ip=0; ip<pkgMap.size(); ip++) { - final String pkgName = pkgMap.keyAt(ip); - if (reqPackage != null && !reqPackage.equals(pkgName)) { - continue; - } - final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - final int uid = uids.keyAt(iu); - final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu); - for (int iv=0; iv<vpkgs.size(); iv++) { - final long vers = vpkgs.keyAt(iv); - final PackageState pkgState = vpkgs.valueAt(iv); - final int NPROCS = pkgState.mProcesses.size(); - final int NSRVS = pkgState.mServices.size(); - final int NASCS = pkgState.mAssociations.size(); - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - proc.dumpPackageProcCheckin(pw, pkgName, uid, vers, - pkgState.mProcesses.keyAt(iproc), now); - } - for (int isvc=0; isvc<NSRVS; isvc++) { - final String serviceName = DumpUtils.collapseString(pkgName, - pkgState.mServices.keyAt(isvc)); - final ServiceState svc = pkgState.mServices.valueAt(isvc); - svc.dumpTimesCheckin(pw, pkgName, uid, vers, serviceName, now); - } - for (int iasc=0; iasc<NASCS; iasc++) { - final String associationName = DumpUtils.collapseString(pkgName, - pkgState.mAssociations.keyAt(iasc)); - final AssociationState asc = pkgState.mAssociations.valueAt(iasc); - asc.dumpTimesCheckin(pw, pkgName, uid, vers, associationName, now); + + if ((section & REPORT_PKG_STATS) != 0) { + for (int ip = 0; ip < pkgMap.size(); ip++) { + final String pkgName = pkgMap.keyAt(ip); + if (reqPackage != null && !reqPackage.equals(pkgName)) { + continue; + } + final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); + for (int iu = 0; iu < uids.size(); iu++) { + final int uid = uids.keyAt(iu); + final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu); + for (int iv = 0; iv < vpkgs.size(); iv++) { + final long vers = vpkgs.keyAt(iv); + final PackageState pkgState = vpkgs.valueAt(iv); + final int NPROCS = pkgState.mProcesses.size(); + final int NSRVS = pkgState.mServices.size(); + final int NASCS = pkgState.mAssociations.size(); + if ((section & REPORT_PKG_PROC_STATS) != 0) { + for (int iproc = 0; iproc < NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + proc.dumpPackageProcCheckin(pw, pkgName, uid, vers, + pkgState.mProcesses.keyAt(iproc), now); + } + } + if ((section & REPORT_PKG_SVC_STATS) != 0) { + for (int isvc = 0; isvc < NSRVS; isvc++) { + final String serviceName = DumpUtils.collapseString(pkgName, + pkgState.mServices.keyAt(isvc)); + final ServiceState svc = pkgState.mServices.valueAt(isvc); + svc.dumpTimesCheckin(pw, pkgName, uid, vers, serviceName, now); + } + } + if ((section & REPORT_PKG_ASC_STATS) != 0) { + for (int iasc = 0; iasc < NASCS; iasc++) { + final String associationName = DumpUtils.collapseString(pkgName, + pkgState.mAssociations.keyAt(iasc)); + final AssociationState asc = pkgState.mAssociations.valueAt(iasc); + asc.dumpTimesCheckin(pw, pkgName, uid, vers, associationName, now); + } + } } } } } - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - for (int ip=0; ip<procMap.size(); ip++) { - String procName = procMap.keyAt(ip); - SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - final int uid = uids.keyAt(iu); - final ProcessState procState = uids.valueAt(iu); - procState.dumpProcCheckin(pw, procName, uid, now); + if ((section & REPORT_PROC_STATS) != 0) { + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + for (int ip = 0; ip < procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu = 0; iu < uids.size(); iu++) { + final int uid = uids.keyAt(iu); + final ProcessState procState = uids.valueAt(iu); + procState.dumpProcCheckin(pw, procName, uid, now); + } } } pw.print("total"); @@ -2013,9 +2074,10 @@ public final class ProcessStats implements Parcelable { } } - public void writeToProto(ProtoOutputStream proto, long fieldId, long now) { - final long token = proto.start(fieldId); - + /** + * Writes to ProtoOutputStream. + */ + public void writeToProto(ProtoOutputStream proto, long now, int section) { proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime); proto.write(ProcessStatsSectionProto.END_REALTIME_MS, mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime); @@ -2024,15 +2086,15 @@ public final class ProcessStats implements Parcelable { proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime); proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss); boolean partial = true; - if ((mFlags&FLAG_SHUTDOWN) != 0) { + if ((mFlags & FLAG_SHUTDOWN) != 0) { proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN); partial = false; } - if ((mFlags&FLAG_SYSPROPS) != 0) { + if ((mFlags & FLAG_SYSPROPS) != 0) { proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS); partial = false; } - if ((mFlags&FLAG_COMPLETE) != 0) { + if ((mFlags & FLAG_COMPLETE) != 0) { proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE); partial = false; } @@ -2041,31 +2103,34 @@ public final class ProcessStats implements Parcelable { } final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - for (int ip=0; ip<procMap.size(); ip++) { - final String procName = procMap.keyAt(ip); - final SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - final int uid = uids.keyAt(iu); - final ProcessState procState = uids.valueAt(iu); - procState.writeToProto(proto, ProcessStatsSectionProto.PROCESS_STATS, procName, - uid, now); + if ((section & REPORT_PROC_STATS) != 0) { + for (int ip = 0; ip < procMap.size(); ip++) { + final String procName = procMap.keyAt(ip); + final SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu = 0; iu < uids.size(); iu++) { + final int uid = uids.keyAt(iu); + final ProcessState procState = uids.valueAt(iu); + procState.writeToProto(proto, ProcessStatsSectionProto.PROCESS_STATS, procName, + uid, now); + } } } - final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = - mPackages.getMap(); - for (int ip = 0; ip < pkgMap.size(); ip++) { - final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); - for (int iu = 0; iu < uids.size(); iu++) { - final LongSparseArray<PackageState> vers = uids.valueAt(iu); - for (int iv = 0; iv < vers.size(); iv++) { - final PackageState pkgState = vers.valueAt(iv); - pkgState.writeToProto(proto, ProcessStatsSectionProto.PACKAGE_STATS, now); + if ((section & REPORT_PKG_STATS) != 0) { + final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = + mPackages.getMap(); + for (int ip = 0; ip < pkgMap.size(); ip++) { + final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip); + for (int iu = 0; iu < uids.size(); iu++) { + final LongSparseArray<PackageState> vers = uids.valueAt(iu); + for (int iv = 0; iv < vers.size(); iv++) { + final PackageState pkgState = vers.valueAt(iv); + pkgState.writeToProto(proto, ProcessStatsSectionProto.PACKAGE_STATS, now, + section); + } } } } - - proto.end(token); } final public static class ProcessStateHolder { @@ -2110,30 +2175,39 @@ public final class ProcessStats implements Parcelable { return as; } - public void writeToProto(ProtoOutputStream proto, long fieldId, long now) { + /** + * Writes the containing stats into proto, with options to choose smaller sections. + */ + public void writeToProto(ProtoOutputStream proto, long fieldId, long now, int section) { final long token = proto.start(fieldId); proto.write(ProcessStatsPackageProto.PACKAGE, mPackageName); proto.write(ProcessStatsPackageProto.UID, mUid); proto.write(ProcessStatsPackageProto.VERSION, mVersionCode); - for (int ip = 0; ip < mProcesses.size(); ip++) { - final String procName = mProcesses.keyAt(ip); - final ProcessState procState = mProcesses.valueAt(ip); - procState.writeToProto(proto, ProcessStatsPackageProto.PROCESS_STATS, procName, - mUid, now); + if ((section & ProcessStats.REPORT_PKG_PROC_STATS) != 0) { + for (int ip = 0; ip < mProcesses.size(); ip++) { + final String procName = mProcesses.keyAt(ip); + final ProcessState procState = mProcesses.valueAt(ip); + procState.writeToProto(proto, ProcessStatsPackageProto.PROCESS_STATS, procName, + mUid, now); + } } - for (int is = 0; is < mServices.size(); is++) { - final ServiceState serviceState = mServices.valueAt(is); - serviceState.writeToProto(proto, ProcessStatsPackageProto.SERVICE_STATS, - now); + if ((section & ProcessStats.REPORT_PKG_SVC_STATS) != 0) { + for (int is = 0; is < mServices.size(); is++) { + final ServiceState serviceState = mServices.valueAt(is); + serviceState.writeToProto(proto, ProcessStatsPackageProto.SERVICE_STATS, + now); + } } - for (int ia=0; ia<mAssociations.size(); ia++) { - final AssociationState ascState = mAssociations.valueAt(ia); - ascState.writeToProto(proto, ProcessStatsPackageProto.ASSOCIATION_STATS, - now); + if ((section & ProcessStats.REPORT_PKG_ASC_STATS) != 0) { + for (int ia = 0; ia < mAssociations.size(); ia++) { + final AssociationState ascState = mAssociations.valueAt(ia); + ascState.writeToProto(proto, ProcessStatsPackageProto.ASSOCIATION_STATS, + now); + } } proto.end(token); diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl index c32894dd04f9..2671781a60c1 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl @@ -28,7 +28,6 @@ import com.android.internal.inputmethod.IInputContentUriToken; interface IInputMethodPrivilegedOperations { void setImeWindowStatus(int vis, int backDisposition); void reportStartInput(in IBinder startInputToken); - void clearLastInputMethodWindowForTransition(); IInputContentUriToken createInputContentUriToken(in Uri contentUri, in String packageName); void reportFullscreenMode(boolean fullscreen); void setInputMethod(String id); diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index f0e8dc59d09a..cdb986ab1a78 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -135,22 +135,6 @@ public final class InputMethodPrivilegedOperations { } /** - * Calls {@link IInputMethodPrivilegedOperations#clearLastInputMethodWindowForTransition()}. - */ - @AnyThread - public void clearLastInputMethodWindowForTransition() { - final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); - if (ops == null) { - return; - } - try { - ops.clearLastInputMethodWindowForTransition(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String)}. * * @param contentUri Content URI to which a temporary read permission should be granted diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 31f13c33c167..33b9ff7cee4f 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -337,6 +337,9 @@ public class BatteryStatsImpl extends BatteryStats { private final PlatformIdleStateCallback mPlatformIdleStateCallback; + /** + * This handler is running on {@link BackgroundThread}. + */ final class MyHandler extends Handler { public MyHandler(Looper looper) { super(looper, null, true); diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index eba4c50b0855..343aef291720 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -674,7 +674,7 @@ int register_android_graphics_Canvas(JNIEnv* env) { int ret = 0; ret |= RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods)); ret |= RegisterMethodsOrDie(env, "android/graphics/BaseCanvas", gDrawMethods, NELEM(gDrawMethods)); - ret |= RegisterMethodsOrDie(env, "android/view/RecordingCanvas", gDrawMethods, NELEM(gDrawMethods)); + ret |= RegisterMethodsOrDie(env, "android/graphics/BaseRecordingCanvas", gDrawMethods, NELEM(gDrawMethods)); return ret; } diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index 0701f3e0d2ed..63b004681df9 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -473,8 +473,8 @@ static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz, // SurfaceView position callback // ---------------------------------------------------------------------------- -jmethodID gSurfaceViewPositionUpdateMethod; -jmethodID gSurfaceViewPositionLostMethod; +jmethodID gPositionListener_PositionChangedMethod; +jmethodID gPositionListener_PositionLostMethod; static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, jlong renderNodePtr, jobject surfaceview) { @@ -531,7 +531,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, return; } - env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod, + env->CallVoidMethod(localref, gPositionListener_PositionLostMethod, info ? info->canvasContext.getFrameNumber() : 0); env->DeleteLocalRef(localref); } @@ -555,7 +555,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, env->DeleteWeakGlobalRef(mWeakRef); mWeakRef = nullptr; } else { - env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod, + env->CallVoidMethod(localref, gPositionListener_PositionChangedMethod, frameNumber, left, top, right, bottom); env->DeleteLocalRef(localref); } @@ -588,7 +588,7 @@ static const JNINativeMethod gMethods[] = { { "nGetDebugSize", "(J)I", (void*) android_view_RenderNode_getDebugSize }, { "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator }, { "nEndAllAnimators", "(J)V", (void*) android_view_RenderNode_endAllAnimators }, - { "nRequestPositionUpdates", "(JLandroid/view/SurfaceView;)V", (void*) android_view_RenderNode_requestPositionUpdates }, + { "nRequestPositionUpdates", "(JLandroid/view/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates }, { "nSetDisplayList", "(JJ)V", (void*) android_view_RenderNode_setDisplayList }, @@ -677,11 +677,11 @@ static const JNINativeMethod gMethods[] = { }; int register_android_view_RenderNode(JNIEnv* env) { - jclass clazz = FindClassOrDie(env, "android/view/SurfaceView"); - gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz, - "updateSurfacePosition_renderWorker", "(JIIII)V"); - gSurfaceViewPositionLostMethod = GetMethodIDOrDie(env, clazz, - "surfacePositionLost_uiRtSync", "(J)V"); + jclass clazz = FindClassOrDie(env, "android/view/RenderNode$PositionUpdateListener"); + gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz, + "positionChanged", "(JIIII)V"); + gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz, + "positionLost", "(J)V"); return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index 9d5f0bcc54d9..ab50ad147107 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -130,6 +130,13 @@ message KeyguardControllerProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional bool keyguard_showing = 1; + repeated KeyguardOccludedProto keyguard_occluded_states= 2; +} + +message KeyguardOccludedProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 display_id = 1; optional bool keyguard_occluded = 2; } diff --git a/core/proto/android/server/backup_chunks_metadata.proto b/core/proto/android/server/backup_chunks_metadata.proto new file mode 100644 index 000000000000..a375f02545c5 --- /dev/null +++ b/core/proto/android/server/backup_chunks_metadata.proto @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2018 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 + */ + +syntax = "proto2"; +package com.android.server.backup.encryption.chunk; + +option java_outer_classname = "ChunksMetadataProto"; + +// Cipher type with which the chunks are encrypted. For now we only support AES/GCM/NoPadding, but +// this is for backwards-compatibility in case we need to change the default Cipher in the future. +enum CipherType { + UNKNOWN_CIPHER_TYPE = 0; + // Chunk is prefixed with a 12-byte nonce. The tag length is 16 bytes. + AES_256_GCM = 1; +} + +// Checksum type with which the plaintext is verified. +enum ChecksumType { + UNKNOWN_CHECKSUM_TYPE = 0; + SHA_256 = 1; +} + +enum ChunkOrderingType { + CHUNK_ORDERING_TYPE_UNSPECIFIED = 0; + // The chunk ordering contains a list of the start position of each chunk in the encrypted file, + // ordered as in the plaintext file. This allows us to recreate the original plaintext file + // during decryption. We use this mode for full backups where the order of the data in the file + // is important. + EXPLICIT_STARTS = 1; + // The chunk ordering does not contain any start positions, and instead each encrypted chunk in + // the backup file is prefixed with its length. This allows us to decrypt each chunk but does + // not give any information about the order. However, we use this mode for key value backups + // where the order does not matter. + INLINE_LENGTHS = 2; +} + +// Chunk entry (for local state) +message Chunk { + // SHA-256 MAC of the plaintext of the chunk + optional bytes hash = 1; + // Number of bytes in encrypted chunk + optional int32 length = 2; +} + +// List of the chunks in the blob, along with the length of each chunk. From this is it possible to +// extract individual chunks. (i.e., start position is equal to the sum of the lengths of all +// preceding chunks.) +// +// This is local state stored on the device. It is never sent to the backup server. See +// ChunkOrdering for how the device restores the chunks in the correct order. +// Next tag : 6 +message ChunkListing { + repeated Chunk chunks = 1; + + // Cipher algorithm with which the chunks are encrypted. + optional CipherType cipher_type = 2; + + // Defines the type of chunk order used to encode the backup file on the server, so that we can + // consistently use the same type between backups. If unspecified this backup file was created + // before INLINE_LENGTHS was supported, thus assume it is EXPLICIT_STARTS. + optional ChunkOrderingType chunk_ordering_type = 5; + + // The document ID returned from Scotty server after uploading the blob associated with this + // listing. This needs to be sent when uploading new diff scripts. + optional string document_id = 3; + + // Fingerprint mixer salt used for content defined chunking. This is randomly generated for each + // package during the initial non-incremental backup and reused for incremental backups. + optional bytes fingerprint_mixer_salt = 4; +} + +// Ordering information about plaintext and checksum. This is used on restore to reconstruct the +// blob in its correct order. (The chunk order is randomized so as to give the server less +// information about which parts of the backup are changing over time.) This proto is encrypted +// before being uploaded to the server, with a key unknown to the server. +message ChunkOrdering { + // For backups where ChunksMetadata#chunk_ordering_type = EXPLICIT STARTS: + // Ordered start positions of chunks. i.e., the file is the chunk starting at this position, + // followed by the chunk starting at this position, followed by ... etc. You can compute the + // lengths of the chunks by sorting this list then looking at the start position of the next + // chunk after the chunk you care about. This is guaranteed to work as all chunks are + // represented in this list. + // + // For backups where ChunksMetadata#chunk_ordering_type = INLINE_LENGTHS: + // This field is unused. See ChunkOrderingType#INLINE_LENGTHS. + repeated int32 starts = 1 [packed = true]; + + // Checksum of plaintext content. (i.e., in correct order.) + // + // Each chunk also has a MAC, as generated by GCM, so this is NOT Mac-then-Encrypt, which has + // security implications. This is an additional checksum to verify that once the chunks have + // been reordered, that the file matches the expected plaintext. This prevents the device + // restoring garbage data in case of a mismatch between the ChunkOrdering and the backup blob. + optional bytes checksum = 2; +} + +// Additional metadata about a backup blob that needs to be synced to the server. This is used on +// restore to reconstruct the blob in its correct order. (The chunk order is randomized so as to +// give the server less information about which parts of the backup are changing over time.) This +// data structure is only ever uploaded to the server encrypted with a key unknown to the server. +// Next tag : 6 +message ChunksMetadata { + // Cipher algorithm with which the chunk listing and chunks are encrypted. + optional CipherType cipher_type = 1; + + // Defines the type of chunk order this metadata contains. If unspecified this backup file was + // created before INLINE_LENGTHS was supported, thus assume it is EXPLICIT_STARTS. + optional ChunkOrderingType chunk_ordering_type = 5 + [default = CHUNK_ORDERING_TYPE_UNSPECIFIED]; + + // Encrypted bytes of ChunkOrdering + optional bytes chunk_ordering = 2; + + // The type of algorithm used for the checksum of the plaintext. (See ChunkOrdering.) This is + // for forwards compatibility in case we change the algorithm in the future. For now, always + // SHA-256. + optional ChecksumType checksum_type = 3; + + // This used to be the plaintext tertiary key. No longer used. + reserved 4; +}
\ No newline at end of file diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5a1f2e8d870c..ade0b111111d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4144,6 +4144,10 @@ <permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" android:protectionLevel="signature" /> + <!-- A subclass of {@link android.app.SmsAppService} must be protected with this permission. --> + <permission android:name="android.permission.BIND_SMS_APP_SERVICE" + android:protectionLevel="signature" /> + <application android:process="system" android:persistent="true" android:hasCode="false" diff --git a/core/res/res/drawable/ic_ab_back_material_settings.xml b/core/res/res/drawable/ic_ab_back_material_settings.xml index 7325a4173a7f..938e36d4ecb5 100644 --- a/core/res/res/drawable/ic_ab_back_material_settings.xml +++ b/core/res/res/drawable/ic_ab_back_material_settings.xml @@ -18,11 +18,12 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" android:autoMirrored="true" - android:tint="?attr/colorControlNormal"> + android:tint="?attr/colorControlNormal" + android:viewportHeight="24" + android:viewportWidth="24"> <path android:fillColor="@color/white" - android:pathData="M20,11H7.62l4.88,-4.88a0.996,0.996 0,1 0,-1.41 -1.41l-6.94,6.94c-0.2,0.2 -0.2,0.51 0,0.71l6.94,6.94a0.996,0.996 0,1 0,1.41 -1.41L7.62,13H20c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1z"/> + android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8l8,8l1.41,-1.41L7.83,13H20V11z"/> </vector> + diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml index 0712cbcfc024..48b59c70a61e 100644 --- a/core/res/res/values-television/themes.xml +++ b/core/res/res/values-television/themes.xml @@ -15,7 +15,6 @@ --> <resources> <style name="Theme.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" /> - <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" /> <style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" /> <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" /> <style name="Theme.Material.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" /> diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml index 227972e6a12e..fdd06242c1e0 100644 --- a/core/res/res/values-television/themes_device_defaults.xml +++ b/core/res/res/values-television/themes_device_defaults.xml @@ -15,14 +15,20 @@ --> <resources> <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" /> + <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" /> <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" /> - <style name="Theme.DeviceDefault.Autofill" parent="Theme.Material"> + <!-- TODO(b/116457731): remove colorBackground from colors_material.xml if not used anymore --> + <style name="Theme.DeviceDefault.Autofill" parent="Theme.Material"> <item name="colorBackground">@color/autofill_background_material_dark</item> </style> <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.Material.Panel"> - <!-- TODO(b/116457731): remove colorBackground from colors_material.xml if not used anymore --> <item name="colorBackground">@color/autofill_background_material_dark</item> </style> + + <!-- TV always use dark mode --> + <style name="Theme.DeviceDefault.Autofill.Light" parent="Theme.DeviceDefault.Autofill"/> + <style name="Theme.DeviceDefault.Light.Autofill.Save" parent="Theme.DeviceDefault.Autofill.Save"/> + <style name="Theme.DeviceDefault.Resolver" parent="Theme.Leanback.Resolver" /> </resources> diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml index a7736e7c3e82..bfba312da016 100644 --- a/core/res/res/values-watch/themes_device_defaults.xml +++ b/core/res/res/values-watch/themes_device_defaults.xml @@ -422,6 +422,13 @@ a similar way. <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item> </style> + <!-- Theme for the dialog shown when an app crashes or ANRs. Override to make it dark. --> + <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert"> + <item name="windowContentTransitions">false</item> + <item name="windowActivityTransitions">false</item> + <item name="windowCloseOnTouchOutside">false</item> + </style> + <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar"> <!-- Color palette Dark --> <item name="colorPrimary">@color/primary_device_default_dark</item> diff --git a/core/res/res/values/colors_car.xml b/core/res/res/values/colors_car.xml index 6053728369e1..32671ac8f752 100644 --- a/core/res/res/values/colors_car.xml +++ b/core/res/res/values/colors_car.xml @@ -17,15 +17,271 @@ */ --> <resources> - <!-- car support colors from - https://cs.corp.google.com/android/frameworks/support/car/res/values/colors.xml --> - <color name="car_user_switcher_user_image_bgcolor">@color/car_grey_50</color> - <color name="car_user_switcher_user_image_fgcolor">@color/car_grey_900</color> - <color name="car_card_dark">@color/car_dark_blue_grey_700</color> - <color name="car_body1_light">@color/car_grey_100</color> - - <color name="car_grey_50">#fffafafa</color> - <color name="car_grey_900">#ff212121</color> - <color name="car_dark_blue_grey_700">#ff172026</color> - <color name="car_grey_100">#fff5f5f5</color> + <color name="car_background">@color/black</color> + + <color name="car_colorPrimary">@color/car_grey_868</color> + <color name="car_colorSecondary">@color/car_grey_900</color> + <color name="car_colorPrimaryDark">@color/car_grey_958</color> + + + <!-- Various colors for text sizes. "Light" and "dark" here refer to the lighter or darker + shades. --> + <color name="car_title_light">@color/car_grey_100</color> + <color name="car_title_dark">@color/car_grey_900</color> + <color name="car_title">@color/car_title_light</color> + + <color name="car_title2_light">@color/car_grey_100</color> + <color name="car_title2_dark">@color/car_grey_900</color> + <color name="car_title2">@color/car_title2_light</color> + + <color name="car_headline1_light">@color/car_grey_100</color> + <color name="car_headline1_dark">@color/car_grey_800</color> + <color name="car_headline1">@color/car_headline1_light</color> + + <color name="car_headline2_light">@color/car_grey_100</color> + <color name="car_headline2_dark">@color/car_grey_900</color> + <color name="car_headline2">@color/car_headline2_light</color> + + <color name="car_headline3_light">@android:color/white</color> + <color name="car_headline3_dark">@color/car_grey_900</color> + <color name="car_headline3">@color/car_headline3_light</color> + + <color name="car_headline4_light">@android:color/white</color> + <color name="car_headline4_dark">@android:color/black</color> + <color name="car_headline4">@color/car_headline4_light</color> + + <color name="car_body1_light">@color/car_grey_100</color> + <color name="car_body1_dark">@color/car_grey_900</color> + <color name="car_body1">@color/car_body1_light</color> + + <color name="car_body2_light">@color/car_grey_300</color> + <color name="car_body2_dark">@color/car_grey_700</color> + <color name="car_body2">@color/car_body2_light</color> + + <color name="car_body3_light">@android:color/white</color> + <color name="car_body3_dark">@android:color/black</color> + <color name="car_body3">@color/car_body3_light</color> + + <color name="car_body4_light">@android:color/white</color> + <color name="car_body4_dark">@android:color/black</color> + <color name="car_body4">@color/car_body4_light</color> + + <color name="car_action1_light">@color/car_grey_900</color> + <color name="car_action1_dark">@color/car_grey_50</color> + <color name="car_action1">@color/car_action1_light</color> + + <!-- The tinting colors to create a light- and dark-colored icon respectively. --> + <color name="car_tint_light">@color/car_grey_50</color> + <color name="car_tint_dark">@color/car_grey_900</color> + + <!-- The tinting color for an icon. This icon is assumed to be on a light background. --> + <color name="car_tint">@color/car_tint_light</color> + + <!-- An inverted tinting from car_tint. --> + <color name="car_tint_inverse">@color/car_tint_dark</color> + + <!-- The color of the divider. The color here is a lighter shade. --> + <color name="car_list_divider_light">#1fffffff</color> + + <!-- The color of the divider. The color here is a darker shade. --> + <color name="car_list_divider_dark">#1f000000</color> + + <!-- The color of the dividers in the list. This color is assumed to be on a light colored + view. --> + <color name="car_list_divider">@color/car_list_divider_light</color> + + <!-- A light and dark colored card. --> + <color name="car_card_light">@color/car_grey_50</color> + <color name="car_card_dark">@color/car_dark_blue_grey_700</color> + + <!-- The default color of a card in car UI. --> + <color name="car_card">@color/car_card_dark</color> + + <!-- The ripple colors. The "dark" and "light" designation here refers to the color of the + ripple itself. --> + <color name="car_card_ripple_background_dark">#8F000000</color> + <color name="car_card_ripple_background_light">#27ffffff</color> + + <!-- The ripple color for a light colored card. --> + <color name="car_card_ripple_background">@color/car_card_ripple_background_light</color> + + <!-- The ripple color for a dark-colored card. This color is the opposite of + car_card_ripple_background. --> + <color name="car_card_ripple_background_inverse">@color/car_card_ripple_background_dark</color> + + <!-- The top margin before the start of content in an application. --> + <dimen name="app_header_height">96dp</dimen> + + <!-- The lighter and darker color for the scrollbar thumb. --> + <color name="car_scrollbar_thumb_light">#99ffffff</color> + <color name="car_scrollbar_thumb_dark">#7f0b0f12</color> + + <!-- The color of the scroll bar indicator in the PagedListView. This color is assumed to be on + a light-colored background. --> + <color name="car_scrollbar_thumb">@color/car_scrollbar_thumb_light</color> + + <!-- The inverted color of the scroll bar indicator. This color is always the opposite of + car_scrollbar_thumb. --> + <color name="car_scrollbar_thumb_inverse">@color/car_scrollbar_thumb_dark</color> + + <!-- The color of the seekbar track secondary progress in SeekbarListItem. --> + <color name="car_seekbar_track_secondary_progress">@color/car_grey_500</color> + + <!-- The lighter and darker color for the seekbar track background. --> + <color name="car_seekbar_track_background_light">@color/car_grey_400</color> + <color name="car_seekbar_track_background_dark">@color/car_grey_700</color> + <!-- The color of the seekbar track background in SeekbarListItem. This color is assumed to be + on a light-colored background. --> + <color name="car_seekbar_track_background">@color/car_seekbar_track_background_dark</color> + <!-- background is car_grey_868 with .9 alpha --> + <color name="car_toast_background">#E6282a2d</color> + + <!-- Misc colors --> + <color name="car_highlight_light">@color/car_teal_700</color> + <color name="car_highlight_dark">@color/car_teal_200</color> + <color name="car_highlight">@color/car_highlight_dark</color> + <color name="car_accent_light">@color/car_highlight_light</color> + <color name="car_accent_dark">@color/car_highlight_dark</color> + <color name="car_accent">@color/car_highlight_dark</color> + + <color name="car_user_switcher_user_image_bgcolor">@color/car_grey_50</color> + <color name="car_user_switcher_user_image_fgcolor">@color/car_grey_900</color> + + <!-- Color palette for cars --> + <color name="car_grey_958">#ff0e1013</color> + <color name="car_grey_928">#ff17181b</color> + <color name="car_grey_900">#ff202124</color> + <color name="car_grey_868">#ff282a2d</color> + <color name="car_grey_846">#ff2e3234</color> + <color name="car_grey_800">#ff3c4043</color> + <color name="car_grey_772">#ff464a4d</color> + <color name="car_grey_746">#ff4d5256</color> + <color name="car_grey_700">#ff5f6368</color> + <color name="car_grey_600">#ff80868b</color> + <color name="car_grey_500">#ff9aa0a6</color> + <color name="car_grey_400">#ffbdc1c6</color> + <color name="car_grey_300">#ffdadce0</color> + <color name="car_grey_200">#ffe8eaed</color> + <color name="car_grey_100">#fff1f3f4</color> + <color name="car_grey_50">#fff8f9fa</color> + + <color name="car_blue_900">#ff1d57a9</color> + <color name="car_blue_800">#ff2065bb</color> + <color name="car_blue_700">#ff2374ce</color> + <color name="car_blue_600">#ff2581df</color> + <color name="car_blue_500">#ff5195ea</color> + <color name="car_blue_400">#ff6ba5ed</color> + <color name="car_blue_300">#ff96bff2</color> + <color name="car_blue_200">#ffb9d4f6</color> + <color name="car_blue_100">#ffd9e6f9</color> + <color name="car_blue_50">#ffebf1fc</color> + + <color name="car_green_900">#ff136e39</color> + <color name="car_green_800">#ff1b7e42</color> + <color name="car_green_700">#ff218c48</color> + <color name="car_green_600">#ff28994f</color> + <color name="car_green_500">#ff41af6a</color> + <color name="car_green_400">#ff5dba80</color> + <color name="car_green_300">#ff8dcfa5</color> + <color name="car_green_200">#ffb3dfc3</color> + <color name="car_green_100">#ffd5ebdf</color> + <color name="car_green_50">#ffe8f3ee</color> + + <color name="car_red_900">#ffa81314</color> + <color name="car_red_800">#ffb41b1a</color> + <color name="car_red_700">#ffc22a2a</color> + <color name="car_red_600">#ffd33b30</color> + <color name="car_red_500">#ffe25142</color> + <color name="car_red_400">#ffe66a5e</color> + <color name="car_red_300">#ffed968d</color> + <color name="car_red_200">#fff3b9b3</color> + <color name="car_red_100">#fff7d8d9</color> + <color name="car_red_50">#fffaebeb</color> + + <color name="car_yellow_900">#ffdd860e</color> + <color name="car_yellow_800">#ffe59810</color> + <color name="car_yellow_700">#ffeda912</color> + <color name="car_yellow_600">#fff3b713</color> + <color name="car_yellow_500">#fff5c518</color> + <color name="car_yellow_400">#fff6cd3a</color> + <color name="car_yellow_300">#fff9dc74</color> + <color name="car_yellow_200">#fffbe7a2</color> + <color name="car_yellow_100">#fffcf0ce</color> + <color name="car_yellow_50">#fffdf7e6</color> + + <color name="car_orange_900">#ffb06000</color> + <color name="car_orange_800">#ffc26401</color> + <color name="car_orange_700">#ffd56e0c</color> + <color name="car_orange_600">#ffe8710a</color> + <color name="car_orange_500">#fffa7b17</color> + <color name="car_orange_400">#fffa903e</color> + <color name="car_orange_300">#fffcad70</color> + <color name="car_orange_200">#fffdc69c</color> + <color name="car_orange_100">#fffedfc8</color> + <color name="car_orange_50">#fffeefe3</color> + + <color name="car_pink_900">#ff9c166b</color> + <color name="car_pink_800">#ffb80672</color> + <color name="car_pink_700">#ffd01884</color> + <color name="car_pink_600">#ffe52592</color> + <color name="car_pink_500">#fff439a0</color> + <color name="car_pink_400">#ffff63b8</color> + <color name="car_pink_300">#ffff8bcb</color> + <color name="car_pink_200">#fffba9d6</color> + <color name="car_pink_100">#fffdcfe8</color> + <color name="car_pink_50">#fffde7f3</color> + + <color name="car_teal_900">#ff004d40</color> + <color name="car_teal_800">#ff00695c</color> + <color name="car_teal_700">#ff00796b</color> + <color name="car_teal_600">#ff00897b</color> + <color name="car_teal_500">#ff009688</color> + <color name="car_teal_400">#ff26a69a</color> + <color name="car_teal_300">#ff4db6ac</color> + <color name="car_teal_200">#ff80cbc4</color> + <color name="car_teal_100">#ffb2dfdb</color> + <color name="car_teal_50">#ffe0f2f1</color> + + <color name="car_purple_900">#ff681da8</color> + <color name="car_purple_800">#ff7627bb</color> + <color name="car_purple_700">#ff8430ce</color> + <color name="car_purple_600">#ff9334e6</color> + <color name="car_purple_500">#ffa142f4</color> + <color name="car_purple_400">#ffaf5cf7</color> + <color name="car_purple_300">#ffc58af9</color> + <color name="car_purple_200">#ffd7aefb</color> + <color name="car_purple_100">#ffe9d2fd</color> + <color name="car_purple_50">#fff3e8fd</color> + + <color name="car_cyan_900">#ff01877e</color> + <color name="car_cyan_800">#ff099091</color> + <color name="car_cyan_700">#ff12a4af</color> + <color name="car_cyan_600">#ff12b5cb</color> + <color name="car_cyan_500">#ff24c1e0</color> + <color name="car_cyan_400">#ff4ecde6</color> + <color name="car_cyan_300">#ff78d9ec</color> + <color name="car_cyan_200">#ffa1e4f2</color> + <color name="car_cyan_100">#ffcbf0f8</color> + <color name="car_cyan_50">#ffe4f7fb</color> + + + <color name="car_grey_1000">#cc000000</color> + <color name="car_white_1000">#1effffff</color> + <color name="car_blue_grey_800">#ff37474F</color> + <color name="car_blue_grey_900">#ff263238</color> + <color name="car_dark_blue_grey_600">#ff1d272d</color> + <color name="car_dark_blue_grey_700">#ff172026</color> + <color name="car_dark_blue_grey_800">#ff11181d</color> + <color name="car_dark_blue_grey_900">#ff0c1013</color> + <color name="car_dark_blue_grey_1000">#ff090c0f</color> + <color name="car_light_blue_300">#ff4fc3f7</color> + <color name="car_light_blue_500">#ff03A9F4</color> + <color name="car_light_blue_600">#ff039be5</color> + <color name="car_light_blue_700">#ff0288d1</color> + <color name="car_light_blue_800">#ff0277bd</color> + <color name="car_light_blue_900">#ff01579b</color> + + + <color name="car_red_500a">#ffd50000</color> + <color name="car_red_a700">#ffd50000</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1404383bc234..9aebf6c4597f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3541,4 +3541,13 @@ <!-- Brand value for attestation of misprovisioned device. --> <string name="config_misprovisionedBrandValue" translatable="false"></string> + + <!-- Pre-scale volume at volume step 1 for Absolute Volume --> + <fraction name="config_prescaleAbsoluteVolume_index1">50%</fraction> + + <!-- Pre-scale volume at volume step 2 for Absolute Volume --> + <fraction name="config_prescaleAbsoluteVolume_index2">70%</fraction> + + <!-- Pre-scale volume at volume step 3 for Absolute Volume --> + <fraction name="config_prescaleAbsoluteVolume_index3">85%</fraction> </resources> diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml index 7d14f868fefc..a0c02ede1890 100644 --- a/core/res/res/values/dimens_car.xml +++ b/core/res/res/values/dimens_car.xml @@ -16,12 +16,86 @@ */ --> <resources> - <!-- TODO replace with car support lib sizes when available --> <dimen name="car_fullscreen_user_pod_icon_text_size">64sp</dimen> <dimen name="car_fullscreen_user_pod_width">243dp</dimen> <dimen name="car_fullscreen_user_pod_height">356dp</dimen> <dimen name="car_fullscreen_user_pod_image_avatar_width">96dp</dimen> <dimen name="car_fullscreen_user_pod_image_avatar_height">96dp</dimen> - <dimen name="car_padding_4">20dp</dimen> + + + <!-- Application Bar --> + <dimen name="car_app_bar_height">80dp</dimen> + <!-- Margin --> + <dimen name="car_margin">20dp</dimen> + <!-- Lists --> + <dimen name="car_single_line_list_item_height">96dp</dimen> + <dimen name="car_double_line_list_item_height">@dimen/car_single_line_list_item_height</dimen> + <dimen name="car_list_divider_height">1dp</dimen> + <!-- The diff between keyline 1 and keyline 3. --> + <dimen name="car_keyline_1_keyline_3_diff">88dp</dimen> + <dimen name="car_dialog_action_bar_height">@dimen/car_card_action_bar_height</dimen> + <dimen name="car_primary_icon_size">44dp</dimen> + + <!-- Text size for car --> + <dimen name="car_title_size">32sp</dimen> + <dimen name="car_title2_size">32sp</dimen> + <dimen name="car_headline1_size">45sp</dimen> + <dimen name="car_headline2_size">32sp</dimen> + <dimen name="car_headline3_size">24sp</dimen> + <dimen name="car_headline4_size">20sp</dimen> <dimen name="car_body1_size">32sp</dimen> -</resources>
\ No newline at end of file + <dimen name="car_body2_size">26sp</dimen> + <dimen name="car_body3_size">16sp</dimen> + <dimen name="car_body4_size">14sp</dimen> + <dimen name="car_body5_size">18sp</dimen> + <dimen name="car_label1_size">26sp</dimen> + <dimen name="car_label2_size">64sp</dimen> + <dimen name="car_action1_size">26sp</dimen> + <dimen name="car_action2_size">26sp</dimen> + + <!-- Common icon size for car app --> + <dimen name="car_icon_size">56dp</dimen> + + <dimen name="car_card_header_height">96dp</dimen> + <dimen name="car_card_action_bar_height">96dp</dimen> + + <!-- Paddings --> + <dimen name="car_padding_1">4dp</dimen> + <dimen name="car_padding_2">10dp</dimen> + <dimen name="car_padding_3">16dp</dimen> + <dimen name="car_padding_4">28dp</dimen> + <dimen name="car_padding_5">32dp</dimen> + + <!-- Radius --> + <dimen name="car_radius_1">4dp</dimen> + <dimen name="car_radius_2">8dp</dimen> + <dimen name="car_radius_3">16dp</dimen> + <dimen name="car_radius_5">100dp</dimen> + + <!-- Keylines for content. --> + <dimen name="car_keyline_1">48dp</dimen> + <dimen name="car_keyline_2">108dp</dimen> + <dimen name="car_keyline_3">152dp</dimen> + <dimen name="car_keyline_4">182dp</dimen> + + <!-- Buttons --> + <dimen name="car_button_height">56dp</dimen> + <dimen name="car_button_min_width">158dp</dimen> + <dimen name="car_button_horizontal_padding">@dimen/car_padding_4</dimen> + <dimen name="car_borderless_button_horizontal_padding">0dp</dimen> + <dimen name="car_button_radius">@dimen/car_radius_1</dimen> + <dimen name="car_pill_button_size">56dp</dimen> + + <!-- Seekbar --> + <dimen name="car_seekbar_height">6dp</dimen> + <dimen name="car_seekbar_padding">26dp</dimen> + <dimen name="car_seekbar_thumb_size">24dp</dimen> + <dimen name="car_seekbar_thumb_stroke">1dp</dimen> + <!-- The space between seekbar and text in ListItem. This value is based on car_seekbar_padding. + It brings seekbar and text closer for visual balance while maintaining touch area. --> + <dimen name="car_seekbar_text_overlap">-20dp</dimen> + + <!-- Progress Bar --> + <dimen name="car_progress_bar_height">@dimen/car_seekbar_height</dimen> + +</resources> diff --git a/core/res/res/values/styles_car.xml b/core/res/res/values/styles_car.xml new file mode 100644 index 000000000000..f6ff1b651788 --- /dev/null +++ b/core/res/res/values/styles_car.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2018 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. +--> +<resources> + <!-- Car text --> + <style name="CarBody1"> + <item name="textStyle">normal</item> + <item name="textSize">@dimen/car_body1_size</item> + <item name="textColor">@color/car_body1</item> + </style> + + <style name="CarBody1.Light"> + <item name="textColor">@color/car_body1_light</item> + </style> + + <style name="CarBody1.Dark"> + <item name="textColor">@color/car_body2_dark</item> + </style> + + <style name="CarBody2"> + <item name="textStyle">normal</item> + <item name="textSize">@dimen/car_body2_size</item> + <item name="textColor">@color/car_body2</item> + </style> + + <style name="CarBody2.Dark"> + <item name="textColor">@color/car_body2_dark</item> + </style> + <style name="CarBody2.Light"> + <item name="textColor">@color/car_body2_light</item> + </style> + + <style name="CarBody3"> + <item name="textStyle">normal</item> + <item name="textSize">@dimen/car_body3_size</item> + <item name="textColor">@color/car_body3</item> + </style> + + <!-- The smallest styling for body text. The color of this text changes based on the day/night + mode. --> + <style name="CarBody4"> + <item name="textStyle">normal</item> + <item name="textSize">@dimen/car_body4_size</item> + <item name="textColor">@color/car_body4</item> + </style> + + <style name="CarAction1"> + <item name="textStyle">bold</item> + <item name="textSize">@dimen/car_action1_size</item> + <item name="textColor">@color/car_highlight</item> + </style> + + <style name="CarAction1.Dark"> + <item name="textColor">@color/car_highlight_dark</item> + </style> + <style name="CarAction1.Light"> + <item name="textColor">@color/car_highlight_light</item> + </style> + + <!-- The styling for title text. The color of this text changes based on day/night mode. --> + <style name="CarTitle" > + <item name="textStyle">bold</item> + <item name="textSize">@dimen/car_title2_size</item> + <item name="textColor">@color/car_title</item> + </style> + + <!-- Title text that is permanently a dark color. --> + <style name="CarTitle.Dark" > + <item name="textColor">@color/car_title_dark</item> + </style> + + <!-- Title text that is permanently a light color. --> + <style name="CarTitle.Light" > + <item name="textColor">@color/car_title_light</item> + </style> + +</resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cf859867f21c..9f2256a6461a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3154,7 +3154,9 @@ <java-symbol type="integer" name="autofill_max_visible_datasets" /> <java-symbol type="style" name="Theme.DeviceDefault.Autofill" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Autofill" /> <java-symbol type="style" name="Theme.DeviceDefault.Autofill.Save" /> + <java-symbol type="style" name="Theme.DeviceDefault.Light.Autofill.Save" /> <java-symbol type="dimen" name="notification_big_picture_max_height"/> <java-symbol type="dimen" name="notification_big_picture_max_width"/> @@ -3468,4 +3470,9 @@ <java-symbol type="integer" name="db_wal_truncate_size" /> <java-symbol type="integer" name="config_wakeUpDelayDoze" /> <java-symbol type="string" name="config_dozeWakeScreenSensorType" /> + + <!-- For Bluetooth AbsoluteVolume --> + <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index1" /> + <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index2" /> + <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index3" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index f951f274785a..3385527ee6ff 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1681,11 +1681,14 @@ easier. </style> - <!-- @hide DeviceDefault theme for the autofill FillUi --> - <style name="Theme.DeviceDefault.Autofill" parent="Theme.DeviceDefault.Light"/> + <!-- @hide DeviceDefault themes for the autofill FillUi --> + <style name="Theme.DeviceDefault.Autofill" /> + <style name="Theme.DeviceDefault.Light.Autofill" /> - <!-- @hide DeviceDefault theme for the autofill SaveUi --> - <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.DeviceDefault.Light.Panel"/> + <!-- @hide DeviceDefault theme for the autofill SaveUi. NOTE: it must be a .Panel so the dialog + is shown at the bottom of the screen --> + <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.DeviceDefault.Panel"/> + <style name="Theme.DeviceDefault.Light.Autofill.Save" parent="Theme.DeviceDefault.Light.Panel"/> <!-- DeviceDefault theme for the default system theme. --> <style name="Theme.DeviceDefault.System" parent="Theme.DeviceDefault.Light.DarkActionBar" /> @@ -1713,4 +1716,10 @@ easier. <item name="colorAccent">@color/accent_device_default_dark</item> </style> + <style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen"> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_device_default_light</item> + <item name="layout_gravity">center</item> + </style> + </resources> diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java new file mode 100644 index 000000000000..2142f27ff6d8 --- /dev/null +++ b/core/tests/coretests/src/android/content/ContentProviderTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.content; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.withSettings; + +import android.content.pm.ApplicationInfo; +import android.content.pm.ProviderInfo; +import android.net.Uri; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; + +@RunWith(AndroidJUnit4.class) +public class ContentProviderTest { + + private Context mContext; + private ContentProvider mCp; + + private ApplicationInfo mProviderApp; + private ProviderInfo mProvider; + + @Before + public void setUp() { + mProviderApp = new ApplicationInfo(); + mProviderApp.uid = 10001; + + mProvider = new ProviderInfo(); + mProvider.authority = "com.example"; + mProvider.applicationInfo = mProviderApp; + + mContext = mock(Context.class); + + mCp = mock(ContentProvider.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)); + mCp.attachInfo(mContext, mProvider); + } + + @Test + public void testValidateIncomingUri_Normal() throws Exception { + assertEquals(Uri.parse("content://com.example/"), + mCp.validateIncomingUri(Uri.parse("content://com.example/"))); + assertEquals(Uri.parse("content://com.example/foo/bar"), + mCp.validateIncomingUri(Uri.parse("content://com.example/foo/bar"))); + assertEquals(Uri.parse("content://com.example/foo%2Fbar"), + mCp.validateIncomingUri(Uri.parse("content://com.example/foo%2Fbar"))); + assertEquals(Uri.parse("content://com.example/foo%2F%2Fbar"), + mCp.validateIncomingUri(Uri.parse("content://com.example/foo%2F%2Fbar"))); + } + + @Test + public void testValidateIncomingUri_Shady() throws Exception { + assertEquals(Uri.parse("content://com.example/"), + mCp.validateIncomingUri(Uri.parse("content://com.example//"))); + assertEquals(Uri.parse("content://com.example/foo/bar/"), + mCp.validateIncomingUri(Uri.parse("content://com.example//foo//bar//"))); + assertEquals(Uri.parse("content://com.example/foo/bar/"), + mCp.validateIncomingUri(Uri.parse("content://com.example/foo///bar/"))); + assertEquals(Uri.parse("content://com.example/foo%2F%2Fbar/baz"), + mCp.validateIncomingUri(Uri.parse("content://com.example/foo%2F%2Fbar//baz"))); + } + + @Test + public void testValidateIncomingUri_NonPath() throws Exception { + // We only touch paths; queries and fragments are left intact + assertEquals(Uri.parse("content://com.example/foo/bar?foo=b//ar#foo=b//ar"), + mCp.validateIncomingUri( + Uri.parse("content://com.example/foo/bar?foo=b//ar#foo=b//ar"))); + } +} diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index 53e9826998f3..fa37bedb06c4 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -28,11 +28,10 @@ import android.text.PrecomputedText; import android.text.SpannableString; import android.text.SpannedString; import android.text.TextUtils; -import android.view.RecordingCanvas; /** * This class is a base class for Canvas's drawing operations. Any modifications here - * should be accompanied by a similar modification to {@link RecordingCanvas}. + * should be accompanied by a similar modification to {@link BaseRecordingCanvas}. * * The purpose of this class is to minimize the cost of deciding between regular JNI * and @FastNative JNI to just the virtual call that Canvas already has. diff --git a/core/java/android/view/RecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java index 33644832bdf1..6e936910ef40 100644 --- a/core/java/android/view/RecordingCanvas.java +++ b/graphics/java/android/graphics/BaseRecordingCanvas.java @@ -14,25 +14,12 @@ * limitations under the License. */ -package android.view; +package android.graphics; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; -import android.graphics.BaseCanvas; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.NinePatch; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.Picture; -import android.graphics.PorterDuff; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.TemporaryBuffer; import android.text.GraphicsOperations; import android.text.MeasuredParagraph; import android.text.PrecomputedText; @@ -49,9 +36,9 @@ import dalvik.annotation.optimization.FastNative; * * @hide */ -public class RecordingCanvas extends Canvas { +public class BaseRecordingCanvas extends Canvas { - public RecordingCanvas(long nativeCanvas) { + public BaseRecordingCanvas(long nativeCanvas) { super(nativeCanvas); } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 33caa00db208..69ff3bca6528 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -592,49 +592,6 @@ public class Paint { mShadowLayerColor = paint.mShadowLayerColor; } - /** - * Returns true if all attributes are equal. - * - * The caller is expected to have checked the trivial cases, like the pointers being equal, - * the objects having different classes, or the parameter being null. - * @hide - */ - public boolean hasEqualAttributes(@NonNull Paint other) { - return mColorFilter == other.mColorFilter - && mMaskFilter == other.mMaskFilter - && mPathEffect == other.mPathEffect - && mShader == other.mShader - && mTypeface == other.mTypeface - && mXfermode == other.mXfermode - && mHasCompatScaling == other.mHasCompatScaling - && mCompatScaling == other.mCompatScaling - && mInvCompatScaling == other.mInvCompatScaling - && mBidiFlags == other.mBidiFlags - && mLocales.equals(other.mLocales) - && TextUtils.equals(mFontFeatureSettings, other.mFontFeatureSettings) - && TextUtils.equals(mFontVariationSettings, other.mFontVariationSettings) - && mShadowLayerRadius == other.mShadowLayerRadius - && mShadowLayerDx == other.mShadowLayerDx - && mShadowLayerDy == other.mShadowLayerDy - && mShadowLayerColor == other.mShadowLayerColor - && getFlags() == other.getFlags() - && getHinting() == other.getHinting() - && getStyle() == other.getStyle() - && getColor() == other.getColor() - && getStrokeWidth() == other.getStrokeWidth() - && getStrokeMiter() == other.getStrokeMiter() - && getStrokeCap() == other.getStrokeCap() - && getStrokeJoin() == other.getStrokeJoin() - && getTextAlign() == other.getTextAlign() - && isElegantTextHeight() == other.isElegantTextHeight() - && getTextSize() == other.getTextSize() - && getTextScaleX() == other.getTextScaleX() - && getTextSkewX() == other.getTextSkewX() - && getLetterSpacing() == other.getLetterSpacing() - && getWordSpacing() == other.getWordSpacing() - && getHyphenEdit() == other.getHyphenEdit(); - } - /** @hide */ @UnsupportedAppUsage public void setCompatibilityScaling(float factor) { @@ -1395,6 +1352,38 @@ public class Paint { } /** + * Returns the blur radius of the shadow layer. + * @see #setShadowLayer(float,float,float,int) + */ + public float getShadowLayerRadius() { + return mShadowLayerRadius; + } + + /** + * Returns the x offset of the shadow layer. + * @see #setShadowLayer(float,float,float,int) + */ + public float getShadowLayerDx() { + return mShadowLayerDx; + } + + /** + * Returns the y offset of the shadow layer. + * @see #setShadowLayer(float,float,float,int) + */ + public float getShadowLayerDy() { + return mShadowLayerDy; + } + + /** + * Returns the color of the shadow layer. + * @see #setShadowLayer(float,float,float,int) + */ + public @ColorInt int getShadowLayerColor() { + return mShadowLayerColor; + } + + /** * Return the paint's Align value for drawing text. This controls how the * text is positioned relative to its origin. LEFT align means that all of * the text will be drawn to the right of its origin (i.e. the origin diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 76f2cfb33824..6c1372ff25b4 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -51,6 +51,7 @@ import android.util.Property; import android.util.TimeUtils; import android.view.Choreographer; import android.view.DisplayListCanvas; +import android.view.NativeVectorDrawableAnimator; import android.view.RenderNode; import android.view.RenderNodeAnimatorSetHelper; import android.view.View; @@ -1231,7 +1232,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { /** * @hide */ - public static class VectorDrawableAnimatorRT implements VectorDrawableAnimator { + public static class VectorDrawableAnimatorRT implements VectorDrawableAnimator, + NativeVectorDrawableAnimator { private static final int START_ANIMATION = 1; private static final int REVERSE_ANIMATION = 2; private static final int RESET_ANIMATION = 3; @@ -1704,6 +1706,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } } + @Override public long getAnimatorNativePtr() { return mSetPtr; } diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index 1458c66a2c54..bd1ac25bf8df 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -422,9 +422,10 @@ public final class Font { nAddAxis(builderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue()); } } - final long ptr = nBuild(builderPtr, mBuffer, mWeight, italic, mTtcIndex); - final Font font = new Font(ptr, mBuffer, mFile, mWeight, italic, mTtcIndex, mAxes, - mLocaleList); + final ByteBuffer readonlyBuffer = mBuffer.asReadOnlyBuffer(); + final long ptr = nBuild(builderPtr, readonlyBuffer, mWeight, italic, mTtcIndex); + final Font font = new Font(ptr, readonlyBuffer, mFile, mWeight, italic, mTtcIndex, + mAxes, mLocaleList); sFontRegistory.registerNativeAllocation(font, ptr); return font; } @@ -477,7 +478,7 @@ public final class Font { } /** - * Retuns a font file buffer. + * Returns a font file buffer. * * @return a font buffer */ diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index f4a2199a6688..5e80749e9233 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -45,7 +45,7 @@ import java.util.Set; /** * Provides the system font configurations. */ -public class SystemFonts { +public final class SystemFonts { private static final String TAG = "SystemFonts"; private static final String DEFAULT_FAMILY = "sans-serif"; @@ -58,8 +58,7 @@ public class SystemFonts { /** * Returns all available font files in the system. * - * Note: The order of this font doesn't indicates anything. - * @return an array of system fonts + * @return a set of system fonts */ public static @NonNull Set<Font> getAvailableFonts() { HashSet<Font> set = new HashSet<>(); diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index e60d43e4001e..285a1a5f4540 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -16,6 +16,8 @@ #include "VulkanManager.h" +#include <private/gui/SyncFeatures.h> + #include "Properties.h" #include "RenderThread.h" #include "renderstate/RenderState.h" @@ -41,6 +43,10 @@ VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { mRenderThread.setGrContext(nullptr); + // We don't need to explicitly free the command buffer since it automatically gets freed when we + // delete the VkCommandPool below. + mDummyCB = VK_NULL_HANDLE; + if (VK_NULL_HANDLE != mCommandPool) { mDestroyCommandPool(mDevice, mCommandPool, nullptr); mCommandPool = VK_NULL_HANDLE; @@ -226,6 +232,11 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(), instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data()); + if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { + this->destroy(); + return false; + } + memset(&features, 0, sizeof(VkPhysicalDeviceFeatures2)); features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; features.pNext = nullptr; @@ -313,6 +324,8 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe GET_DEV_PROC(DeviceWaitIdle); GET_DEV_PROC(CreateSemaphore); GET_DEV_PROC(DestroySemaphore); + GET_DEV_PROC(ImportSemaphoreFdKHR); + GET_DEV_PROC(GetSemaphoreFdKHR); GET_DEV_PROC(CreateFence); GET_DEV_PROC(DestroyFence); GET_DEV_PROC(WaitForFences); @@ -384,6 +397,14 @@ void VulkanManager::initialize() { &mCommandPool); SkASSERT(VK_SUCCESS == res); } + LOG_ALWAYS_FATAL_IF(mCommandPool == VK_NULL_HANDLE); + + if (!setupDummyCommandBuffer()) { + this->destroy(); + return; + } + LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); + mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue); @@ -976,19 +997,176 @@ int VulkanManager::getAge(VulkanSurface* surface) { return surface->mCurrentTime - lastUsed; } +bool VulkanManager::setupDummyCommandBuffer() { + if (mDummyCB != VK_NULL_HANDLE) { + return true; + } + + VkCommandBufferAllocateInfo commandBuffersInfo; + memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo)); + commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + commandBuffersInfo.pNext = nullptr; + commandBuffersInfo.commandPool = mCommandPool; + commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + commandBuffersInfo.commandBufferCount = 1; + + VkResult err = mAllocateCommandBuffers(mDevice, &commandBuffersInfo, &mDummyCB); + if (err != VK_SUCCESS) { + // It is probably unnecessary to set this back to VK_NULL_HANDLE, but we set it anyways to + // make sure the driver didn't set a value and then return a failure. + mDummyCB = VK_NULL_HANDLE; + return false; + } + + VkCommandBufferBeginInfo beginInfo; + memset(&beginInfo, 0, sizeof(VkCommandBufferBeginInfo)); + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + + mBeginCommandBuffer(mDummyCB, &beginInfo); + mEndCommandBuffer(mDummyCB); + return true; +} + status_t VulkanManager::fenceWait(sp<Fence>& fence) { - //TODO: Insert a wait on fence command into the Vulkan command buffer. - // Block CPU on the fence. - status_t err = fence->waitForever("VulkanManager::fenceWait"); - if (err != NO_ERROR) { - ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err); - return err; + if (!hasVkContext()) { + ALOGE("VulkanManager::fenceWait: VkDevice not initialized"); + return INVALID_OPERATION; + } + + if (SyncFeatures::getInstance().useWaitSync() && + SyncFeatures::getInstance().useNativeFenceSync()) { + // Block GPU on the fence. + int fenceFd = fence->dup(); + if (fenceFd == -1) { + ALOGE("VulkanManager::fenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + + VkSemaphoreCreateInfo semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = nullptr; + semaphoreInfo.flags = 0; + VkSemaphore semaphore; + VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); + if (VK_SUCCESS != err) { + ALOGE("Failed to create import semaphore, err: %d", err); + return UNKNOWN_ERROR; + } + VkImportSemaphoreFdInfoKHR importInfo; + importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; + importInfo.pNext = nullptr; + importInfo.semaphore = semaphore; + importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; + importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + importInfo.fd = fenceFd; + + err = mImportSemaphoreFdKHR(mDevice, &importInfo); + if (VK_SUCCESS != err) { + ALOGE("Failed to import semaphore, err: %d", err); + return UNKNOWN_ERROR; + } + + LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); + + VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + + VkSubmitInfo submitInfo; + memset(&submitInfo, 0, sizeof(VkSubmitInfo)); + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.waitSemaphoreCount = 1; + // Wait to make sure aquire semaphore set above has signaled. + submitInfo.pWaitSemaphores = &semaphore; + submitInfo.pWaitDstStageMask = &waitDstStageFlags; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &mDummyCB; + submitInfo.signalSemaphoreCount = 0; + + mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + + // On Android when we import a semaphore, it is imported using temporary permanence. That + // means as soon as we queue the semaphore for a wait it reverts to its previous permanent + // state before importing. This means it will now be in an idle state with no pending + // signal or wait operations, so it is safe to immediately delete it. + mDestroySemaphore(mDevice, semaphore, nullptr); + } else { + // Block CPU on the fence. + status_t err = fence->waitForever("VulkanManager::fenceWait"); + if (err != NO_ERROR) { + ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err); + return err; + } } return OK; } status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence) { - //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed. + if (!hasVkContext()) { + ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized"); + return INVALID_OPERATION; + } + + if (SyncFeatures::getInstance().useFenceSync()) { + ALOGE("VulkanManager::createReleaseFence: Vk backend doesn't support non-native fences"); + return INVALID_OPERATION; + } + + if (!SyncFeatures::getInstance().useNativeFenceSync()) { + return OK; + } + + VkExportSemaphoreCreateInfo exportInfo; + exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; + exportInfo.pNext = nullptr; + exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + + VkSemaphoreCreateInfo semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = &exportInfo; + semaphoreInfo.flags = 0; + VkSemaphore semaphore; + VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); + if (VK_SUCCESS != err) { + ALOGE("VulkanManager::createReleaseFence: Failed to create semaphore"); + return INVALID_OPERATION; + } + + LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); + + VkSubmitInfo submitInfo; + memset(&submitInfo, 0, sizeof(VkSubmitInfo)); + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.waitSemaphoreCount = 0; + submitInfo.pWaitSemaphores = nullptr; + submitInfo.pWaitDstStageMask = nullptr; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &mDummyCB; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &semaphore; + + mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + + VkSemaphoreGetFdInfoKHR getFdInfo; + getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; + getFdInfo.pNext = nullptr; + getFdInfo.semaphore = semaphore; + getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + + int fenceFd = 0; + + err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); + if (VK_SUCCESS != err) { + ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd"); + return INVALID_OPERATION; + } + nativeFence = new Fence(fenceFd); + + // Exporting a semaphore with copy transference via vkGetSemahporeFdKHR, has the same effect of + // destroying the semaphore and creating a new one with the same handle, and the payloads + // ownership is move to the Fd we created. Thus the semahpore is in a state that we can delete + // it and we don't need to wait on the command buffer we submitted to finish. + mDestroySemaphore(mDevice, semaphore, nullptr); + return OK; } diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 7a539ae42605..c211f5d2b5d7 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -139,6 +139,8 @@ private: VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface); + bool setupDummyCommandBuffer(); + // simple wrapper class that exists only to initialize a pointer to NULL template <typename FNPTR_TYPE> class VkPtr { @@ -199,6 +201,8 @@ private: VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore; VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore; + VkPtr<PFN_vkImportSemaphoreFdKHR> mImportSemaphoreFdKHR; + VkPtr<PFN_vkGetSemaphoreFdKHR> mGetSemaphoreFdKHR; VkPtr<PFN_vkCreateFence> mCreateFence; VkPtr<PFN_vkDestroyFence> mDestroyFence; VkPtr<PFN_vkWaitForFences> mWaitForFences; @@ -216,6 +220,8 @@ private: VkQueue mPresentQueue = VK_NULL_HANDLE; VkCommandPool mCommandPool = VK_NULL_HANDLE; + VkCommandBuffer mDummyCB = VK_NULL_HANDLE; + enum class SwapBehavior { Discard, BufferAge, diff --git a/media/lib/remotedisplay/OWNERS b/media/lib/remotedisplay/OWNERS new file mode 100644 index 000000000000..7e7335d68d3b --- /dev/null +++ b/media/lib/remotedisplay/OWNERS @@ -0,0 +1 @@ +michaelwr@google.com diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp index 86f90327ce4f..1b1bf8d4b34a 100644 --- a/packages/CarSystemUI/Android.bp +++ b/packages/CarSystemUI/Android.bp @@ -30,6 +30,7 @@ android_app { "SystemUIPluginLib", "SystemUISharedLib", "SettingsLib", + "android.car.user", "androidx.car_car", "androidx.legacy_legacy-support-v4", "androidx.recyclerview_recyclerview", @@ -51,7 +52,6 @@ android_app { libs: [ "telephony-common", "android.car", - "android.car.user", ], manifest: "AndroidManifest.xml", diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 943dd84eccf9..ee4c95445bff 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -14,6 +14,7 @@ android_library { static_libs: [ "SettingsLibHelpUtils", "SettingsLibRestrictedLockUtils", + "SettingsLibAppPreference", ], // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES diff --git a/packages/SettingsLib/AppPreference/Android.bp b/packages/SettingsLib/AppPreference/Android.bp new file mode 100644 index 000000000000..b56181d75e6a --- /dev/null +++ b/packages/SettingsLib/AppPreference/Android.bp @@ -0,0 +1,13 @@ +android_library { + name: "SettingsLibAppPreference", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + libs: [ + "androidx.annotation_annotation", + "androidx.preference_preference", + ], + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/AppPreference/AndroidManifest.xml b/packages/SettingsLib/AppPreference/AndroidManifest.xml new file mode 100644 index 000000000000..7e71308444cd --- /dev/null +++ b/packages/SettingsLib/AppPreference/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.widget.apppreference"> + + <uses-sdk android:minSdkVersion="21" /> + +</manifest> diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml new file mode 100644 index 000000000000..6d35550f1b77 --- /dev/null +++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 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:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground" + android:gravity="center_vertical" + android:minHeight="?android:attr/listPreferredItemHeightSmall" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> + + <LinearLayout + android:id="@+id/icon_frame" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="start|center_vertical" + android:minWidth="56dp" + android:orientation="horizontal" + android:paddingEnd="8dp" + android:paddingTop="4dp" + android:paddingBottom="4dp"> + <ImageView + android:id="@android:id/icon" + android:layout_width="@dimen/secondary_app_icon_size" + android:layout_height="@dimen/secondary_app_icon_size"/> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical" + android:paddingTop="16dp" + android:paddingBottom="16dp"> + + <TextView android:id="@android:id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textAppearance="@android:style/TextAppearance.Material.Subhead" + android:ellipsize="marquee" + android:fadingEdge="horizontal"/> + + <LinearLayout + android:id="@+id/summary_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone"> + <TextView android:id="@android:id/summary" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textAppearance="@android:style/TextAppearance.Material.Small" + android:textAlignment="viewStart" + android:textColor="?android:attr/textColorSecondary"/> + + <TextView android:id="@+id/appendix" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textAppearance="@android:style/TextAppearance.Material.Small" + android:textAlignment="viewEnd" + android:textColor="?android:attr/textColorSecondary" + android:maxLines="1" + android:ellipsize="end"/> + </LinearLayout> + <ProgressBar + android:id="@android:id/progress" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:max="100" + android:visibility="gone"/> + </LinearLayout> + + <LinearLayout + android:id="@android:id/widget_frame" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center" + android:minWidth="64dp" + android:orientation="vertical"/> + +</LinearLayout> diff --git a/packages/SettingsLib/AppPreference/res/values/dimens.xml b/packages/SettingsLib/AppPreference/res/values/dimens.xml new file mode 100644 index 000000000000..e2a7a191f9de --- /dev/null +++ b/packages/SettingsLib/AppPreference/res/values/dimens.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 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. + --> + +<resources> + <dimen name="secondary_app_icon_size">32dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java new file mode 100644 index 000000000000..593b6f5b35f4 --- /dev/null +++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 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.settingslib.widget.apppreference; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ProgressBar; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +public class AppPreference extends Preference { + + private int mProgress; + private boolean mProgressVisible; + + public AppPreference(Context context) { + super(context); + setLayoutResource(R.layout.preference_app); + } + + public AppPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.preference_app); + } + + public void setProgress(int amount) { + mProgress = amount; + mProgressVisible = true; + notifyChanged(); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + super.onBindViewHolder(view); + + view.findViewById(R.id.summary_container) + .setVisibility(TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE); + final ProgressBar progress = (ProgressBar) view.findViewById(android.R.id.progress); + if (mProgressVisible) { + progress.setProgress(mProgress); + progress.setVisibility(View.VISIBLE); + } else { + progress.setVisibility(View.GONE); + } + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index a71041045df2..58feef55bd29 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -102,7 +102,7 @@ public class A2dpProfile implements LocalBluetoothProfile { BluetoothProfile.A2DP); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java index e13e566ec901..988062de0a37 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java @@ -96,7 +96,7 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { BluetoothProfile.A2DP_SINK); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index b9f7323b1a9e..750a8438c00d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -29,16 +29,15 @@ import android.os.SystemClock; import android.text.TextUtils; import android.util.Log; -import androidx.annotation.VisibleForTesting; - import com.android.settingslib.R; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; +import androidx.annotation.VisibleForTesting; + /** * CachedBluetoothDevice represents a remote Bluetooth device. It contains * attributes of the device (such as the address, name, RSSI, etc.) and @@ -48,6 +47,10 @@ import java.util.List; public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> { private static final String TAG = "CachedBluetoothDevice"; + // See mConnectAttempted + private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000; + private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000; + private final Context mContext; private final BluetoothAdapter mLocalAdapter; private final LocalBluetoothProfileManager mProfileManager; @@ -55,7 +58,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private long mHiSyncId; // Need this since there is no method for getting RSSI private short mRssi; - private HashMap<LocalBluetoothProfile, Integer> mProfileConnectionState; private final List<LocalBluetoothProfile> mProfiles = new ArrayList<LocalBluetoothProfile>(); @@ -78,17 +80,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private final static String MESSAGE_REJECTION_COUNT_PREFS_NAME = "bluetooth_message_reject"; - public long getHiSyncId() { - return mHiSyncId; - } - - public void setHiSyncId(long id) { - if (BluetoothUtils.D) { - Log.d(TAG, "setHiSyncId: mDevice " + mDevice + ", id " + id); - } - mHiSyncId = id; - } - /** * Last time a bt profile auto-connect was attempted. * If an ACTION_UUID intent comes in within @@ -97,14 +88,21 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> */ private long mConnectAttempted; - // See mConnectAttempted - private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000; - private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000; - // Active device state private boolean mIsActiveDeviceA2dp = false; private boolean mIsActiveDeviceHeadset = false; private boolean mIsActiveDeviceHearingAid = false; + + CachedBluetoothDevice(Context context, LocalBluetoothProfileManager profileManager, + BluetoothDevice device) { + mContext = context; + mLocalAdapter = BluetoothAdapter.getDefaultAdapter(); + mProfileManager = profileManager; + mDevice = device; + fillData(); + mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID; + } + /** * Describes the current device and profile for logging. * @@ -133,7 +131,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } return; } - mProfileConnectionState.put(profile, newProfileState); if (newProfileState == BluetoothProfile.STATE_CONNECTED) { if (profile instanceof MapProfile) { profile.setPreferred(mDevice, true); @@ -161,18 +158,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> fetchActiveDevices(); } - CachedBluetoothDevice(Context context, - LocalBluetoothProfileManager profileManager, - BluetoothDevice device) { - mContext = context; - mLocalAdapter = BluetoothAdapter.getDefaultAdapter(); - mProfileManager = profileManager; - mDevice = device; - mProfileConnectionState = new HashMap<LocalBluetoothProfile, Integer>(); - fillData(); - mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID; - } - public void disconnect() { for (LocalBluetoothProfile profile : mProfiles) { disconnect(profile); @@ -204,6 +189,17 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> connectWithoutResettingTimer(connectAllProfiles); } + public long getHiSyncId() { + return mHiSyncId; + } + + public void setHiSyncId(long id) { + if (BluetoothUtils.D) { + Log.d(TAG, "setHiSyncId: mDevice " + mDevice + ", id " + id); + } + mHiSyncId = id; + } + void onBondingDockConnect() { // Attempt to connect if UUIDs are available. Otherwise, // we will connect when the ACTION_UUID intent arrives. @@ -226,7 +222,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> int preferredProfiles = 0; for (LocalBluetoothProfile profile : mProfiles) { - if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) { + if (connectAllProfiles ? profile.accessProfileEnabled() : profile.isAutoConnectable()) { if (profile.isPreferred(mDevice)) { ++preferredProfiles; connectInt(profile); @@ -300,14 +296,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return true; } - /** - * Return true if user initiated pairing on this device. The message text is - * slightly different for local vs. remote initiated pairing dialogs. - */ - boolean isUserInitiatedPairing() { - return mDevice.isBondingInitiatedLocally(); - } - public void unpair() { int state = getBondState(); @@ -332,22 +320,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } public int getProfileConnectionState(LocalBluetoothProfile profile) { - if (mProfileConnectionState.get(profile) == null) { - // If cache is empty make the binder call to get the state - int state = profile.getConnectionStatus(mDevice); - mProfileConnectionState.put(profile, state); - } - return mProfileConnectionState.get(profile); - } - - public void clearProfileConnectionState () - { - if (BluetoothUtils.D) { - Log.d(TAG," Clearing all connection state for dev:" + mDevice.getName()); - } - for (LocalBluetoothProfile profile :getProfiles()) { - mProfileConnectionState.put(profile, BluetoothProfile.STATE_DISCONNECTED); - } + return profile != null + ? profile.getConnectionStatus(mDevice) + : BluetoothProfile.STATE_DISCONNECTED; } // TODO: do any of these need to run async on a background thread? @@ -669,7 +644,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> List<LocalBluetoothProfile> connectableProfiles = new ArrayList<LocalBluetoothProfile>(); for (LocalBluetoothProfile profile : mProfiles) { - if (profile.isConnectable()) { + if (profile.accessProfileEnabled()) { connectableProfiles.add(profile); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java index 5a64e02cece4..21cf0c27a8d5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java @@ -285,11 +285,6 @@ public class CachedBluetoothDeviceManager { { mCachedDevicesMapForHearingAids.remove(cachedDevice.getHiSyncId()); } - } else { - // For bonded devices, we need to clear the connection status so that - // when BT is enabled next time, device connection status shall be retrieved - // by making a binder call. - cachedDevice.clearProfileConnectionState(); } } for (int i = mHearingAidDevicesNotAddedInCache.size() - 1; i >= 0; i--) { @@ -297,11 +292,6 @@ public class CachedBluetoothDeviceManager { if (notCachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) { notCachedDevice.setJustDiscovered(false); mHearingAidDevicesNotAddedInCache.remove(i); - } else { - // For bonded devices, we need to clear the connection status so that - // when BT is enabled next time, device connection status shall be retrieved - // by making a binder call. - notCachedDevice.clearProfileConnectionState(); } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index 2dd8eaf7021d..62507f58426f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -103,7 +103,7 @@ public class HeadsetProfile implements LocalBluetoothProfile { BluetoothProfile.HEADSET); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java index 1eeb4f058d1d..8bc0acf5f824 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java @@ -100,7 +100,7 @@ public class HearingAidProfile implements LocalBluetoothProfile { new HearingAidServiceListener(), BluetoothProfile.HEARING_AID); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return false; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java index 4b6a22c9b2c8..4879144a5994 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java @@ -104,7 +104,7 @@ final class HfpClientProfile implements LocalBluetoothProfile { } @Override - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java index c8d4fc84f4b4..61e5b6b3e125 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java @@ -94,7 +94,7 @@ public class HidDeviceProfile implements LocalBluetoothProfile { } @Override - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java index fe6b22224819..75d16db13efe 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java @@ -93,7 +93,7 @@ public class HidProfile implements LocalBluetoothProfile { BluetoothProfile.HID_HOST); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java index 0447f378fca1..4b0ca7434f9a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java @@ -26,9 +26,9 @@ import android.bluetooth.BluetoothDevice; public interface LocalBluetoothProfile { /** - * Returns true if the user can initiate a connection, false otherwise. + * Return {@code true} if the user can initiate a connection for this profile in UI. */ - boolean isConnectable(); + boolean accessProfileEnabled(); /** * Returns true if the user can enable auto connection for this profile. diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 7000f9d7a7d2..0c29f431ef3f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -290,10 +290,11 @@ public class LocalBluetoothProfileManager { } } - mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState, - mProfile.getProfileId()); cachedDevice.onProfileStateChanged(mProfile, newState); cachedDevice.refresh(); + // Dispatch profile changed after device update + mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState, + mProfile.getProfileId()); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java index 7ad2e28c84b5..1e22f440b5f8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java @@ -105,7 +105,7 @@ public final class MapClientProfile implements LocalBluetoothProfile { new MapClientServiceListener(), BluetoothProfile.MAP_CLIENT); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java index caea04f87a50..758202412c93 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java @@ -104,7 +104,7 @@ public class MapProfile implements LocalBluetoothProfile { BluetoothProfile.MAP); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java index dfd16224ef8f..e1e5dbe29a1a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java @@ -32,7 +32,7 @@ final class OppProfile implements LocalBluetoothProfile { // Order of this profile in device profiles list private static final int ORDINAL = 2; - public boolean isConnectable() { + public boolean accessProfileEnabled() { return false; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java index 02afe8db7201..7b811624a6f3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java @@ -78,7 +78,7 @@ public class PanProfile implements LocalBluetoothProfile { BluetoothProfile.PAN); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java index 8fefb2fc01b8..1f15601f2756 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java @@ -107,7 +107,7 @@ public final class PbapClientProfile implements LocalBluetoothProfile { new PbapClientServiceListener(), BluetoothProfile.PBAP_CLIENT); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java index e9d8cb5a4b2b..adef0841cb2a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java @@ -80,7 +80,7 @@ public class PbapServerProfile implements LocalBluetoothProfile { BluetoothPbap pbap = new BluetoothPbap(context, new PbapServiceListener()); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java index 61602c6463ee..9a6f104fadd5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java @@ -106,7 +106,7 @@ final class SapProfile implements LocalBluetoothProfile { BluetoothProfile.SAP); } - public boolean isConnectable() { + public boolean accessProfileEnabled() { return true; } diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java index c3241bbd2123..74bd97f40ff7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java @@ -36,7 +36,12 @@ import com.android.settingslib.AppItem; /** * Loader for historical chart data for both network and UID details. + * + * Deprecated in favor of {@link NetworkCycleDataLoader} + * + * @deprecated */ +@Deprecated public class ChartDataLoaderCompat extends AsyncTaskLoader<ChartData> { private static final String KEY_TEMPLATE = "template"; private static final String KEY_APP = "app"; diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java new file mode 100644 index 000000000000..2d8c0de42ba4 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 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.settingslib.net; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Data structure representing usage data in a billing cycle. + */ +public class NetworkCycleData { + public static final long BUCKET_DURATION_MS = TimeUnit.DAYS.toMillis(1); + public long startTime; + public long endTime; + public long totalUsage; + public List<NetworkCycleData> usageBuckets; + + private NetworkCycleData(Builder builder) { + startTime = builder.mStart; + endTime = builder.mEnd; + totalUsage = builder.mTotalUsage; + usageBuckets = builder.mUsageBuckets; + } + + public static class Builder { + private long mStart; + private long mEnd; + private long mTotalUsage; + private List<NetworkCycleData> mUsageBuckets; + + public Builder setStartTime(long start) { + mStart = start; + return this; + } + + public Builder setEndTime(long end) { + mEnd = end; + return this; + } + + public Builder setTotalUsage(long total) { + mTotalUsage = total; + return this; + } + + public Builder setUsageBuckets(List<NetworkCycleData> buckets) { + mUsageBuckets = buckets; + return this; + } + + public NetworkCycleData build() { + return new NetworkCycleData(this); + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java new file mode 100644 index 000000000000..80e13563d74b --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2018 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.settingslib.net; + +import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; +import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; + +import android.app.usage.NetworkStats; +import android.app.usage.NetworkStatsManager; +import android.content.Context; +import android.net.INetworkStatsService; +import android.net.INetworkStatsSession; +import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; +import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; +import android.net.TrafficStats; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.text.format.DateUtils; +import android.util.Log; +import android.util.Pair; + +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.loader.content.AsyncTaskLoader; + +/** + * Loader for network data usage history. It returns a list of usage data per billing cycle. + */ +public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleData>> { + private static final String TAG = "CycleDataSummaryLoader"; + private final NetworkStatsManager mNetworkStatsManager; + private final String mSubId; + private final int mNetworkType; + private final NetworkPolicy mPolicy; + private final NetworkTemplate mNetworkTemplate; + @VisibleForTesting + final INetworkStatsService mNetworkStatsService; + + private NetworkCycleDataLoader(Builder builder) { + super(builder.mContext); + mPolicy = builder.mPolicy; + mSubId = builder.mSubId; + mNetworkType = builder.mNetworkType; + mNetworkTemplate = builder.mNetworkTemplate; + mNetworkStatsManager = (NetworkStatsManager) + builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE); + mNetworkStatsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + } + + @Override + protected void onStartLoading() { + super.onStartLoading(); + forceLoad(); + } + + @Override + public List<NetworkCycleData> loadInBackground() { + if (mPolicy == null) { + return loadFourWeeksData(); + } + final List<NetworkCycleData> data = new ArrayList<>(); + final Iterator<Pair<ZonedDateTime, ZonedDateTime>> iterator = NetworkPolicyManager + .cycleIterator(mPolicy); + while (iterator.hasNext()) { + final Pair<ZonedDateTime, ZonedDateTime> cycle = iterator.next(); + final long cycleStart = cycle.first.toInstant().toEpochMilli(); + final long cycleEnd = cycle.second.toInstant().toEpochMilli(); + getUsage(cycleStart, cycleEnd, data); + } + return data; + } + + @Override + protected void onStopLoading() { + super.onStopLoading(); + cancelLoad(); + } + + @Override + protected void onReset() { + super.onReset(); + cancelLoad(); + } + + @VisibleForTesting + List<NetworkCycleData> loadFourWeeksData() { + final List<NetworkCycleData> data = new ArrayList<>(); + try { + final INetworkStatsSession networkSession = mNetworkStatsService.openSession(); + final NetworkStatsHistory networkHistory = networkSession.getHistoryForNetwork( + mNetworkTemplate, FIELD_RX_BYTES | FIELD_TX_BYTES); + final long historyStart = networkHistory.getStart(); + final long historyEnd = networkHistory.getEnd(); + + long cycleEnd = historyEnd; + while (cycleEnd > historyStart) { + final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4); + getUsage(cycleStart, cycleEnd, data); + cycleEnd = cycleStart; + } + + TrafficStats.closeQuietly(networkSession); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + return data; + } + + @VisibleForTesting + void getUsage(long start, long end, @NonNull List<NetworkCycleData> data) { + try { + final NetworkStats stats = mNetworkStatsManager.querySummary( + mNetworkType, mSubId, start, end); + final long total = getTotalUsage(stats); + if (total > 0L) { + data.add(new NetworkCycleData.Builder() + .setStartTime(start) + .setEndTime(end) + .setTotalUsage(total) + .setUsageBuckets(getUsageBuckets(start, end)) + .build()); + } + } catch (RemoteException e) { + Log.e(TAG, "Exception querying network detail.", e); + } + } + + private long getTotalUsage(NetworkStats stats) { + long bytes = 0L; + if (stats != null) { + final NetworkStats.Bucket bucket = new NetworkStats.Bucket(); + while (stats.hasNextBucket() && stats.getNextBucket(bucket)) { + bytes += bucket.getRxBytes() + bucket.getTxBytes(); + } + stats.close(); + } + return bytes; + } + + private List<NetworkCycleData> getUsageBuckets(long start, long end) { + final List<NetworkCycleData> data = new ArrayList<>(); + long bucketStart = start; + long bucketEnd = start + NetworkCycleData.BUCKET_DURATION_MS; + while (bucketEnd <= end) { + long usage = 0L; + try { + final NetworkStats stats = mNetworkStatsManager.querySummary( + mNetworkType, mSubId, bucketStart, bucketEnd); + usage = getTotalUsage(stats); + } catch (RemoteException e) { + Log.e(TAG, "Exception querying network detail.", e); + } + data.add(new NetworkCycleData.Builder() + .setStartTime(bucketStart).setEndTime(bucketEnd).setTotalUsage(usage).build()); + bucketStart = bucketEnd; + bucketEnd += NetworkCycleData.BUCKET_DURATION_MS; + } + return data; + } + + public static class Builder { + private final Context mContext; + private NetworkPolicy mPolicy; + private String mSubId; + private int mNetworkType; + private NetworkTemplate mNetworkTemplate; + + public Builder(Context context) { + mContext = context; + } + + public Builder setNetworkPolicy(NetworkPolicy policy) { + mPolicy = policy; + return this; + } + + public Builder setSubscriberId(String subId) { + mSubId = subId; + return this; + } + + public Builder setNetworkType(int networkType) { + mNetworkType = networkType; + return this; + } + + public Builder setNetworkTemplate(NetworkTemplate template) { + mNetworkTemplate = template; + return this; + } + + public NetworkCycleDataLoader build() { + return new NetworkCycleDataLoader(this); + } + } + +} diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java index a070b2a5f768..34e6097ea46e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java @@ -20,25 +20,23 @@ import android.app.usage.NetworkStatsManager; import android.app.usage.NetworkStats; import android.content.Context; import android.os.RemoteException; -import android.telephony.TelephonyManager; import android.util.Log; import androidx.loader.content.AsyncTaskLoader; /** - * Loader for retrieving the network stats details for all UIDs. + * Loader for retrieving the network stats summary for all UIDs. */ -public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { +public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> { private static final String TAG = "NetworkDetailLoader"; private final NetworkStatsManager mNetworkStatsManager; - private final TelephonyManager mTelephonyManager; private final long mStart; private final long mEnd; - private final int mSubId; + private final String mSubId; private final int mNetworkType; - private NetworkStatsDetailLoader(Builder builder) { + private NetworkStatsSummaryLoader(Builder builder) { super(builder.mContext); mStart = builder.mStart; mEnd = builder.mEnd; @@ -46,8 +44,6 @@ public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { mNetworkType = builder.mNetworkType; mNetworkStatsManager = (NetworkStatsManager) builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE); - mTelephonyManager = - (TelephonyManager) builder.mContext.getSystemService(Context.TELEPHONY_SERVICE); } @Override @@ -59,8 +55,7 @@ public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { @Override public NetworkStats loadInBackground() { try { - return mNetworkStatsManager.queryDetails( - mNetworkType, mTelephonyManager.getSubscriberId(mSubId), mStart, mEnd); + return mNetworkStatsManager.querySummary(mNetworkType, mSubId, mStart, mEnd); } catch (RemoteException e) { Log.e(TAG, "Exception querying network detail.", e); return null; @@ -83,7 +78,7 @@ public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { private final Context mContext; private long mStart; private long mEnd; - private int mSubId; + private String mSubId; private int mNetworkType; public Builder(Context context) { @@ -100,7 +95,7 @@ public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { return this; } - public Builder setSubscriptionId(int subId) { + public Builder setSubscriberId(String subId) { mSubId = subId; return this; } @@ -110,8 +105,8 @@ public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { return this; } - public NetworkStatsDetailLoader build() { - return new NetworkStatsDetailLoader(this); + public NetworkStatsSummaryLoader build() { + return new NetworkStatsSummaryLoader(this); } } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java index 0ca2e87de336..ede248b85083 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java @@ -52,6 +52,7 @@ public class SettingsLibRobolectricTestRunner extends RobolectricTestRunner { @Override public List<ResourcePath> getIncludedResourcePaths() { final List<ResourcePath> paths = super.getIncludedResourcePaths(); + paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/AppPreference/res")); paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res")); paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res")); paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res")); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java index 7baded8da1d4..62b5688fc9e6 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java @@ -118,7 +118,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify addDevice(). */ @Test - public void testAddDevice_validCachedDevices_devicesAdded() { + public void addDevice_validCachedDevices_devicesAdded() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); @@ -136,7 +136,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify getName(). */ @Test - public void testGetName_validCachedDevice_nameFound() { + public void getName_validCachedDevice_nameFound() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1); @@ -146,7 +146,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify onDeviceNameUpdated(). */ @Test - public void testOnDeviceNameUpdated_validName_nameUpdated() { + public void onDeviceNameUpdated_validName_nameUpdated() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); assertThat(cachedDevice1.getName()).isEqualTo(DEVICE_ALIAS_1); @@ -161,7 +161,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify clearNonBondedDevices(). */ @Test - public void testClearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() { + public void clearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); @@ -193,7 +193,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify clearNonBondedDevices() for hearing aids. */ @Test - public void testClearNonBondedDevices_HearingAids_nonBondedHAsClearedFromCachedDevicesMap() { + public void clearNonBondedDevices_HearingAids_nonBondedHAsClearedFromCachedDevicesMap() { when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE); @@ -214,7 +214,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify onHiSyncIdChanged() for hearing aid devices with same HiSyncId. */ @Test - public void testOnHiSyncIdChanged_sameHiSyncId_populateInDifferentLists() { + public void onHiSyncIdChanged_sameHiSyncId_populateInDifferentLists() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); @@ -247,7 +247,7 @@ public class CachedBluetoothDeviceManagerTest { * device is connected and other is disconnected. The connected device should be chosen. */ @Test - public void testOnHiSyncIdChanged_sameHiSyncIdAndOneConnected_chooseConnectedDevice() { + public void onHiSyncIdChanged_sameHiSyncIdAndOneConnected_chooseConnectedDevice() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); @@ -282,7 +282,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify onHiSyncIdChanged() for hearing aid devices with different HiSyncId. */ @Test - public void testOnHiSyncIdChanged_differentHiSyncId_populateInSameList() { + public void onHiSyncIdChanged_differentHiSyncId_populateInSameList() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); @@ -316,7 +316,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify onProfileConnectionStateChanged() for single hearing aid device connection. */ @Test - public void testOnProfileConnectionStateChanged_singleDeviceConnected_visible() { + public void onProfileConnectionStateChanged_singleDeviceConnected_visible() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED); @@ -353,7 +353,7 @@ public class CachedBluetoothDeviceManagerTest { * devices are disconnected and they get connected. */ @Test - public void testOnProfileConnectionStateChanged_twoDevicesConnected_oneDeviceVisible() { + public void onProfileConnectionStateChanged_twoDevicesConnected_oneDeviceVisible() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); @@ -405,7 +405,7 @@ public class CachedBluetoothDeviceManagerTest { * devices are connected and they get disconnected. */ @Test - public void testOnProfileConnectionStateChanged_twoDevicesDisconnected_oneDeviceVisible() { + public void onProfileConnectionStateChanged_twoDevicesDisconnected_oneDeviceVisible() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); @@ -458,7 +458,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify OnDeviceUnpaired() for a paired hearing Aid device pair. */ @Test - public void testOnDeviceUnpaired_bothHearingAidsPaired_removesItsPairFromList() { + public void onDeviceUnpaired_bothHearingAidsPaired_removesItsPairFromList() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); @@ -488,7 +488,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify OnDeviceUnpaired() for paired hearing Aid devices which are not a pair. */ @Test - public void testOnDeviceUnpaired_bothHearingAidsNotPaired_doesNotRemoveAnyDeviceFromList() { + public void onDeviceUnpaired_bothHearingAidsNotPaired_doesNotRemoveAnyDeviceFromList() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); @@ -532,7 +532,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify addDevice() for hearing aid devices with same HiSyncId. */ @Test - public void testAddDevice_hearingAidDevicesWithSameHiSyncId_populateInDifferentLists() { + public void addDevice_hearingAidDevicesWithSameHiSyncId_populateInDifferentLists() { doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager) .getHearingAidProfile(); doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1); @@ -560,7 +560,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify addDevice() for hearing aid devices with different HiSyncId. */ @Test - public void testAddDevice_hearingAidDevicesWithDifferentHiSyncId_populateInSameList() { + public void addDevice_hearingAidDevicesWithDifferentHiSyncId_populateInSameList() { doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager) .getHearingAidProfile(); doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1); @@ -592,7 +592,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify getHearingAidPairDeviceSummary() for hearing aid devices with same HiSyncId. */ @Test - public void testGetHearingAidPairDeviceSummary_bothHearingAidsPaired_returnsSummaryOfPair() { + public void getHearingAidPairDeviceSummary_bothHearingAidsPaired_returnsSummaryOfPair() { mCachedDevice1.setHiSyncId(HISYNCID1); mCachedDevice2.setHiSyncId(HISYNCID1); mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); @@ -609,7 +609,7 @@ public class CachedBluetoothDeviceManagerTest { * HiSyncId. */ @Test - public void testGetHearingAidPairDeviceSummary_bothHearingAidsNotPaired_returnsNull() { + public void getHearingAidPairDeviceSummary_bothHearingAidsNotPaired_returnsNull() { mCachedDevice1.setHiSyncId(HISYNCID1); mCachedDevice2.setHiSyncId(HISYNCID2); mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); @@ -625,7 +625,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify updateHearingAidsDevices(). */ @Test - public void testUpdateHearingAidDevices_hiSyncIdAvailable_setsHiSyncId() { + public void updateHearingAidDevices_hiSyncIdAvailable_setsHiSyncId() { doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager) .getHearingAidProfile(); doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1); @@ -643,7 +643,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify onBtClassChanged(). */ @Test - public void testOnBtClassChanged_validBtClass_classChanged() { + public void onBtClassChanged_validBtClass_classChanged() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); assertThat(cachedDevice1.getBtClass()).isEqualTo(DEVICE_CLASS_1); @@ -658,7 +658,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify onDeviceDisappeared(). */ @Test - public void testOnDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() { + public void onDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); @@ -673,7 +673,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify onActiveDeviceChanged(). */ @Test - public void testOnActiveDeviceChanged_connectedDevices_activeDeviceChanged() { + public void onActiveDeviceChanged_connectedDevices_activeDeviceChanged() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); @@ -736,7 +736,7 @@ public class CachedBluetoothDeviceManagerTest { * Test to verify onActiveDeviceChanged() with A2DP and Hearing Aid. */ @Test - public void testOnActiveDeviceChanged_withA2dpAndHearingAid() { + public void onActiveDeviceChanged_withA2dpAndHearingAid() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index c18db11a71a3..f6201dd0b5f1 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -79,74 +80,74 @@ public class CachedBluetoothDeviceTest { } @Test - public void testGetConnectionSummary_testSingleProfileConnectDisconnect() { + public void getConnectionSummary_testSingleProfileConnectDisconnect() { // Test without battery level // Set PAN profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Set PAN profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Test with battery level mBatteryLevel = 10; // Set PAN profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery"); // Set PAN profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Set PAN profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Set PAN profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); } @Test - public void testGetConnectionSummary_testMultipleProfileConnectDisconnect() { + public void getConnectionSummary_testMultipleProfileConnectDisconnect() { mBatteryLevel = 10; // Set HFP, A2DP and PAN profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery"); // Disconnect HFP only and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( "10% battery"); // Disconnect A2DP only and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( "10% battery"); // Disconnect both HFP and A2DP and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( "10% battery"); // Disconnect all profiles and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); } @Test - public void testGetConnectionSummary_testSingleProfileActiveDeviceA2dp() { + public void getConnectionSummary_testSingleProfileActiveDeviceA2dp() { // Test without battery level // Set A2DP profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Set device as Active for A2DP and test connection state summary @@ -159,26 +160,26 @@ public class CachedBluetoothDeviceTest { "Active, 10% battery"); // Set A2DP profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Set A2DP profile to be connected, Active and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active"); // Set A2DP profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); } @Test - public void testGetConnectionSummary_testSingleProfileActiveDeviceHfp() { + public void getConnectionSummary_testSingleProfileActiveDeviceHfp() { // Test without battery level // Set HFP profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Set device as Active for HFP and test connection state summary @@ -193,26 +194,26 @@ public class CachedBluetoothDeviceTest { "Active, 10% battery"); // Set HFP profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Set HFP profile to be connected, Active and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active"); // Set HFP profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); } @Test - public void testGetConnectionSummary_testSingleProfileActiveDeviceHearingAid() { + public void getConnectionSummary_testSingleProfileActiveDeviceHearingAid() { // Test without battery level // Set Hearing Aid profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Set device as Active for Hearing Aid and test connection state summary @@ -227,11 +228,11 @@ public class CachedBluetoothDeviceTest { } @Test - public void testGetConnectionSummary_testMultipleProfilesActiveDevice() { + public void getConnectionSummary_testMultipleProfilesActiveDevice() { // Test without battery level // Set A2DP and HFP profiles to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); // Set device as Active for A2DP and HFP and test connection state summary @@ -246,14 +247,14 @@ public class CachedBluetoothDeviceTest { // Disconnect A2DP only and test connection state summary mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP); - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( "10% battery"); // Disconnect HFP only and test connection state summary mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET); - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( "Active, 10% battery"); @@ -261,15 +262,15 @@ public class CachedBluetoothDeviceTest { // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Set A2DP and HFP profiles to be connected, Active and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active"); // Set A2DP and HFP profiles to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getConnectionSummary()).isNull(); } @@ -277,32 +278,32 @@ public class CachedBluetoothDeviceTest { public void getCarConnectionSummary_singleProfileConnectDisconnect() { // Test without battery level // Set PAN profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected"); // Set PAN profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isNull(); // Test with battery level mBatteryLevel = 10; // Set PAN profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, battery 10%"); // Set PAN profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isNull(); // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Set PAN profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected"); // Set PAN profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isNull(); } @@ -311,29 +312,29 @@ public class CachedBluetoothDeviceTest { mBatteryLevel = 10; // Set HFP, A2DP and PAN profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, battery 10%"); // Disconnect HFP only and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo( "Connected (no phone), battery 10%"); // Disconnect A2DP only and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo( "Connected (no media), battery 10%"); // Disconnect both HFP and A2DP and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo( "Connected (no phone or media), battery 10%"); // Disconnect all profiles and test connection state summary - mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isNull(); } @@ -341,7 +342,7 @@ public class CachedBluetoothDeviceTest { public void getCarConnectionSummary_singleProfileActiveDeviceA2dp() { // Test without battery level // Set A2DP profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected"); // Set device as Active for A2DP and test connection state summary @@ -354,18 +355,18 @@ public class CachedBluetoothDeviceTest { "Connected, battery 10%, active (media)"); // Set A2DP profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isNull(); // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Set A2DP profile to be connected, Active and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active (media)"); // Set A2DP profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isNull(); } @@ -373,7 +374,7 @@ public class CachedBluetoothDeviceTest { public void getCarConnectionSummary_singleProfileActiveDeviceHfp() { // Test without battery level // Set HFP profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected"); // Set device as Active for HFP and test connection state summary @@ -386,18 +387,18 @@ public class CachedBluetoothDeviceTest { "Connected, battery 10%, active (phone)"); // Set HFP profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isNull(); // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Set HFP profile to be connected, Active and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active (phone)"); // Set HFP profile to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isNull(); } @@ -405,7 +406,7 @@ public class CachedBluetoothDeviceTest { public void getCarConnectionSummary_singleProfileActiveDeviceHearingAid() { // Test without battery level // Set Hearing Aid profile to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected"); // Set device as Active for Hearing Aid and test connection state summary @@ -414,8 +415,7 @@ public class CachedBluetoothDeviceTest { // Set Hearing Aid profile to be disconnected and test connection state summary mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID); - mCachedDevice.onProfileStateChanged(mHearingAidProfile, - BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isNull(); } @@ -423,8 +423,8 @@ public class CachedBluetoothDeviceTest { public void getCarConnectionSummary_multipleProfilesActiveDevice() { // Test without battery level // Set A2DP and HFP profiles to be connected and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected"); // Set device as Active for A2DP and HFP and test connection state summary @@ -439,14 +439,14 @@ public class CachedBluetoothDeviceTest { // Disconnect A2DP only and test connection state summary mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP); - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo( "Connected (no media), battery 10%, active (phone)"); // Disconnect HFP only and test connection state summary mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET); - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo( "Connected (no phone), battery 10%, active (media)"); @@ -454,21 +454,21 @@ public class CachedBluetoothDeviceTest { // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; // Set A2DP and HFP profiles to be connected, Active and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET); assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active"); // Set A2DP and HFP profiles to be disconnected and test connection state summary - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.getCarConnectionSummary()).isNull(); } @Test - public void testDeviceName_testAliasNameAvailable() { + public void deviceName_testAliasNameAvailable() { when(mDevice.getAliasName()).thenReturn(DEVICE_ALIAS); when(mDevice.getName()).thenReturn(DEVICE_NAME); CachedBluetoothDevice cachedBluetoothDevice = @@ -480,7 +480,7 @@ public class CachedBluetoothDeviceTest { } @Test - public void testDeviceName_testNameNotAvailable() { + public void deviceName_testNameNotAvailable() { CachedBluetoothDevice cachedBluetoothDevice = new CachedBluetoothDevice(mContext, mProfileManager, mDevice); // Verify device address is returned on getName @@ -490,7 +490,7 @@ public class CachedBluetoothDeviceTest { } @Test - public void testDeviceName_testRenameDevice() { + public void deviceName_testRenameDevice() { final String[] alias = {DEVICE_ALIAS}; doAnswer(invocation -> alias[0]).when(mDevice).getAliasName(); doAnswer(invocation -> { @@ -513,7 +513,7 @@ public class CachedBluetoothDeviceTest { } @Test - public void testSetActive() { + public void setActive() { when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile); when(mA2dpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true); @@ -521,19 +521,19 @@ public class CachedBluetoothDeviceTest { assertThat(mCachedDevice.setActive()).isFalse(); - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.setActive()).isTrue(); - mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED); + updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); assertThat(mCachedDevice.setActive()).isTrue(); - mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); + updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED); assertThat(mCachedDevice.setActive()).isFalse(); } @Test - public void testIsA2dpDevice_isA2dpDevice() { + public void isA2dpDevice_isA2dpDevice() { when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); when(mA2dpProfile.getConnectionStatus(mDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); @@ -542,7 +542,7 @@ public class CachedBluetoothDeviceTest { } @Test - public void testIsA2dpDevice_isNotA2dpDevice() { + public void isA2dpDevice_isNotA2dpDevice() { when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile); when(mA2dpProfile.getConnectionStatus(mDevice)). thenReturn(BluetoothProfile.STATE_DISCONNECTING); @@ -551,7 +551,7 @@ public class CachedBluetoothDeviceTest { } @Test - public void testIsHfpDevice_isHfpDevice() { + public void isHfpDevice_isHfpDevice() { when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile); when(mHfpProfile.getConnectionStatus(mDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); @@ -637,4 +637,24 @@ public class CachedBluetoothDeviceTest { verify(mDevice, never()).setAlias(any()); } + + @Test + public void getProfileConnectionState_nullProfile_returnDisconnected() { + assertThat(mCachedDevice.getProfileConnectionState(null)).isEqualTo( + BluetoothProfile.STATE_DISCONNECTED); + } + + @Test + public void getProfileConnectionState_profileConnected_returnConnected() { + doReturn(BluetoothProfile.STATE_CONNECTED).when(mA2dpProfile).getConnectionStatus( + any(BluetoothDevice.class)); + + assertThat(mCachedDevice.getProfileConnectionState(mA2dpProfile)).isEqualTo( + BluetoothProfile.STATE_CONNECTED); + } + + private void updateProfileStatus(LocalBluetoothProfile profile, int status) { + doReturn(status).when(profile).getConnectionStatus(mDevice); + mCachedDevice.onProfileStateChanged(profile, status); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java index c23ad79a52fe..887c1d57c870 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java @@ -38,13 +38,13 @@ import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu; import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; -import com.android.settingslib.testutils.FragmentTestUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.android.controller.ActivityController; +import org.robolectric.shadows.androidx.fragment.FragmentController; @RunWith(SettingsLibRobolectricTestRunner.class) public class LifecycleTest { @@ -184,7 +184,7 @@ public class LifecycleTest { @Test public void runThroughDialogFragmentLifecycles_shouldObserveEverything() { final TestDialogFragment fragment = new TestDialogFragment(); - FragmentTestUtils.startFragment(fragment); + FragmentController.setupFragment(fragment); fragment.onCreateOptionsMenu(null, null); fragment.onPrepareOptionsMenu(null); @@ -208,7 +208,7 @@ public class LifecycleTest { @Test public void runThroughFragmentLifecycles_shouldObserveEverything() { final TestFragment fragment = new TestFragment(); - FragmentTestUtils.startFragment(fragment); + FragmentController.setupFragment(fragment); fragment.onCreateOptionsMenu(null, null); fragment.onPrepareOptionsMenu(null); @@ -248,7 +248,7 @@ public class LifecycleTest { @Test public void onOptionItemSelectedShortCircuitsIfAnObserverHandlesTheMenuItem() { final TestFragment fragment = new TestFragment(); - FragmentTestUtils.startFragment(fragment); + FragmentController.setupFragment(fragment); final OptionItemAccepter accepter = new OptionItemAccepter(); fragment.getLifecycle().addObserver(accepter); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java new file mode 100644 index 000000000000..4c4207b23cab --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2018 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.settingslib.net; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Matchers.nullable; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.usage.NetworkStatsManager; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.INetworkStatsService; +import android.net.INetworkStatsSession; +import android.net.NetworkPolicy; +import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; +import android.os.RemoteException; +import android.text.format.DateUtils; +import android.util.Range; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.util.ReflectionHelpers; + +import java.time.ZonedDateTime; +import java.util.Iterator; + +@RunWith(SettingsLibRobolectricTestRunner.class) +public class NetworkCycleDataLoaderTest { + + @Mock + private NetworkStatsManager mNetworkStatsManager; + @Mock + private Context mContext; + @Mock + private NetworkPolicy mPolicy; + @Mock + private Iterator<Range<ZonedDateTime>> mIterator; + @Mock + private INetworkStatsService mNetworkStatsService; + + private NetworkCycleDataLoader mLoader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE)) + .thenReturn(mNetworkStatsManager); + when(mPolicy.cycleIterator()).thenReturn(mIterator); + } + + @Test + public void loadInBackground_noNetworkPolicy_shouldLoad4WeeksData() { + mLoader = spy(new NetworkCycleDataLoader.Builder(mContext).build()); + doReturn(null).when(mLoader).loadFourWeeksData(); + + mLoader.loadInBackground(); + + verify(mLoader).loadFourWeeksData(); + } + + @Test + public void loadInBackground_shouldQueryNetworkSummary() throws RemoteException { + final int networkType = ConnectivityManager.TYPE_MOBILE; + final String subId = "TestSubscriber"; + final ZonedDateTime now = ZonedDateTime.now(); + final Range<ZonedDateTime> cycle = new Range<>(now, now); + // mock 1 cycle data. + // hasNext() will be called internally in next(), hence setting it to return true twice. + when(mIterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false); + when(mIterator.next()).thenReturn(cycle); + mLoader = new NetworkCycleDataLoader.Builder(mContext) + .setNetworkPolicy(mPolicy).setNetworkType(networkType).setSubscriberId(subId).build(); + + mLoader.loadInBackground(); + + verify(mNetworkStatsManager).querySummary(eq(networkType), eq(subId), anyLong(), anyLong()); + } + + @Test + public void loadFourWeeksData_shouldGetUsageForLast4Weeks() throws RemoteException { + mLoader = spy(new NetworkCycleDataLoader.Builder(mContext).build()); + ReflectionHelpers.setField(mLoader, "mNetworkStatsService", mNetworkStatsService); + final INetworkStatsSession networkSession = mock(INetworkStatsSession.class); + when(mNetworkStatsService.openSession()).thenReturn(networkSession); + final NetworkStatsHistory networkHistory = mock(NetworkStatsHistory.class); + when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt())).thenReturn(networkHistory); + final long now = System.currentTimeMillis(); + final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4); + when(networkHistory.getStart()).thenReturn(fourWeeksAgo); + when(networkHistory.getEnd()).thenReturn(now); + + mLoader.loadFourWeeksData(); + + verify(mLoader).getUsage(eq(fourWeeksAgo), eq(now), any()); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java new file mode 100644 index 000000000000..10c9dfbe6067 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2018 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.settingslib.widget.apppreference; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.view.View; + +import androidx.preference.PreferenceViewHolder; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsLibRobolectricTestRunner.class) +public class AppPreferenceTest { + + private Context mContext; + private View mRootView; + private AppPreference mPref; + private PreferenceViewHolder mHolder; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mRootView = View.inflate(mContext, R.layout.preference_app, null /* parent */); + mHolder = PreferenceViewHolder.createInstanceForTests(mRootView); + mPref = new AppPreference(mContext); + } + + @Test + public void setProgress_showProgress() { + mPref.setProgress(1); + mPref.onBindViewHolder(mHolder); + + assertThat(mHolder.findViewById(android.R.id.progress).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void setSummary_showSummaryContainer() { + mPref.setSummary("test"); + mPref.onBindViewHolder(mHolder); + + assertThat(mHolder.findViewById(R.id.summary_container).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void noSummary_hideSummaryContainer() { + mPref.setSummary(null); + mPref.onBindViewHolder(mHolder); + + assertThat(mHolder.findViewById(R.id.summary_container).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void foobar_testName() { + float iconSize = mContext.getResources().getDimension(R.dimen.secondary_app_icon_size); + assertThat(Float.floatToIntBits(iconSize)).isEqualTo(Float.floatToIntBits(32)); + } +} diff --git a/packages/SystemUI/res/layout/qs_detail_buttons.xml b/packages/SystemUI/res/layout/qs_detail_buttons.xml index 03ed62b3e600..75f43f9a5808 100644 --- a/packages/SystemUI/res/layout/qs_detail_buttons.xml +++ b/packages/SystemUI/res/layout/qs_detail_buttons.xml @@ -26,6 +26,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" + android:minHeight="48dp" android:minWidth="132dp" android:textAppearance="@style/TextAppearance.QS.DetailButton" android:focusable="true" /> @@ -35,6 +36,7 @@ style="@style/QSBorderlessButton" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:minHeight="48dp" android:minWidth="88dp" android:textAppearance="@style/TextAppearance.QS.DetailButton" android:focusable="true"/> diff --git a/core/res/res/values-watch/themes.xml b/packages/SystemUI/res/values-night/styles.xml index 1be47baf4e7f..3ab6b56672f8 100644 --- a/core/res/res/values-watch/themes.xml +++ b/packages/SystemUI/res/values-night/styles.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project +<!-- Copyright (C) 2018 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. @@ -13,11 +13,15 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> - <!-- Theme for the dialog shown when an app crashes or ANRs. Override to make it dark. --> - <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert"> - <item name="windowContentTransitions">false</item> - <item name="windowActivityTransitions">false</item> - <item name="windowCloseOnTouchOutside">false</item> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + + <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog" /> + + <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert" /> + + <style name="Theme.SystemUI.Dialog.GlobalActions" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"> + <item name="android:windowIsFloating">true</item> </style> + </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java index ab890439a381..e253360a5364 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java @@ -21,13 +21,11 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.GraphicBuffer; +import android.graphics.Picture; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; import android.os.RemoteException; -import android.view.DisplayListCanvas; -import android.view.RenderNode; -import android.view.ThreadedRenderer; import android.view.View; import java.util.function.Consumer; @@ -108,12 +106,10 @@ public class RecentsTransition { * null if we were unable to allocate a hardware bitmap. */ public static Bitmap createHardwareBitmap(int width, int height, Consumer<Canvas> consumer) { - RenderNode node = RenderNode.create("RecentsTransition", null); - node.setLeftTopRightBottom(0, 0, width, height); - node.setClipToBounds(false); - DisplayListCanvas c = node.start(width, height); - consumer.accept(c); - node.end(c); - return ThreadedRenderer.createHardwareBitmap(node, width, height); + final Picture picture = new Picture(); + final Canvas canvas = picture.beginRecording(width, height); + consumer.accept(canvas); + picture.endRecording(); + return Bitmap.createBitmap(picture); } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index b04d04717be5..c7910f97675e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -342,6 +342,20 @@ public class ActivityManagerWrapper { } /** + * Moves an already resumed task to the side of the screen to initiate split screen. + */ + public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, + Rect initialBounds) { + try { + return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(taskId, + createMode, true /* onTop */, false /* animate */, initialBounds, + true /* showRecents */); + } catch (RemoteException e) { + return false; + } + } + + /** * Registers a task stack listener with the system. * This should be called on the main thread. */ diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index d83b36d6ea80..3191d14c5a83 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -151,6 +151,25 @@ public class WindowManagerWrapper { } } + public void setPipVisibility(final boolean visible) { + try { + WindowManagerGlobal.getWindowManagerService().setPipVisibility(visible); + } catch (RemoteException e) { + Log.e(TAG, "Unable to reach window manager", e); + } + } + + /** + * @return whether there is a soft nav bar. + */ + public boolean hasSoftNavigationBar() { + try { + return WindowManagerGlobal.getWindowManagerService().hasNavigationBar(); + } catch (RemoteException e) { + return false; + } + } + /** * @return The side of the screen where navigation bar is positioned. * @see #NAV_BAR_POS_RIGHT diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index 5ffdc7b221f3..2daa33bb1ea5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -377,6 +377,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit @Override public void onResume(int reason) { + displayDefaultSecurityMessage(); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index b159b393862a..b8df3c067969 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -342,12 +342,11 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe case SimPuk: // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); - if (securityMode != SecurityMode.None - || !mLockPatternUtils.isLockScreenDisabled( + if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled( KeyguardUpdateMonitor.getCurrentUser())) { - showSecurityScreen(securityMode); - } else { finish = true; + } else { + showSecurityScreen(securityMode); } break; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 10c8ec09bd5b..f1b53fec0714 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -86,9 +86,8 @@ import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; import com.android.settingslib.WirelessUtils; -import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.shared.system.ActivityManagerWrapper; - +import com.android.systemui.shared.system.TaskStackChangeListener; import com.google.android.collect.Lists; import java.io.FileDescriptor; @@ -2218,8 +2217,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } - private final SysUiTaskStackChangeListener - mTaskStackListener = new SysUiTaskStackChangeListener() { + private final TaskStackChangeListener + mTaskStackListener = new TaskStackChangeListener() { @Override public void onTaskStackChangedBackground() { try { diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 77f4bf529f21..408e599bc289 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -256,12 +256,6 @@ public class ImageWallpaper extends WallpaperService { Log.d(TAG, "onSurfaceRedrawNeeded"); } super.onSurfaceRedrawNeeded(holder); - // At the end of this method we should have drawn into the surface. - // This means that the bitmap should be loaded synchronously if - // it was already unloaded. - if (mBackground == null) { - updateBitmap(mWallpaperManager.getBitmap(true /* hardware */)); - } mSurfaceRedrawNeeded = true; drawFrame(); } diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index bd2b7a577b3d..1af2156c4bbe 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -16,6 +16,11 @@ package com.android.systemui; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; +import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType; + import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -33,11 +38,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Log; import android.view.MotionEvent; - import com.android.systemui.OverviewProxyService.OverviewProxyListener; -import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -46,17 +47,11 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.CallbackController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; -import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP; -import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; -import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType; - /** * Class to send information from overview to launcher with a binder. */ @@ -133,7 +128,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } long token = Binder.clearCallingIdentity(); try { - EventBus.getDefault().post(new DockedFirstAnimationFrameEvent()); + Divider divider = SysUiServiceProvider.getComponent(mContext, Divider.class); + if (divider != null) { + divider.onDockedFirstAnimationFrame(); + } } finally { Binder.restoreCallingIdentity(token); } @@ -314,8 +312,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis getDefaultInteractionFlags()); // Listen for the package update changes. - if (SystemServicesProxy.getInstance(context) - .isSystemUser(mDeviceProvisionedController.getCurrentUser())) { + if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) { updateEnabledState(); mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback); IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java index f9dbf4a15e5c..fb343f9b9b45 100644 --- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java @@ -22,27 +22,10 @@ import android.view.View; public interface RecentsComponent { void showRecentApps(boolean triggeredFromAltTab); - void showNextAffiliatedTask(); - void showPrevAffiliatedTask(); /** * Docks the top-most task and opens recents. */ - boolean splitPrimaryTask(int dragMode, int stackCreateMode, Rect initialBounds, + boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds, int metricsDockAction); - - /** - * Called during a drag-from-navbar-in gesture. - * - * @param distanceFromTop the distance of the current drag in gesture from the top of the - * screen - */ - void onDraggingInRecents(float distanceFromTop); - - /** - * Called when the gesture to drag in recents ended. - * - * @param velocity the velocity of the finger when releasing it in pixels per second - */ - void onDraggingInRecentsEnded(float velocity); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 36b234704592..c5664605dcef 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -16,11 +16,15 @@ package com.android.systemui.doze; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.os.Build; import android.os.Handler; import android.os.Trace; import android.os.UserHandle; @@ -31,7 +35,12 @@ import com.android.internal.annotations.VisibleForTesting; /** * Controls the screen brightness when dozing. */ -public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListener { +public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part, + SensorEventListener { + protected static final String ACTION_AOD_BRIGHTNESS = + "com.android.systemui.doze.AOD_BRIGHTNESS"; + protected static final String BRIGHTNESS_BUCKET = "brightness_bucket"; + private final Context mContext; private final DozeMachine.Service mDozeService; private final DozeHost mDozeHost; @@ -40,36 +49,52 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen private final Sensor mLightSensor; private final int[] mSensorToBrightness; private final int[] mSensorToScrimOpacity; + private final boolean mDebuggable; private boolean mRegistered; private int mDefaultDozeBrightness; private boolean mPaused = false; private int mLastSensorValue = -1; + /** + * Debug value used for emulating various display brightness buckets: + * + * {@code am broadcast -p com.android.systemui -a com.android.systemui.doze.AOD_BRIGHTNESS + * --ei brightness_bucket 1} + */ + private int mDebugBrightnessBucket = -1; + + @VisibleForTesting public DozeScreenBrightness(Context context, DozeMachine.Service service, SensorManager sensorManager, Sensor lightSensor, DozeHost host, Handler handler, int defaultDozeBrightness, int[] sensorToBrightness, - int[] sensorToScrimOpacity) { + int[] sensorToScrimOpacity, boolean debuggable) { mContext = context; mDozeService = service; mSensorManager = sensorManager; mLightSensor = lightSensor; mDozeHost = host; mHandler = handler; + mDebuggable = debuggable; mDefaultDozeBrightness = defaultDozeBrightness; mSensorToBrightness = sensorToBrightness; mSensorToScrimOpacity = sensorToScrimOpacity; + + if (mDebuggable) { + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_AOD_BRIGHTNESS); + mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null); + } } - @VisibleForTesting public DozeScreenBrightness(Context context, DozeMachine.Service service, SensorManager sensorManager, Sensor lightSensor, DozeHost host, Handler handler, AlwaysOnDisplayPolicy policy) { this(context, service, sensorManager, lightSensor, host, handler, context.getResources().getInteger( com.android.internal.R.integer.config_screenBrightnessDoze), - policy.screenBrightnessArray, policy.dimmingScrimArray); + policy.screenBrightnessArray, policy.dimmingScrimArray, Build.IS_DEBUGGABLE); } @Override @@ -87,7 +112,7 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen resetBrightnessToDefault(); break; case FINISH: - setLightSensorEnabled(false); + onDestroy(); break; } if (newState != DozeMachine.State.FINISH) { @@ -95,6 +120,13 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen } } + private void onDestroy() { + setLightSensorEnabled(false); + if (mDebuggable) { + mContext.unregisterReceiver(this); + } + } + @Override public void onSensorChanged(SensorEvent event) { Trace.beginSection("DozeScreenBrightness.onSensorChanged" + event.values[0]); @@ -110,7 +142,9 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen private void updateBrightnessAndReady() { if (mRegistered) { - int brightness = computeBrightness(mLastSensorValue); + int sensorValue = mDebugBrightnessBucket == -1 + ? mLastSensorValue : mDebugBrightnessBucket; + int brightness = computeBrightness(sensorValue); boolean brightnessReady = brightness > 0; if (brightnessReady) { mDozeService.setDozeScreenBrightness(clampToUserSetting(brightness)); @@ -125,7 +159,7 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen scrimOpacity = 255; } else if (brightnessReady) { // Only unblank scrim once brightness is ready. - scrimOpacity = computeScrimOpacity(mLastSensorValue); + scrimOpacity = computeScrimOpacity(sensorValue); } if (scrimOpacity >= 0) { mDozeHost.setAodDimmingScrim(scrimOpacity / 255f); @@ -184,4 +218,9 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen } } + @Override + public void onReceive(Context context, Intent intent) { + mDebugBrightnessBucket = intent.getIntExtra(BRIGHTNESS_BUCKET, -1); + updateBrightnessAndReady(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java b/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java index 745f312a29b4..d833c16c04b3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/DismissCallbackRegistry.java @@ -19,9 +19,6 @@ package com.android.systemui.keyguard; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.systemui.Dependency; import com.android.systemui.UiOffloadThread; -import com.android.systemui.recents.Recents; -import com.android.systemui.recents.misc.SystemServicesProxy; - import java.util.ArrayList; /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 757c821f37e3..4988f07ca3f3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1938,6 +1938,12 @@ public class KeyguardViewMediator extends SystemUI { mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE); } + + // TODO(b/113914868): investigation log for disappearing home button + Log.d(TAG, "adjustStatusBarLocked (b/113914868): mShowing=" + mShowing + + " mStatusBarManager=" + mStatusBarManager + " mOccluded=" + + mOccluded + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons); + if (mStatusBarManager == null) { Log.w(TAG, "Could not get status bar manager"); } else { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java index 0cedf9825990..74f770679cc9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java @@ -19,7 +19,6 @@ package com.android.systemui.keyguard; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ActivityTaskManager; -import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.app.KeyguardManager; import android.content.ComponentName; @@ -29,11 +28,9 @@ import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; - import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; public class WorkLockActivityController { private static final String TAG = WorkLockActivityController.class.getSimpleName(); @@ -111,7 +108,7 @@ public class WorkLockActivityController { } } - private final SysUiTaskStackChangeListener mLockListener = new SysUiTaskStackChangeListener() { + private final TaskStackChangeListener mLockListener = new TaskStackChangeListener() { @Override public void onTaskProfileLocked(int taskId, int userId) { startWorkChallengeInTask(taskId, userId); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java index b7164cbb3271..864a6f9185fa 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java @@ -21,11 +21,12 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import android.content.pm.PackageManager; import android.content.res.Configuration; - +import android.os.UserHandle; +import android.os.UserManager; import com.android.systemui.SystemUI; -import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.component.ExpandPipEvent; import com.android.systemui.statusbar.CommandQueue; - import java.io.FileDescriptor; import java.io.PrintWriter; @@ -47,8 +48,8 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks { } // Ensure that we are the primary user's SystemUI. - final int processUser = SystemServicesProxy.getInstance(mContext).getProcessUser(); - if (!SystemServicesProxy.getInstance(mContext).isSystemUser(processUser)) { + final int processUser = UserManager.get(mContext).getUserHandle(); + if (processUser != UserHandle.USER_SYSTEM) { throw new IllegalStateException("Non-primary Pip component not currently supported."); } @@ -58,6 +59,7 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks { mPipManager.initialize(mContext); getComponent(CommandQueue.class).addCallbacks(this); + putComponent(PipUI.class, this); } @Override @@ -65,6 +67,10 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks { mPipManager.showPictureInPictureMenu(); } + public void expandPip() { + EventBus.getDefault().send(new ExpandPipEvent()); + } + @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java b/packages/SystemUI/src/com/android/systemui/pip/phone/ForegroundThread.java index 784ac4e16838..9bf46bb488f3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/ForegroundThread.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.recents.misc; +package com.android.systemui.pip.phone; import android.os.Handler; import android.os.HandlerThread; diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java index 5547e2d3985d..9ce2606a2a15 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java @@ -19,21 +19,17 @@ package com.android.systemui.pip.phone; import android.content.Context; import android.graphics.PixelFormat; import android.graphics.Point; -import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnLayoutChangeListener; -import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.widget.FrameLayout; - import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.shared.system.WindowManagerWrapper; public class PipDismissViewController { @@ -59,7 +55,7 @@ public class PipDismissViewController { if (mDismissView == null) { // Determine sizes for the view final Rect stableInsets = new Rect(); - SystemServicesProxy.getInstance(mContext).getStableInsets(stableInsets); + WindowManagerWrapper.getInstance().getStableInsets(stableInsets); final Point windowSize = new Point(); mWindowManager.getDefaultDisplay().getRealSize(windowSize); final int gradientHeight = mContext.getResources().getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index ee15655d87b2..04746c16585a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -17,7 +17,6 @@ package com.android.systemui.pip.phone; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.INPUT_CONSUMER_PIP; import android.app.ActivityManager; import android.app.ActivityTaskManager; @@ -36,15 +35,15 @@ import android.view.IPinnedStackController; import android.view.IPinnedStackListener; import android.view.IWindowManager; import android.view.WindowManagerGlobal; - +import com.android.systemui.Dependency; +import com.android.systemui.UiOffloadThread; import com.android.systemui.pip.BasePipManager; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.component.ExpandPipEvent; -import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; - +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.WindowManagerWrapper; import java.io.PrintWriter; /** @@ -72,7 +71,7 @@ public class PipManager implements BasePipManager { /** * Handler for system task stack changes. */ - SysUiTaskStackChangeListener mTaskStackListener = new SysUiTaskStackChangeListener() { + TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { @Override public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { mTouchHandler.onActivityPinned(); @@ -80,7 +79,9 @@ public class PipManager implements BasePipManager { mMenuController.onActivityPinned(); mAppOpsListener.onActivityPinned(packageName); - SystemServicesProxy.getInstance(mContext).setPipVisibility(true); + Dependency.get(UiOffloadThread.class).submit(() -> { + WindowManagerWrapper.getInstance().setPipVisibility(true); + }); } @Override @@ -93,7 +94,9 @@ public class PipManager implements BasePipManager { mTouchHandler.onActivityUnpinned(topActivity); mAppOpsListener.onActivityUnpinned(); - SystemServicesProxy.getInstance(mContext).setPipVisibility(topActivity != null); + Dependency.get(UiOffloadThread.class).submit(() -> { + WindowManagerWrapper.getInstance().setPipVisibility(topActivity != null); + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index f0ab046f7dbe..ce7da79de794 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -30,7 +30,6 @@ import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.ActivityManager.StackInfo; -import android.app.ActivityTaskManager; import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.content.Context; @@ -43,14 +42,11 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; import android.view.animation.Interpolator; - import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.os.SomeArgs; import com.android.internal.policy.PipSnapAlgorithm; -import com.android.systemui.recents.misc.ForegroundThread; -import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.FlingAnimationUtils; - import java.io.PrintWriter; /** @@ -116,7 +112,7 @@ public class PipMotionHelper implements Handler.Callback { */ void onConfigurationChanged() { mSnapAlgorithm.onConfigurationChanged(); - SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets); + WindowManagerWrapper.getInstance().getStableInsets(mStableInsets); } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 020c5500c0a0..43e9db7f0ee5 100755 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -16,6 +16,11 @@ package com.android.systemui.pip.tv; +import static android.app.ActivityTaskManager.INVALID_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.view.Display.DEFAULT_DISPLAY; + import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.StackInfo; @@ -44,22 +49,17 @@ import android.view.IPinnedStackController; import android.view.IPinnedStackListener; import android.view.IWindowManager; import android.view.WindowManagerGlobal; - +import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.UiOffloadThread; import com.android.systemui.pip.BasePipManager; -import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; - +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.WindowManagerWrapper; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import static android.app.ActivityTaskManager.INVALID_STACK_ID; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.view.Display.DEFAULT_DISPLAY; - /** * Manages the picture-in-picture (PIP) UI and states. */ @@ -630,7 +630,7 @@ public class PipManager implements BasePipManager { return false; } - private SysUiTaskStackChangeListener mTaskStackListener = new SysUiTaskStackChangeListener() { + private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() { @Override public void onTaskStackChanged() { if (DEBUG) Log.d(TAG, "onTaskStackChanged()"); @@ -754,7 +754,9 @@ public class PipManager implements BasePipManager { } private void updatePipVisibility(final boolean visible) { - SystemServicesProxy.getInstance(mContext).setPipVisibility(visible); + Dependency.get(UiOffloadThread.class).submit(() -> { + WindowManagerWrapper.getInstance().setPipVisibility(visible); + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index c36cdf6ac262..79e508611750 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -256,7 +256,7 @@ public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { public void setExpanded(boolean expanded) { if (DEBUG) Log.d(TAG, "setExpanded " + expanded); mQsExpanded = expanded; - mQSPanel.setListening(mListening && mQsExpanded); + mQSPanel.setListening(mListening, mQsExpanded); updateQsState(); } @@ -287,8 +287,7 @@ public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { mListening = listening; mHeader.setListening(listening); mFooter.setListening(listening); - mQSPanel.setListening(mListening && mQsExpanded); - mQSPanel.getFooter().setListening(listening); + mQSPanel.setListening(mListening, mQsExpanded); } @Override @@ -365,7 +364,11 @@ public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - getView().animate().setListener(null); + if (getView() != null) { + // The view could be destroyed before the animation completes when + // switching users. + getView().animate().setListener(null); + } mHeaderAnimating = false; updateQsState(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 7a57fdde6712..8b2e1d5eef64 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -353,12 +353,21 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mListening) { refreshAllTiles(); } - if (mBrightnessView.getVisibility() == View.VISIBLE) { - if (listening) { - mBrightnessController.registerCallbacks(); - } else { - mBrightnessController.unregisterCallbacks(); - } + } + + public void setListening(boolean listening, boolean expanded) { + setListening(listening && expanded); + getFooter().setListening(listening); + // Set the listening as soon as the QS fragment starts listening regardless of the expansion, + // so it will update the current brightness before the slider is visible. + setBrightnessListening(listening); + } + + public void setBrightnessListening(boolean listening) { + if (listening) { + mBrightnessController.registerCallbacks(); + } else { + mBrightnessController.unregisterCallbacks(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java index 12daff1f12f9..9edd65e14bf2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java @@ -25,6 +25,7 @@ import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.nfc.NfcAdapter; import android.provider.Settings; +import android.service.quicksettings.Tile; import android.widget.Switch; import com.android.internal.logging.MetricsLogger; @@ -77,6 +78,9 @@ public class NfcTile extends QSTileImpl<BooleanState> { @Override protected void handleClick() { + if (getAdapter() == null) { + return; + } if (!getAdapter().isEnabled()) { getAdapter().enable(); } else { @@ -96,13 +100,13 @@ public class NfcTile extends QSTileImpl<BooleanState> { @Override protected void handleUpdateState(BooleanState state, Object arg) { - final Drawable mEnable = mContext.getDrawable(R.drawable.ic_qs_nfc_enabled); - final Drawable mDisable = mContext.getDrawable(R.drawable.ic_qs_nfc_disabled); - - if (getAdapter() == null) return; - state.value = getAdapter().isEnabled(); + state.value = getAdapter() != null && getAdapter().isEnabled(); + state.state = getAdapter() == null + ? Tile.STATE_UNAVAILABLE + : state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + state.icon = ResourceIcon.get( + state.value ? R.drawable.ic_qs_nfc_enabled : R.drawable.ic_qs_nfc_disabled); state.label = mContext.getString(R.string.quick_settings_nfc_label); - state.icon = new DrawableIcon(state.value ? mEnable : mDisable); state.expandedAccessibilityClassName = Switch.class.getName(); state.contentDescription = state.label; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl index fc1831d55c9d..90c10992bc94 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl @@ -31,8 +31,7 @@ oneway interface IRecentsNonSystemUserCallbacks { void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey); void toggleRecents(int recentsGrowTarget); void onConfigurationChanged(); - void splitPrimaryTask(int topTaskId, int dragMode, int stackCreateMode, - in Rect initialBounds); + void splitPrimaryTask(int topTaskId, int stackCreateMode, in Rect initialBounds); void onDraggingInRecents(float distanceFromTop); void onDraggingInRecentsEnded(float velocity); void showCurrentUserToast(int msgResId, int msgLength); diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl index 58d8d8fd600a..e97714486dcf 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl @@ -29,7 +29,7 @@ oneway interface IRecentsSystemUserCallbacks { void updateRecentsVisibility(boolean visible); void startScreenPinning(int taskId); void sendRecentsDrawnEvent(); - void sendDockingTopTaskEvent(int dragMode, in Rect initialRect); + void sendDockingTopTaskEvent(in Rect initialRect); void sendLaunchRecentsEvent(); void sendDockedFirstAnimationFrameEvent(); void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart); diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 8bb3c0231a76..74f6c2dec4e7 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -53,6 +53,7 @@ import com.android.systemui.OverviewProxyService; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.SystemUIApplication; +import com.android.systemui.recents.events.ui.RecentsGrowingEvent; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.SystemUI; import com.android.systemui.recents.events.EventBus; @@ -248,6 +249,10 @@ public class Recents extends SystemUI mImpl.onBootCompleted(); } + public void growRecents() { + EventBus.getDefault().send(new RecentsGrowingEvent()); + } + /** * Shows the Recents. */ @@ -463,7 +468,7 @@ public class Recents extends SystemUI } @Override - public boolean splitPrimaryTask(int dragMode, int stackCreateMode, Rect initialBounds, + public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds, int metricsDockAction) { // Ensure the device has been provisioned before allowing the user to interact with // recents @@ -495,16 +500,15 @@ public class Recents extends SystemUI runningTask.topActivity.flattenToShortString()); } if (sSystemServicesProxy.isSystemUser(currentUser)) { - mImpl.splitPrimaryTask(runningTask.id, dragMode, stackCreateMode, - initialBounds); + mImpl.splitPrimaryTask(runningTask.id, stackCreateMode, initialBounds); } else { if (mSystemToUserCallbacks != null) { IRecentsNonSystemUserCallbacks callbacks = mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser); if (callbacks != null) { try { - callbacks.splitPrimaryTask(runningTask.id, dragMode, - stackCreateMode, initialBounds); + callbacks.splitPrimaryTask(runningTask.id, stackCreateMode, + initialBounds); } catch (RemoteException e) { Log.e(TAG, "Callback failed", e); } @@ -552,53 +556,6 @@ public class Recents extends SystemUI } } - @Override - public void onDraggingInRecents(float distanceFromTop) { - if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) { - mImpl.onDraggingInRecents(distanceFromTop); - } else { - if (mSystemToUserCallbacks != null) { - IRecentsNonSystemUserCallbacks callbacks = - mSystemToUserCallbacks.getNonSystemUserRecentsForUser( - mDraggingInRecentsCurrentUser); - if (callbacks != null) { - try { - callbacks.onDraggingInRecents(distanceFromTop); - } catch (RemoteException e) { - Log.e(TAG, "Callback failed", e); - } - } else { - Log.e(TAG, "No SystemUI callbacks found for user: " - + mDraggingInRecentsCurrentUser); - } - } - } - } - - @Override - public void onDraggingInRecentsEnded(float velocity) { - if (sSystemServicesProxy.isSystemUser(mDraggingInRecentsCurrentUser)) { - mImpl.onDraggingInRecentsEnded(velocity); - } else { - if (mSystemToUserCallbacks != null) { - IRecentsNonSystemUserCallbacks callbacks = - mSystemToUserCallbacks.getNonSystemUserRecentsForUser( - mDraggingInRecentsCurrentUser); - if (callbacks != null) { - try { - callbacks.onDraggingInRecentsEnded(velocity); - } catch (RemoteException e) { - Log.e(TAG, "Callback failed", e); - } - } else { - Log.e(TAG, "No SystemUI callbacks found for user: " - + mDraggingInRecentsCurrentUser); - } - } - } - } - - @Override public void showNextAffiliatedTask() { // Ensure the device has been provisioned before allowing the user to interact with // recents @@ -609,7 +566,6 @@ public class Recents extends SystemUI mImpl.showNextAffiliatedTask(); } - @Override public void showPrevAffiliatedTask() { // Ensure the device has been provisioned before allowing the user to interact with // recents @@ -686,7 +642,12 @@ public class Recents extends SystemUI public final void onBusEvent(DockedFirstAnimationFrameEvent event) { SystemServicesProxy ssp = Recents.getSystemServices(); int processUser = ssp.getProcessUser(); - if (!ssp.isSystemUser(processUser)) { + if (ssp.isSystemUser(processUser)) { + final Divider divider = getComponent(Divider.class); + if (divider != null) { + divider.onDockedFirstAnimationFrame(); + } + } else { postToSystemUser(new Runnable() { @Override public void run() { @@ -723,7 +684,12 @@ public class Recents extends SystemUI public final void onBusEvent(final RecentsDrawnEvent event) { int processUser = sSystemServicesProxy.getProcessUser(); - if (!sSystemServicesProxy.isSystemUser(processUser)) { + if (sSystemServicesProxy.isSystemUser(processUser)) { + final Divider divider = getComponent(Divider.class); + if (divider != null) { + divider.onRecentsDrawn(); + } + } else { postToSystemUser(new Runnable() { @Override public void run() { @@ -739,13 +705,17 @@ public class Recents extends SystemUI public final void onBusEvent(final DockedTopTaskEvent event) { int processUser = sSystemServicesProxy.getProcessUser(); - if (!sSystemServicesProxy.isSystemUser(processUser)) { + if (sSystemServicesProxy.isSystemUser(processUser)) { + final Divider divider = getComponent(Divider.class); + if (divider != null) { + divider.onDockedTopTask(); + } + } else { postToSystemUser(new Runnable() { @Override public void run() { try { - mUserToSystemCallbacks.sendDockingTopTaskEvent(event.dragMode, - event.initialRect); + mUserToSystemCallbacks.sendDockingTopTaskEvent(event.initialRect); } catch (RemoteException e) { Log.e(TAG, "Callback failed", e); } @@ -756,7 +726,12 @@ public class Recents extends SystemUI public final void onBusEvent(final RecentsActivityStartingEvent event) { int processUser = sSystemServicesProxy.getProcessUser(); - if (!sSystemServicesProxy.isSystemUser(processUser)) { + if (sSystemServicesProxy.isSystemUser(processUser)) { + final Divider divider = getComponent(Divider.class); + if (divider != null) { + divider.onRecentsActivityStarting(); + } + } else { postToSystemUser(new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 63a65d030f6d..d95c7313a282 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -49,6 +49,7 @@ import android.widget.Toast; import com.android.systemui.Dependency; import com.android.systemui.OverviewProxyService; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.pip.phone.ForegroundThread; import com.google.android.collect.Lists; import com.android.internal.logging.MetricsLogger; @@ -72,7 +73,6 @@ import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; import com.android.systemui.recents.events.ui.DraggingInRecentsEvent; import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent; import com.android.systemui.recents.misc.DozeTrigger; -import com.android.systemui.recents.misc.ForegroundThread; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan; @@ -690,14 +690,13 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener showRelativeAffiliatedTask(false); } - public void splitPrimaryTask(int taskId, int dragMode, int stackCreateMode, - Rect initialBounds) { + public void splitPrimaryTask(int taskId, int stackCreateMode, Rect initialBounds) { SystemServicesProxy ssp = Recents.getSystemServices(); // Make sure we inform DividerView before we actually start the activity so we can change // the resize mode already. if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) { - EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds)); + EventBus.getDefault().send(new DockedTopTaskEvent(initialBounds)); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java index beec4b395e9c..a1da785f2a80 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java @@ -87,12 +87,11 @@ public class RecentsImplProxy extends IRecentsNonSystemUserCallbacks.Stub { } @Override - public void splitPrimaryTask(int topTaskId, int dragMode, int stackCreateMode, - Rect initialBounds) throws RemoteException { + public void splitPrimaryTask(int topTaskId, int stackCreateMode, Rect initialBounds) + throws RemoteException { SomeArgs args = SomeArgs.obtain(); args.argi1 = topTaskId; - args.argi2 = dragMode; - args.argi3 = stackCreateMode; + args.argi2 = stackCreateMode; args.arg1 = initialBounds; mHandler.sendMessage(mHandler.obtainMessage(MSG_DOCK_TOP_TASK, args)); } @@ -141,7 +140,7 @@ public class RecentsImplProxy extends IRecentsNonSystemUserCallbacks.Stub { break; case MSG_DOCK_TOP_TASK: args = (SomeArgs) msg.obj; - mImpl.splitPrimaryTask(args.argi1, args.argi2, args.argi3 = 0, + mImpl.splitPrimaryTask(args.argi1, args.argi2 = 0, (Rect) args.arg1); break; case MSG_ON_DRAGGING_IN_RECENTS: diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java index ff1f7dc5a2a8..c5e9f046aa16 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java @@ -26,13 +26,13 @@ import android.util.SparseArray; import com.android.systemui.EventLogConstants; import com.android.systemui.EventLogTags; +import com.android.systemui.pip.phone.ForegroundThread; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; import com.android.systemui.recents.events.activity.DockedTopTaskEvent; import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent; import com.android.systemui.recents.events.ui.RecentsDrawnEvent; -import com.android.systemui.recents.misc.ForegroundThread; /** * An implementation of the system user's Recents interface to be called remotely by secondary @@ -99,8 +99,8 @@ public class RecentsSystemUser extends IRecentsSystemUserCallbacks.Stub { } @Override - public void sendDockingTopTaskEvent(int dragMode, Rect initialRect) throws RemoteException { - EventBus.getDefault().post(new DockedTopTaskEvent(dragMode, initialRect)); + public void sendDockingTopTaskEvent(Rect initialRect) throws RemoteException { + EventBus.getDefault().post(new DockedTopTaskEvent(initialRect)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java index f1bc214670f5..9e3ced3f3757 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java @@ -26,11 +26,9 @@ import com.android.systemui.recents.events.EventBus; */ public class DockedTopTaskEvent extends EventBus.Event { - public int dragMode; public Rect initialRect; - public DockedTopTaskEvent(int dragMode, Rect initialRect) { - this.dragMode = dragMode; + public DockedTopTaskEvent(Rect initialRect) { this.initialRect = initialRect; } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index 117872558f7f..3ed5f70b8915 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -460,7 +460,7 @@ public class BrightnessController implements ToggleSlider.Listener { private void animateSliderTo(int target) { if (!mControlValueInitialized) { - // Don't animate the first value since it's default state isn't meaningful to users. + // Don't animate the first value since its default state isn't meaningful to users. mControl.setValue(target); mControlValueInitialized = true; } @@ -470,10 +470,12 @@ public class BrightnessController implements ToggleSlider.Listener { mSliderAnimator = ValueAnimator.ofInt(mControl.getValue(), target); mSliderAnimator.addUpdateListener((ValueAnimator animation) -> { mExternalChange = true; - mControl.setValue((int)animation.getAnimatedValue()); + mControl.setValue((int) animation.getAnimatedValue()); mExternalChange = false; }); - mSliderAnimator.setDuration(SLIDER_ANIMATION_DURATION); + final long animationDuration = SLIDER_ANIMATION_DURATION * Math.abs( + mControl.getValue() - target) / GAMMA_SPACE_MAX; + mSliderAnimator.setDuration(animationDuration); mSliderAnimator.start(); } diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java index 750002cbc5ca..64fa8f86c5f6 100644 --- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java @@ -18,11 +18,7 @@ package com.android.systemui.shortcut; import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT; import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; -import static android.os.UserHandle.USER_CURRENT; -import static com.android.systemui.statusbar.phone.NavigationBarGestureHelper.DRAG_MODE_NONE; - -import android.app.ActivityManager; import android.content.res.Configuration; import android.os.RemoteException; import android.util.Log; @@ -30,17 +26,11 @@ import android.view.IWindowManager; import android.view.KeyEvent; import android.view.WindowManager; import android.view.WindowManagerGlobal; - import com.android.internal.policy.DividerSnapAlgorithm; import com.android.systemui.SystemUI; import com.android.systemui.recents.Recents; -import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.stackdivider.DividerView; -import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; - -import java.util.List; /** * Dispatches shortcut to System UI components @@ -94,7 +84,7 @@ public class ShortcutKeyDispatcher extends SystemUI if (dockSide == WindowManager.DOCKED_INVALID) { // Split the screen Recents recents = getComponent(Recents.class); - recents.splitPrimaryTask(DRAG_MODE_NONE, (shortcutCode == SC_DOCK_LEFT) + recents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT) ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1); } else { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index da0a43551f1f..ea194a70adf2 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -16,29 +16,28 @@ package com.android.systemui.stackdivider; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + import android.content.res.Configuration; import android.os.RemoteException; +import android.util.Log; import android.view.IDockedStackListener; import android.view.LayoutInflater; import android.view.View; - +import android.view.WindowManagerGlobal; import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.recents.Recents; -import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.events.ui.RecentsDrawnEvent; -import com.android.systemui.recents.misc.SystemServicesProxy; - -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; - import java.io.FileDescriptor; import java.io.PrintWriter; /** * Controls the docked stack divider. */ -public class Divider extends SystemUI { +public class Divider extends SystemUI implements DividerView.DividerCallbacks { + private static final String TAG = "Divider"; + private DividerWindowManager mWindowManager; private DividerView mView; private final DividerState mDividerState = new DividerState(); @@ -55,10 +54,13 @@ public class Divider extends SystemUI { update(mContext.getResources().getConfiguration()); putComponent(Divider.class, this); mDockDividerVisibilityListener = new DockDividerVisibilityListener(); - SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.registerDockedStackListener(mDockDividerVisibilityListener); + try { + WindowManagerGlobal.getWindowManagerService().registerDockedStackListener( + mDockDividerVisibilityListener); + } catch (Exception e) { + Log.e(TAG, "Failed to register docked stack listener", e); + } mForcedResizableController = new ForcedResizableInfoActivityController(mContext); - EventBus.getDefault().register(this); } @Override @@ -82,7 +84,7 @@ public class Divider extends SystemUI { private void addDivider(Configuration configuration) { mView = (DividerView) LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null); - mView.injectDependencies(mWindowManager, mDividerState); + mView.injectDependencies(mWindowManager, mDividerState, this); mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE); mView.setMinimizedDockStack(mMinimized, mHomeStackResizable); final int size = mContext.getResources().getDimensionPixelSize( @@ -156,18 +158,64 @@ public class Divider extends SystemUI { mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme); } + public void onRecentsActivityStarting() { + if (mView != null) { + mView.onRecentsActivityStarting(); + } + } + /** - * Workaround for b/62528361, at the time RecentsDrawnEvent is sent, it may happen before a + * Workaround for b/62528361, at the time recents has drawn, it may happen before a * configuration change to the Divider, and internally, the event will be posted to the * subscriber, or DividerView, which has been removed and prevented from resizing. Instead, * register the event handler here and proxy the event to the current DividerView. */ - public final void onBusEvent(RecentsDrawnEvent drawnEvent) { + public void onRecentsDrawn() { if (mView != null) { mView.onRecentsDrawn(); } } + public void onUndockingTask() { + if (mView != null) { + mView.onUndockingTask(); + } + } + + public void onDockedFirstAnimationFrame() { + if (mView != null) { + mView.onDockedFirstAnimationFrame(); + } + } + + public void onDockedTopTask() { + if (mView != null) { + mView.onDockedTopTask(); + } + } + + public void onAppTransitionFinished() { + mForcedResizableController.onAppTransitionFinished(); + } + + @Override + public void onDraggingStart() { + mForcedResizableController.onDraggingStart(); + } + + @Override + public void onDraggingEnd() { + mForcedResizableController.onDraggingEnd(); + } + + @Override + public void growRecents() { + Recents recents = getComponent(Recents.class); + if (recents != null) { + recents.growRecents(); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.print(" mVisible="); pw.println(mVisible); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 98925b9ba9e5..fa01af68364e 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -55,7 +55,6 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import android.widget.FrameLayout; - import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.policy.DividerSnapAlgorithm; @@ -64,17 +63,8 @@ import com.android.internal.policy.DockedDividerUtils; import com.android.internal.view.SurfaceFlingerVsyncChoreographer; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; -import com.android.systemui.recents.events.activity.DockedTopTaskEvent; -import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; -import com.android.systemui.recents.events.activity.UndockingTaskEvent; -import com.android.systemui.recents.events.ui.RecentsGrowingEvent; -import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.stackdivider.events.StartedDragingEvent; -import com.android.systemui.stackdivider.events.StoppedDragingEvent; +import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.FlingAnimationUtils; -import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; /** * Docked stack divider. @@ -82,6 +72,12 @@ import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; public class DividerView extends FrameLayout implements OnTouchListener, OnComputeInternalInsetsListener { + public interface DividerCallbacks { + void onDraggingStart(); + void onDraggingEnd(); + void growRecents(); + } + static final long TOUCH_ANIMATION_DURATION = 150; static final long TOUCH_RELEASE_ANIMATION_DURATION = 200; @@ -149,6 +145,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, private FlingAnimationUtils mFlingAnimationUtils; private DividerSnapAlgorithm mSnapAlgorithm; private DividerSnapAlgorithm mMinimizedSnapAlgorithm; + private DividerCallbacks mCallback; private final Rect mStableInsets = new Rect(); private boolean mGrowRecents; @@ -162,6 +159,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, private DividerState mState; private final SurfaceFlingerVsyncChoreographer mSfChoreographer; + // The view is removed or in the process of been removed from the system. private boolean mRemoved; @@ -306,7 +304,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - EventBus.getDefault().register(this); // Save the current target if not minimized once attached to window if (mHomeStackResizable && mDockSide != WindowManager.DOCKED_INVALID @@ -315,14 +312,9 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - EventBus.getDefault().unregister(this); - } - void onDividerRemoved() { mRemoved = true; + mCallback = null; mHandler.removeMessages(MSG_RESIZE_STACK); } @@ -364,13 +356,15 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } - public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState) { + public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState, + DividerCallbacks callback) { mWindowManager = windowManager; mState = dividerState; + mCallback = callback; // Set the previous position ratio before minimized state after attaching this divider if (mStableInsets.isEmpty()) { - SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets); + WindowManagerWrapper.getInstance().getStableInsets(mStableInsets); } if (mState.mRatioPositionBeforeMinimized == 0) { @@ -419,7 +413,9 @@ public class DividerView extends FrameLayout implements OnTouchListener, mWindowManager.setSlippery(false); liftBackground(); } - EventBus.getDefault().send(new StartedDragingEvent()); + if (mCallback != null) { + mCallback.onDraggingStart(); + } return mDockSide != WindowManager.DOCKED_INVALID; } @@ -617,7 +613,9 @@ public class DividerView extends FrameLayout implements OnTouchListener, mCurrentAnimator = null; mEntranceAnimationRunning = false; mExitAnimationRunning = false; - EventBus.getDefault().send(new StoppedDragingEvent()); + if (mCallback != null) { + mCallback.onDraggingEnd(); + } // Record last snap target the divider moved to if (mHomeStackResizable && !mIsInMinimizeInteraction) { @@ -776,7 +774,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, if (mDisplayRotation != mDefaultDisplay.getRotation()) { // Splitscreen to minimize is about to starts after rotating landscape to seascape, // update insets, display info and snap algorithm targets - SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets); + WindowManagerWrapper.getInstance().getStableInsets(mStableInsets); repositionSnapTargetBeforeMinimized(); updateDisplayInfo(); } else { @@ -910,7 +908,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, requestLayout(); // Update the snap position to the new docked side with correct insets - SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets); + WindowManagerWrapper.getInstance().getStableInsets(mStableInsets); mMinimizedSnapAlgorithm = null; initializeSnapAlgorithm(); @@ -1271,7 +1269,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } - public final void onBusEvent(RecentsActivityStartingEvent recentsActivityStartingEvent) { + void onRecentsActivityStarting() { if (mGrowRecents && mDockSide == WindowManager.DOCKED_TOP && getSnapAlgorithm().getMiddleTarget() != getSnapAlgorithm().getLastSplitTarget() && getCurrentPosition() == getSnapAlgorithm().getLastSplitTarget().position) { @@ -1280,16 +1278,14 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } - public final void onBusEvent(DockedFirstAnimationFrameEvent event) { + void onDockedFirstAnimationFrame() { saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget()); } - public final void onBusEvent(DockedTopTaskEvent event) { - if (event.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) { - mState.growAfterRecentsDrawn = false; - mState.animateAfterRecentsDrawn = true; - startDragging(false /* animate */, false /* touching */); - } + void onDockedTopTask() { + mState.growAfterRecentsDrawn = false; + mState.animateAfterRecentsDrawn = true; + startDragging(false /* animate */, false /* touching */); updateDockSide(); mEntranceAnimationRunning = true; @@ -1297,7 +1293,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, mSnapAlgorithm.getMiddleTarget()); } - public void onRecentsDrawn() { + void onRecentsDrawn() { updateDockSide(); final int position = calculatePositionForInsetBounds(); if (mState.animateAfterRecentsDrawn) { @@ -1314,13 +1310,15 @@ public class DividerView extends FrameLayout implements OnTouchListener, if (mState.growAfterRecentsDrawn) { mState.growAfterRecentsDrawn = false; updateDockSide(); - EventBus.getDefault().send(new RecentsGrowingEvent()); + if (mCallback != null) { + mCallback.growRecents(); + } stopDragging(position, getSnapAlgorithm().getMiddleTarget(), 336, Interpolators.FAST_OUT_SLOW_IN); } } - public final void onBusEvent(UndockingTaskEvent undockingTaskEvent) { + void onUndockingTask() { int dockSide = mWindowManagerProxy.getDockSide(); if (dockSide != WindowManager.DOCKED_INVALID && (mHomeStackResizable || !mDockedStackMinimized)) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java index 4415bd7a631b..02f75050c061 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java @@ -52,7 +52,7 @@ public class ForcedResizableInfoActivity extends Activity implements OnTouchList protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.forced_resizable_activity); - TextView tv = (TextView) findViewById(com.android.internal.R.id.message); + TextView tv = findViewById(com.android.internal.R.id.message); int reason = getIntent().getIntExtra(EXTRA_FORCED_RESIZEABLE_REASON, -1); String text; switch (reason) { diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java index 826fa6cefccc..f66db48f441c 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java @@ -16,8 +16,7 @@ package com.android.systemui.stackdivider; -import static com.android.systemui.stackdivider.ForcedResizableInfoActivity - .EXTRA_FORCED_RESIZEABLE_REASON; +import static com.android.systemui.stackdivider.ForcedResizableInfoActivity.EXTRA_FORCED_RESIZEABLE_REASON; import android.app.ActivityOptions; import android.content.Context; @@ -26,16 +25,9 @@ import android.os.Handler; import android.os.UserHandle; import android.util.ArraySet; import android.widget.Toast; - import com.android.systemui.R; -import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent; -import com.android.systemui.recents.events.component.ShowUserToastEvent; -import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.stackdivider.events.StartedDragingEvent; -import com.android.systemui.stackdivider.events.StoppedDragingEvent; +import com.android.systemui.shared.system.TaskStackChangeListener; /** * Controller that decides when to show the {@link ForcedResizableInfoActivity}. @@ -49,7 +41,7 @@ public class ForcedResizableInfoActivityController { private final Handler mHandler = new Handler(); private final ArraySet<PendingTaskRecord> mPendingTasks = new ArraySet<>(); private final ArraySet<String> mPackagesShownInSession = new ArraySet<>(); - private boolean mDividerDraging; + private boolean mDividerDragging; private final Runnable mTimeoutRunnable = new Runnable() { @Override @@ -75,9 +67,8 @@ public class ForcedResizableInfoActivityController { public ForcedResizableInfoActivityController(Context context) { mContext = context; - EventBus.getDefault().register(this); ActivityManagerWrapper.getInstance().registerTaskStackListener( - new SysUiTaskStackChangeListener() { + new TaskStackChangeListener() { @Override public void onActivityForcedResizable(String packageName, int taskId, int reason) { @@ -102,19 +93,19 @@ public class ForcedResizableInfoActivityController { } } - public final void onBusEvent(AppTransitionFinishedEvent event) { - if (!mDividerDraging) { + public void onAppTransitionFinished() { + if (!mDividerDragging) { showPending(); } } - public final void onBusEvent(StartedDragingEvent event) { - mDividerDraging = true; + void onDraggingStart() { + mDividerDragging = true; mHandler.removeCallbacks(mTimeoutRunnable); } - public final void onBusEvent(StoppedDragingEvent event) { - mDividerDraging = false; + void onDraggingEnd() { + mDividerDragging = false; showPending(); } @@ -127,13 +118,13 @@ public class ForcedResizableInfoActivityController { } private void activityDismissingDockedStack() { - EventBus.getDefault().send(new ShowUserToastEvent( - R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT)); + Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, + Toast.LENGTH_SHORT).show(); } private void activityLaunchOnSecondaryDisplayFailed() { - EventBus.getDefault().send(new ShowUserToastEvent( - R.string.activity_launch_on_secondary_display_failed_text, Toast.LENGTH_SHORT)); + Toast.makeText(mContext, R.string.activity_launch_on_secondary_display_failed_text, + Toast.LENGTH_SHORT).show(); } private void showPending() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index 247e3d342bbb..00e0b954d7be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -16,6 +16,10 @@ package com.android.systemui.statusbar; +import static android.content.Context.LAYOUT_INFLATER_SERVICE; +import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlertDialog; @@ -30,10 +34,10 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; import android.content.res.ColorStateList; -import android.graphics.drawable.Icon; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.hardware.input.InputManager; import android.os.Handler; import android.os.Looper; @@ -51,29 +55,23 @@ import android.view.View; import android.view.View.AccessibilityDelegate; import android.view.ViewGroup; import android.view.Window; +import android.view.WindowManager; import android.view.WindowManager.KeyboardShortcutsReceiver; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; - import com.android.internal.app.AssistUtils; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.settingslib.Utils; import com.android.systemui.R; -import com.android.systemui.recents.misc.SystemServicesProxy; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import static android.content.Context.LAYOUT_INFLATER_SERVICE; -import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES; -import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; - /** * Contains functionality for handling keyboard shortcuts. */ @@ -372,19 +370,19 @@ public final class KeyboardShortcuts { private void showKeyboardShortcuts(int deviceId) { retrieveKeyCharacterMap(deviceId); - SystemServicesProxy.getInstance(mContext).requestKeyboardShortcuts(mContext, - new KeyboardShortcutsReceiver() { - @Override - public void onKeyboardShortcutsReceived( - final List<KeyboardShortcutGroup> result) { - result.add(getSystemShortcuts()); - final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts(); - if (appShortcuts != null) { - result.add(appShortcuts); - } - showKeyboardShortcutsDialog(result); - } - }, deviceId); + WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + wm.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() { + @Override + public void onKeyboardShortcutsReceived( + final List<KeyboardShortcutGroup> result) { + result.add(getSystemShortcuts()); + final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts(); + if (appShortcuts != null) { + result.add(appShortcuts); + } + showKeyboardShortcutsDialog(result); + } + }, deviceId); } private void dismissKeyboardShortcuts() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index d6886f56cbe5..89a842e11c68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; import android.app.ActivityManager; +import android.app.KeyguardManager; import android.app.Notification; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; @@ -37,12 +38,15 @@ import android.util.SparseBooleanArray; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; +import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.OverviewProxyService; +import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import java.io.FileDescriptor; @@ -52,7 +56,7 @@ import java.io.PrintWriter; * Handles keeping track of the current user, profiles, and various things related to hiding * contents, redacting notifications, and the lockscreen. */ -public class NotificationLockscreenUserManager implements Dumpable { +public class NotificationLockscreenUserManager implements Dumpable, StateListener { private static final String TAG = "LockscreenUserManager"; private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false; public static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; @@ -67,9 +71,13 @@ public class NotificationLockscreenUserManager implements Dumpable { Dependency.get(DeviceProvisionedController.class); private final UserManager mUserManager; private final IStatusBarService mBarService; + private final LockPatternUtils mLockPatternUtils; + private final KeyguardManager mKeyguardManager; + private StatusBarKeyguardViewManager mKeyguardViewManager; private boolean mShowLockscreenNotifications; private boolean mAllowLockscreenRemoteInput; + private int mState = StatusBarState.SHADE; protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { @Override @@ -84,7 +92,9 @@ public class NotificationLockscreenUserManager implements Dumpable { mEntryManager.updateNotifications(); } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) { if (userId != mCurrentUserId && isCurrentProfile(userId)) { + updatePublicMode(); mPresenter.onWorkChallengeChanged(); + mEntryManager.updateNotifications(); } } } @@ -100,8 +110,9 @@ public class NotificationLockscreenUserManager implements Dumpable { Log.v(TAG, "userId " + mCurrentUserId + " is in the house"); updateLockscreenNotificationSetting(); - + updatePublicMode(); mPresenter.onUserSwitched(mCurrentUserId); + mEntryManager.getNotificationData().filterAndSort(); } else if (Intent.ACTION_USER_ADDED.equals(action)) { updateCurrentProfilesCache(); } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { @@ -150,6 +161,9 @@ public class NotificationLockscreenUserManager implements Dumpable { mCurrentUserId = ActivityManager.getCurrentUser(); mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + mLockPatternUtils = new LockPatternUtils(mContext); + mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); + Dependency.get(StatusBarStateController.class).addListener(this); } public void setUpWithPresenter(NotificationPresenter presenter, @@ -239,6 +253,43 @@ public class NotificationLockscreenUserManager implements Dumpable { } } + public void setKeyguardViewManager(StatusBarKeyguardViewManager sbkvm) { + mKeyguardViewManager = sbkvm; + } + + @Override + public void onStateChanged(int newState) { + mState = newState; + updatePublicMode(); + } + + public void updatePublicMode() { + //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns + // false when it should be true. Therefore, if we are not on the SHADE, don't even bother + // asking if the keyguard is showing. We still need to check it though because showing the + // camera on the keyguard has a state of SHADE but the keyguard is still showing. + boolean showingKeyguard = mState != StatusBarState.SHADE + || mKeyguardViewManager.isShowing(); + boolean devicePublic = showingKeyguard && mKeyguardViewManager.isSecure(getCurrentUserId()); + + SparseArray<UserInfo> currentProfiles = getCurrentProfiles(); + for (int i = currentProfiles.size() - 1; i >= 0; i--) { + final int userId = currentProfiles.valueAt(i).id; + boolean isProfilePublic = devicePublic; + if (!devicePublic && userId != mCurrentUserId) { + // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge + // due to a race condition where this code could be called before + // TrustManagerService updates its internal records, resulting in an incorrect + // state being cached in mLockscreenPublicMode. (b/35951989) + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) + && mKeyguardViewManager.isSecure(userId)) { + isProfilePublic = mKeyguardManager.isDeviceLocked(userId); + } + } + setLockscreenPublicMode(isProfilePublic, userId); + } + } + /** * Returns true if notifications are temporarily disabled for this user for security reasons, * regardless of the normal settings for that user. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 304a00fbba4a..2450e448c4f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.car; import android.app.ActivityTaskManager; -import android.car.user.CarUserManagerHelper; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; import android.util.Log; @@ -36,8 +35,8 @@ import com.android.systemui.classifier.FalsingLog; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.recents.Recents; -import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.car.hvac.HvacController; import com.android.systemui.statusbar.car.hvac.TemperatureView; @@ -461,16 +460,11 @@ public class CarStatusBar extends StatusBar implements } } - - public boolean hasDockedTask() { - return Recents.getSystemServices().hasDockedTask(); - } - /** - * An implementation of SysUiTaskStackChangeListener, that listens for changes in the system + * An implementation of TaskStackChangeListener, that listens for changes in the system * task stack and notifies the navigation bar. */ - private class TaskStackListenerImpl extends SysUiTaskStackChangeListener { + private class TaskStackListenerImpl extends TaskStackChangeListener { @Override public void onTaskStackChanged() { try { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java index 67e512cf23f9..618a4c134049 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java @@ -22,7 +22,7 @@ import static android.content.DialogInterface.BUTTON_POSITIVE; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.Dialog; -import android.car.user.CarUserManagerHelper; +import android.car.userlib.CarUserManagerHelper; import android.content.Context; import android.content.DialogInterface; import android.content.pm.UserInfo; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 935eaac78362..a3e982e77522 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -28,15 +28,19 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.database.ContentObserver; import android.os.Build; +import android.os.Bundle; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.service.dreams.DreamService; +import android.service.dreams.IDreamManager; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; @@ -56,7 +60,6 @@ import com.android.systemui.EventLogTags; import com.android.systemui.ForegroundServiceController; import com.android.systemui.R; import com.android.systemui.UiOffloadThread; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AmbientPulseManager; @@ -125,11 +128,11 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. Dependency.get(NotificationListener.class); protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class); + protected IDreamManager mDreamManager; protected IStatusBarService mBarService; protected NotificationPresenter mPresenter; protected Callback mCallback; protected PowerManager mPowerManager; - protected SystemServicesProxy mSystemServicesProxy; protected NotificationListenerService.RankingMap mLatestRankingMap; protected HeadsUpManager mHeadsUpManager; protected NotificationData mNotificationData; @@ -221,8 +224,9 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + mDreamManager = IDreamManager.Stub.asInterface( + ServiceManager.checkService(DreamService.DREAM_SERVICE)); mMessagingUtil = new NotificationMessagingUtil(context); - mSystemServicesProxy = SystemServicesProxy.getInstance(mContext); mGroupManager.setPendingEntries(mPendingNotifications); } @@ -685,7 +689,13 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } else { // Stop screensaver if the notification has a fullscreen intent. // (like an incoming phone call) - SystemServicesProxy.getInstance(mContext).awakenDreamsAsync(); + Dependency.get(UiOffloadThread.class).submit(() -> { + try { + mDreamManager.awaken(); + } catch (RemoteException e) { + e.printStackTrace(); + } + }); // not immersive & a fullscreen alert should be shown if (DEBUG) @@ -896,7 +906,13 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. return false; } - boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming(); + boolean isDreaming = false; + try { + isDreaming = mDreamManager.isDreaming(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to query dream manager.", e); + } + boolean inUse = mPowerManager.isScreenOn() && !isDreaming; if (!inUse) { if (DEBUG) { @@ -979,6 +995,16 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. return false; } + Bundle extras = sbn.getNotification().extras; + CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE); + CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT); + if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) { + if (DEBUG) { + Log.d(TAG, "No pulsing: title and text are empty: " + sbn.getKey()); + } + return false; + } + return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 9e2331f69e30..903c27277b70 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -103,8 +103,15 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G }; private OnClickListener mOnStopOrMinimizeNotifications = v -> { - mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS; - swapContent(false); + Runnable saveImportance = () -> { + mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS; + swapContent(false); + }; + if (mCheckSaveListener != null) { + mCheckSaveListener.checkSave(saveImportance, mSbn); + } else { + saveImportance.run(); + } }; private OnClickListener mOnUndo = v -> { @@ -304,15 +311,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private void saveImportance() { if (!mIsNonblockable) { - // Only go through the lock screen/bouncer if the user hit 'Stop notifications'. - // Otherwise, update the importance immediately. - if (mCheckSaveListener != null - && NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS.equals( - mExitReason)) { - mCheckSaveListener.checkSave(this::updateImportance, mSbn); - } else { - updateImportance(); - } + updateImportance(); } } @@ -523,6 +522,11 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G return getHeight(); } + @VisibleForTesting + public boolean isAnimating() { + return mExpandAnimation != null && mExpandAnimation.isRunning(); + } + /** * Runnable to either update the given channel (with a new importance value) or, if no channel * is provided, update notifications enabled state for the package. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java index 4f957bfcbbcc..c7ab27ba715d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java @@ -67,9 +67,15 @@ public class ContextualButton extends ButtonDispatcher { } protected KeyButtonDrawable getNewDrawable() { - return KeyButtonDrawable.create(getContext(), mIconResId, false /* shadow */); + return KeyButtonDrawable.create(getContext().getApplicationContext(), mIconResId, + false /* shadow */); } + /** + * This context is from the view that could be stale after rotation or config change. To get + * correct resources use getApplicationContext() as well. + * @return current view context + */ protected Context getContext() { return getCurrentView().getContext(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index cbbb0e3dea96..9c579daa38c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -35,6 +35,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.annotation.IdRes; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.Fragment; import android.app.IActivityManager; @@ -88,8 +89,8 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.recents.Recents; -import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; @@ -284,7 +285,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mNavigationBarView = (NavigationBarView) view; mNavigationBarView.setDisabledFlags(mDisabledFlags1); - mNavigationBarView.setComponents(mRecents, mDivider, mStatusBar.getPanel()); + mNavigationBarView.setComponents(mStatusBar.getPanel()); mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged); mNavigationBarView.setOnTouchListener(this::onNavigationTouch); if (savedInstanceState != null) { @@ -946,7 +947,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private boolean onLongPressRecents() { if (mRecents == null || !ActivityTaskManager.supportsMultiWindow(getContext()) || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible() - || Recents.getConfiguration().isLowRamDevice + || ActivityManager.isLowRamDeviceStatic() // If we are connected to the overview service, then disable the recents button || mOverviewProxyService.getProxy() != null) { return false; @@ -1110,7 +1111,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } }; - class TaskStackListenerImpl extends SysUiTaskStackChangeListener { + class TaskStackListenerImpl extends TaskStackChangeListener { // Invalidate any rotation suggestion on task change or activity orientation change // Note: all callbacks happen on main thread diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java index 8c02e1f8220b..62d2099204e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -16,93 +16,36 @@ package com.android.systemui.statusbar.phone; -import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT; -import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; -import static android.view.WindowManager.DOCKED_INVALID; -import static android.view.WindowManager.DOCKED_LEFT; -import static android.view.WindowManager.DOCKED_TOP; - -import android.app.ActivityManager; import android.content.Context; -import android.content.res.Resources; import android.graphics.Canvas; -import android.graphics.Rect; import android.view.MotionEvent; -import android.view.VelocityTracker; import android.view.View; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; -import com.android.systemui.Dependency; -import com.android.systemui.R; -import com.android.systemui.RecentsComponent; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper; -import com.android.systemui.stackdivider.Divider; -import com.android.systemui.tuner.TunerService; /** - * Class to detect gestures on the navigation bar. + * TODO: Remove and replace with QuickStepController */ -public class NavigationBarGestureHelper implements TunerService.Tunable, GestureHelper { - - private static final String TAG = "NavBarGestureHelper"; - private static final String KEY_DOCK_WINDOW_GESTURE = "overview_nav_bar_gesture"; - /** - * When dragging from the navigation bar, we drag in recents. - */ - public static final int DRAG_MODE_NONE = -1; - - /** - * When dragging from the navigation bar, we drag in recents. - */ - public static final int DRAG_MODE_RECENTS = 0; +public class NavigationBarGestureHelper implements GestureHelper { - /** - * When dragging from the navigation bar, we drag the divider. - */ - public static final int DRAG_MODE_DIVIDER = 1; + private static final String TAG = "NavigationBarGestureHelper"; - private RecentsComponent mRecentsComponent; - private Divider mDivider; - private Context mContext; private NavigationBarView mNavigationBarView; - private boolean mIsVertical; private final QuickStepController mQuickStepController; - private final int mScrollTouchSlop; private final StatusBar mStatusBar; - private int mTouchDownX; - private int mTouchDownY; - private boolean mDownOnRecents; - private VelocityTracker mVelocityTracker; - - private boolean mDockWindowEnabled; - private boolean mDockWindowTouchSlopExceeded; - private int mDragMode; public NavigationBarGestureHelper(Context context) { - mContext = context; mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class); - Resources r = context.getResources(); - mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance); mQuickStepController = new QuickStepController(context); - Dependency.get(TunerService.class).addTunable(this, KEY_DOCK_WINDOW_GESTURE); } - public void destroy() { - Dependency.get(TunerService.class).removeTunable(this); - } - - public void setComponents(RecentsComponent recentsComponent, Divider divider, - NavigationBarView navigationBarView) { - mRecentsComponent = recentsComponent; - mDivider = divider; + public void setComponents(NavigationBarView navigationBarView) { mNavigationBarView = navigationBarView; mQuickStepController.setComponents(mNavigationBarView); } public void setBarState(boolean isVertical, boolean isRTL) { - mIsVertical = isVertical; mQuickStepController.setBarState(isVertical, isRTL); } @@ -110,22 +53,14 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture if (!canHandleGestures()) { return false; } - boolean result = mQuickStepController.onInterceptTouchEvent(event); - if (mDockWindowEnabled) { - result |= interceptDockWindowEvent(event); - } - return result; + return mQuickStepController.onInterceptTouchEvent(event); } public boolean onTouchEvent(MotionEvent event) { if (!canHandleGestures()) { return false; } - boolean result = mQuickStepController.onTouchEvent(event); - if (mDockWindowEnabled) { - result |= handleDockWindowEvent(event); - } - return result; + return mQuickStepController.onTouchEvent(event); } public void onDraw(Canvas canvas) { @@ -144,152 +79,7 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture mQuickStepController.onNavigationButtonLongPress(v); } - private boolean interceptDockWindowEvent(MotionEvent event) { - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - handleDragActionDownEvent(event); - break; - case MotionEvent.ACTION_MOVE: - return handleDragActionMoveEvent(event); - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - handleDragActionUpEvent(event); - break; - } - return false; - } - - private boolean handleDockWindowEvent(MotionEvent event) { - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - handleDragActionDownEvent(event); - break; - case MotionEvent.ACTION_MOVE: - handleDragActionMoveEvent(event); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - handleDragActionUpEvent(event); - break; - } - return true; - } - - private void handleDragActionDownEvent(MotionEvent event) { - mVelocityTracker = VelocityTracker.obtain(); - mVelocityTracker.addMovement(event); - mDockWindowTouchSlopExceeded = false; - mTouchDownX = (int) event.getX(); - mTouchDownY = (int) event.getY(); - - if (mNavigationBarView != null) { - View recentsButton = mNavigationBarView.getRecentsButton().getCurrentView(); - if (recentsButton != null) { - mDownOnRecents = mTouchDownX >= recentsButton.getLeft() - && mTouchDownX <= recentsButton.getRight() - && mTouchDownY >= recentsButton.getTop() - && mTouchDownY <= recentsButton.getBottom(); - } else { - mDownOnRecents = false; - } - } - } - - private boolean handleDragActionMoveEvent(MotionEvent event) { - mVelocityTracker.addMovement(event); - int x = (int) event.getX(); - int y = (int) event.getY(); - int xDiff = Math.abs(x - mTouchDownX); - int yDiff = Math.abs(y - mTouchDownY); - if (mDivider == null || mRecentsComponent == null) { - return false; - } - if (!mDockWindowTouchSlopExceeded) { - boolean touchSlopExceeded = !mIsVertical - ? yDiff > mScrollTouchSlop && yDiff > xDiff - : xDiff > mScrollTouchSlop && xDiff > yDiff; - if (mDownOnRecents && touchSlopExceeded - && mDivider.getView().getWindowManagerProxy().getDockSide() == DOCKED_INVALID) { - Rect initialBounds = null; - int dragMode = calculateDragMode(); - int createMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; - if (dragMode == DRAG_MODE_DIVIDER) { - initialBounds = new Rect(); - mDivider.getView().calculateBoundsForPosition(mIsVertical - ? (int) event.getRawX() - : (int) event.getRawY(), - mDivider.getView().isHorizontalDivision() - ? DOCKED_TOP - : DOCKED_LEFT, - initialBounds); - } else if (dragMode == DRAG_MODE_RECENTS && mTouchDownX - < mContext.getResources().getDisplayMetrics().widthPixels / 2) { - createMode = SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT; - } - boolean docked = mRecentsComponent.splitPrimaryTask(dragMode, createMode, - initialBounds, MetricsEvent.ACTION_WINDOW_DOCK_SWIPE); - if (docked) { - mDragMode = dragMode; - if (mDragMode == DRAG_MODE_DIVIDER) { - mDivider.getView().startDragging(false /* animate */, true /* touching*/); - } - mDockWindowTouchSlopExceeded = true; - return true; - } - } - } else { - if (mDragMode == DRAG_MODE_DIVIDER) { - int position = !mIsVertical ? (int) event.getRawY() : (int) event.getRawX(); - SnapTarget snapTarget = mDivider.getView().getSnapAlgorithm() - .calculateSnapTarget(position, 0f /* velocity */, false /* hardDismiss */); - mDivider.getView().resizeStack(position, snapTarget.position, snapTarget); - } else if (mDragMode == DRAG_MODE_RECENTS) { - mRecentsComponent.onDraggingInRecents(event.getRawY()); - } - } - return false; - } - - private void handleDragActionUpEvent(MotionEvent event) { - mVelocityTracker.addMovement(event); - mVelocityTracker.computeCurrentVelocity(1000); - if (mDockWindowTouchSlopExceeded && mDivider != null && mRecentsComponent != null) { - if (mDragMode == DRAG_MODE_DIVIDER) { - mDivider.getView().stopDragging(mIsVertical - ? (int) event.getRawX() - : (int) event.getRawY(), - mIsVertical - ? mVelocityTracker.getXVelocity() - : mVelocityTracker.getYVelocity(), - true /* avoidDismissStart */, false /* logMetrics */); - } else if (mDragMode == DRAG_MODE_RECENTS) { - mRecentsComponent.onDraggingInRecentsEnded(mVelocityTracker.getYVelocity()); - } - } - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - private boolean canHandleGestures() { return !mStatusBar.isKeyguardShowing(); } - - private int calculateDragMode() { - if (mIsVertical && !mDivider.getView().isHorizontalDivision()) { - return DRAG_MODE_DIVIDER; - } - if (!mIsVertical && mDivider.getView().isHorizontalDivision()) { - return DRAG_MODE_DIVIDER; - } - return DRAG_MODE_RECENTS; - } - - @Override - public void onTuningChanged(String key, String newValue) { - switch (key) { - case KEY_DOCK_WINDOW_GESTURE: - mDockWindowEnabled = newValue != null && (Integer.parseInt(newValue) != 0); - break; - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index aebcb9f27e24..e5c910069f82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -144,8 +144,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private Configuration mConfiguration; private NavigationBarInflaterView mNavigationInflaterView; - private RecentsComponent mRecentsComponent; - private Divider mDivider; private RecentsOnboarding mRecentsOnboarding; private NotificationPanelView mPanelView; @@ -314,14 +312,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav return mBarTransitions.getLightTransitionsController(); } - public void setComponents(RecentsComponent recentsComponent, Divider divider, - NotificationPanelView panel) { - mRecentsComponent = recentsComponent; - mDivider = divider; + public void setComponents(NotificationPanelView panel) { mPanelView = panel; if (mGestureHelper instanceof NavigationBarGestureHelper) { - ((NavigationBarGestureHelper) mGestureHelper).setComponents( - recentsComponent, divider, this); + ((NavigationBarGestureHelper) mGestureHelper).setComponents(this); } } @@ -595,6 +589,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav boolean disableHome = ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); + // TODO(b/113914868): investigation log for disappearing home button + Log.i(TAG, "updateNavButtonIcons (b/113914868): home disabled=" + disableHome + + " mDisabledFlags=" + mDisabledFlags); + // Always disable recents when alternate car mode UI is active. boolean disableRecent = mUseCarModeUi || !isOverviewEnabled(); @@ -1074,7 +1072,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav @Override public void onPluginDisconnected(NavGesture plugin) { NavigationBarGestureHelper defaultHelper = new NavigationBarGestureHelper(getContext()); - defaultHelper.setComponents(mRecentsComponent, mDivider, this); + defaultHelper.setComponents(this); if (mGestureHelper != null) { mGestureHelper.destroy(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java index 09833d44915d..1524f806f871 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java @@ -95,6 +95,9 @@ public class NearestTouchFrame extends FrameLayout { } private View findNearestChild(MotionEvent event) { + if (mClickableChildren.isEmpty()) { + return null; + } return mClickableChildren .stream() .filter(v -> v.isAttachedToWindow()) 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 1afdc66b9227..553165b40153 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -67,8 +67,8 @@ import com.android.systemui.SysUiServiceProvider; import com.android.systemui.UiOffloadThread; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.RotationLockTile; -import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.policy.BluetoothController; @@ -797,7 +797,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mIconController.setIconVisibility(mSlotDataSaver, isDataSaving); } - private final SysUiTaskStackChangeListener mTaskListener = new SysUiTaskStackChangeListener() { + private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() { @Override public void onTaskStackChanged() { // Listen for changes to stacks and then check which instant apps are foreground. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 45bcc4e5c415..226b6453dbab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -102,6 +102,8 @@ import android.os.UserManager; import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings; +import android.service.dreams.DreamService; +import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; @@ -154,6 +156,7 @@ import com.android.systemui.Interpolators; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.RecentsComponent; +import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SystemUI; import com.android.systemui.SystemUIFactory; import com.android.systemui.UiOffloadThread; @@ -179,10 +182,6 @@ import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.car.CarQSFragment; import com.android.systemui.recents.Recents; import com.android.systemui.recents.ScreenPinningRequest; -import com.android.systemui.recents.events.EventBus; -import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent; -import com.android.systemui.recents.events.activity.UndockingTaskEvent; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.stackdivider.WindowManagerProxy; @@ -635,6 +634,8 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + mDreamManager = IDreamManager.Stub.asInterface( + ServiceManager.checkService(DreamService.DREAM_SERVICE)); mDisplay = mWindowManager.getDefaultDisplay(); updateDisplaySize(); @@ -1167,6 +1168,8 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController, this, UnlockMethodCache.getInstance(mContext)); mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, getBouncerContainer(), mNotificationPanel, mBiometricUnlockController); + //TODO: Can we put the keyguard view manager in Dependency? + mLockscreenUserManager.setKeyguardViewManager(mStatusBarKeyguardViewManager); mKeyguardIndicationController .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); @@ -1212,17 +1215,18 @@ public class StatusBar extends SystemUI implements DemoMode, int createMode = navbarPos == NAV_BAR_POS_LEFT ? SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT : SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; - return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE, createMode, - null, metricsDockAction); + return mRecents.splitPrimaryTask(createMode, null, metricsDockAction); } else { Divider divider = getComponent(Divider.class); - if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) { - // Undocking from the minimized state is not supported - return false; - } else { - EventBus.getDefault().send(new UndockingTaskEvent()); - if (metricsUndockAction != -1) { - mMetricsLogger.action(metricsUndockAction); + if (divider != null) { + if (divider.isMinimized() && !divider.isHomeStackResizable()) { + // Undocking from the minimized state is not supported + return false; + } else { + divider.onUndockingTask(); + if (metricsUndockAction != -1) { + mMetricsLogger.action(metricsUndockAction); + } } } } @@ -2946,8 +2950,6 @@ public class StatusBar extends SystemUI implements DemoMode, // End old BaseStatusBar.userSwitched if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); animateCollapsePanels(); - updatePublicMode(); - mEntryManager.getNotificationData().filterAndSort(); if (mReinflateNotificationsOnUserSwitched) { mEntryManager.updateNotificationsOnDensityOrFontScaleChanged(); mReinflateNotificationsOnUserSwitched = false; @@ -3493,7 +3495,7 @@ public class StatusBar extends SystemUI implements DemoMode, // notify listeners. // If the state didn't change, we may still need to update public mode - updatePublicMode(); + mLockscreenUserManager.updatePublicMode(); mEntryManager.updateNotifications(); } View viewToClick = null; @@ -3581,34 +3583,6 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController.setExpansionAffectsAlpha(true); } - // TODO: Move this to NotificationLockscreenUserManager. - private void updatePublicMode() { - final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing(); - final boolean devicePublic = showingKeyguard - && mStatusBarKeyguardViewManager.isSecure( - mLockscreenUserManager.getCurrentUserId()); - - // Look for public mode users. Users are considered public in either case of: - // - device keyguard is shown in secure mode; - // - profile is locked with a work challenge. - SparseArray<UserInfo> currentProfiles = mLockscreenUserManager.getCurrentProfiles(); - for (int i = currentProfiles.size() - 1; i >= 0; i--) { - final int userId = currentProfiles.valueAt(i).id; - boolean isProfilePublic = devicePublic; - if (!devicePublic && userId != mLockscreenUserManager.getCurrentUserId()) { - // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge - // due to a race condition where this code could be called before - // TrustManagerService updates its internal records, resulting in an incorrect - // state being cached in mLockscreenPublicMode. (b/35951989) - if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) - && mStatusBarKeyguardViewManager.isSecure(userId)) { - isProfilePublic = mKeyguardManager.isDeviceLocked(userId); - } - } - mLockscreenUserManager.setLockscreenPublicMode(isProfilePublic, userId); - } - } - /** * Switches theme from light to dark and vice-versa. */ @@ -3799,8 +3773,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } updateDozingState(); - updatePublicMode(); - mEntryManager.updateNotifications(); checkBarModes(); updateScrimController(); updateMediaMetaData(false, mState != StatusBarState.KEYGUARD); @@ -4053,8 +4025,6 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onWorkChallengeChanged() { - updatePublicMode(); - mEntryManager.updateNotifications(); if (mPendingWorkRemoteInputView != null && !mLockscreenUserManager.isAnyProfilePublicMode()) { // Expand notification panel and the notification row, then click on remote input view @@ -4278,12 +4248,12 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void appTransitionCancelled() { - EventBus.getDefault().send(new AppTransitionFinishedEvent()); + getComponent(Divider.class).onAppTransitionFinished(); } @Override public void appTransitionFinished() { - EventBus.getDefault().send(new AppTransitionFinishedEvent()); + getComponent(Divider.class).onAppTransitionFinished(); } @Override @@ -4695,6 +4665,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected WindowManager mWindowManager; protected IWindowManager mWindowManagerService; + private IDreamManager mDreamManager; protected Display mDisplay; @@ -4999,7 +4970,13 @@ public class StatusBar extends SystemUI implements DemoMode, } void awakenDreams() { - SystemServicesProxy.getInstance(mContext).awakenDreamsAsync(); + Dependency.get(UiOffloadThread.class).submit(() -> { + try { + mDreamManager.awaken(); + } catch (RemoteException e) { + e.printStackTrace(); + } + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index e9efaa14ef7c..0a72c3f9e8d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -17,10 +17,7 @@ package com.android.systemui.statusbar.policy; import android.app.ActivityManager; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.UserManager; @@ -38,14 +35,13 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final ArrayList<Callback> mCallbacks = new ArrayList<>(); - private final WifiStateReceiver mWifiStateReceiver = new WifiStateReceiver(); private final ConnectivityManager mConnectivityManager; private final WifiManager mWifiManager; private final Context mContext; private int mHotspotState; private int mNumConnectedDevices; - private boolean mWaitingForCallback; + private boolean mWaitingForTerminalState; public HotspotControllerImpl(Context context) { mContext = context; @@ -63,7 +59,9 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("HotspotController state:"); - pw.print(" mHotspotEnabled="); pw.println(stateToString(mHotspotState)); + pw.print(" mHotspotState="); pw.println(stateToString(mHotspotState)); + pw.print(" mNumConnectedDevices="); pw.println(mNumConnectedDevices); + pw.print(" mWaitingForTerminalState="); pw.println(mWaitingForTerminalState); } private static String stateToString(int hotspotState) { @@ -99,7 +97,6 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof if (DEBUG) Log.d(TAG, "removeCallback " + callback); synchronized (mCallbacks) { mCallbacks.remove(callback); - updateWifiStateListeners(!mCallbacks.isEmpty()); } } @@ -112,7 +109,6 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof * @param shouldListen whether we should start listening to various wifi statuses */ private void updateWifiStateListeners(boolean shouldListen) { - mWifiStateReceiver.setListening(shouldListen); if (shouldListen) { mWifiManager.registerSoftApCallback( this, @@ -129,21 +125,27 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof @Override public boolean isHotspotTransient() { - return mWaitingForCallback || (mHotspotState == WifiManager.WIFI_AP_STATE_ENABLING); + return mWaitingForTerminalState || (mHotspotState == WifiManager.WIFI_AP_STATE_ENABLING); } @Override public void setHotspotEnabled(boolean enabled) { - if (mWaitingForCallback) { - if (DEBUG) Log.d(TAG, "Ignoring setHotspotEnabled; waiting for callback."); + if (mWaitingForTerminalState) { + if (DEBUG) Log.d(TAG, "Ignoring setHotspotEnabled; waiting for terminal state."); return; } if (enabled) { - OnStartTetheringCallback callback = new OnStartTetheringCallback(); - mWaitingForCallback = true; + mWaitingForTerminalState = true; if (DEBUG) Log.d(TAG, "Starting tethering"); - mConnectivityManager.startTethering( - ConnectivityManager.TETHERING_WIFI, false, callback); + mConnectivityManager.startTethering(ConnectivityManager.TETHERING_WIFI, false, + new ConnectivityManager.OnStartTetheringCallback() { + @Override + public void onTetheringFailed() { + if (DEBUG) Log.d(TAG, "onTetheringFailed"); + maybeResetSoftApState(); + fireHotspotChangedCallback(); + } + }); } else { mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); } @@ -155,99 +157,56 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof } /** - * Sends a hotspot changed callback with the new enabled status. Wraps - * {@link #fireHotspotChangedCallback(boolean, int)} and assumes that the number of devices has - * not changed. - * - * @param enabled whether the hotspot is enabled - */ - private void fireHotspotChangedCallback(boolean enabled) { - fireHotspotChangedCallback(enabled, mNumConnectedDevices); - } - - /** - * Sends a hotspot changed callback with the new enabled status & the number of devices - * connected to the hotspot. Be careful when calling over multiple threads, especially if one of - * them is the main thread (as it can be blocked). - * - * @param enabled whether the hotspot is enabled - * @param numConnectedDevices number of devices connected to the hotspot + * Sends a hotspot changed callback. + * Be careful when calling over multiple threads, especially if one of them is the main thread + * (as it can be blocked). */ - private void fireHotspotChangedCallback(boolean enabled, int numConnectedDevices) { + private void fireHotspotChangedCallback() { synchronized (mCallbacks) { for (Callback callback : mCallbacks) { - callback.onHotspotChanged(enabled, numConnectedDevices); + callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices); } } } @Override public void onStateChanged(int state, int failureReason) { - // Do nothing - we don't care about changing anything here. - } + // Update internal hotspot state for tracking before using any enabled/callback methods. + mHotspotState = state; + + maybeResetSoftApState(); + if (!isHotspotEnabled()) { + // Reset num devices if the hotspot is no longer enabled so we don't get ghost + // counters. + mNumConnectedDevices = 0; + } - @Override - public void onNumClientsChanged(int numConnectedDevices) { - mNumConnectedDevices = numConnectedDevices; - fireHotspotChangedCallback(isHotspotEnabled(), numConnectedDevices); + fireHotspotChangedCallback(); } - private final class OnStartTetheringCallback extends - ConnectivityManager.OnStartTetheringCallback { - @Override - public void onTetheringStarted() { - if (DEBUG) Log.d(TAG, "onTetheringStarted"); - mWaitingForCallback = false; - // Don't fire a callback here, instead wait for the next update from wifi. + private void maybeResetSoftApState() { + if (!mWaitingForTerminalState) { + return; // Only reset soft AP state if enabled from this controller. } - - @Override - public void onTetheringFailed() { - if (DEBUG) Log.d(TAG, "onTetheringFailed"); - mWaitingForCallback = false; - // TODO(b/110697252): stopTethering must be called to reset soft ap state after failure - mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); - fireHotspotChangedCallback(isHotspotEnabled()); - // TODO: Show error. + switch (mHotspotState) { + case WifiManager.WIFI_AP_STATE_FAILED: + // TODO(b/110697252): must be called to reset soft ap state after failure + mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); + // Fall through + case WifiManager.WIFI_AP_STATE_ENABLED: + case WifiManager.WIFI_AP_STATE_DISABLED: + mWaitingForTerminalState = false; + break; + case WifiManager.WIFI_AP_STATE_ENABLING: + case WifiManager.WIFI_AP_STATE_DISABLING: + default: + break; } } - /** - * Class to listen in on wifi state and update the hotspot state - */ - private final class WifiStateReceiver extends BroadcastReceiver { - private boolean mRegistered; - - public void setListening(boolean listening) { - if (listening && !mRegistered) { - if (DEBUG) Log.d(TAG, "Registering receiver"); - final IntentFilter filter = new IntentFilter(); - filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - mContext.registerReceiver(this, filter); - mRegistered = true; - } else if (!listening && mRegistered) { - if (DEBUG) Log.d(TAG, "Unregistering receiver"); - mContext.unregisterReceiver(this); - mRegistered = false; - } - } - - @Override - public void onReceive(Context context, Intent intent) { - int state = intent.getIntExtra( - WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED); - if (DEBUG) Log.d(TAG, "onReceive " + state); - - // Update internal hotspot state for tracking before using any enabled/callback methods. - mHotspotState = state; - - if (!isHotspotEnabled()) { - // Reset num devices if the hotspot is no longer enabled so we don't get ghost - // counters. - mNumConnectedDevices = 0; - } - - fireHotspotChangedCallback(isHotspotEnabled()); - } + @Override + public void onNumClientsChanged(int numConnectedDevices) { + mNumConnectedDevices = numConnectedDevices; + fireHotspotChangedCallback(); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt new file mode 100644 index 000000000000..cfe981890bbf --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 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.keyguard + +import android.support.test.filters.SmallTest +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.LayoutInflater + +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat + +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class KeyguardPatternViewTest : SysuiTestCase() { + + private lateinit var mKeyguardPatternView: KeyguardPatternView + private lateinit var mSecurityMessage: KeyguardMessageArea + + @Before + fun setup() { + val inflater = LayoutInflater.from(context) + mKeyguardPatternView = inflater.inflate(R.layout.keyguard_pattern_view, null) + as KeyguardPatternView + mSecurityMessage = KeyguardMessageArea.findSecurityMessageDisplay(mKeyguardPatternView) + as KeyguardMessageArea + } + + @Test + fun onResume_clearsTextField() { + mSecurityMessage.setMessage("an old message") + mKeyguardPatternView.onResume(KeyguardSecurityView.SCREEN_ON) + assertThat(mSecurityMessage.text).isEqualTo("") + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index eaa0dcf88588..1cf73b00d219 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -33,6 +33,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import android.content.Intent; import android.os.PowerManager; import android.os.UserHandle; import android.provider.Settings; @@ -71,7 +72,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mSensor = mSensorManager.getFakeLightSensor(); mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, mSensor.getSensor(), mHostFake, null /* handler */, - DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY); + DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, + true /* debuggable */); } @Test @@ -93,6 +95,19 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test + public void testAod_usesDebugValue() throws Exception { + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD); + + Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS); + intent.putExtra(DozeScreenBrightness.BRIGHTNESS_BUCKET, 1); + mScreen.onReceive(mContext, intent); + mSensor.sendSensorEvent(3); + + assertEquals(1, mServiceFake.screenBrightness); + } + + @Test public void testAod_usesLightSensorRespectingUserSetting() throws Exception { int maxBrightness = 3; Settings.System.putIntForUser(mContext.getContentResolver(), @@ -160,7 +175,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { public void testNullSensor() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, null /* sensor */, mHostFake, null /* handler */, - DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY); + DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, + true /* debuggable */); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java index f8aa28dbb945..199c4c283452 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java @@ -40,9 +40,9 @@ import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; -import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -65,7 +65,7 @@ public class WorkLockActivityControllerTest extends SysuiTestCase { private @Mock IActivityTaskManager mIActivityTaskManager; private WorkLockActivityController mController; - private SysUiTaskStackChangeListener mTaskStackListener; + private TaskStackChangeListener mTaskStackListener; @Before public void setUp() throws Exception { @@ -75,8 +75,8 @@ public class WorkLockActivityControllerTest extends SysuiTestCase { doReturn("com.example.test").when(mContext).getPackageName(); // Construct controller. Save the TaskStackListener for injecting events. - final ArgumentCaptor<SysUiTaskStackChangeListener> listenerCaptor = - ArgumentCaptor.forClass(SysUiTaskStackChangeListener.class); + final ArgumentCaptor<TaskStackChangeListener> listenerCaptor = + ArgumentCaptor.forClass(TaskStackChangeListener.class); mController = new WorkLockActivityController(mContext, mActivityManager, mIActivityTaskManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index a7758a60ebb2..515c10980ff6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -40,8 +40,11 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.util.Log; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.google.android.collect.Lists; @@ -61,7 +64,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { // Dependency mocks: @Mock private NotificationEntryManager mEntryManager; + @Mock private NotificationData mNotificationData; @Mock private DeviceProvisionedController mDeviceProvisionedController; + @Mock private StatusBarKeyguardViewManager mKeyguardViewManager; private int mCurrentUserId; private TestNotificationLockscreenUserManager mLockscreenUserManager; @@ -79,9 +84,11 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { when(mUserManager.getProfiles(mCurrentUserId)).thenReturn(Lists.newArrayList( new UserInfo(mCurrentUserId, "", 0), new UserInfo(mCurrentUserId + 1, "", 0))); when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper())); + when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext); mLockscreenUserManager.setUpWithPresenter(mPresenter, mEntryManager); + mLockscreenUserManager.setKeyguardViewManager(mKeyguardViewManager); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index c236fbe12391..ca968a8af85b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -158,6 +158,11 @@ public class NotificationInfoTest extends SysuiTestCase { PollingCheck.waitFor(1000, () -> VISIBLE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility()); } + private void ensureNoUndoButton() { + PollingCheck.waitFor(1000, + () -> GONE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility() + && !mNotificationInfo.isAnimating()); + } private void waitForStopButton() { PollingCheck.waitFor(1000, () -> VISIBLE == mNotificationInfo.findViewById(R.id.prompt).getVisibility()); @@ -583,9 +588,6 @@ public class NotificationInfoTest extends SysuiTestCase { true /* isUserSentimentNegative */); mNotificationInfo.findViewById(R.id.block).performClick(); - waitForUndoButton(); - mNotificationInfo.handleCloseControls(true /* save */, false /* force */); - mTestableLooper.processAllMessages(); verify(listener).checkSave(any(Runnable.class), eq(mSbn)); } @@ -805,7 +807,7 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test - public void testCloseControlsDoesNotUpdateIfCheckSaveListenerIsNoOp() throws Exception { + public void testBlockDoesNothingIfCheckSaveListenerIsNoOp() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, @@ -813,10 +815,10 @@ public class NotificationInfoTest extends SysuiTestCase { }, null, null, true, true); mNotificationInfo.findViewById(R.id.block).performClick(); - waitForUndoButton(); + mTestableLooper.processAllMessages(); + ensureNoUndoButton(); mNotificationInfo.handleCloseControls(true, false); - mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel)); } @@ -831,6 +833,10 @@ public class NotificationInfoTest extends SysuiTestCase { }, null, null, true, false); mNotificationInfo.findViewById(R.id.block).performClick(); + mTestableLooper.processAllMessages(); + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel)); + waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 1e3d42ba6ede..b545e61a446a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -32,6 +32,7 @@ import android.os.Handler; import android.os.IPowerManager; import android.os.Looper; import android.os.PowerManager; +import android.service.dreams.IDreamManager; import android.support.test.annotation.UiThreadTest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -39,7 +40,6 @@ import android.support.test.runner.AndroidJUnit4; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.NotificationPresenter; @@ -91,7 +91,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationData mNotificationData; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private RemoteInputController mRemoteInputController; - @Mock private SystemServicesProxy mSystemServicesProxy; + @Mock private IDreamManager mDreamManager; private PowerManager mPowerManager; private TestableNotificationEntryManager mEntryManager; @@ -111,7 +111,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mPowerManager = new PowerManager(mContext, powerManagerService, Handler.createAsync(Looper.myLooper())); - mEntryManager = new TestableNotificationEntryManager(mSystemServicesProxy, mPowerManager, + mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager, mContext); mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, null, mHeadsUpManager, mNotificationData); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 5006b0b29b0c..da93327061a8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -50,6 +50,7 @@ import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; +import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; import android.support.test.filters.SmallTest; import android.support.test.metricshelper.MetricsAsserts; @@ -119,9 +120,9 @@ public class StatusBarTest extends SysuiTestCase { @Mock private KeyguardIndicationController mKeyguardIndicationController; @Mock private NotificationStackScrollLayout mStackScroller; @Mock private HeadsUpManagerPhone mHeadsUpManager; - @Mock private SystemServicesProxy mSystemServicesProxy; @Mock private NotificationPanelView mNotificationPanelView; @Mock private IStatusBarService mBarService; + @Mock private IDreamManager mDreamManager; @Mock private ScrimController mScrimController; @Mock private ArrayList<Entry> mNotificationList; @Mock private BiometricUnlockController mBiometricUnlockController; @@ -194,8 +195,7 @@ public class StatusBarTest extends SysuiTestCase { return null; }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any()); - mEntryManager = new TestableNotificationEntryManager(mSystemServicesProxy, mPowerManager, - mContext); + mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager, mContext); when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache, mKeyguardIndicationController, mStackScroller, mHeadsUpManager, @@ -358,12 +358,12 @@ public class StatusBarTest extends SysuiTestCase { } @Test - public void testShouldHeadsUp_nonSuppressedGroupSummary() { + public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception { when(mPowerManager.isScreenOn()).thenReturn(true); when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false); when(mNotificationData.shouldFilterOut(any())).thenReturn(false); - when(mSystemServicesProxy.isDreaming()).thenReturn(false); + when(mDreamManager.isDreaming()).thenReturn(false); when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH); Notification n = new Notification.Builder(getContext(), "a") @@ -379,12 +379,12 @@ public class StatusBarTest extends SysuiTestCase { } @Test - public void testShouldHeadsUp_suppressedGroupSummary() { + public void testShouldHeadsUp_suppressedGroupSummary() throws Exception { when(mPowerManager.isScreenOn()).thenReturn(true); when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false); when(mNotificationData.shouldFilterOut(any())).thenReturn(false); - when(mSystemServicesProxy.isDreaming()).thenReturn(false); + when(mDreamManager.isDreaming()).thenReturn(false); when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH); Notification n = new Notification.Builder(getContext(), "a") @@ -400,11 +400,11 @@ public class StatusBarTest extends SysuiTestCase { } @Test - public void testShouldHeadsUp_suppressedHeadsUp() { + public void testShouldHeadsUp_suppressedHeadsUp() throws Exception { when(mPowerManager.isScreenOn()).thenReturn(true); when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationData.shouldFilterOut(any())).thenReturn(false); - when(mSystemServicesProxy.isDreaming()).thenReturn(false); + when(mDreamManager.isDreaming()).thenReturn(false); when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH); when(mNotificationData.shouldSuppressPeek(any())).thenReturn(true); @@ -418,11 +418,11 @@ public class StatusBarTest extends SysuiTestCase { } @Test - public void testShouldHeadsUp_noSuppressedHeadsUp() { + public void testShouldHeadsUp_noSuppressedHeadsUp() throws Exception { when(mPowerManager.isScreenOn()).thenReturn(true); when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationData.shouldFilterOut(any())).thenReturn(false); - when(mSystemServicesProxy.isDreaming()).thenReturn(false); + when(mDreamManager.isDreaming()).thenReturn(false); when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH); when(mNotificationData.shouldSuppressPeek(any())).thenReturn(false); @@ -690,10 +690,10 @@ public class StatusBarTest extends SysuiTestCase { public static class TestableNotificationEntryManager extends NotificationEntryManager { - public TestableNotificationEntryManager(SystemServicesProxy systemServicesProxy, + public TestableNotificationEntryManager(IDreamManager dreamManager, PowerManager powerManager, Context context) { super(context); - mSystemServicesProxy = systemServicesProxy; + mDreamManager = dreamManager; mPowerManager = powerManager; } diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index de85dcb16adb..90c10fdcbfde 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6524,6 +6524,31 @@ message MetricsEvent { // Tag of a field for the length of a text FIELD_AUTOFILL_TEXT_LEN = 1572; + // Action: the notification assistant is changing a notification + // OS: Q + NOTIFICATION_ASSISTANT_ADJUSTMENT = 1573; + + // Subtype: The people attached to a notification was changed + ADJUSTMENT_KEY_PEOPLE = 1574; + + // Subtype: The snooze options attached to a notification was changed + ADJUSTMENT_KEY_SNOOZE_CRITERIA = 1575; + + // Subtype: The group of a notification was changed + ADJUSTMENT_KEY_GROUP_KEY = 1576; + + // Subtype: The user sentiment of a notification was changed + ADJUSTMENT_KEY_USER_SENTIMENT = 1577; + + // Subtype: New actions have been added to a notification + ADJUSTMENT_KEY_SMART_ACTIONS = 1578; + + // Subtype: New smart replies have been added to a notification + ADJUSTMENT_KEY_SMART_REPLIES = 1579; + + // Subtype: The importance of a notification has been changed + ADJUSTMENT_KEY_IMPORTANCE = 1580; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index 596240601f55..fe86ab3a3f26 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -43,6 +43,8 @@ import android.widget.Toast; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.server.LocalServices; +import com.android.server.UiModeManagerInternal; import com.android.server.UiThread; import com.android.server.autofill.Helper; @@ -69,6 +71,7 @@ public final class AutoFillUI { private final MetricsLogger mMetricsLogger = new MetricsLogger(); private final @NonNull OverlayControl mOverlayControl; + private final @NonNull UiModeManagerInternal mUiModeMgr; public interface AutoFillUiCallback { void authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent, @@ -86,6 +89,7 @@ public final class AutoFillUI { public AutoFillUI(@NonNull Context context) { mContext = context; mOverlayControl = new OverlayControl(context); + mUiModeMgr = LocalServices.getService(UiModeManagerInternal.class); } public void setCallback(@NonNull AutoFillUiCallback callback) { @@ -193,7 +197,9 @@ public final class AutoFillUI { } hideAllUiThread(callback); mFillUi = new FillUi(mContext, response, focusedId, - filterText, mOverlayControl, serviceLabel, serviceIcon, new FillUi.Callback() { + filterText, mOverlayControl, serviceLabel, serviceIcon, + mUiModeMgr.isNightMode(), + new FillUi.Callback() { @Override public void onResponsePicked(FillResponse response) { log.setType(MetricsEvent.TYPE_DETAIL); @@ -332,7 +338,7 @@ public final class AutoFillUI { } mMetricsLogger.write(log); } - }, isUpdate, compatMode); + }, mUiModeMgr.isNightMode(), isUpdate, compatMode); }); } @@ -368,6 +374,7 @@ public final class AutoFillUI { pw.println("Autofill UI"); final String prefix = " "; final String prefix2 = " "; + pw.print(prefix); pw.print("Night mode: "); pw.println(mUiModeMgr.isNightMode()); if (mFillUi != null) { pw.print(prefix); pw.println("showsFillUi: true"); mFillUi.dump(pw, prefix2); diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 68a495fe9a2f..742d49443549 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -73,7 +73,10 @@ import java.util.stream.Collectors; final class FillUi { private static final String TAG = "FillUi"; - private static final int THEME_ID = com.android.internal.R.style.Theme_DeviceDefault_Autofill; + private static final int THEME_ID_LIGHT = + com.android.internal.R.style.Theme_DeviceDefault_Light_Autofill; + private static final int THEME_ID_DARK = + com.android.internal.R.style.Theme_DeviceDefault_Autofill; private static final TypedValue sTempTypedValue = new TypedValue(); @@ -117,6 +120,8 @@ final class FillUi { private boolean mDestroyed; + private final int mThemeId; + public static boolean isFullScreen(Context context) { if (sFullScreenMode != null) { if (sVerbose) Slog.v(TAG, "forcing full-screen mode to " + sFullScreenMode); @@ -128,10 +133,13 @@ final class FillUi { FillUi(@NonNull Context context, @NonNull FillResponse response, @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText, @NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel, - @NonNull Drawable serviceIcon, @NonNull Callback callback) { + @NonNull Drawable serviceIcon, boolean nightMode, @NonNull Callback callback) { + if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode); + mThemeId = nightMode ? THEME_ID_DARK : THEME_ID_LIGHT; mCallback = callback; mFullScreen = isFullScreen(context); - mContext = new ContextThemeWrapper(context, THEME_ID); + mContext = new ContextThemeWrapper(context, mThemeId); + final LayoutInflater inflater = LayoutInflater.from(mContext); final RemoteViews headerPresentation = response.getHeader(); @@ -216,7 +224,7 @@ final class FillUi { ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker); final View content; try { - response.getPresentation().setApplyTheme(THEME_ID); + response.getPresentation().setApplyTheme(mThemeId); content = response.getPresentation().apply(mContext, decor, interceptionHandler); container.addView(content); } catch (RuntimeException e) { @@ -257,7 +265,7 @@ final class FillUi { RemoteViews.OnClickHandler clickBlocker = null; if (headerPresentation != null) { clickBlocker = newClickBlocker(); - headerPresentation.setApplyTheme(THEME_ID); + headerPresentation.setApplyTheme(mThemeId); mHeader = headerPresentation.apply(mContext, null, clickBlocker); final LinearLayout headerContainer = decor.findViewById(R.id.autofill_dataset_header); @@ -275,7 +283,7 @@ final class FillUi { if (clickBlocker == null) { // already set for header clickBlocker = newClickBlocker(); } - footerPresentation.setApplyTheme(THEME_ID); + footerPresentation.setApplyTheme(mThemeId); mFooter = footerPresentation.apply(mContext, null, clickBlocker); // Footer not supported on some platform e.g. TV if (sVerbose) Slog.v(TAG, "adding footer"); @@ -302,7 +310,7 @@ final class FillUi { final View view; try { if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId); - presentation.setApplyTheme(THEME_ID); + presentation.setApplyTheme(mThemeId); view = presentation.apply(mContext, null, interceptionHandler); } catch (RuntimeException e) { Slog.e(TAG, "Error inflating remote views", e); @@ -732,6 +740,18 @@ final class FillUi { pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth); pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight); pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed); + pw.print(prefix); pw.print("theme id: "); pw.print(mThemeId); + switch (mThemeId) { + case THEME_ID_DARK: + pw.println(" (dark)"); + break; + case THEME_ID_LIGHT: + pw.println(" (light)"); + break; + default: + pw.println("(UNKNOWN_MODE)"); + break; + } if (mWindow != null) { pw.print(prefix); pw.print("mWindow: "); final String prefix2 = prefix + " "; diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 9d3d3cbe4741..89b442ecbbda 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -72,9 +72,11 @@ import java.util.ArrayList; */ final class SaveUi { - private static final String TAG = "AutofillSaveUi"; + private static final String TAG = "SaveUi"; - private static final int THEME_ID = + private static final int THEME_ID_LIGHT = + com.android.internal.R.style.Theme_DeviceDefault_Light_Autofill_Save; + private static final int THEME_ID_DARK = com.android.internal.R.style.Theme_DeviceDefault_Autofill_Save; public interface OnSaveListener { @@ -144,6 +146,7 @@ final class SaveUi { private final String mServicePackageName; private final ComponentName mComponentName; private final boolean mCompatMode; + private final int mThemeId; private boolean mDestroyed; @@ -152,7 +155,9 @@ final class SaveUi { @Nullable String servicePackageName, @NonNull ComponentName componentName, @NonNull SaveInfo info, @NonNull ValueFinder valueFinder, @NonNull OverlayControl overlayControl, @NonNull OnSaveListener listener, - boolean isUpdate, boolean compatMode) { + boolean nightMode, boolean isUpdate, boolean compatMode) { + if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode); + mThemeId = nightMode ? THEME_ID_DARK : THEME_ID_LIGHT; mPendingUi= pendingUi; mListener = new OneActionThenDestroyListener(listener); mOverlayControl = overlayControl; @@ -160,7 +165,7 @@ final class SaveUi { mComponentName = componentName; mCompatMode = compatMode; - context = new ContextThemeWrapper(context, THEME_ID); + context = new ContextThemeWrapper(context, mThemeId); final LayoutInflater inflater = LayoutInflater.from(context); final View view = inflater.inflate(R.layout.autofill_save, null); @@ -250,7 +255,7 @@ final class SaveUi { } yesButton.setOnClickListener((v) -> mListener.onSave()); - mDialog = new Dialog(context, THEME_ID); + mDialog = new Dialog(context, mThemeId); mDialog.setContentView(view); // Dialog can be dismissed when touched outside, but the negative listener should not be @@ -337,7 +342,7 @@ final class SaveUi { try { // Create the remote view peer. - template.setApplyTheme(THEME_ID); + template.setApplyTheme(mThemeId); final View customSubtitleView = template.apply(context, null, handler); // Apply batch updates (if any). @@ -556,7 +561,18 @@ final class SaveUi { pw.print(prefix); pw.print("service: "); pw.println(mServicePackageName); pw.print(prefix); pw.print("app: "); pw.println(mComponentName.toShortString()); pw.print(prefix); pw.print("compat mode: "); pw.println(mCompatMode); - + pw.print(prefix); pw.print("theme id: "); pw.print(mThemeId); + switch (mThemeId) { + case THEME_ID_DARK: + pw.println(" (dark)"); + break; + case THEME_ID_LIGHT: + pw.println(" (light)"); + break; + default: + pw.println("(UNKNOWN_MODE)"); + break; + } final View view = mDialog.getWindow().getDecorView(); final int[] loc = view.getLocationOnScreen(); pw.print(prefix); pw.print("coordinates: "); diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/Chunk.java b/services/backup/java/com/android/server/backup/encryption/chunk/Chunk.java new file mode 100644 index 000000000000..5bec1a94e915 --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/chunk/Chunk.java @@ -0,0 +1,54 @@ +package com.android.server.backup.encryption.chunk; + +import android.util.proto.ProtoInputStream; + +import java.io.IOException; + +/** + * Information about a chunk entry in a protobuf. Only used for reading from a {@link + * ProtoInputStream}. + */ +public class Chunk { + /** + * Reads a Chunk from a {@link ProtoInputStream}. Expects the message to be of format {@link + * ChunksMetadataProto.Chunk}. + * + * @param inputStream currently at a {@link ChunksMetadataProto.Chunk} message. + * @throws IOException when the message is not structured as expected or a field can not be + * read. + */ + static Chunk readFromProto(ProtoInputStream inputStream) throws IOException { + Chunk result = new Chunk(); + + while (inputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (inputStream.getFieldNumber()) { + case (int) ChunksMetadataProto.Chunk.HASH: + result.mHash = inputStream.readBytes(ChunksMetadataProto.Chunk.HASH); + break; + case (int) ChunksMetadataProto.Chunk.LENGTH: + result.mLength = inputStream.readInt(ChunksMetadataProto.Chunk.LENGTH); + break; + } + } + + return result; + } + + private int mLength; + private byte[] mHash; + + /** Private constructor. This class should only be instantiated by calling readFromProto. */ + private Chunk() { + // Set default values for fields in case they are not available in the proto. + mHash = new byte[]{}; + mLength = 0; + } + + public int getLength() { + return mLength; + } + + public byte[] getHash() { + return mHash; + } +}
\ No newline at end of file diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListing.java b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListing.java new file mode 100644 index 000000000000..2d2e88afccf1 --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListing.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.encryption.chunk; + +import android.annotation.Nullable; +import android.util.proto.ProtoInputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Chunk listing in a format optimized for quick look-up of chunks via their hash keys. This is + * useful when building an incremental backup. After a chunk has been produced, the algorithm can + * quickly look up whether the chunk existed in the previous backup by checking this chunk listing. + * It can then tell the server to use that chunk, through telling it the position and length of the + * chunk in the previous backup's blob. + */ +public class ChunkListing { + /** + * Reads a ChunkListing from a {@link ProtoInputStream}. Expects the message to be of format + * {@link ChunksMetadataProto.ChunkListing}. + * + * @param inputStream Currently at a {@link ChunksMetadataProto.ChunkListing} message. + * @throws IOException when the message is not structured as expected or a field can not be + * read. + */ + public static ChunkListing readFromProto(ProtoInputStream inputStream) throws IOException { + Map<ChunkHash, Entry> entries = new HashMap(); + + long start = 0; + + while (inputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + if (inputStream.getFieldNumber() == (int) ChunksMetadataProto.ChunkListing.CHUNKS) { + long chunkToken = inputStream.start(ChunksMetadataProto.ChunkListing.CHUNKS); + Chunk chunk = Chunk.readFromProto(inputStream); + entries.put(new ChunkHash(chunk.getHash()), new Entry(start, chunk.getLength())); + start += chunk.getLength(); + inputStream.end(chunkToken); + } + } + + return new ChunkListing(entries); + } + + private final Map<ChunkHash, Entry> mChunksByHash; + + private ChunkListing(Map<ChunkHash, Entry> chunksByHash) { + mChunksByHash = Collections.unmodifiableMap(new HashMap<>(chunksByHash)); + } + + /** Returns {@code true} if there is a chunk with the given SHA-256 MAC key in the listing. */ + public boolean hasChunk(ChunkHash hash) { + return mChunksByHash.containsKey(hash); + } + + /** + * Returns the entry for the chunk with the given hash. + * + * @param hash The SHA-256 MAC of the plaintext of the chunk. + * @return The entry, containing position and length of the chunk in the backup blob, or null if + * it does not exist. + */ + @Nullable + public Entry getChunkEntry(ChunkHash hash) { + return mChunksByHash.get(hash); + } + + /** Returns the number of chunks in this listing. */ + public int getChunkCount() { + return mChunksByHash.size(); + } + + /** Information about a chunk entry in a backup blob - i.e., its position and length. */ + public static final class Entry { + private final int mLength; + private final long mStart; + + private Entry(long start, int length) { + mStart = start; + mLength = length; + } + + /** Returns the length of the chunk in bytes. */ + public int getLength() { + return mLength; + } + + /** Returns the start position of the chunk in the backup blob, in bytes. */ + public long getStart() { + return mStart; + } + } +} diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java b/services/backup/java/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java new file mode 100644 index 000000000000..3a6d1f62faaa --- /dev/null +++ b/services/backup/java/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.encryption.chunk; + +import java.util.Arrays; + +/** + * Holds the bytes of an encrypted {@link ChunksMetadataProto.ChunkOrdering}. + * + * <p>TODO(b/116575321): After all code is ported, remove the factory method and rename + * encryptedChunkOrdering() to getBytes(). + */ +public class EncryptedChunkOrdering { + /** + * Constructs a new object holding the given bytes of an encrypted {@link + * ChunksMetadataProto.ChunkOrdering}. + * + * <p>Note that this just holds an ordering which is already encrypted, it does not encrypt the + * ordering. + */ + public static EncryptedChunkOrdering create(byte[] encryptedChunkOrdering) { + return new EncryptedChunkOrdering(encryptedChunkOrdering); + } + + private final byte[] mEncryptedChunkOrdering; + + public byte[] encryptedChunkOrdering() { + return mEncryptedChunkOrdering; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EncryptedChunkOrdering)) { + return false; + } + + EncryptedChunkOrdering encryptedChunkOrdering = (EncryptedChunkOrdering) o; + return Arrays.equals( + mEncryptedChunkOrdering, encryptedChunkOrdering.mEncryptedChunkOrdering); + } + + @Override + public int hashCode() { + return Arrays.hashCode(mEncryptedChunkOrdering); + } + + private EncryptedChunkOrdering(byte[] encryptedChunkOrdering) { + mEncryptedChunkOrdering = encryptedChunkOrdering; + } +} diff --git a/services/backup/java/com/android/server/backup/keyvalue/AgentException.java b/services/backup/java/com/android/server/backup/keyvalue/AgentException.java new file mode 100644 index 000000000000..e2ca35116bdc --- /dev/null +++ b/services/backup/java/com/android/server/backup/keyvalue/AgentException.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.keyvalue; + +/** + * This represents something wrong with a specific package. For example: + * <ul> + * <li>Package unknown. + * <li>Package is not eligible for backup anymore. + * <li>Backup agent timed out. + * <li>Backup agent wrote protected keys. + * <li>... + * </ul> + * + * @see KeyValueBackupTask + * @see TaskException + */ +class AgentException extends BackupException { + static AgentException transitory() { + return new AgentException(/* transitory */ true); + } + + static AgentException transitory(Exception cause) { + return new AgentException(/* transitory */ true, cause); + } + + static AgentException permanent() { + return new AgentException(/* transitory */ false); + } + + static AgentException permanent(Exception cause) { + return new AgentException(/* transitory */ false, cause); + } + + private final boolean mTransitory; + + private AgentException(boolean transitory) { + mTransitory = transitory; + } + + private AgentException(boolean transitory, Exception cause) { + super(cause); + mTransitory = transitory; + } + + boolean isTransitory() { + return mTransitory; + } +} diff --git a/services/backup/java/com/android/server/backup/keyvalue/BackupException.java b/services/backup/java/com/android/server/backup/keyvalue/BackupException.java new file mode 100644 index 000000000000..27b2d35b13ca --- /dev/null +++ b/services/backup/java/com/android/server/backup/keyvalue/BackupException.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.keyvalue; + +import android.util.AndroidException; + +/** + * Key-value backup task exception. + * + * @see AgentException + * @see TaskException + */ +class BackupException extends AndroidException { + BackupException() {} + + BackupException(Exception cause) { + super(cause); + } +} diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java index 54e6b1d82f74..bb8a1d1339a7 100644 --- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java +++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java @@ -54,7 +54,7 @@ import java.util.List; public class KeyValueBackupReporter { @VisibleForTesting static final String TAG = "KeyValueBackupTask"; private static final boolean DEBUG = BackupManagerService.DEBUG; - @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || true; + @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || false; static void onNewThread(String threadName) { if (DEBUG) { @@ -153,16 +153,18 @@ public class KeyValueBackupReporter { mObserver, packageName, BackupManager.ERROR_BACKUP_NOT_ALLOWED); } - void onBindAgentError(SecurityException e) { - Slog.d(TAG, "Error in bind/backup", e); - } - void onAgentUnknown(String packageName) { Slog.d(TAG, "Package does not exist, skipping"); BackupObserverUtils.sendBackupOnPackageResult( mObserver, packageName, BackupManager.ERROR_PACKAGE_NOT_FOUND); } + void onBindAgentError(String packageName, SecurityException e) { + Slog.d(TAG, "Error in bind/backup", e); + BackupObserverUtils.sendBackupOnPackageResult( + mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE); + } + void onAgentError(String packageName) { if (MORE_DEBUG) { Slog.i(TAG, "Agent failure for " + packageName + ", re-staging"); @@ -190,6 +192,8 @@ public class KeyValueBackupReporter { void onCallAgentDoBackupError(String packageName, boolean callingAgent, Exception e) { if (callingAgent) { Slog.e(TAG, "Error invoking agent on " + packageName + ": " + e); + BackupObserverUtils.sendBackupOnPackageResult( + mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE); } else { Slog.e(TAG, "Error before invoking agent on " + packageName + ": " + e); } @@ -220,12 +224,8 @@ public class KeyValueBackupReporter { } } - void onReadAgentDataError(String packageName, IOException e) { - Slog.w(TAG, "Unable read backup data for " + packageName + ": " + e); - } - - void onWriteWidgetDataError(String packageName, IOException e) { - Slog.w(TAG, "Unable to save widget data for " + packageName + ": " + e); + void onAgentDataError(String packageName, IOException e) { + Slog.w(TAG, "Unable to read/write agent data for " + packageName + ": " + e); } void onDigestError(NoSuchAlgorithmException e) { @@ -243,16 +243,12 @@ public class KeyValueBackupReporter { } } - void onSendDataToTransport(String packageName) { + void onTransportPerformBackup(String packageName) { if (MORE_DEBUG) { Slog.v(TAG, "Sending non-empty data to transport for " + packageName); } } - void onNonIncrementalAndNonIncrementalRequired() { - Slog.e(TAG, "Transport requested non-incremental but already the case"); - } - void onEmptyData(PackageInfo packageInfo) { if (MORE_DEBUG) { Slog.i(TAG, "No backup data written, not calling transport"); @@ -302,13 +298,20 @@ public class KeyValueBackupReporter { /* extras */ null); } + void onPackageBackupNonIncrementalAndNonIncrementalRequired(String packageName) { + Slog.e(TAG, "Transport requested non-incremental but already the case"); + BackupObserverUtils.sendBackupOnPackageResult( + mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED); + EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName); + } + void onPackageBackupTransportFailure(String packageName) { BackupObserverUtils.sendBackupOnPackageResult( mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED); EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName); } - void onPackageBackupError(String packageName, Exception e) { + void onPackageBackupTransportError(String packageName, Exception e) { Slog.e(TAG, "Transport error backing up " + packageName, e); BackupObserverUtils.sendBackupOnPackageResult( mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED); diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java index e915ce16a2ef..6904b3fc6b9c 100644 --- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java +++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java @@ -16,6 +16,7 @@ package com.android.server.backup.keyvalue; +import static android.app.ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL; import static android.os.ParcelFileDescriptor.MODE_CREATE; import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; @@ -25,8 +26,8 @@ import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE; import static com.android.server.backup.BackupManagerService.OP_PENDING; import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP; +import android.annotation.IntDef; import android.annotation.Nullable; -import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; @@ -47,7 +48,6 @@ import android.os.RemoteException; import android.os.SELinux; import android.os.UserHandle; import android.os.WorkSource; -import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -77,6 +77,8 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -173,10 +175,8 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { private static final AtomicInteger THREAD_COUNT = new AtomicInteger(); private static final String BLANK_STATE_FILE_NAME = "blank_state"; private static final String PM_PACKAGE = BackupManagerService.PACKAGE_MANAGER_SENTINEL; - @VisibleForTesting - public static final String STAGING_FILE_SUFFIX = ".data"; - @VisibleForTesting - public static final String NEW_STATE_FILE_SUFFIX = ".new"; + @VisibleForTesting public static final String STAGING_FILE_SUFFIX = ".data"; + @VisibleForTesting public static final String NEW_STATE_FILE_SUFFIX = ".new"; /** * Creates a new {@link KeyValueBackupTask} for key-value backup operation, spins up a new @@ -244,13 +244,13 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { private final int mCurrentOpToken; private final File mStateDirectory; private final File mDataDirectory; + private final File mBlankStateFile; private final List<String> mOriginalQueue; private final List<String> mQueue; private final List<String> mPendingFullBackups; private final Object mQueueLock; @Nullable private final DataChangedJournal mJournal; - private int mStatus; @Nullable private PerformFullTransportBackupTask mFullBackupTask; @Nullable private IBackupAgent mAgent; @Nullable private PackageInfo mCurrentPackage; @@ -316,6 +316,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { mDataDirectory = mBackupManagerService.getDataDir(); mCurrentOpToken = backupManagerService.generateRandomIntegerToken(); mQueueLock = mBackupManagerService.getQueueLock(); + mBlankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME); } private void registerTask() { @@ -331,45 +332,43 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { public void run() { Process.setThreadPriority(THREAD_PRIORITY); - boolean processQueue = startTask(); - while (processQueue && !mQueue.isEmpty() && !mCancelled) { - String packageName = mQueue.remove(0); - if (PM_PACKAGE.equals(packageName)) { - processQueue = backupPm(); - } else { - processQueue = backupPackage(packageName); + int status = BackupTransport.TRANSPORT_OK; + try { + startTask(); + while (!mQueue.isEmpty() && !mCancelled) { + String packageName = mQueue.remove(0); + try { + if (PM_PACKAGE.equals(packageName)) { + backupPm(); + } else { + backupPackage(packageName); + } + } catch (AgentException e) { + if (e.isTransitory()) { + // We try again this package in the next backup pass. + mBackupManagerService.dataChangedImpl(packageName); + } + } } + } catch (TaskException e) { + if (e.isStateCompromised()) { + mBackupManagerService.resetBackupState(mStateDirectory); + } + revertTask(); + status = e.getStatus(); } - finishTask(); + finishTask(status); } - /** Returns whether to consume next queue package. */ - private boolean handleAgentResult(@Nullable PackageInfo packageInfo, RemoteResult result) { - if (result == RemoteResult.FAILED_THREAD_INTERRUPTED) { - // Not an explicit cancel, we need to flag it. - mCancelled = true; - mReporter.onAgentCancelled(packageInfo); - cleanUpAgentForAgentError(); - return false; - } - if (result == RemoteResult.FAILED_CANCELLED) { - mReporter.onAgentCancelled(packageInfo); - cleanUpAgentForAgentError(); - return false; - } - if (result == RemoteResult.FAILED_TIMED_OUT) { - mReporter.onAgentTimedOut(packageInfo); - cleanUpAgentForAgentError(); - return true; - } - Preconditions.checkState(result.isPresent()); - long agentResult = result.get(); - if (agentResult == BackupAgent.RESULT_ERROR) { - mReporter.onAgentResultError(packageInfo); - cleanUpAgentForAgentError(); - return true; + /** Returns transport status. */ + private int sendDataToTransport(@Nullable PackageInfo packageInfo) + throws AgentException, TaskException { + try { + return sendDataToTransport(); + } catch (IOException e) { + mReporter.onAgentDataError(packageInfo.packageName, e); + throw TaskException.causedBy(e); } - return sendDataToTransport(); } @Override @@ -378,11 +377,10 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { @Override public void operationComplete(long unusedResult) {} - /** Returns whether to consume next queue package. */ - private boolean startTask() { + private void startTask() throws TaskException { if (mBackupManagerService.isBackupOperationInProgress()) { mReporter.onSkipBackup(); - return false; + throw TaskException.create(); } // Unfortunately full backup task constructor registers the task with BMS, so we have to @@ -390,11 +388,9 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { mFullBackupTask = createFullBackupTask(mPendingFullBackups); registerTask(); - mStatus = BackupTransport.TRANSPORT_OK; - if (mQueue.isEmpty() && mPendingFullBackups.isEmpty()) { mReporter.onEmptyQueueAtStart(); - return false; + return; } // We only backup PM if it was explicitly in the queue or if it's incremental. boolean backupPm = mQueue.remove(PM_PACKAGE) || !mNonIncremental; @@ -415,20 +411,18 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { if (pmState.length() <= 0) { mReporter.onInitializeTransport(transportName); mBackupManagerService.resetBackupState(mStateDirectory); - mStatus = transport.initializeDevice(); - mReporter.onTransportInitialized(mStatus); + int status = transport.initializeDevice(); + mReporter.onTransportInitialized(status); + if (status != BackupTransport.TRANSPORT_OK) { + throw TaskException.stateCompromised(); + } } + } catch (TaskException e) { + throw e; } catch (Exception e) { mReporter.onInitializeTransportError(e); - mStatus = BackupTransport.TRANSPORT_ERROR; - } - - if (mStatus != BackupTransport.TRANSPORT_OK) { - mBackupManagerService.resetBackupState(mStateDirectory); - return false; + throw TaskException.stateCompromised(); } - - return true; } private PerformFullTransportBackupTask createFullBackupTask(List<String> packages) { @@ -446,120 +440,82 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { mUserInitiated); } - /** Returns whether to consume next queue package. */ - private boolean backupPm() { - RemoteResult agentResult = null; - try { - mCurrentPackage = new PackageInfo(); - mCurrentPackage.packageName = PM_PACKAGE; - - // Since PM is running in the system process we can set up its agent directly. - BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent(); - mAgent = IBackupAgent.Stub.asInterface(pmAgent.onBind()); + private void backupPm() throws TaskException { + mReporter.onStartPackageBackup(PM_PACKAGE); + mCurrentPackage = new PackageInfo(); + mCurrentPackage.packageName = PM_PACKAGE; - Pair<Integer, RemoteResult> statusAndResult = extractAgentData(PM_PACKAGE, mAgent); - mStatus = statusAndResult.first; - agentResult = statusAndResult.second; - } catch (Exception e) { + try { + extractPmAgentData(mCurrentPackage); + int status = sendDataToTransport(mCurrentPackage); + cleanUpAgentForTransportStatus(status); + } catch (AgentException | TaskException e) { mReporter.onExtractPmAgentDataError(e); - mStatus = BackupTransport.TRANSPORT_ERROR; - } - - if (mStatus != BackupTransport.TRANSPORT_OK) { - // In this case either extractAgentData() already made the agent clean-up or we haven't - // prepared the state for calling the agent, in either case we don't need to clean-up. - mBackupManagerService.resetBackupState(mStateDirectory); - return false; + cleanUpAgentForError(e); + // PM agent failure is task failure. + throw TaskException.stateCompromised(e); } - - Preconditions.checkNotNull(agentResult); - return handleAgentResult(mCurrentPackage, agentResult); } - /** Returns whether to consume next queue package. */ - private boolean backupPackage(String packageName) { + private void backupPackage(String packageName) throws AgentException, TaskException { mReporter.onStartPackageBackup(packageName); - mStatus = BackupTransport.TRANSPORT_OK; + mCurrentPackage = getPackageForBackup(packageName); - // Verify that the requested app is eligible for key-value backup. - RemoteResult agentResult = null; try { - mCurrentPackage = mPackageManager.getPackageInfo( - packageName, PackageManager.GET_SIGNING_CERTIFICATES); - ApplicationInfo applicationInfo = mCurrentPackage.applicationInfo; - if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mPackageManager)) { - // The manifest has changed. This won't happen again because the app won't be - // requesting further backups. - mReporter.onPackageNotEligibleForBackup(packageName); - return true; - } - - if (AppBackupUtils.appGetsFullBackup(mCurrentPackage)) { - // Initially enqueued for key-value backup, but only supports full-backup now. - mReporter.onPackageEligibleForFullBackup(packageName); - return true; - } - - if (AppBackupUtils.appIsStopped(applicationInfo)) { - // Just as it won't receive broadcasts, we won't run it for backup. - mReporter.onPackageStopped(packageName); - return true; - } + extractAgentData(mCurrentPackage); + int status = sendDataToTransport(mCurrentPackage); + cleanUpAgentForTransportStatus(status); + } catch (AgentException | TaskException e) { + cleanUpAgentForError(e); + throw e; + } + } - try { - mBackupManagerService.setWorkSource(new WorkSource(applicationInfo.uid)); - IBackupAgent agent = - mBackupManagerService.bindToAgentSynchronous( - applicationInfo, - ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL); - if (agent != null) { - mAgent = agent; - Pair<Integer, RemoteResult> statusAndResult = - extractAgentData(packageName, agent); - mStatus = statusAndResult.first; - agentResult = statusAndResult.second; - } else { - // Timeout waiting for the agent to bind. - mStatus = BackupTransport.AGENT_ERROR; - } - } catch (SecurityException e) { - mReporter.onBindAgentError(e); - mStatus = BackupTransport.AGENT_ERROR; - } + private PackageInfo getPackageForBackup(String packageName) throws AgentException { + final PackageInfo packageInfo; + try { + packageInfo = + mPackageManager.getPackageInfo( + packageName, PackageManager.GET_SIGNING_CERTIFICATES); } catch (PackageManager.NameNotFoundException e) { - mStatus = BackupTransport.AGENT_UNKNOWN; - } finally { - mBackupManagerService.setWorkSource(null); + mReporter.onAgentUnknown(packageName); + throw AgentException.permanent(e); } + ApplicationInfo applicationInfo = packageInfo.applicationInfo; + if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mPackageManager)) { + mReporter.onPackageNotEligibleForBackup(packageName); + throw AgentException.permanent(); + } + if (AppBackupUtils.appGetsFullBackup(packageInfo)) { + mReporter.onPackageEligibleForFullBackup(packageName); + throw AgentException.permanent(); + } + if (AppBackupUtils.appIsStopped(applicationInfo)) { + mReporter.onPackageStopped(packageName); + throw AgentException.permanent(); + } + return packageInfo; + } - if (mStatus != BackupTransport.TRANSPORT_OK) { - // In this case either extractAgentData() already made the agent clean-up or we haven't - // prepared the state for calling the agent, in either case we don't need to clean-up. - Preconditions.checkState(mAgent == null); - - if (mStatus == BackupTransport.AGENT_ERROR) { + private IBackupAgent bindAgent(PackageInfo packageInfo) throws AgentException { + String packageName = packageInfo.packageName; + final IBackupAgent agent; + try { + agent = + mBackupManagerService.bindToAgentSynchronous( + packageInfo.applicationInfo, BACKUP_MODE_INCREMENTAL); + if (agent == null) { mReporter.onAgentError(packageName); - mBackupManagerService.dataChangedImpl(packageName); - mStatus = BackupTransport.TRANSPORT_OK; - return true; + throw AgentException.transitory(); } - - if (mStatus == BackupTransport.AGENT_UNKNOWN) { - mReporter.onAgentUnknown(packageName); - mStatus = BackupTransport.TRANSPORT_OK; - return true; - } - - // Transport-level failure, re-enqueue everything. - revertTask(); - return false; + } catch (SecurityException e) { + mReporter.onBindAgentError(packageName, e); + throw AgentException.transitory(e); } - - Preconditions.checkNotNull(agentResult); - return handleAgentResult(mCurrentPackage, agentResult); + return agent; } - private void finishTask() { + private void finishTask(int status) { // Mark packages that we couldn't backup as pending backup. for (String packageName : mQueue) { mBackupManagerService.dataChangedImpl(packageName); @@ -576,7 +532,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { // If we succeeded and this is the first time we've done a backup, we can record the current // backup dataset token. long currentToken = mBackupManagerService.getCurrentToken(); - if ((mStatus == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) { + if ((status == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) { try { IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString); mBackupManagerService.setCurrentToken(transport.getCurrentRestoreSet()); @@ -589,9 +545,14 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { synchronized (mQueueLock) { mBackupManagerService.setBackupRunning(false); - if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { + if (status == BackupTransport.TRANSPORT_NOT_INITIALIZED) { mReporter.onTransportNotInitialized(); - triggerTransportInitializationLocked(); + try { + triggerTransportInitializationLocked(); + } catch (Exception e) { + mReporter.onPendingInitializeTransportError(e); + status = BackupTransport.TRANSPORT_ERROR; + } } } @@ -605,7 +566,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { } if (!mCancelled - && mStatus == BackupTransport.TRANSPORT_OK + && status == BackupTransport.TRANSPORT_OK && mFullBackupTask != null && !mPendingFullBackups.isEmpty()) { mReporter.onStartFullBackup(mPendingFullBackups); @@ -621,7 +582,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { mFullBackupTask.unregisterTask(); } mTaskFinishedListener.onFinished(callerLogString); - mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, mStatus)); + mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, status)); mBackupManagerService.getWakelock().release(); } @@ -642,17 +603,12 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { } @GuardedBy("mQueueLock") - private void triggerTransportInitializationLocked() { - try { - IBackupTransport transport = - mTransportClient.connectOrThrow("KVBT.triggerTransportInitializationLocked"); - mBackupManagerService.getPendingInits().add(transport.name()); - deletePmStateFile(); - mBackupManagerService.backupNow(); - } catch (Exception e) { - mReporter.onPendingInitializeTransportError(e); - mStatus = BackupTransport.TRANSPORT_ERROR; - } + private void triggerTransportInitializationLocked() throws Exception { + IBackupTransport transport = + mTransportClient.connectOrThrow("KVBT.triggerTransportInitializationLocked"); + mBackupManagerService.getPendingInits().add(transport.name()); + deletePmStateFile(); + mBackupManagerService.backupNow(); } /** Removes PM state, triggering initialization in the next key-value task. */ @@ -660,35 +616,69 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { new File(mStateDirectory, PM_PACKAGE).delete(); } + /** Same as {@link #extractAgentData(PackageInfo)}, but only for PM package. */ + private void extractPmAgentData(PackageInfo packageInfo) throws AgentException, TaskException { + Preconditions.checkArgument(packageInfo.packageName.equals(PM_PACKAGE)); + BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent(); + mAgent = IBackupAgent.Stub.asInterface(pmAgent.onBind()); + extractAgentData(packageInfo, mAgent); + } + /** - * Returns a {@link Pair}. The first of the pair contains the status. In case the status is - * {@link BackupTransport#TRANSPORT_OK}, the second of the pair contains the agent result, - * otherwise {@code null}. + * Binds to the agent and extracts its backup data. If this method returns, the data in {@code + * mBackupData} is ready to be sent to the transport, otherwise it will throw. + * + * <p>This method leaves agent resources (agent binder, files and file-descriptors) opened that + * need to be cleaned up after terminating, either successfully or exceptionally. This clean-up + * can be done with methods {@link #cleanUpAgentForTransportStatus(int)} and {@link + * #cleanUpAgentForError(BackupException)}, depending on whether data was successfully sent to + * the transport or not. It's the caller responsibility to do the clean-up or delegate it. */ - private Pair<Integer, RemoteResult> extractAgentData(String packageName, IBackupAgent agent) { + private void extractAgentData(PackageInfo packageInfo) throws AgentException, TaskException { + mBackupManagerService.setWorkSource(new WorkSource(packageInfo.applicationInfo.uid)); + try { + mAgent = bindAgent(packageInfo); + extractAgentData(packageInfo, mAgent); + } finally { + mBackupManagerService.setWorkSource(null); + } + } + + /** + * Calls agent {@link IBackupAgent#doBackup(ParcelFileDescriptor, ParcelFileDescriptor, + * ParcelFileDescriptor, long, IBackupCallback, int)} and waits for the result. If this method + * returns, the data in {@code mBackupData} is ready to be sent to the transport, otherwise it + * will throw. + * + * <p>This method creates files and file-descriptors for the agent that need to be deleted and + * closed after terminating, either successfully or exceptionally. This clean-up can be done + * with methods {@link #cleanUpAgentForTransportStatus(int)} and {@link + * #cleanUpAgentForError(BackupException)}, depending on whether data was successfully sent to + * the transport or not. It's the caller responsibility to do the clean-up or delegate it. + */ + private void extractAgentData(PackageInfo packageInfo, IBackupAgent agent) + throws AgentException, TaskException { + String packageName = packageInfo.packageName; mReporter.onExtractAgentData(packageName); - File blankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME); mSavedStateFile = new File(mStateDirectory, packageName); mBackupDataFile = new File(mDataDirectory, packageName + STAGING_FILE_SUFFIX); mNewStateFile = new File(mStateDirectory, packageName + NEW_STATE_FILE_SUFFIX); mReporter.onAgentFilesReady(mBackupDataFile); - mSavedState = null; - mBackupData = null; - mNewState = null; - boolean callingAgent = false; final RemoteResult agentResult; try { - File savedStateFileForAgent = (mNonIncremental) ? blankStateFile : mSavedStateFile; + File savedStateFileForAgent = (mNonIncremental) ? mBlankStateFile : mSavedStateFile; // MODE_CREATE to make an empty file if necessary - mSavedState = ParcelFileDescriptor.open( - savedStateFileForAgent, MODE_READ_ONLY | MODE_CREATE); - mBackupData = ParcelFileDescriptor.open( - mBackupDataFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE); - mNewState = ParcelFileDescriptor.open( - mNewStateFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE); + mSavedState = + ParcelFileDescriptor.open(savedStateFileForAgent, MODE_READ_ONLY | MODE_CREATE); + mBackupData = + ParcelFileDescriptor.open( + mBackupDataFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE); + mNewState = + ParcelFileDescriptor.open( + mNewStateFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE); if (!SELinux.restorecon(mBackupDataFile)) { mReporter.onRestoreconFailed(mBackupDataFile); @@ -713,15 +703,40 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { "doBackup()"); } catch (Exception e) { mReporter.onCallAgentDoBackupError(packageName, callingAgent, e); - cleanUpAgentForAgentError(); - // TODO: Remove the check on callingAgent when RemoteCall supports local agent calls. - int status = - callingAgent ? BackupTransport.AGENT_ERROR : BackupTransport.TRANSPORT_ERROR; - return Pair.create(status, null); + if (callingAgent) { + throw AgentException.transitory(e); + } else { + throw TaskException.create(); + } + } finally { + mBlankStateFile.delete(); } - blankStateFile.delete(); + checkAgentResult(packageInfo, agentResult); + } - return Pair.create(BackupTransport.TRANSPORT_OK, agentResult); + private void checkAgentResult(PackageInfo packageInfo, RemoteResult result) + throws AgentException, TaskException { + if (result == RemoteResult.FAILED_THREAD_INTERRUPTED) { + // Not an explicit cancel, we need to flag it. + mCancelled = true; + mReporter.onAgentCancelled(packageInfo); + throw TaskException.create(); + } + if (result == RemoteResult.FAILED_CANCELLED) { + mReporter.onAgentCancelled(packageInfo); + throw TaskException.create(); + } + if (result == RemoteResult.FAILED_TIMED_OUT) { + mReporter.onAgentTimedOut(packageInfo); + throw AgentException.transitory(); + } + Preconditions.checkState(result.isPresent()); + long resultCode = result.get(); + if (resultCode == BackupAgent.RESULT_ERROR) { + mReporter.onAgentResultError(packageInfo); + throw AgentException.transitory(); + } + Preconditions.checkState(resultCode == BackupAgent.RESULT_SUCCESS); } private void agentFail(IBackupAgent agent, String message) { @@ -801,94 +816,79 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { } } - /** Returns whether to consume next queue package. */ - private boolean sendDataToTransport() { + /** Returns transport status. */ + private int sendDataToTransport() throws AgentException, TaskException, IOException { Preconditions.checkState(mBackupData != null); + checkBackupData(mCurrentPackage.applicationInfo, mBackupDataFile); String packageName = mCurrentPackage.packageName; - ApplicationInfo applicationInfo = mCurrentPackage.applicationInfo; + writeWidgetPayloadIfAppropriate(mBackupData.getFileDescriptor(), packageName); - boolean writingWidgetData = false; - try { - if (!validateBackupData(applicationInfo, mBackupDataFile)) { - cleanUpAgentForAgentError(); - return true; - } - writingWidgetData = true; - writeWidgetPayloadIfAppropriate(mBackupData.getFileDescriptor(), packageName); - } catch (IOException e) { - if (writingWidgetData) { - mReporter.onWriteWidgetDataError(packageName, e); - } else { - mReporter.onReadAgentDataError(packageName, e); - } - cleanUpAgentForAgentError(); - revertTask(); - return false; + boolean nonIncremental = mSavedStateFile.length() == 0; + int status = transportPerformBackup(mCurrentPackage, mBackupDataFile, nonIncremental); + handleTransportStatus(status, packageName, mBackupDataFile.length()); + return status; + } + + private int transportPerformBackup( + PackageInfo packageInfo, File backupDataFile, boolean nonIncremental) + throws TaskException { + String packageName = packageInfo.packageName; + long size = backupDataFile.length(); + if (size <= 0) { + mReporter.onEmptyData(packageInfo); + return BackupTransport.TRANSPORT_OK; } - boolean nonIncremental = mSavedStateFile.length() == 0; - long size = mBackupDataFile.length(); - if (size > 0) { - try (ParcelFileDescriptor backupData = - ParcelFileDescriptor.open(mBackupDataFile, MODE_READ_ONLY)) { - IBackupTransport transport = - mTransportClient.connectOrThrow("KVBT.sendDataToTransport()"); - mReporter.onSendDataToTransport(packageName); - int flags = getPerformBackupFlags(mUserInitiated, nonIncremental); + int status; + try (ParcelFileDescriptor backupData = + ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) { + IBackupTransport transport = + mTransportClient.connectOrThrow("KVBT.transportPerformBackup()"); + mReporter.onTransportPerformBackup(packageName); + int flags = getPerformBackupFlags(mUserInitiated, nonIncremental); - mStatus = transport.performBackup(mCurrentPackage, backupData, flags); - if (mStatus == BackupTransport.TRANSPORT_OK) { - mStatus = transport.finishBackup(); - } - } catch (Exception e) { - mReporter.onPackageBackupError(packageName, e); - mStatus = BackupTransport.TRANSPORT_ERROR; + status = transport.performBackup(packageInfo, backupData, flags); + if (status == BackupTransport.TRANSPORT_OK) { + status = transport.finishBackup(); } - } else { - mReporter.onEmptyData(mCurrentPackage); - mStatus = BackupTransport.TRANSPORT_OK; + } catch (Exception e) { + mReporter.onPackageBackupTransportError(packageName, e); + throw TaskException.causedBy(e); } - if (nonIncremental - && mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) { - mReporter.onNonIncrementalAndNonIncrementalRequired(); - mStatus = BackupTransport.TRANSPORT_ERROR; + if (nonIncremental && status == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) { + mReporter.onPackageBackupNonIncrementalAndNonIncrementalRequired(packageName); + throw TaskException.create(); } - - boolean processQueue = handleTransportStatus(mStatus, packageName, size); - // We might report quota exceeded to the agent in handleTransportStatus() above, so we - // only clean-up after it. - cleanUpAgentForTransportStatus(mStatus); - return processQueue; + return status; } - /** Returns whether to consume next queue package. */ - private boolean handleTransportStatus(int status, String packageName, long size) { + private void handleTransportStatus(int status, String packageName, long size) + throws TaskException, AgentException { if (status == BackupTransport.TRANSPORT_OK) { mReporter.onPackageBackupComplete(packageName, size); - return true; - } - if (status == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { - mReporter.onPackageBackupRejected(packageName); - return true; + return; } if (status == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) { mReporter.onPackageBackupNonIncrementalRequired(mCurrentPackage); // Immediately retry the current package. mQueue.add(0, packageName); - return true; + return; + } + if (status == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { + mReporter.onPackageBackupRejected(packageName); + throw AgentException.permanent(); } if (status == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) { mReporter.onPackageBackupQuotaExceeded(packageName); agentDoQuotaExceeded(mAgent, packageName, size); - return true; + throw AgentException.permanent(); } // Any other error here indicates a transport-level failure. mReporter.onPackageBackupTransportFailure(packageName); - revertTask(); - return false; + throw TaskException.forStatus(status); } private void agentDoQuotaExceeded(@Nullable IBackupAgent agent, String packageName, long size) { @@ -908,19 +908,17 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { } /** - * For system apps and pseudo-apps always return {@code true}. For regular apps returns whether - * {@code backupDataFile} doesn't have any protected keys. - * - * <p>If the app has attempted to write any protected keys we also crash them. + * For system apps and pseudo-apps never throws. For regular apps throws {@link AgentException} + * if {@code backupDataFile} has any protected keys, also crashing the app. */ - private boolean validateBackupData( - @Nullable ApplicationInfo applicationInfo, File backupDataFile) throws IOException { + private void checkBackupData(@Nullable ApplicationInfo applicationInfo, File backupDataFile) + throws IOException, AgentException { if (applicationInfo == null || (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { // System apps and pseudo-apps can write what they want. - return true; + return; } try (ParcelFileDescriptor backupData = - ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) { + ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) { BackupDataInput backupDataInput = new BackupDataInput(backupData.getFileDescriptor()); while (backupDataInput.readNextHeader()) { String key = backupDataInput.getKey(); @@ -928,12 +926,11 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { mReporter.onAgentIllegalKey(mCurrentPackage, key); // Crash them if they wrote any protected keys. agentFail(mAgent, "Illegal backup key: " + key); - return false; + throw AgentException.permanent(); } backupDataInput.skipEntityData(); } } - return true; } private int getPerformBackupFlags(boolean userInitiated, boolean nonIncremental) { @@ -1009,44 +1006,39 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { } } - /** Cleans-up after having called the agent. */ - private void cleanUpAgentForTransportStatus(int status) { - updateFiles(status); - cleanUpAgent(); - } - - /** Cleans-up if we failed to call the agent. */ - private void cleanUpAgentForAgentError() { - mBackupDataFile.delete(); - mNewStateFile.delete(); - cleanUpAgent(); + /** + * Cleans up agent resources opened by {@link #extractAgentData(PackageInfo)} for exceptional + * case. + * + * <p>Note: Declaring exception parameter so that the caller only calls this when an exception + * is thrown. + */ + private void cleanUpAgentForError(BackupException exception) { + cleanUpAgent(StateTransaction.DISCARD_NEW); } - private void updateFiles(int status) { + /** + * Cleans up agent resources opened by {@link #extractAgentData(PackageInfo)} according to + * transport status returned in {@link #sendDataToTransport(PackageInfo)}. + */ + private void cleanUpAgentForTransportStatus(int status) { switch (status) { case BackupTransport.TRANSPORT_OK: - mBackupDataFile.delete(); - mNewStateFile.renameTo(mSavedStateFile); + cleanUpAgent(StateTransaction.COMMIT_NEW); break; case BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED: - mSavedStateFile.delete(); - mBackupDataFile.delete(); - mNewStateFile.delete(); + cleanUpAgent(StateTransaction.DISCARD_ALL); break; default: - // Includes: - // * BackupTransport.TRANSPORT_PACKAGE_REJECTED - // * BackupTransport.TRANSPORT_QUOTA_EXCEEDED - // * BackupTransport.TRANSPORT_ERROR - mBackupDataFile.delete(); - mNewStateFile.delete(); - break; + // All other transport statuses are properly converted to agent or task exceptions. + throw new AssertionError(); } } - /** Cleans-up file-descriptors and unbinds agent. */ - private void cleanUpAgent() { - mAgent = null; + private void cleanUpAgent(@StateTransaction int stateTransaction) { + applyStateTransaction(stateTransaction); + mBackupDataFile.delete(); + mBlankStateFile.delete(); tryCloseFileDescriptor(mSavedState, "old state"); tryCloseFileDescriptor(mBackupData, "backup data"); tryCloseFileDescriptor(mNewState, "new state"); @@ -1058,6 +1050,24 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { if (mCurrentPackage.applicationInfo != null) { mBackupManagerService.unbindAgent(mCurrentPackage.applicationInfo); } + mAgent = null; + } + + private void applyStateTransaction(@StateTransaction int stateTransaction) { + switch (stateTransaction) { + case StateTransaction.COMMIT_NEW: + mNewStateFile.renameTo(mSavedStateFile); + break; + case StateTransaction.DISCARD_NEW: + mNewStateFile.delete(); + break; + case StateTransaction.DISCARD_ALL: + mSavedStateFile.delete(); + mNewStateFile.delete(); + break; + default: + throw new IllegalArgumentException("Unknown state transaction " + stateTransaction); + } } private void tryCloseFileDescriptor(@Nullable Closeable closeable, String logName) { @@ -1079,4 +1089,16 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { mPendingCall = null; return result; } + + @IntDef({ + StateTransaction.COMMIT_NEW, + StateTransaction.DISCARD_NEW, + StateTransaction.DISCARD_ALL, + }) + @Retention(RetentionPolicy.SOURCE) + private @interface StateTransaction { + int COMMIT_NEW = 0; + int DISCARD_NEW = 1; + int DISCARD_ALL = 2; + } } diff --git a/services/backup/java/com/android/server/backup/keyvalue/TaskException.java b/services/backup/java/com/android/server/backup/keyvalue/TaskException.java new file mode 100644 index 000000000000..08d289556ca3 --- /dev/null +++ b/services/backup/java/com/android/server/backup/keyvalue/TaskException.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.keyvalue; + +import android.app.backup.BackupTransport; + +import com.android.internal.util.Preconditions; + +/** + * The key-value backup task has failed, no more packages will be processed and we shouldn't attempt + * any more backups now. These can be caused by transport failures (as opposed to agent failures). + * + * @see KeyValueBackupTask + * @see AgentException + */ +class TaskException extends BackupException { + private static final int DEFAULT_STATUS = BackupTransport.TRANSPORT_ERROR; + + static TaskException stateCompromised() { + return new TaskException(/* stateCompromised */ true, DEFAULT_STATUS); + } + + static TaskException stateCompromised(Exception cause) { + if (cause instanceof TaskException) { + TaskException exception = (TaskException) cause; + return new TaskException(cause, /* stateCompromised */ true, exception.getStatus()); + } + return new TaskException(cause, /* stateCompromised */ true, DEFAULT_STATUS); + } + + static TaskException forStatus(int status) { + Preconditions.checkArgument( + status != BackupTransport.TRANSPORT_OK, "Exception based on TRANSPORT_OK"); + return new TaskException(/* stateCompromised */ false, status); + } + + static TaskException causedBy(Exception cause) { + if (cause instanceof TaskException) { + return (TaskException) cause; + } + return new TaskException(cause, /* stateCompromised */ false, DEFAULT_STATUS); + } + + static TaskException create() { + return new TaskException(/* stateCompromised */ false, DEFAULT_STATUS); + } + + private final boolean mStateCompromised; + private final int mStatus; + + private TaskException(Exception cause, boolean stateCompromised, int status) { + super(cause); + mStateCompromised = stateCompromised; + mStatus = status; + } + + private TaskException(boolean stateCompromised, int status) { + mStateCompromised = stateCompromised; + mStatus = status; + } + + boolean isStateCompromised() { + return mStateCompromised; + } + + int getStatus() { + return mStatus; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java b/services/core/java/com/android/server/UiModeManagerInternal.java index 5d1985123b32..ef29069df2e2 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java +++ b/services/core/java/com/android/server/UiModeManagerInternal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2018 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. @@ -11,15 +11,17 @@ * 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 + * limitations under the License. */ -package com.android.systemui.stackdivider.events; - -import com.android.systemui.recents.events.EventBus; +package com.android.server; /** - * Sent when the divider is being draged either manually or by an animation. + * UiModeManager local system service interface. + * + * @hide Only for use within the system server. */ -public class StartedDragingEvent extends EventBus.Event { +public abstract class UiModeManagerInternal { + + public abstract boolean isNightMode(); } diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index f436286a1e40..5538e72703e9 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -108,6 +108,8 @@ final class UiModeManagerService extends SystemService { private PowerManager.WakeLock mWakeLock; + private final LocalService mLocalService = new LocalService(); + public UiModeManagerService(Context context) { super(context); } @@ -242,6 +244,7 @@ final class UiModeManagerService extends SystemService { }, TAG + ".onStart"); publishBinderService(Context.UI_MODE_SERVICE, mService); + publishLocalService(UiModeManagerInternal.class, mLocalService); } private final IUiModeManager.Stub mService = new IUiModeManager.Stub() { @@ -367,7 +370,8 @@ final class UiModeManagerService extends SystemService { pw.println("Current UI Mode Service state:"); pw.print(" mDockState="); pw.print(mDockState); pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState); - pw.print(" mNightMode="); pw.print(mNightMode); + pw.print(" mNightMode="); pw.print(mNightMode); pw.print(" ("); + pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") "); pw.print(" mNightModeLocked="); pw.print(mNightModeLocked); pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled); pw.print(" mComputedNightMode="); pw.print(mComputedNightMode); @@ -839,4 +843,22 @@ final class UiModeManagerService extends SystemService { } } } + + public final class LocalService extends UiModeManagerInternal { + + @Override + public boolean isNightMode() { + synchronized (mLock) { + final boolean isIt = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_YES) != 0; + if (LOG) { + Slog.d(TAG, + "LocalService.isNightMode(): mNightMode=" + mNightMode + + "; mComputedNightMode=" + mComputedNightMode + + "; uiMode=" + mConfiguration.uiMode + + "; isIt=" + isIt); + } + return isIt; + } + } + } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 510d333355a3..461d39d0a29a 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -629,7 +629,7 @@ public final class ActiveServices { return false; } - IIntentSender target = mAm.getIntentSenderLocked( + IIntentSender target = mAm.mPendingIntentController.getIntentSender( ActivityManager.INTENT_SENDER_SERVICE, callingPackage, callingUid, userId, null, null, 0, new Intent[]{service}, new String[]{service.resolveType(mAm.mContext.getContentResolver())}, diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index 01421c7e65c2..fab967c01086 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -890,6 +890,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> mRemoved = true; releaseSelfIfNeeded(); + + mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId); } private void releaseSelfIfNeeded() { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8c7fc849b79e..692f9cfaa833 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -134,7 +134,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; @@ -417,7 +416,6 @@ public class ActivityManagerService extends IActivityManager.Stub private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE; private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS; - private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION; // Mock "pretend we're idle now" broadcast action to the job scheduler; declared // here so that while the job scheduler can depend on AMS, the other way around @@ -563,6 +561,7 @@ public class ActivityManagerService extends IActivityManager.Stub String mDeviceOwnerName; final UserController mUserController; + final PendingIntentController mPendingIntentController; final AppErrors mAppErrors; @@ -821,12 +820,6 @@ public class ActivityManagerService extends IActivityManager.Stub final SparseArray<UidRecord> mValidateUids = new SparseArray<>(); /** - * Set of IntentSenderRecord objects that are currently active. - */ - final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords - = new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>(); - - /** * Fingerprints (hashCode()) of stack traces that we've * already logged DropBox entries for. Guarded by itself. If * something (rogue user app) forces this over @@ -1426,7 +1419,6 @@ public class ActivityManagerService extends IActivityManager.Stub static final int UPDATE_TIME_ZONE = 13; static final int PROC_START_TIMEOUT_MSG = 20; static final int KILL_APPLICATION_MSG = 22; - static final int FINALIZE_PENDING_INTENT_MSG = 23; static final int SHOW_STRICT_MODE_VIOLATION_UI_MSG = 26; static final int CHECK_EXCESSIVE_POWER_USE_MSG = 27; static final int CLEAR_DNS_CACHE_MSG = 28; @@ -1445,7 +1437,6 @@ public class ActivityManagerService extends IActivityManager.Stub static final int IDLE_UIDS_MSG = 58; static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63; static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66; - static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67; static final int PUSH_TEMP_WHITELIST_UI_MSG = 68; static final int SERVICE_FOREGROUND_CRASH_MSG = 69; static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70; @@ -1644,21 +1635,6 @@ public class ActivityManagerService extends IActivityManager.Stub mServices.serviceForegroundCrash( (ProcessRecord) msg.obj, msg.getData().getCharSequence(SERVICE_RECORD_KEY)); } break; - case DISPATCH_PENDING_INTENT_CANCEL_MSG: { - RemoteCallbackList<IResultReceiver> callbacks - = (RemoteCallbackList<IResultReceiver>)msg.obj; - int N = callbacks.beginBroadcast(); - for (int i = 0; i < N; i++) { - try { - callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, null); - } catch (RemoteException e) { - } - } - callbacks.finishBroadcast(); - // We have to clean up the RemoteCallbackList here, because otherwise it will - // needlessly hold the enclosed callbacks until the remote process dies. - callbacks.kill(); - } break; case UPDATE_TIME_ZONE: { synchronized (ActivityManagerService.this) { for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { @@ -1738,9 +1714,6 @@ public class ActivityManagerService extends IActivityManager.Stub false, userId, reason); } } break; - case FINALIZE_PENDING_INTENT_MSG: { - ((PendingIntentRecord)msg.obj).completeFinalize(); - } break; case CHECK_EXCESSIVE_POWER_USE_MSG: { synchronized (ActivityManagerService.this) { checkExcessivePowerUsageLocked(); @@ -2354,6 +2327,7 @@ public class ActivityManagerService extends IActivityManager.Stub mSystemThread = null; mUiHandler = injector.getUiHandler(null); mUserController = null; + mPendingIntentController = null; mProcStartHandlerThread = null; mProcStartHandler = null; mHiddenApiBlacklist = null; @@ -2407,7 +2381,8 @@ public class ActivityManagerService extends IActivityManager.Stub final File systemDir = SystemServiceManager.ensureSystemDir(); // TODO: Move creation of battery stats service outside of activity manager service. - mBatteryStatsService = new BatteryStatsService(systemContext, systemDir, mHandler); + mBatteryStatsService = new BatteryStatsService(systemContext, systemDir, + BackgroundThread.get().getHandler()); mBatteryStatsService.getActiveStatistics().readLocked(); mBatteryStatsService.scheduleWriteToDisk(); mOnBattery = DEBUG_POWER ? true @@ -2438,6 +2413,9 @@ public class ActivityManagerService extends IActivityManager.Stub mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); mStackSupervisor = mActivityTaskManager.mStackSupervisor; + mPendingIntentController = new PendingIntentController( + mHandlerThread.getLooper(), mUserController); + mProcessCpuThread = new Thread("CpuTracker") { @Override public void run() { @@ -2508,6 +2486,7 @@ public class ActivityManagerService extends IActivityManager.Stub LocalServices.addService(ActivityManagerInternal.class, new LocalService()); mActivityTaskManager.onActivityManagerInternalAdded(); mUgmInternal.onActivityManagerInternalAdded(); + mPendingIntentController.onActivityManagerInternalAdded(); // Wait for the synchronized block started in mProcessCpuThread, // so that any other access to mProcessCpuTracker from main thread // will be blocked during mProcessCpuTracker initialization. @@ -5511,55 +5490,8 @@ public class ActivityManagerService extends IActivityManager.Stub } if (packageName == null || uninstalling) { - // Remove pending intents. For now we only do this when force - // stopping users, because we have some problems when doing this - // for packages -- app widgets are not currently cleaned up for - // such packages, so they can be left with bad pending intents. - if (mIntentSenderRecords.size() > 0) { - Iterator<WeakReference<PendingIntentRecord>> it - = mIntentSenderRecords.values().iterator(); - while (it.hasNext()) { - WeakReference<PendingIntentRecord> wpir = it.next(); - if (wpir == null) { - it.remove(); - continue; - } - PendingIntentRecord pir = wpir.get(); - if (pir == null) { - it.remove(); - continue; - } - if (packageName == null) { - // Stopping user, remove all objects for the user. - if (pir.key.userId != userId) { - // Not the same user, skip it. - continue; - } - } else { - if (UserHandle.getAppId(pir.uid) != appId) { - // Different app id, skip it. - continue; - } - if (userId != UserHandle.USER_ALL && pir.key.userId != userId) { - // Different user, skip it. - continue; - } - if (!pir.key.packageName.equals(packageName)) { - // Different package, skip it. - continue; - } - } - if (!doit) { - return true; - } - didSomething = true; - it.remove(); - makeIntentSenderCanceledLocked(pir); - if (pir.key.activity != null && pir.key.activity.pendingResults != null) { - pir.key.activity.pendingResults.remove(pir.ref); - } - } - } + didSomething |= mPendingIntentController.removePendingIntentsForPackage( + packageName, userId, appId, doit); } if (doit) { @@ -6342,90 +6274,19 @@ public class ActivityManagerService extends IActivityManager.Stub } } - return getIntentSenderLocked(type, packageName, callingUid, userId, - token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions); - + if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) { + return mAtmInternal.getIntentSender(type, packageName, callingUid, userId, + token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions); + } + return mPendingIntentController.getIntentSender(type, packageName, callingUid, + userId, token, resultWho, requestCode, intents, resolvedTypes, flags, + bOptions); } catch (RemoteException e) { throw new SecurityException(e); } } } - IIntentSender getIntentSenderLocked(int type, String packageName, - int callingUid, int userId, IBinder token, String resultWho, - int requestCode, Intent[] intents, String[] resolvedTypes, int flags, - Bundle bOptions) { - if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid); - ActivityRecord activity = null; - if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) { - activity = ActivityRecord.isInStackLocked(token); - if (activity == null) { - Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack"); - return null; - } - if (activity.finishing) { - Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing"); - return null; - } - } - - // We're going to be splicing together extras before sending, so we're - // okay poking into any contained extras. - if (intents != null) { - for (int i = 0; i < intents.length; i++) { - intents[i].setDefusable(true); - } - } - Bundle.setDefusable(bOptions, true); - - final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0; - final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0; - final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0; - flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT - |PendingIntent.FLAG_UPDATE_CURRENT); - - PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, activity, - resultWho, requestCode, intents, resolvedTypes, flags, - SafeActivityOptions.fromBundle(bOptions), userId); - WeakReference<PendingIntentRecord> ref; - ref = mIntentSenderRecords.get(key); - PendingIntentRecord rec = ref != null ? ref.get() : null; - if (rec != null) { - if (!cancelCurrent) { - if (updateCurrent) { - if (rec.key.requestIntent != null) { - rec.key.requestIntent.replaceExtras(intents != null ? - intents[intents.length - 1] : null); - } - if (intents != null) { - intents[intents.length-1] = rec.key.requestIntent; - rec.key.allIntents = intents; - rec.key.allResolvedTypes = resolvedTypes; - } else { - rec.key.allIntents = null; - rec.key.allResolvedTypes = null; - } - } - return rec; - } - makeIntentSenderCanceledLocked(rec); - mIntentSenderRecords.remove(key); - } - if (noCreate) { - return rec; - } - rec = new PendingIntentRecord(this, key, callingUid); - mIntentSenderRecords.put(key, rec.ref); - if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) { - if (activity.pendingResults == null) { - activity.pendingResults - = new HashSet<WeakReference<PendingIntentRecord>>(); - } - activity.pendingResults.add(rec.ref); - } - return rec; - } - @Override public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code, Intent intent, String resolvedType, @@ -6465,44 +6326,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void cancelIntentSender(IIntentSender sender) { - if (!(sender instanceof PendingIntentRecord)) { - return; - } - synchronized(this) { - PendingIntentRecord rec = (PendingIntentRecord)sender; - try { - final int uid = AppGlobals.getPackageManager().getPackageUid(rec.key.packageName, - MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getCallingUserId()); - if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) { - String msg = "Permission Denial: cancelIntentSender() from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid() - + " is not allowed to cancel package " - + rec.key.packageName; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - } catch (RemoteException e) { - throw new SecurityException(e); - } - cancelIntentSenderLocked(rec, true); - } - } - - void cancelIntentSenderLocked(PendingIntentRecord rec, boolean cleanActivity) { - makeIntentSenderCanceledLocked(rec); - mIntentSenderRecords.remove(rec.key); - if (cleanActivity && rec.key.activity != null) { - rec.key.activity.pendingResults.remove(rec.ref); - } - } - - void makeIntentSenderCanceledLocked(PendingIntentRecord rec) { - rec.canceled = true; - RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked(); - if (callbacks != null) { - mHandler.obtainMessage(DISPATCH_PENDING_INTENT_CANCEL_MSG, callbacks).sendToTarget(); - } + mPendingIntentController.cancelIntentSender(sender); } @Override @@ -10866,7 +10690,7 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println("-------------------------------------------------------------------------------"); } - dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage); + mPendingIntentController.dumpPendingIntents(pw, dumpAll, dumpPackage); pw.println(); if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); @@ -11159,7 +10983,7 @@ public class ActivityManagerService extends IActivityManager.Stub opti++; } synchronized (this) { - dumpPendingIntentsLocked(fd, pw, args, opti, true, dumpPackage); + mPendingIntentController.dumpPendingIntents(pw, true, dumpPackage); } } else if ("processes".equals(cmd) || "p".equals(cmd)) { if (opti < args.length) { @@ -12857,61 +12681,6 @@ public class ActivityManagerService extends IActivityManager.Stub mUgmInternal.dump(pw, dumpAll, dumpPackage); } - void dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll, String dumpPackage) { - boolean printed = false; - - pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)"); - - if (mIntentSenderRecords.size() > 0) { - // Organize these by package name, so they are easier to read. - final ArrayMap<String, ArrayList<PendingIntentRecord>> byPackage = new ArrayMap<>(); - final ArrayList<WeakReference<PendingIntentRecord>> weakRefs = new ArrayList<>(); - final Iterator<WeakReference<PendingIntentRecord>> it - = mIntentSenderRecords.values().iterator(); - while (it.hasNext()) { - WeakReference<PendingIntentRecord> ref = it.next(); - PendingIntentRecord rec = ref != null ? ref.get() : null; - if (rec == null) { - weakRefs.add(ref); - continue; - } - if (dumpPackage != null && !dumpPackage.equals(rec.key.packageName)) { - continue; - } - ArrayList<PendingIntentRecord> list = byPackage.get(rec.key.packageName); - if (list == null) { - list = new ArrayList<>(); - byPackage.put(rec.key.packageName, list); - } - list.add(rec); - } - for (int i = 0; i < byPackage.size(); i++) { - ArrayList<PendingIntentRecord> intents = byPackage.valueAt(i); - printed = true; - pw.print(" * "); pw.print(byPackage.keyAt(i)); - pw.print(": "); pw.print(intents.size()); pw.println(" items"); - for (int j = 0; j < intents.size(); j++) { - pw.print(" #"); pw.print(j); pw.print(": "); pw.println(intents.get(j)); - if (dumpAll) { - intents.get(j).dump(pw, " "); - } - } - } - if (weakRefs.size() > 0) { - printed = true; - pw.println(" * WEAK REFS:"); - for (int i = 0; i < weakRefs.size(); i++) { - pw.print(" #"); pw.print(i); pw.print(": "); pw.println(weakRefs.get(i)); - } - } - } - - if (!printed) { - pw.println(" (nothing)"); - } - } - private static final int dumpProcessList(PrintWriter pw, ActivityManagerService service, List list, String prefix, String normalLabel, String persistentLabel, @@ -15186,24 +14955,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - ComponentName startServiceInPackage(int uid, Intent service, String resolvedType, - boolean fgRequired, String callingPackage, int userId) - throws TransactionTooLargeException { - synchronized(this) { - if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, - "startServiceInPackage: " + service + " type=" + resolvedType); - final long origId = Binder.clearCallingIdentity(); - ComponentName res; - try { - res = mServices.startServiceLocked(null, service, - resolvedType, -1, uid, fgRequired, callingPackage, userId); - } finally { - Binder.restoreCallingIdentity(origId); - } - return res; - } - } - @Override public int stopService(IApplicationThread caller, Intent service, String resolvedType, int userId) { @@ -20892,7 +20643,8 @@ public class ActivityManagerService extends IActivityManager.Stub memoryStat.pgmajfault, memoryStat.rssInBytes, memoryStat.cacheInBytes, - memoryStat.swapInBytes); + memoryStat.swapInBytes, + memoryStat.rssHighWatermarkInBytes); processMemoryStates.add(processMemoryState); } } @@ -21091,6 +20843,46 @@ public class ActivityManagerService extends IActivityManager.Stub public void finishBooting() { ActivityManagerService.this.finishBooting(); } + + @Override + public void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid, + long duration, String tag) { + synchronized (ActivityManagerService.this) { + ActivityManagerService.this.tempWhitelistForPendingIntentLocked( + callerPid, callerUid, targetUid, duration, tag); + } + } + + @Override + public int broadcastIntentInPackage(String packageName, int uid, Intent intent, + String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, + Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized, + boolean sticky, int userId) { + synchronized (ActivityManagerService.this) { + return ActivityManagerService.this.broadcastIntentInPackage(packageName, uid, + intent, resolvedType, resultTo, resultCode, resultData, resultExtras, + requiredPermission, bOptions, serialized, sticky, userId); + } + } + + @Override + public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType, + boolean fgRequired, String callingPackage, int userId) + throws TransactionTooLargeException { + synchronized(ActivityManagerService.this) { + if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, + "startServiceInPackage: " + service + " type=" + resolvedType); + final long origId = Binder.clearCallingIdentity(); + ComponentName res; + try { + res = mServices.startServiceLocked(null, service, + resolvedType, -1, uid, fgRequired, callingPackage, userId); + } finally { + Binder.restoreCallingIdentity(origId); + } + return res; + } + } } /** diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java index 263c34f850ce..78b42f2068ee 100644 --- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java @@ -3,13 +3,13 @@ package com.android.server.am; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityManager.processStateAmToProto; -import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ACTIVITY_START; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_BIND_APPLICATION_DELAY_MS; @@ -28,9 +28,9 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_VISIBLE; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_LAUNCH_MODE; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_LAUNCH_MODE; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_PROCESS_NAME; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_REAL_ACTIVITY; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME; @@ -57,16 +57,16 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PENDING_UI_CLEAN; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_PROCESS_RECORD_PROCESS_NAME; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_PROC_STATE; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_HAS_ANY_VISIBLE_WINDOW; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_REAL_CALLING_UID_PROC_STATE; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_PACKAGE_NAME; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_SHORT_COMPONENT_NAME; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_UID_PROC_STATE; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TARGET_WHITELIST_TAG; -import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_REASON; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_FILTER; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_REASON; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE; @@ -77,6 +77,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.MemoryStatUtil.MemoryStat; import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; +import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT; import android.content.Context; import android.content.Intent; @@ -98,8 +99,6 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.SomeArgs; import com.android.server.LocalServices; -import java.util.ArrayList; - /** * Handles logging into Tron. */ diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 35a1eb8ff616..9f59bd8db62a 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2450,13 +2450,24 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (shouldSleepOrShutDownActivities() && mLastPausedActivity == next && mStackSupervisor.allPausedActivitiesComplete()) { - // Make sure we have executed any pending transitions, since there - // should be nothing left to do at this point. - executeAppTransition(options); - if (DEBUG_STATES) Slog.d(TAG_STATES, - "resumeTopActivityLocked: Going to sleep and all paused"); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); - return false; + // If the current top activity may be able to occlude keyguard but the occluded state + // has not been set, update visibility and check again if we should continue to resume. + boolean nothingToResume = true; + if (!mService.mShuttingDown && !mTopActivityOccludesKeyguard + && next.canShowWhenLocked()) { + ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */, + !PRESERVE_WINDOWS); + nothingToResume = shouldSleepActivities(); + } + if (nothingToResume) { + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + executeAppTransition(options); + if (DEBUG_STATES) Slog.d(TAG_STATES, + "resumeTopActivityLocked: Going to sleep and all paused"); + if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); + return false; + } } // Make sure that the user who owns this activity is started. If not, @@ -4144,7 +4155,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai for (WeakReference<PendingIntentRecord> apr : r.pendingResults) { PendingIntentRecord rec = apr.get(); if (rec != null) { - mService.mAm.cancelIntentSenderLocked(rec, false); + mService.mPendingIntentController.cancelIntentSender(rec, false); } } r.pendingResults = null; diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java index 177e2f563a4b..1fb8f871efcd 100644 --- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java @@ -126,7 +126,7 @@ class ActivityStartInterceptor { private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) { Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary(); - final IIntentSender target = mService.mAm.getIntentSenderLocked( + final IIntentSender target = mService.getIntentSenderLocked( INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/, null /*resultCode*/, 0 /*requestCode*/, new Intent[] { mIntent }, new String[] { mResolvedType }, diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 7da0519ef2f0..890aafefdf0f 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -783,7 +783,7 @@ class ActivityStarter { if (aInfo != null) { if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( aInfo.packageName, userId)) { - IIntentSender target = mService.mAm.getIntentSenderLocked( + IIntentSender target = mService.getIntentSenderLocked( ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingUid, userId, null, null, 0, new Intent[]{intent}, new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT @@ -1096,7 +1096,7 @@ class ActivityStarter { } } - IIntentSender target = mService.mAm.getIntentSenderLocked( + IIntentSender target = mService.getIntentSenderLocked( ActivityManager.INTENT_SENDER_ACTIVITY, "android", appCallingUid, userId, null, null, 0, new Intent[] { intent }, new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java index 4dc28510c5ec..add9f2a7e9d8 100644 --- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java @@ -239,7 +239,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -276,6 +278,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { UriGrantsManagerInternal mUgmInternal; private PackageManagerInternal mPmInternal; private ActivityTaskManagerInternal mInternal; + PendingIntentController mPendingIntentController; /* Global service lock used by the package the owns this service. */ Object mGlobalLock; ActivityStackSupervisor mStackSupervisor; @@ -628,6 +631,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final File systemDir = SystemServiceManager.ensureSystemDir(); mAppWarnings = new AppWarnings(this, mUiContext, mH, mUiHandler, systemDir); mCompatModePackages = new CompatModePackages(this, systemDir, mH); + mPendingIntentController = mAm.mPendingIntentController; mTempConfig.setToDefaults(); mTempConfig.setLocales(LocaleList.getDefault()); @@ -5019,6 +5023,39 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } + IIntentSender getIntentSenderLocked(int type, String packageName, int callingUid, int userId, + IBinder token, String resultWho, int requestCode, Intent[] intents, + String[] resolvedTypes, int flags, Bundle bOptions) { + + ActivityRecord activity = null; + if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) { + activity = ActivityRecord.isInStackLocked(token); + if (activity == null) { + Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack"); + return null; + } + if (activity.finishing) { + Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing"); + return null; + } + } + + final PendingIntentRecord rec = mPendingIntentController.getIntentSender(type, packageName, + callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags, + bOptions); + final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0; + if (noCreate) { + return rec; + } + if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) { + if (activity.pendingResults == null) { + activity.pendingResults = new HashSet<>(); + } + activity.pendingResults.add(rec.ref); + } + return rec; + } + // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities private void startTimeTrackingFocusedActivityLocked() { final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity(); @@ -5310,6 +5347,31 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents, + String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId, + boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) { + synchronized (mGlobalLock) { + return getActivityStartController().startActivitiesInPackage(uid, callingPackage, + intents, resolvedTypes, resultTo, options, userId, validateIncomingUser, + originatingPendingIntent); + } + } + + @Override + public int startActivityInPackage(int uid, int realCallingPid, int realCallingUid, + String callingPackage, Intent intent, String resolvedType, IBinder resultTo, + String resultWho, int requestCode, int startFlags, SafeActivityOptions options, + int userId, TaskRecord inTask, String reason, boolean validateIncomingUser, + PendingIntentRecord originatingPendingIntent) { + synchronized (mGlobalLock) { + return getActivityStartController().startActivityInPackage(uid, realCallingPid, + realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho, + requestCode, startFlags, options, userId, inTask, reason, + validateIncomingUser, originatingPendingIntent); + } + } + + @Override public int startActivityAsUser(IApplicationThread caller, String callerPacakge, Intent intent, Bundle options, int userId) { return ActivityTaskManagerService.this.startActivityAsUser( @@ -5684,5 +5746,39 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } }); } + + @Override + public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho, + int requestCode, int resultCode, Intent data) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken); + if (r != null && r.getStack() != null) { + r.getStack().sendActivityResultLocked(callingUid, r, resultWho, requestCode, + resultCode, data); + } + } + } + + @Override + public void clearPendingResultForActivity(IBinder activityToken, + WeakReference<PendingIntentRecord> pir) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken); + if (r != null && r.pendingResults != null) { + r.pendingResults.remove(pir); + } + } + } + + @Override + public IIntentSender getIntentSender(int type, String packageName, + int callingUid, int userId, IBinder token, String resultWho, + int requestCode, Intent[] intents, String[] resolvedTypes, int flags, + Bundle bOptions) { + synchronized (mGlobalLock) { + return getIntentSenderLocked(type, packageName, callingUid, userId, token, + resultWho, requestCode, intents, resolvedTypes, flags, bOptions); + } + } } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 16c3235cd729..e2035f67031a 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -741,7 +741,7 @@ public final class BroadcastQueue { // Show a permission review UI only for explicit broadcast from a foreground app if (callerForeground && receiverRecord.intent.getComponent() != null) { - IIntentSender target = mService.getIntentSenderLocked( + IIntentSender target = mService.mPendingIntentController.getIntentSender( ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage, receiverRecord.callingUid, receiverRecord.userId, null, null, 0, new Intent[]{receiverRecord.intent}, diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java index ee4e36ff1fd1..cfe282917f3b 100644 --- a/services/core/java/com/android/server/am/KeyguardController.java +++ b/services/core/java/com/android/server/am/KeyguardController.java @@ -29,16 +29,20 @@ import static android.view.WindowManager.TRANSIT_UNSET; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; + import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; -import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED; +import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES; import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING; +import static com.android.server.am.KeyguardOccludedProto.DISPLAY_ID; +import static com.android.server.am.KeyguardOccludedProto.KEYGUARD_OCCLUDED; import android.os.IBinder; import android.os.RemoteException; import android.os.Trace; import android.util.Slog; +import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.policy.IKeyguardDismissCallback; @@ -58,19 +62,18 @@ class KeyguardController { private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_AM; - private final ActivityTaskManagerService mService; private final ActivityStackSupervisor mStackSupervisor; private WindowManagerService mWindowManager; private boolean mKeyguardShowing; private boolean mAodShowing; private boolean mKeyguardGoingAway; - private boolean mOccluded; private boolean mDismissalRequested; - private ActivityRecord mDismissingKeyguardActivity; private int mBeforeUnoccludeTransit; private int mVisibilityTransactionDepth; - private SleepToken mSleepToken; + // TODO(b/111955725): Support multiple external displays private int mSecondaryDisplayShowing = INVALID_DISPLAY; + private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>(); + private final ActivityTaskManagerService mService; KeyguardController(ActivityTaskManagerService service, ActivityStackSupervisor stackSupervisor) { @@ -87,8 +90,8 @@ class KeyguardController { * on the given display, false otherwise */ boolean isKeyguardOrAodShowing(int displayId) { - return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway && - (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing); + return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway + && !isDisplayOccluded(displayId); } /** @@ -96,8 +99,7 @@ class KeyguardController { * display, false otherwise */ boolean isKeyguardShowing(int displayId) { - return mKeyguardShowing && !mKeyguardGoingAway && - (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing); + return mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId); } /** @@ -133,6 +135,7 @@ class KeyguardController { if (showingChanged) { dismissDockedStackIfNeeded(); setKeyguardGoingAway(false); + // TODO(b/113840485): Check usage for non-default display mWindowManager.setKeyguardOrAodShowingOnDefaultDisplay( isKeyguardOrAodShowing(DEFAULT_DISPLAY)); if (keyguardShowing) { @@ -248,7 +251,8 @@ class KeyguardController { // already the dismissing activity, in which case we don't allow it to repeatedly dismiss // Keyguard. return dismissKeyguard && canDismissKeyguard() && !mAodShowing - && (mDismissalRequested || r != mDismissingKeyguardActivity); + && (mDismissalRequested + || getDisplay(r.getDisplayId()).mDismissingKeyguardActivity != r); } /** @@ -259,44 +263,16 @@ class KeyguardController { } private void visibilitiesUpdated() { - final boolean lastOccluded = mOccluded; - final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity; - mOccluded = false; - mDismissingKeyguardActivity = null; - + boolean requestDismissKeyguard = false; for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) { final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - - // Only the top activity of the focused stack on the default display may control - // occluded state. - if (display.mDisplayId == DEFAULT_DISPLAY - && mStackSupervisor.isTopDisplayFocusedStack(stack)) { - - // A dismissing activity occludes Keyguard in the insecure case for legacy - // reasons. - final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity(); - mOccluded = - stack.topActivityOccludesKeyguard() - || (topDismissing != null - && stack.topRunningActivityLocked() == topDismissing - && canShowWhileOccluded( - true /* dismissKeyguard */, - false /* showWhenLocked */)); - } - - if (mDismissingKeyguardActivity == null - && stack.getTopDismissingKeyguardActivity() != null) { - mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity(); - } - } - } - mOccluded |= mWindowManager.isShowingDream(); - if (mOccluded != lastOccluded) { - handleOccludedChanged(); + final KeyguardDisplayState state = getDisplay(display.mDisplayId); + state.visibilitiesUpdated(this, display); + requestDismissKeyguard |= state.mRequestDismissKeyguard; } - if (mDismissingKeyguardActivity != lastDismissingKeyguardActivity) { + + // Dismissing Keyguard happens globally using the information from all displays. + if (requestDismissKeyguard) { handleDismissKeyguard(); } } @@ -305,7 +281,7 @@ class KeyguardController { * Called when occluded state changed. */ private void handleOccludedChanged() { - mWindowManager.onKeyguardOccludedChanged(mOccluded); + mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY)); if (isKeyguardLocked()) { mWindowManager.deferSurfaceLayout(); try { @@ -322,14 +298,13 @@ class KeyguardController { } /** - * Called when somebody might want to dismiss the Keyguard. + * Called when somebody wants to dismiss the Keyguard via the flag. */ private void handleDismissKeyguard() { // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded. - if (!mOccluded && mDismissingKeyguardActivity != null - && mWindowManager.isKeyguardSecure()) { + if (mWindowManager.isKeyguardSecure()) { mWindowManager.dismissKeyguard(null /* callback */, null /* message */); mDismissalRequested = true; @@ -345,6 +320,10 @@ class KeyguardController { } } + private boolean isDisplayOccluded(int displayId) { + return getDisplay(displayId).mOccluded; + } + /** * @return true if Keyguard can be currently dismissed without entering credentials. */ @@ -355,12 +334,14 @@ class KeyguardController { private int resolveOccludeTransit() { if (mBeforeUnoccludeTransit != TRANSIT_UNSET && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE - && mOccluded) { + // TODO(b/113840485): Handle app transition for individual display. + && isDisplayOccluded(DEFAULT_DISPLAY)) { // Reuse old transit in case we are occluding Keyguard again, meaning that we never // actually occclude/unocclude Keyguard, but just run a normal transition. return mBeforeUnoccludeTransit; - } else if (!mOccluded) { + // TODO(b/113840485): Handle app transition for individual display. + } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) { // Save transit in case we dismiss/occlude Keyguard shortly after. mBeforeUnoccludeTransit = mWindowManager.getPendingAppTransition(); @@ -371,7 +352,8 @@ class KeyguardController { } private void dismissDockedStackIfNeeded() { - if (mKeyguardShowing && mOccluded) { + // TODO(b/113840485): Handle docked stack for individual display. + if (mKeyguardShowing && isDisplayOccluded(DEFAULT_DISPLAY)) { // The lock screen is currently showing, but is occluded by a window that can // show on top of the lock screen. In this can we want to dismiss the docked // stack since it will be complicated/risky to try to put the activity on top @@ -386,11 +368,116 @@ class KeyguardController { } private void updateKeyguardSleepToken() { - if (mSleepToken == null && isKeyguardOrAodShowing(DEFAULT_DISPLAY)) { - mSleepToken = mService.acquireSleepToken("Keyguard", DEFAULT_DISPLAY); - } else if (mSleepToken != null && !isKeyguardOrAodShowing(DEFAULT_DISPLAY)) { - mSleepToken.release(); - mSleepToken = null; + for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) { + final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx); + final KeyguardDisplayState state = getDisplay(display.mDisplayId); + if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) { + state.acquiredSleepToken(); + } else if (!isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken != null) { + state.releaseSleepToken(); + } + } + } + + private KeyguardDisplayState getDisplay(int displayId) { + if (mDisplayStates.get(displayId) == null) { + mDisplayStates.append(displayId, + new KeyguardDisplayState(mService, displayId)); + } + return mDisplayStates.get(displayId); + } + + void onDisplayRemoved(int displayId) { + if (mDisplayStates.get(displayId) != null) { + mDisplayStates.get(displayId).onRemoved(); + mDisplayStates.remove(displayId); + } + } + + /** Represents Keyguard state per individual display. */ + private static class KeyguardDisplayState { + private final int mDisplayId; + private boolean mOccluded; + private ActivityRecord mDismissingKeyguardActivity; + private boolean mRequestDismissKeyguard; + private final ActivityTaskManagerService mService; + private SleepToken mSleepToken; + + KeyguardDisplayState(ActivityTaskManagerService service, int displayId) { + mService = service; + mDisplayId = displayId; + } + + void onRemoved() { + mDismissingKeyguardActivity = null; + releaseSleepToken(); + } + + void acquiredSleepToken() { + if (mSleepToken == null) { + mSleepToken = mService.acquireSleepToken("keyguard", mDisplayId); + } + } + + void releaseSleepToken() { + if (mSleepToken != null) { + mSleepToken.release(); + mSleepToken = null; + } + } + + void visibilitiesUpdated(KeyguardController controller, ActivityDisplay display) { + final boolean lastOccluded = mOccluded; + final ActivityRecord lastDismissActivity = mDismissingKeyguardActivity; + mRequestDismissKeyguard = false; + mOccluded = false; + mDismissingKeyguardActivity = null; + + // Only the top activity of the focused stack on each display may control it's + // occluded state. + final ActivityStack focusedStack = display.getFocusedStack(); + if (focusedStack != null) { + final ActivityRecord topDismissing = + focusedStack.getTopDismissingKeyguardActivity(); + mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null + && focusedStack.topRunningActivityLocked() == topDismissing + && controller.canShowWhileOccluded( + true /* dismissKeyguard */, + false /* showWhenLocked */)); + if (focusedStack.getTopDismissingKeyguardActivity() != null) { + mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity(); + } + mOccluded |= controller.mWindowManager.isShowingDream(); + } + + // TODO(b/113840485): Handle app transition for individual display. + // For now, only default display can change occluded. + if (lastOccluded != mOccluded && mDisplayId == DEFAULT_DISPLAY) { + controller.handleOccludedChanged(); + } + if (lastDismissActivity != mDismissingKeyguardActivity && !mOccluded + && mDismissingKeyguardActivity != null + && controller.mWindowManager.isKeyguardSecure()) { + mRequestDismissKeyguard = true; + } + } + + void dumpStatus(PrintWriter pw, String prefix) { + final StringBuilder sb = new StringBuilder(); + sb.append(prefix); + sb.append(" Occluded=").append(mOccluded) + .append(" DismissingKeyguardActivity=") + .append(mDismissingKeyguardActivity) + .append(" at display=") + .append(mDisplayId); + pw.println(sb.toString()); + } + + void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(DISPLAY_ID, mDisplayId); + proto.write(KEYGUARD_OCCLUDED, mOccluded); + proto.end(token); } } @@ -399,8 +486,7 @@ class KeyguardController { pw.println(prefix + " mKeyguardShowing=" + mKeyguardShowing); pw.println(prefix + " mAodShowing=" + mAodShowing); pw.println(prefix + " mKeyguardGoingAway=" + mKeyguardGoingAway); - pw.println(prefix + " mOccluded=" + mOccluded); - pw.println(prefix + " mDismissingKeyguardActivity=" + mDismissingKeyguardActivity); + dumpDisplayStates(pw, prefix); pw.println(prefix + " mDismissalRequested=" + mDismissalRequested); pw.println(prefix + " mVisibilityTransactionDepth=" + mVisibilityTransactionDepth); } @@ -408,7 +494,19 @@ class KeyguardController { void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(KEYGUARD_SHOWING, mKeyguardShowing); - proto.write(KEYGUARD_OCCLUDED, mOccluded); + writeDisplayStatesToProto(proto, KEYGUARD_OCCLUDED_STATES); proto.end(token); } + + private void dumpDisplayStates(PrintWriter pw, String prefix) { + for (int i = 0; i < mDisplayStates.size(); i++) { + mDisplayStates.valueAt(i).dumpStatus(pw, prefix); + } + } + + private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) { + for (int i = 0; i < mDisplayStates.size(); i++) { + mDisplayStates.valueAt(i).writeToProto(proto, fieldId); + } + } } diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java index aad890b8bd74..228c71d91b8f 100644 --- a/services/core/java/com/android/server/am/MemoryStatUtil.java +++ b/services/core/java/com/android/server/am/MemoryStatUtil.java @@ -37,18 +37,25 @@ import java.util.regex.Pattern; * Static utility methods related to {@link MemoryStat}. */ final class MemoryStatUtil { + static final int BYTES_IN_KILOBYTE = 1024; + private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM; /** True if device has per-app memcg */ - private static final Boolean DEVICE_HAS_PER_APP_MEMCG = + private static final boolean DEVICE_HAS_PER_APP_MEMCG = SystemProperties.getBoolean("ro.config.per_app_memcg", false); /** Path to check if device has memcg */ private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat"; /** Path to memory stat file for logging app start memory state */ private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat"; + /** Path to memory max usage file for logging app memory state */ + private static final String MEMORY_MAX_USAGE_FILE_FMT = + "/dev/memcg/apps/uid_%d/pid_%d/memory.max_usage_in_bytes"; /** Path to procfs stat file for logging app start memory state */ private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat"; + /** Path to procfs status file for logging app memory state */ + private static final String PROC_STATUS_FILE_FMT = "/proc/%d/status"; private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)"); private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)"); @@ -56,6 +63,9 @@ final class MemoryStatUtil { private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)"); private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)"); + private static final Pattern RSS_HIGH_WATERMARK_IN_BYTES = + Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB"); + private static final int PGFAULT_INDEX = 9; private static final int PGMAJFAULT_INDEX = 11; private static final int RSS_IN_BYTES_INDEX = 23; @@ -80,8 +90,15 @@ final class MemoryStatUtil { */ @Nullable static MemoryStat readMemoryStatFromMemcg(int uid, int pid) { - final String path = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid); - return parseMemoryStatFromMemcg(readFileContents(path)); + final String statPath = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid); + MemoryStat stat = parseMemoryStatFromMemcg(readFileContents(statPath)); + if (stat == null) { + return null; + } + String maxUsagePath = String.format(Locale.US, MEMORY_MAX_USAGE_FILE_FMT, uid, pid); + stat.rssHighWatermarkInBytes = parseMemoryMaxUsageFromMemCg( + readFileContents(maxUsagePath)); + return stat; } /** @@ -91,8 +108,14 @@ final class MemoryStatUtil { */ @Nullable static MemoryStat readMemoryStatFromProcfs(int pid) { - final String path = String.format(Locale.US, PROC_STAT_FILE_FMT, pid); - return parseMemoryStatFromProcfs(readFileContents(path)); + final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid); + MemoryStat stat = parseMemoryStatFromProcfs(readFileContents(statPath)); + if (stat == null) { + return null; + } + final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid); + stat.rssHighWatermarkInBytes = parseVmHWMFromProcfs(readFileContents(statusPath)); + return stat; } private static String readFileContents(String path) { @@ -113,7 +136,7 @@ final class MemoryStatUtil { /** * Parses relevant statistics out from the contents of a memory.stat file in memcg. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @VisibleForTesting @Nullable static MemoryStat parseMemoryStatFromMemcg(String memoryStatContents) { if (memoryStatContents == null || memoryStatContents.isEmpty()) { @@ -135,10 +158,18 @@ final class MemoryStatUtil { return memoryStat; } + @VisibleForTesting + static long parseMemoryMaxUsageFromMemCg(String memoryMaxUsageContents) { + if (memoryMaxUsageContents == null || memoryMaxUsageContents.isEmpty()) { + return 0; + } + return Long.valueOf(memoryMaxUsageContents); + } + /** - * Parses relevant statistics out from the contents of a /proc/pid/stat file in procfs. + * Parses relevant statistics out from the contents of the /proc/pid/stat file in procfs. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @VisibleForTesting @Nullable static MemoryStat parseMemoryStatFromProcfs(String procStatContents) { if (procStatContents == null || procStatContents.isEmpty()) { @@ -158,6 +189,20 @@ final class MemoryStatUtil { } /** + * Parses RSS high watermark out from the contents of the /proc/pid/status file in procfs. The + * returned value is in bytes. + */ + @VisibleForTesting + static long parseVmHWMFromProcfs(String procStatusContents) { + if (procStatusContents == null || procStatusContents.isEmpty()) { + return 0; + } + Matcher m = RSS_HIGH_WATERMARK_IN_BYTES.matcher(procStatusContents); + // Convert value read from /proc/pid/status from kilobytes to bytes. + return m.find() ? Long.valueOf(m.group(1)) * BYTES_IN_KILOBYTE : 0; + } + + /** * Returns whether per-app memcg is available on device. */ static boolean hasMemcg() { @@ -175,5 +220,7 @@ final class MemoryStatUtil { long cacheInBytes; /** Number of bytes of swap usage */ long swapInBytes; + /** Number of bytes of peak anonymous and swap cache memory */ + long rssHighWatermarkInBytes; } } diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java new file mode 100644 index 000000000000..a9c00a70650c --- /dev/null +++ b/services/core/java/com/android/server/am/PendingIntentController.java @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; + +import android.app.Activity; +import android.app.ActivityManagerInternal; +import android.app.AppGlobals; +import android.app.PendingIntent; +import android.content.IIntentSender; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.Slog; +import com.android.internal.os.IResultReceiver; +import com.android.internal.util.function.pooled.PooledLambda; +import com.android.server.LocalServices; +import com.android.server.wm.ActivityTaskManagerInternal; + +import java.io.PrintWriter; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +/** + * Helper class for {@link ActivityManagerService} responsible for managing pending intents. + * + * <p>This class uses {@link #mLock} to synchronize access to internal state and doesn't make use of + * {@link ActivityManagerService} lock since there can be direct calls into this class from outside + * AM. This helps avoid deadlocks. + */ +public class PendingIntentController { + private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentController" : TAG_AM; + private static final String TAG_MU = TAG + POSTFIX_MU; + + /** Lock for internal state. */ + final Object mLock = new Object(); + final Handler mH; + ActivityManagerInternal mAmInternal; + final UserController mUserController; + final ActivityTaskManagerInternal mAtmInternal; + + /** Set of IntentSenderRecord objects that are currently active. */ + final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords + = new HashMap<>(); + + PendingIntentController(Looper looper, UserController userController) { + mH = new Handler(looper); + mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); + mUserController = userController; + } + + void onActivityManagerInternalAdded() { + synchronized (mLock) { + mAmInternal = LocalServices.getService(ActivityManagerInternal.class); + } + } + + PendingIntentRecord getIntentSender(int type, String packageName, int callingUid, int userId, + IBinder token, String resultWho, int requestCode, Intent[] intents, + String[] resolvedTypes, int flags, Bundle bOptions) { + synchronized (mLock) { + if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid); + + // We're going to be splicing together extras before sending, so we're + // okay poking into any contained extras. + if (intents != null) { + for (int i = 0; i < intents.length; i++) { + intents[i].setDefusable(true); + } + } + Bundle.setDefusable(bOptions, true); + + final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0; + final boolean cancelCurrent = (flags & PendingIntent.FLAG_CANCEL_CURRENT) != 0; + final boolean updateCurrent = (flags & PendingIntent.FLAG_UPDATE_CURRENT) != 0; + flags &= ~(PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_CANCEL_CURRENT + | PendingIntent.FLAG_UPDATE_CURRENT); + + PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, token, + resultWho, requestCode, intents, resolvedTypes, flags, + SafeActivityOptions.fromBundle(bOptions), userId); + WeakReference<PendingIntentRecord> ref; + ref = mIntentSenderRecords.get(key); + PendingIntentRecord rec = ref != null ? ref.get() : null; + if (rec != null) { + if (!cancelCurrent) { + if (updateCurrent) { + if (rec.key.requestIntent != null) { + rec.key.requestIntent.replaceExtras(intents != null ? + intents[intents.length - 1] : null); + } + if (intents != null) { + intents[intents.length - 1] = rec.key.requestIntent; + rec.key.allIntents = intents; + rec.key.allResolvedTypes = resolvedTypes; + } else { + rec.key.allIntents = null; + rec.key.allResolvedTypes = null; + } + } + return rec; + } + makeIntentSenderCanceled(rec); + mIntentSenderRecords.remove(key); + } + if (noCreate) { + return rec; + } + rec = new PendingIntentRecord(this, key, callingUid); + mIntentSenderRecords.put(key, rec.ref); + return rec; + } + } + + boolean removePendingIntentsForPackage(String packageName, int userId, int appId, + boolean doIt) { + + boolean didSomething = false; + synchronized (mLock) { + + // Remove pending intents. For now we only do this when force stopping users, because + // we have some problems when doing this for packages -- app widgets are not currently + // cleaned up for such packages, so they can be left with bad pending intents. + if (mIntentSenderRecords.size() <= 0) { + return false; + } + + Iterator<WeakReference<PendingIntentRecord>> it + = mIntentSenderRecords.values().iterator(); + while (it.hasNext()) { + WeakReference<PendingIntentRecord> wpir = it.next(); + if (wpir == null) { + it.remove(); + continue; + } + PendingIntentRecord pir = wpir.get(); + if (pir == null) { + it.remove(); + continue; + } + if (packageName == null) { + // Stopping user, remove all objects for the user. + if (pir.key.userId != userId) { + // Not the same user, skip it. + continue; + } + } else { + if (UserHandle.getAppId(pir.uid) != appId) { + // Different app id, skip it. + continue; + } + if (userId != UserHandle.USER_ALL && pir.key.userId != userId) { + // Different user, skip it. + continue; + } + if (!pir.key.packageName.equals(packageName)) { + // Different package, skip it. + continue; + } + } + if (!doIt) { + return true; + } + didSomething = true; + it.remove(); + makeIntentSenderCanceled(pir); + if (pir.key.activity != null) { + final Message m = PooledLambda.obtainMessage( + PendingIntentController::clearPendingResultForActivity, this, + pir.key.activity, pir.ref); + mH.sendMessage(m); + } + } + } + + return didSomething; + } + + public void cancelIntentSender(IIntentSender sender) { + if (!(sender instanceof PendingIntentRecord)) { + return; + } + synchronized (mLock) { + final PendingIntentRecord rec = (PendingIntentRecord) sender; + try { + final int uid = AppGlobals.getPackageManager().getPackageUid(rec.key.packageName, + MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getCallingUserId()); + if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) { + String msg = "Permission Denial: cancelIntentSender() from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + + " is not allowed to cancel package " + rec.key.packageName; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } catch (RemoteException e) { + throw new SecurityException(e); + } + cancelIntentSender(rec, true); + } + } + + public void cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity) { + synchronized (mLock) { + makeIntentSenderCanceled(rec); + mIntentSenderRecords.remove(rec.key); + if (cleanActivity && rec.key.activity != null) { + final Message m = PooledLambda.obtainMessage( + PendingIntentController::clearPendingResultForActivity, this, + rec.key.activity, rec.ref); + mH.sendMessage(m); + } + } + } + + private void makeIntentSenderCanceled(PendingIntentRecord rec) { + rec.canceled = true; + final RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked(); + if (callbacks != null) { + final Message m = PooledLambda.obtainMessage( + PendingIntentController::handlePendingIntentCancelled, this, callbacks); + mH.sendMessage(m); + } + } + + private void handlePendingIntentCancelled(RemoteCallbackList<IResultReceiver> callbacks) { + int N = callbacks.beginBroadcast(); + for (int i = 0; i < N; i++) { + try { + callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, null); + } catch (RemoteException e) { + // Process is not longer running...whatever. + } + } + callbacks.finishBroadcast(); + // We have to clean up the RemoteCallbackList here, because otherwise it will + // needlessly hold the enclosed callbacks until the remote process dies. + callbacks.kill(); + } + + private void clearPendingResultForActivity(IBinder activityToken, + WeakReference<PendingIntentRecord> pir) { + mAtmInternal.clearPendingResultForActivity(activityToken, pir); + } + + void dumpPendingIntents(PrintWriter pw, boolean dumpAll, String dumpPackage) { + synchronized (mLock) { + boolean printed = false; + + pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)"); + + if (mIntentSenderRecords.size() > 0) { + // Organize these by package name, so they are easier to read. + final ArrayMap<String, ArrayList<PendingIntentRecord>> byPackage = new ArrayMap<>(); + final ArrayList<WeakReference<PendingIntentRecord>> weakRefs = new ArrayList<>(); + final Iterator<WeakReference<PendingIntentRecord>> it + = mIntentSenderRecords.values().iterator(); + while (it.hasNext()) { + WeakReference<PendingIntentRecord> ref = it.next(); + PendingIntentRecord rec = ref != null ? ref.get() : null; + if (rec == null) { + weakRefs.add(ref); + continue; + } + if (dumpPackage != null && !dumpPackage.equals(rec.key.packageName)) { + continue; + } + ArrayList<PendingIntentRecord> list = byPackage.get(rec.key.packageName); + if (list == null) { + list = new ArrayList<>(); + byPackage.put(rec.key.packageName, list); + } + list.add(rec); + } + for (int i = 0; i < byPackage.size(); i++) { + ArrayList<PendingIntentRecord> intents = byPackage.valueAt(i); + printed = true; + pw.print(" * "); pw.print(byPackage.keyAt(i)); + pw.print(": "); pw.print(intents.size()); pw.println(" items"); + for (int j = 0; j < intents.size(); j++) { + pw.print(" #"); pw.print(j); pw.print(": "); pw.println(intents.get(j)); + if (dumpAll) { + intents.get(j).dump(pw, " "); + } + } + } + if (weakRefs.size() > 0) { + printed = true; + pw.println(" * WEAK REFS:"); + for (int i = 0; i < weakRefs.size(); i++) { + pw.print(" #"); pw.print(i); pw.print(": "); pw.println(weakRefs.get(i)); + } + } + } + + if (!printed) { + pw.println(" (nothing)"); + } + } + } +} diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index ee1166e2a6e8..b9c6fa6020c4 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -38,15 +38,16 @@ import android.util.Slog; import android.util.TimeUtils; import com.android.internal.os.IResultReceiver; +import com.android.internal.util.function.pooled.PooledLambda; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.Objects; -final class PendingIntentRecord extends IIntentSender.Stub { +public final class PendingIntentRecord extends IIntentSender.Stub { private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentRecord" : TAG_AM; - final ActivityManagerService owner; + final PendingIntentController controller; final Key key; final int uid; final WeakReference<PendingIntentRecord> ref; @@ -62,7 +63,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { final static class Key { final int type; final String packageName; - final ActivityRecord activity; + final IBinder activity; final String who; final int requestCode; final Intent requestIntent; @@ -76,7 +77,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { private static final int ODD_PRIME_NUMBER = 37; - Key(int _t, String _p, ActivityRecord _a, String _w, + Key(int _t, String _p, IBinder _a, String _w, int _r, Intent[] _i, String[] _it, int _f, SafeActivityOptions _o, int _userId) { type = _t; packageName = _p; @@ -114,6 +115,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { // + Integer.toHexString(hashCode)); } + @Override public boolean equals(Object otherObj) { if (otherObj == null) { return false; @@ -188,11 +190,11 @@ final class PendingIntentRecord extends IIntentSender.Stub { } } - PendingIntentRecord(ActivityManagerService _owner, Key _k, int _u) { - owner = _owner; + PendingIntentRecord(PendingIntentController _controller, Key _k, int _u) { + controller = _controller; key = _k; uid = _u; - ref = new WeakReference<PendingIntentRecord>(this); + ref = new WeakReference<>(this); } void setWhitelistDurationLocked(IBinder whitelistToken, long duration) { @@ -247,189 +249,196 @@ final class PendingIntentRecord extends IIntentSender.Stub { } int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken, - IIntentReceiver finishedReceiver, - String requiredPermission, IBinder resultTo, String resultWho, int requestCode, - int flagsMask, int flagsValues, Bundle options) { + IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, + String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) { if (intent != null) intent.setDefusable(true); if (options != null) options.setDefusable(true); - synchronized (owner) { - if (!canceled) { - sent = true; - if ((key.flags&PendingIntent.FLAG_ONE_SHOT) != 0) { - owner.cancelIntentSenderLocked(this, true); - } + Long duration = null; + Intent finalIntent = null; + Intent[] allIntents = null; + String[] allResolvedTypes = null; + SafeActivityOptions mergedOptions = null; + synchronized (controller.mLock) { + if (canceled) { + return ActivityManager.START_CANCELED; + } - Intent finalIntent = key.requestIntent != null - ? new Intent(key.requestIntent) : new Intent(); + sent = true; + if ((key.flags & PendingIntent.FLAG_ONE_SHOT) != 0) { + controller.cancelIntentSender(this, true); + } - final boolean immutable = (key.flags & PendingIntent.FLAG_IMMUTABLE) != 0; - if (!immutable) { - if (intent != null) { - int changes = finalIntent.fillIn(intent, key.flags); - if ((changes & Intent.FILL_IN_DATA) == 0) { - resolvedType = key.requestResolvedType; - } - } else { + finalIntent = key.requestIntent != null ? new Intent(key.requestIntent) : new Intent(); + + final boolean immutable = (key.flags & PendingIntent.FLAG_IMMUTABLE) != 0; + if (!immutable) { + if (intent != null) { + int changes = finalIntent.fillIn(intent, key.flags); + if ((changes & Intent.FILL_IN_DATA) == 0) { resolvedType = key.requestResolvedType; } - flagsMask &= ~Intent.IMMUTABLE_FLAGS; - flagsValues &= flagsMask; - finalIntent.setFlags((finalIntent.getFlags() & ~flagsMask) | flagsValues); } else { resolvedType = key.requestResolvedType; } + flagsMask &= ~Intent.IMMUTABLE_FLAGS; + flagsValues &= flagsMask; + finalIntent.setFlags((finalIntent.getFlags() & ~flagsMask) | flagsValues); + } else { + resolvedType = key.requestResolvedType; + } - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); + // Extract options before clearing calling identity + mergedOptions = key.options; + if (mergedOptions == null) { + mergedOptions = SafeActivityOptions.fromBundle(options); + } else { + mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options)); + } - // Extract options before clearing calling identity - SafeActivityOptions mergedOptions = key.options; - if (mergedOptions == null) { - mergedOptions = SafeActivityOptions.fromBundle(options); - } else { - mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options)); + if (whitelistDuration != null) { + duration = whitelistDuration.get(whitelistToken); + } + + if (key.type == ActivityManager.INTENT_SENDER_ACTIVITY + && key.allIntents != null && key.allIntents.length > 1) { + // Copy all intents and resolved types while we have the controller lock so we can + // use it later when the lock isn't held. + allIntents = new Intent[key.allIntents.length]; + allResolvedTypes = new String[key.allIntents.length]; + System.arraycopy(key.allIntents, 0, allIntents, 0, key.allIntents.length); + if (key.allResolvedTypes != null) { + System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0, + key.allResolvedTypes.length); } + allIntents[allIntents.length - 1] = finalIntent; + allResolvedTypes[allResolvedTypes.length - 1] = resolvedType; + } - final long origId = Binder.clearCallingIdentity(); - - if (whitelistDuration != null) { - Long duration = whitelistDuration.get(whitelistToken); - if (duration != null) { - int procState = owner.getUidState(callingUid); - if (!ActivityManager.isProcStateBackground(procState)) { - StringBuilder tag = new StringBuilder(64); - tag.append("pendingintent:"); - UserHandle.formatUid(tag, callingUid); - tag.append(":"); - if (finalIntent.getAction() != null) { - tag.append(finalIntent.getAction()); - } else if (finalIntent.getComponent() != null) { - finalIntent.getComponent().appendShortString(tag); - } else if (finalIntent.getData() != null) { - tag.append(finalIntent.getData().toSafeString()); - } - owner.tempWhitelistForPendingIntentLocked(callingPid, - callingUid, uid, duration, tag.toString()); - } else { - Slog.w(TAG, "Not doing whitelist " + this + ": caller state=" - + procState); - } + } + // We don't hold the controller lock beyond this point as we will be calling into AM and WM. + + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final long origId = Binder.clearCallingIdentity(); + + int res = START_SUCCESS; + try { + if (duration != null) { + int procState = controller.mAmInternal.getUidProcessState(callingUid); + if (!ActivityManager.isProcStateBackground(procState)) { + StringBuilder tag = new StringBuilder(64); + tag.append("pendingintent:"); + UserHandle.formatUid(tag, callingUid); + tag.append(":"); + if (finalIntent.getAction() != null) { + tag.append(finalIntent.getAction()); + } else if (finalIntent.getComponent() != null) { + finalIntent.getComponent().appendShortString(tag); + } else if (finalIntent.getData() != null) { + tag.append(finalIntent.getData().toSafeString()); } + controller.mAmInternal.tempWhitelistForPendingIntent(callingPid, callingUid, + uid, duration, tag.toString()); + } else { + Slog.w(TAG, "Not doing whitelist " + this + ": caller state=" + procState); } + } - boolean sendFinish = finishedReceiver != null; - int userId = key.userId; - if (userId == UserHandle.USER_CURRENT) { - userId = owner.mUserController.getCurrentOrTargetUserId(); - } - int res = START_SUCCESS; - switch (key.type) { - case ActivityManager.INTENT_SENDER_ACTIVITY: - try { - // Note when someone has a pending intent, even from different - // users, then there's no need to ensure the calling user matches - // the target user, so validateIncomingUser is always false below. - - if (key.allIntents != null && key.allIntents.length > 1) { - Intent[] allIntents = new Intent[key.allIntents.length]; - String[] allResolvedTypes = new String[key.allIntents.length]; - System.arraycopy(key.allIntents, 0, allIntents, 0, - key.allIntents.length); - if (key.allResolvedTypes != null) { - System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0, - key.allResolvedTypes.length); - } - allIntents[allIntents.length-1] = finalIntent; - allResolvedTypes[allResolvedTypes.length-1] = resolvedType; - - res = owner.mActivityTaskManager.getActivityStartController().startActivitiesInPackage( - uid, key.packageName, allIntents, allResolvedTypes, - resultTo, mergedOptions, userId, - false /* validateIncomingUser */, - this /* originatingPendingIntent */); - } else { - res = owner.mActivityTaskManager.getActivityStartController().startActivityInPackage(uid, - callingPid, callingUid, key.packageName, finalIntent, - resolvedType, resultTo, resultWho, requestCode, 0, - mergedOptions, userId, null, "PendingIntentRecord", - false /* validateIncomingUser */, - this /* originatingPendingIntent */); - } - } catch (RuntimeException e) { - Slog.w(TAG, "Unable to send startActivity intent", e); - } - break; - case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT: - final ActivityStack stack = key.activity.getStack(); - if (stack != null) { - stack.sendActivityResultLocked(-1, key.activity, key.who, - key.requestCode, code, finalIntent); - } - break; - case ActivityManager.INTENT_SENDER_BROADCAST: - try { - // If a completion callback has been requested, require - // that the broadcast be delivered synchronously - int sent = owner.broadcastIntentInPackage(key.packageName, uid, - finalIntent, resolvedType, finishedReceiver, code, null, null, - requiredPermission, options, (finishedReceiver != null), - false, userId); - if (sent == ActivityManager.BROADCAST_SUCCESS) { - sendFinish = false; - } - } catch (RuntimeException e) { - Slog.w(TAG, "Unable to send startActivity intent", e); + boolean sendFinish = finishedReceiver != null; + int userId = key.userId; + if (userId == UserHandle.USER_CURRENT) { + userId = controller.mUserController.getCurrentOrTargetUserId(); + } + + switch (key.type) { + case ActivityManager.INTENT_SENDER_ACTIVITY: + try { + // Note when someone has a pending intent, even from different + // users, then there's no need to ensure the calling user matches + // the target user, so validateIncomingUser is always false below. + + if (key.allIntents != null && key.allIntents.length > 1) { + res = controller.mAtmInternal.startActivitiesInPackage( + uid, key.packageName, allIntents, allResolvedTypes, resultTo, + mergedOptions, userId, false /* validateIncomingUser */, + this /* originatingPendingIntent */); + } else { + res = controller.mAtmInternal.startActivityInPackage( + uid, callingPid, callingUid, key.packageName, finalIntent, + resolvedType, resultTo, resultWho, requestCode, 0, + mergedOptions, userId, null, "PendingIntentRecord", + false /* validateIncomingUser */, + this /* originatingPendingIntent */); } - break; - case ActivityManager.INTENT_SENDER_SERVICE: - case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE: - try { - owner.startServiceInPackage(uid, finalIntent, resolvedType, - key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE, - key.packageName, userId); - } catch (RuntimeException e) { - Slog.w(TAG, "Unable to send startService intent", e); - } catch (TransactionTooLargeException e) { - res = ActivityManager.START_CANCELED; + } catch (RuntimeException e) { + Slog.w(TAG, "Unable to send startActivity intent", e); + } + break; + case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT: + controller.mAtmInternal.sendActivityResult(-1, key.activity, key.who, + key.requestCode, code, finalIntent); + break; + case ActivityManager.INTENT_SENDER_BROADCAST: + try { + // If a completion callback has been requested, require + // that the broadcast be delivered synchronously + int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName, + uid, finalIntent, resolvedType, finishedReceiver, code, null, null, + requiredPermission, options, (finishedReceiver != null), + false, userId); + if (sent == ActivityManager.BROADCAST_SUCCESS) { + sendFinish = false; } - break; - } - - if (sendFinish && res != ActivityManager.START_CANCELED) { + } catch (RuntimeException e) { + Slog.w(TAG, "Unable to send startActivity intent", e); + } + break; + case ActivityManager.INTENT_SENDER_SERVICE: + case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE: try { - finishedReceiver.performReceive(new Intent(finalIntent), 0, - null, null, false, false, key.userId); - } catch (RemoteException e) { + controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType, + key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE, + key.packageName, userId); + } catch (RuntimeException e) { + Slog.w(TAG, "Unable to send startService intent", e); + } catch (TransactionTooLargeException e) { + res = ActivityManager.START_CANCELED; } - } - - Binder.restoreCallingIdentity(origId); + break; + } - return res; + if (sendFinish && res != ActivityManager.START_CANCELED) { + try { + finishedReceiver.performReceive(new Intent(finalIntent), 0, + null, null, false, false, key.userId); + } catch (RemoteException e) { + } } + } finally { + Binder.restoreCallingIdentity(origId); } - return ActivityManager.START_CANCELED; + + return res; } @Override protected void finalize() throws Throwable { try { if (!canceled) { - owner.mHandler.sendMessage(owner.mHandler.obtainMessage( - ActivityManagerService.FINALIZE_PENDING_INTENT_MSG, this)); + controller.mH.sendMessage(PooledLambda.obtainMessage( + PendingIntentRecord::completeFinalize, this)); } } finally { super.finalize(); } } - public void completeFinalize() { - synchronized(owner) { - WeakReference<PendingIntentRecord> current = - owner.mIntentSenderRecords.get(key); + private void completeFinalize() { + synchronized(controller.mLock) { + WeakReference<PendingIntentRecord> current = controller.mIntentSenderRecords.get(key); if (current == ref) { - owner.mIntentSenderRecords.remove(key); + controller.mIntentSenderRecords.remove(key); } } } diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java index 8ce650c1a514..6ffd8a9180ba 100644 --- a/services/core/java/com/android/server/am/ProcessStatsService.java +++ b/services/core/java/com/android/server/am/ProcessStatsService.java @@ -23,8 +23,10 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.service.procstats.ProcessStatsServiceDumpProto; +import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.AtomicFile; +import android.util.Log; import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; @@ -47,6 +49,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.locks.ReentrantLock; @@ -482,6 +485,23 @@ public final class ProcessStatsService extends IProcessStats.Stub { return finalRes; } + static int parseSectionOptions(String optionsStr) { + final String sep = ","; + String[] sectionsStr = optionsStr.split(sep); + if (sectionsStr.length == 0) { + return ProcessStats.REPORT_ALL; + } + int res = 0; + List<String> optionStrList = Arrays.asList(ProcessStats.OPTIONS_STR); + for (String sectionStr : sectionsStr) { + int optionIndex = optionStrList.indexOf(sectionStr); + if (optionIndex != -1) { + res |= ProcessStats.OPTIONS[optionIndex]; + } + } + return res; + } + public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) { mAm.mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); @@ -514,6 +534,95 @@ public final class ProcessStatsService extends IProcessStats.Stub { return current.marshall(); } + /** + * Get stats committed after highWaterMarkMs + * @param highWaterMarkMs Report stats committed after this time. + * @param section Integer mask to indicage which sections to include in the stats. + * @param doAggregate Whether to aggregate the stats or keep them separated. + * @return List of proto binary of individual commit files or one that is merged from them. + */ + @Override + public long getCommittedStats(long highWaterMarkMs, int section, boolean doAggregate, + List<ParcelFileDescriptor> committedStats) { + mAm.mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_USAGE_STATS, null); + + ProcessStats mergedStats = new ProcessStats(false); + long newHighWaterMark = highWaterMarkMs; + mWriteLock.lock(); + try { + ArrayList<String> files = getCommittedFiles(0, false, true); + if (files != null) { + String highWaterMarkStr = + DateFormat.format("yyyy-MM-dd-HH-mm-ss", highWaterMarkMs).toString(); + ProcessStats stats = new ProcessStats(false); + for (int i = files.size() - 1; i >= 0; i--) { + String fileName = files.get(i); + try { + String startTimeStr = fileName.substring( + fileName.lastIndexOf(STATE_FILE_PREFIX) + + STATE_FILE_PREFIX.length(), + fileName.lastIndexOf(STATE_FILE_SUFFIX)); + if (startTimeStr.compareToIgnoreCase(highWaterMarkStr) > 0) { + ParcelFileDescriptor pfd = ParcelFileDescriptor.open( + new File(fileName), + ParcelFileDescriptor.MODE_READ_ONLY); + InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + stats.reset(); + stats.read(is); + is.close(); + if (stats.mTimePeriodStartClock > newHighWaterMark) { + newHighWaterMark = stats.mTimePeriodStartClock; + } + if (doAggregate) { + mergedStats.add(stats); + } else { + committedStats.add(protoToParcelFileDescriptor(stats, section)); + } + if (stats.mReadError != null) { + Log.w(TAG, "Failure reading process stats: " + stats.mReadError); + continue; + } + } + } catch (IOException e) { + Slog.w(TAG, "Failure opening procstat file " + fileName, e); + } catch (IndexOutOfBoundsException e) { + Slog.w(TAG, "Failure to read and parse commit file " + fileName, e); + } + } + if (doAggregate) { + committedStats.add(protoToParcelFileDescriptor(mergedStats, section)); + } + return newHighWaterMark; + } + } catch (IOException e) { + Slog.w(TAG, "Failure opening procstat file", e); + } finally { + mWriteLock.unlock(); + } + return newHighWaterMark; + } + + private ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section) + throws IOException { + final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); + Thread thr = new Thread("ProcessStats pipe output") { + public void run() { + try { + FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]); + final ProtoOutputStream proto = new ProtoOutputStream(fout); + stats.writeToProto(proto, stats.mTimePeriodEndRealtime, section); + proto.flush(); + fout.close(); + } catch (IOException e) { + Slog.w(TAG, "Failure writing pipe", e); + } + } + }; + thr.start(); + return fds[0]; + } + public ParcelFileDescriptor getStatsOverTime(long minTime) { mAm.mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); @@ -594,7 +703,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { private void dumpAggregatedStats(PrintWriter pw, long aggregateHours, long now, String reqPackage, boolean isCompact, boolean dumpDetails, boolean dumpFullDetails, - boolean dumpAll, boolean activeOnly) { + boolean dumpAll, boolean activeOnly, int section) { ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000 - (ProcessStats.COMMIT_PERIOD/2)); if (pfd == null) { @@ -609,11 +718,11 @@ public final class ProcessStatsService extends IProcessStats.Stub { return; } if (isCompact) { - stats.dumpCheckinLocked(pw, reqPackage); + stats.dumpCheckinLocked(pw, reqPackage, section); } else { if (dumpDetails || dumpFullDetails) { stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll, - activeOnly); + activeOnly, section); } else { stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); } @@ -643,6 +752,8 @@ public final class ProcessStatsService extends IProcessStats.Stub { pw.println(" --max: for -a, max num of historical batches to print."); pw.println(" --active: only show currently active processes/services."); pw.println(" --commit: commit current stats to disk and reset to start new stats."); + pw.println(" --section: proc|pkg-proc|pkg-svc|pkg-asc|pkg-all|all "); + pw.println(" options can be combined to select desired stats"); pw.println(" --reset: reset current stats, without committing."); pw.println(" --clear: clear all stats; does both --reset and deletes old stats."); pw.println(" --write: write current in-memory stats to disk."); @@ -696,6 +807,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL}; boolean csvSepProcStats = true; int[] csvProcStats = ProcessStats.ALL_PROC_STATES; + int section = ProcessStats.REPORT_ALL; if (args != null) { for (int i=0; i<args.length; i++) { String arg = args[i]; @@ -814,13 +926,14 @@ public final class ProcessStatsService extends IProcessStats.Stub { pw.println("Process stats committed."); quit = true; } - } else if ("--reset".equals(arg)) { - synchronized (mAm) { - mProcessStats.resetSafely(); - mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); - pw.println("Process stats reset."); - quit = true; + } else if ("--section".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("Error: argument required for --section"); + dumpHelp(pw); + return; } + section = parseSectionOptions(args[i]); } else if ("--clear".equals(arg)) { synchronized (mAm) { mProcessStats.resetSafely(); @@ -946,7 +1059,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } else if (aggregateHours != 0) { pw.print("AGGREGATED OVER LAST "); pw.print(aggregateHours); pw.println(" HOURS:"); dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact, - dumpDetails, dumpFullDetails, dumpAll, activeOnly); + dumpDetails, dumpFullDetails, dumpAll, activeOnly, section); return; } else if (lastIndex > 0) { pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":"); @@ -968,7 +1081,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); if (isCheckin || isCompact) { // Don't really need to lock because we uniquely own this object. - processStats.dumpCheckinLocked(pw, reqPackage); + processStats.dumpCheckinLocked(pw, reqPackage, section); } else { pw.print("COMMITTED STATS FROM "); pw.print(processStats.mTimePeriodStartClockStr); @@ -976,7 +1089,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { pw.println(":"); if (dumpDetails || dumpFullDetails) { processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, - dumpAll, activeOnly); + dumpAll, activeOnly, section); if (dumpAll) { pw.print(" mFile="); pw.println(mFile.getBaseFile()); } @@ -1015,7 +1128,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); if (isCheckin || isCompact) { // Don't really need to lock because we uniquely own this object. - processStats.dumpCheckinLocked(pw, reqPackage); + processStats.dumpCheckinLocked(pw, reqPackage, section); } else { if (sepNeeded) { pw.println(); @@ -1031,7 +1144,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { // much crud. if (dumpFullDetails) { processStats.dumpLocked(pw, reqPackage, now, false, false, - false, activeOnly); + false, activeOnly, section); } else { processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); } @@ -1054,7 +1167,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { if (!isCheckin) { synchronized (mAm) { if (isCompact) { - mProcessStats.dumpCheckinLocked(pw, reqPackage); + mProcessStats.dumpCheckinLocked(pw, reqPackage, section); } else { if (sepNeeded) { pw.println(); @@ -1062,7 +1175,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { pw.println("CURRENT STATS:"); if (dumpDetails || dumpFullDetails) { mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, - dumpAll, activeOnly); + dumpAll, activeOnly, section); if (dumpAll) { pw.print(" mFile="); pw.println(mFile.getBaseFile()); } @@ -1078,11 +1191,11 @@ public final class ProcessStatsService extends IProcessStats.Stub { } pw.println("AGGREGATED OVER LAST 24 HOURS:"); dumpAggregatedStats(pw, 24, now, reqPackage, isCompact, - dumpDetails, dumpFullDetails, dumpAll, activeOnly); + dumpDetails, dumpFullDetails, dumpAll, activeOnly, section); pw.println(); pw.println("AGGREGATED OVER LAST 3 HOURS:"); dumpAggregatedStats(pw, 3, now, reqPackage, isCompact, - dumpDetails, dumpFullDetails, dumpAll, activeOnly); + dumpDetails, dumpFullDetails, dumpAll, activeOnly, section); } } } @@ -1099,7 +1212,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { if (stats.mReadError != null) { return; } - stats.writeToProto(proto, fieldId, now); + final long token = proto.start(fieldId); + stats.writeToProto(proto, now, ProcessStats.REPORT_ALL); + proto.end(token); } private void dumpProto(FileDescriptor fd) { @@ -1109,7 +1224,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { long now; synchronized (mAm) { now = SystemClock.uptimeMillis(); - mProcessStats.writeToProto(proto,ProcessStatsServiceDumpProto.PROCSTATS_NOW, now); + final long token = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW); + mProcessStats.writeToProto(proto, now, ProcessStats.REPORT_ALL); + proto.end(token); } // aggregated over last 3 hours procstats diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java index f7de7f475b7b..fa0cb47ade02 100644 --- a/services/core/java/com/android/server/am/SafeActivityOptions.java +++ b/services/core/java/com/android/server/am/SafeActivityOptions.java @@ -44,7 +44,7 @@ import com.android.internal.annotations.VisibleForTesting; * the inner options. Also supports having two set of options: Once from the original caller, and * once from the caller that is overriding it, which happens when sending a {@link PendingIntent}. */ -class SafeActivityOptions { +public class SafeActivityOptions { private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_AM; diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 9b42d65483df..ef8cb1c07969 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -129,7 +129,8 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Objects; -class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener { +// TODO: Make package private again once move to WM package is complete. +public class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM; private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE; private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS; diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java new file mode 100644 index 000000000000..91b3b21632c6 --- /dev/null +++ b/services/core/java/com/android/server/appbinding/AppBindingService.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appbinding; + +import android.app.AppGlobals; +import android.content.Context; +import android.content.pm.IPackageManager; +import android.os.Binder; +import android.os.Handler; + +import com.android.internal.os.BackgroundThread; +import com.android.internal.util.DumpUtils; +import com.android.server.SystemService; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * System server that keeps a binding to an app to keep it always running. + */ +public class AppBindingService extends Binder { + public static final String TAG = "AppBindingService"; + + private static final boolean DEBUG = false; + + private final Object mLock = new Object(); + + private final Injector mInjector; + private final Context mContext; + private final Handler mHandler; + private final IPackageManager mIPackageManager; + + static class Injector { + public IPackageManager getIPackageManager() { + return AppGlobals.getPackageManager(); + } + } + + /** + * System service interacts with this service via this class. + */ + public static final class Lifecycle extends SystemService { + final AppBindingService mService; + + public Lifecycle(Context context) { + super(context); + mService = new AppBindingService(new Injector(), context); + } + + @Override + public void onStart() { + publishBinderService(Context.APP_BINDING_SERVICE, mService); + } + } + + private AppBindingService(Injector injector, Context context) { + mInjector = injector; + mContext = context; + mIPackageManager = injector.getIPackageManager(); + mHandler = BackgroundThread.getHandler(); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + } +} diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 8caa70283acf..66c7c437284b 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -627,6 +627,13 @@ public class AudioService extends IAudioService.Stub // If absolute volume is supported in AVRCP device private boolean mAvrcpAbsVolSupported = false; + // Pre-scale for Bluetooth Absolute Volume + private float[] mPrescaleAbsoluteVolume = new float[] { + 0.5f, // Pre-scale for index 1 + 0.7f, // Pre-scale for index 2 + 0.85f, // Pre-scale for index 3 + }; + private static Long mLastDeviceConnectMsgTime = new Long(0); private NotificationManager mNm; @@ -878,6 +885,23 @@ public class AudioService extends IAudioService.Stub mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); mRecordMonitor.initMonitor(); + + final float[] preScale = new float[3]; + preScale[0] = mContext.getResources().getFraction( + com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1, + 1, 1); + preScale[1] = mContext.getResources().getFraction( + com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2, + 1, 1); + preScale[2] = mContext.getResources().getFraction( + com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3, + 1, 1); + for (int i = 0; i < preScale.length; i++) { + if (0.0f <= preScale[i] && preScale[i] <= 1.0f) { + mPrescaleAbsoluteVolume[i] = preScale[i]; + } + } + } public void systemReady() { @@ -4926,18 +4950,12 @@ public class AudioService extends IAudioService.Stub if (index == 0) { // 0% for volume 0 index = 0; - } else if (index == 1) { - // 50% for volume 1 - index = (int)(mIndexMax * 0.5) /10; - } else if (index == 2) { - // 70% for volume 2 - index = (int)(mIndexMax * 0.70) /10; - } else if (index == 3) { - // 85% for volume 3 - index = (int)(mIndexMax * 0.85) /10; + } else if (index > 0 && index <= 3) { + // Pre-scale for volume steps 1 2 and 3 + index = (int) (mIndexMax * mPrescaleAbsoluteVolume[index - 1]) / 10; } else { // otherwise, full gain - index = (mIndexMax + 5)/10; + index = (mIndexMax + 5) / 10; } return index; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 6f5f90a781e3..a9b0d5c42f73 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1976,7 +1976,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub throw new IllegalArgumentException("Unknown id: " + mCurMethodId); } - unbindCurrentMethodLocked(true); + unbindCurrentMethodLocked(); mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE); mCurIntent.setComponent(info.getComponent()); @@ -2020,7 +2020,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurMethod = IInputMethod.Stub.asInterface(service); if (mCurToken == null) { Slog.w(TAG, "Service connected without a token!"); - unbindCurrentMethodLocked(false); + unbindCurrentMethodLocked(); return; } if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); @@ -2059,7 +2059,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub channel.dispose(); } - void unbindCurrentMethodLocked(boolean savePosition) { + void unbindCurrentMethodLocked() { if (mVisibleBound) { mContext.unbindService(mVisibleConnection); mVisibleBound = false; @@ -2076,10 +2076,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.v(TAG, "Removing window token: " + mCurToken + " for display: " + mCurTokenDisplayId); } - if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && savePosition) { - // The current IME is shown. Hence an IME switch (transition) is happening. - mWindowManagerInternal.saveLastInputMethodWindowForTransition(); - } mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId); } catch (RemoteException e) { } @@ -2094,7 +2090,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub void resetCurrentMethodAndClient( /* @InputMethodClient.UnbindReason */ final int unbindClientReason) { mCurMethodId = null; - unbindCurrentMethodLocked(false); + unbindCurrentMethodLocked(); unbindCurrentClientLocked(unbindClientReason); } @@ -2865,7 +2861,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final int newFocusDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); if (newFocusDisplayId != mCurTokenDisplayId) { - unbindCurrentMethodLocked(false); + unbindCurrentMethodLocked(); } } } else if (isTextEditor && doAutoShow && (softInputMode & @@ -3237,19 +3233,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - private void clearLastInputMethodWindowForTransition(IBinder token) { - if (!calledFromValidUser()) { - return; - } - synchronized (mMethodMap) { - if (!calledWithValidToken(token)) { - return; - } - } - mWindowManagerInternal.clearLastInputMethodWindowForTransition(); - } - - @BinderThread private void notifyUserAction(@NonNull IBinder token) { if (DEBUG) { Slog.d(TAG, "Got the notification of a user action."); @@ -4957,7 +4940,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { synchronized (mMethodMap) { hideCurrentInputLocked(0, null); - unbindCurrentMethodLocked(false); + unbindCurrentMethodLocked(); // Reset the current IME resetSelectedInputMethodAndSubtypeLocked(null); // Also reset the settings of the current IME @@ -5030,12 +5013,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public void clearLastInputMethodWindowForTransition() { - mImms.clearLastInputMethodWindowForTransition(mToken); - } - - @BinderThread - @Override public IInputContentUriToken createInputContentUriToken(Uri contentUri, String packageName) { return mImms.createInputContentUriToken(mToken, contentUri, packageName); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e81b32c4a7a2..cade07cfb821 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1152,15 +1152,18 @@ public class NotificationManagerService extends SystemService { // turn off LED when user passes through lock screen mNotificationLight.turnOff(); } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { - final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); - // reload per-user settings - mSettingsObserver.update(null); + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); mUserProfiles.updateCache(context); - // Refresh managed services - mConditionProviders.onUserSwitched(user); - mListeners.onUserSwitched(user); - mAssistants.onUserSwitched(user); - mZenModeHelper.onUserSwitched(user); + if (!mUserProfiles.isManagedProfile(userId)) { + // reload per-user settings + mSettingsObserver.update(null); + // Refresh managed services + mConditionProviders.onUserSwitched(userId); + mListeners.onUserSwitched(userId); + mZenModeHelper.onUserSwitched(userId); + } + // assistant is the only thing that cares about managed profiles specifically + mAssistants.onUserSwitched(userId); } else if (action.equals(Intent.ACTION_USER_ADDED)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); if (userId != USER_NULL) { @@ -1170,20 +1173,23 @@ public class NotificationManagerService extends SystemService { } } } else if (action.equals(Intent.ACTION_USER_REMOVED)) { - final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); mUserProfiles.updateCache(context); - mZenModeHelper.onUserRemoved(user); - mPreferencesHelper.onUserRemoved(user); - mListeners.onUserRemoved(user); - mConditionProviders.onUserRemoved(user); - mAssistants.onUserRemoved(user); + mZenModeHelper.onUserRemoved(userId); + mPreferencesHelper.onUserRemoved(userId); + mListeners.onUserRemoved(userId); + mConditionProviders.onUserRemoved(userId); + mAssistants.onUserRemoved(userId); savePolicyFile(); } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) { - final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); - mConditionProviders.onUserUnlocked(user); - mListeners.onUserUnlocked(user); - mAssistants.onUserUnlocked(user); - mZenModeHelper.onUserUnlocked(user); + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); + mUserProfiles.updateCache(context); + mAssistants.onUserUnlocked(userId); + if (!mUserProfiles.isManagedProfile(userId)) { + mConditionProviders.onUserUnlocked(userId); + mListeners.onUserUnlocked(userId); + mZenModeHelper.onUserUnlocked(userId); + } } } }; @@ -4278,8 +4284,12 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId) { + if (userId == UserHandle.USER_ALL) { + userId = USER_SYSTEM; + } // posted from app A on behalf of app A - if (isCallerSameApp(targetPkg, callingUid) && TextUtils.equals(callingPkg, targetPkg)) { + if (isCallerSameApp(targetPkg, callingUid, userId) + && TextUtils.equals(callingPkg, targetPkg)) { return callingUid; } @@ -4316,7 +4326,7 @@ public class NotificationManagerService extends SystemService { if (!isSystemNotification && !isNotificationFromListener) { synchronized (mNotificationLock) { if (mNotificationsByKey.get(r.sbn.getKey()) == null - && isCallerInstantApp(pkg, callingUid)) { + && isCallerInstantApp(pkg, callingUid, r.getUserId())) { // Ephemeral apps have some special constraints for notifications. // They are not allowed to create new notifications however they are allowed to // update notifications created by the system (e.g. a foreground service @@ -6410,7 +6420,8 @@ public class NotificationManagerService extends SystemService { } } - private boolean isCallerInstantApp(String pkg, int callingUid) { + @VisibleForTesting + boolean isCallerInstantApp(String pkg, int callingUid, int userId) { // System is always allowed to act for ephemeral apps. if (isUidSystemOrPhone(callingUid)) { return false; @@ -6419,8 +6430,7 @@ public class NotificationManagerService extends SystemService { mAppOps.checkPackage(callingUid, pkg); try { - ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, - UserHandle.getCallingUserId()); + ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId); if (ai == null) { throw new SecurityException("Unknown package " + pkg); } @@ -6432,13 +6442,13 @@ public class NotificationManagerService extends SystemService { } private void checkCallerIsSameApp(String pkg) { - checkCallerIsSameApp(pkg, Binder.getCallingUid()); + checkCallerIsSameApp(pkg, Binder.getCallingUid(), UserHandle.getCallingUserId()); } - private void checkCallerIsSameApp(String pkg, int uid) { + private void checkCallerIsSameApp(String pkg, int uid, int userId) { try { ApplicationInfo ai = mPackageManager.getApplicationInfo( - pkg, 0, UserHandle.getCallingUserId()); + pkg, 0, userId); if (ai == null) { throw new SecurityException("Unknown package " + pkg); } @@ -6460,9 +6470,9 @@ public class NotificationManagerService extends SystemService { } } - private boolean isCallerSameApp(String pkg, int uid) { + private boolean isCallerSameApp(String pkg, int uid, int userId) { try { - checkCallerIsSameApp(pkg, uid); + checkCallerIsSameApp(pkg, uid, userId); return true; } catch (SecurityException e) { return false; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 9bf21c8cb943..760f1559c845 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -430,7 +430,7 @@ public class ZenModeHelper { for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) { ZenRule currRule = mConfig.automaticRules.get(defaultRule.id); // if default rule wasn't modified, use localized name instead of previous - if (!currRule.modified && !defaultRule.name.equals(currRule.name)) { + if (currRule != null && !currRule.modified && !defaultRule.name.equals(currRule.name)) { if (canManageAutomaticZenRule(defaultRule)) { if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name " + "from " + currRule.name + " to " + defaultRule.name); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 91af0eca8707..e10827bc6101 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -232,6 +232,7 @@ import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; +import android.permission.PermissionManager; import android.provider.Settings.Global; import android.provider.Settings.Secure; import android.security.KeyStore; @@ -2912,13 +2913,15 @@ public class PackageManagerService extends IPackageManager.Stub if (mIsUpgrade) { final int callingUid = getCallingUid(); - final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length; + final List<PermissionManager.SplitPermissionInfo> splitPermissions = + mContext.getSystemService(PermissionManager.class).getSplitPermissions(); + final int numSplitPerms = splitPermissions.size(); for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) { - final PackageParser.SplitPermissionInfo splitPerm = - PackageParser.SPLIT_PERMISSIONS[splitPermNum]; - final String rootPerm = splitPerm.rootPerm; + final PermissionManager.SplitPermissionInfo splitPerm = + splitPermissions.get(splitPermNum); + final String rootPerm = splitPerm.getRootPermission(); - if (preUpgradeSdkVersion >= splitPerm.targetSdk) { + if (preUpgradeSdkVersion >= splitPerm.getTargetSdk()) { continue; } @@ -2926,7 +2929,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int packageNum = 0; packageNum < numPackages; packageNum++) { final PackageParser.Package pkg = mPackages.valueAt(packageNum); - if (pkg.applicationInfo.targetSdkVersion >= splitPerm.targetSdk + if (pkg.applicationInfo.targetSdkVersion >= splitPerm.getTargetSdk() || !pkg.requestedPermissions.contains(rootPerm)) { continue; } @@ -2938,7 +2941,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } - final String[] newPerms = splitPerm.newPerms; + final String[] newPerms = splitPerm.getNewPermissions(); final int numNewPerms = newPerms.length; for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) { @@ -15336,7 +15339,9 @@ public class PackageManagerService extends IPackageManager.Stub // This needs to be done before invoking dexopt so that any install-time profile // can be used for optimizations. mArtManagerService.prepareAppProfiles( - pkg, resolveUserIds(reconciledPkg.installForUser.getIdentifier())); + pkg, + resolveUserIds(reconciledPkg.installForUser.getIdentifier()), + /* updateReferenceProfileContent= */ true); // Check whether we need to dexopt the app. // @@ -21184,8 +21189,18 @@ public class PackageManagerService extends IPackageManager.Stub // // We also have to cover non system users because we do not call the usual install package // methods for them. + // + // NOTE: in order to speed up first boot time we only create the current profile and do not + // update the content of the reference profile. A system image should already be configured + // with the right profile keys and the profiles for the speed-profile prebuilds should + // already be copied. That's done in #performDexOptUpgrade. + // + // TODO(calin, mathieuc): We should use .dm files for prebuilds profiles instead of + // manually copying them in #performDexOptUpgrade. When we do that we should have a more + // granular check here and only update the existing profiles. if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) { - mArtManagerService.prepareAppProfiles(pkg, userId); + mArtManagerService.prepareAppProfiles(pkg, userId, + /* updateReferenceProfileContent= */ false); } if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) { diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 21daa39e05e0..910ea738acd8 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -389,7 +389,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { * - create the current primary profile to save time at app startup time. * - copy the profiles from the associated dex metadata file to the reference profile. */ - public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user) { + public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user, + boolean updateReferenceProfileContent) { final int appId = UserHandle.getAppId(pkg.applicationInfo.uid); if (user < 0) { Slog.wtf(TAG, "Invalid user id: " + user); @@ -404,8 +405,14 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) { String codePath = codePathsProfileNames.keyAt(i); String profileName = codePathsProfileNames.valueAt(i); - File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath)); - String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath(); + String dexMetadataPath = null; + // Passing the dex metadata file to the prepare method will update the reference + // profile content. As such, we look for the dex metadata file only if we need to + // perform an update. + if (updateReferenceProfileContent) { + File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath)); + dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath(); + } synchronized (mInstaller) { boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId, profileName, codePath, dexMetadataPath); @@ -423,9 +430,10 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { /** * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}. */ - public void prepareAppProfiles(PackageParser.Package pkg, int[] user) { + public void prepareAppProfiles(PackageParser.Package pkg, int[] user, + boolean updateReferenceProfileContent) { for (int i = 0; i < user.length; i++) { - prepareAppProfiles(pkg, user[i]); + prepareAppProfiles(pkg, user[i], updateReferenceProfileContent); } } diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java index e3e15907939a..602ce3b2a0c1 100644 --- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java +++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java @@ -93,7 +93,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { @GuardedBy("mPackageUseInfoMap") private final Map<String, PackageUseInfo> mPackageUseInfoMap; - public PackageDexUsage() { + /* package */ PackageDexUsage() { super("package-dex-usage.list", "PackageDexUsage_DiskWriter", /*lock*/ false); mPackageUseInfoMap = new HashMap<>(); } @@ -116,7 +116,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { * @return true if the dex load constitutes new information, or false if this information * has been seen before. */ - public boolean record(String owningPackageName, String dexPath, int ownerUserId, + /* package */ boolean record(String owningPackageName, String dexPath, int ownerUserId, String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit, String loadingPackageName, String classLoaderContext) { if (!PackageManagerServiceUtils.checkISA(loaderIsa)) { @@ -193,7 +193,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { * Convenience method for sync reads which does not force the user to pass a useless * (Void) null. */ - public void read() { + /* package */ void read() { read((Void) null); } @@ -558,7 +558,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { * Remove the usage data associated with package {@code packageName}. * @return true if the package usage was found and removed successfully. */ - public boolean removePackage(String packageName) { + /* package */ boolean removePackage(String packageName) { synchronized (mPackageUseInfoMap) { return mPackageUseInfoMap.remove(packageName) != null; } @@ -653,11 +653,12 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { return packages; } - public void clear() { + /* package */ void clear() { synchronized (mPackageUseInfoMap) { mPackageUseInfoMap.clear(); } } + // Creates a deep copy of the class' mPackageUseInfoMap. private Map<String, PackageUseInfo> clonePackageUseInfoMap() { Map<String, PackageUseInfo> clone = new HashMap<>(); @@ -679,7 +680,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { throw new IllegalArgumentException("Unknown bool encoding: " + bool); } - public String dump() { + /* package */ String dump() { StringWriter sw = new StringWriter(); write(sw); return sw.toString(); diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 3c9dd636e12b..6f644dd70339 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -34,7 +34,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal.PackagesProvider; import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider; -import android.content.pm.PackageParser; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.media.RingtoneManager; @@ -48,6 +47,7 @@ import android.os.Message; import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManager; +import android.permission.PermissionManager; import android.print.PrintManager; import android.provider.CalendarContract; import android.provider.ContactsContract; @@ -1024,15 +1024,17 @@ public final class DefaultPermissionGrantPolicy { ApplicationInfo applicationInfo = pkg.applicationInfo; // Automatically attempt to grant split permissions to older APKs - final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length; + final List<PermissionManager.SplitPermissionInfo> splitPermissions = + mContext.getSystemService(PermissionManager.class).getSplitPermissions(); + final int numSplitPerms = splitPermissions.size(); for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) { - final PackageParser.SplitPermissionInfo splitPerm = - PackageParser.SPLIT_PERMISSIONS[splitPermNum]; + final PermissionManager.SplitPermissionInfo splitPerm = + splitPermissions.get(splitPermNum); if (applicationInfo != null - && applicationInfo.targetSdkVersion < splitPerm.targetSdk - && permissionsWithoutSplits.contains(splitPerm.rootPerm)) { - Collections.addAll(permissions, splitPerm.newPerms); + && applicationInfo.targetSdkVersion < splitPerm.getTargetSdk() + && permissionsWithoutSplits.contains(splitPerm.getRootPermission())) { + Collections.addAll(permissions, splitPerm.getNewPermissions()); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 91fd8d0e960d..2557f46ba34b 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -533,8 +533,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { GlobalActions mGlobalActions; Handler mHandler; - WindowState mLastInputMethodWindow = null; - WindowState mLastInputMethodTargetWindow = null; // FIXME This state is shared between the input reader and handler thread. // Technically it's broken and buggy but it has been like this for many years @@ -2830,7 +2828,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Assumes it's safe to show starting windows of launched apps while // the keyguard is being hidden. This is okay because starting windows never show // secret information. - if (mKeyguardOccluded) { + // TODO(b/113840485): Occluded may not only happen on default display + if (displayId == DEFAULT_DISPLAY && mKeyguardOccluded) { windowFlags |= FLAG_SHOW_WHEN_LOCKED; } } @@ -4732,12 +4731,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } final WindowManager.LayoutParams attrs = win.getAttrs(); final boolean isDefaultDisplay = win.isDefaultDisplay(); - final boolean needsToOffsetInputMethodTarget = - (win == mLastInputMethodTargetWindow) && (mLastInputMethodWindow != null); - if (needsToOffsetInputMethodTarget) { - if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state"); - offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames); - } final int type = attrs.type; final int fl = PolicyControl.getWindowFlags(win, attrs); @@ -5191,7 +5184,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { // can't appear underneath them. if (type == TYPE_INPUT_METHOD && win.isVisibleLw() && !win.getGivenInsetsPendingLw()) { - setLastInputMethodWindowLw(null, null); offsetInputMethodWindowLw(win, displayFrames); } if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw() @@ -7822,12 +7814,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public void setLastInputMethodWindowLw(WindowState ime, WindowState target) { - mLastInputMethodWindow = ime; - mLastInputMethodTargetWindow = target; - } - - @Override public void setDismissImeOnBackKeyPressed(boolean newValue) { mDismissImeOnBackKeyPressed = newValue; } @@ -7845,7 +7831,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (statusBar != null) { statusBar.setCurrentUser(newUserId); } - setLastInputMethodWindowLw(null, null); } @Override @@ -8020,14 +8005,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream); pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen); pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken); - if (mLastInputMethodWindow != null) { - pw.print(prefix); pw.print("mLastInputMethodWindow="); - pw.println(mLastInputMethodWindow); - } - if (mLastInputMethodTargetWindow != null) { - pw.print(prefix); pw.print("mLastInputMethodTargetWindow="); - pw.println(mLastInputMethodTargetWindow); - } if (mStatusBar != null) { pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar); pw.print(" isStatusBarKeyguard="); diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 1fcdd63c4488..27ab3efb3fb1 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -1536,13 +1536,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public void lockNow(Bundle options); /** - * Set the last used input method window state. This state is used to make IME transition - * smooth. - * @hide - */ - public void setLastInputMethodWindowLw(WindowState ime, WindowState target); - - /** * An internal callback (from InputMethodManagerService) to notify a state change regarding * whether the back key should dismiss the software keyboard (IME) or not. * diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 68e636a2afc2..5adc248cee79 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -190,6 +190,8 @@ public class Notifier { try { mBatteryStats.noteInteractive(true); } catch (RemoteException ex) { } + StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED, + StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON); } /** @@ -401,6 +403,9 @@ public class Notifier { try { mBatteryStats.noteInteractive(interactive); } catch (RemoteException ex) { } + StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED, + interactive ? StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON : + StatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF); // Handle early behaviors. mInteractive = interactive; diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index bfa03ca9f2be..c6e6449313c0 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -966,6 +966,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { e.writeLong(processMemoryState.rssInBytes); e.writeLong(processMemoryState.cacheInBytes); e.writeLong(processMemoryState.swapInBytes); + e.writeLong(processMemoryState.rssHighWatermarkInBytes); pulledData.add(e); } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 7d2fc15a28c5..bcf9212464db 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -22,6 +22,7 @@ import android.app.AppProtoEnums; import android.app.IActivityManager; import android.app.IApplicationThread; import android.content.ComponentName; +import android.content.IIntentSender; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; @@ -32,8 +33,12 @@ import android.service.voice.IVoiceInteractionSession; import android.util.SparseIntArray; import com.android.internal.app.IVoiceInteractor; +import com.android.server.am.PendingIntentRecord; +import com.android.server.am.SafeActivityOptions; +import com.android.server.am.TaskRecord; import com.android.server.am.WindowProcessController; +import java.lang.ref.WeakReference; import java.util.List; /** @@ -178,6 +183,27 @@ public abstract class ActivityTaskManagerInternal { int userId, Intent[] intents, Bundle bOptions); /** + * Start intents as a package. + * + * @param uid Make a call as if this UID did. + * @param callingPackage Make a call as if this package did. + * @param intents Intents to start. + * @param userId Start the intents on this user. + * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID. + * @param originatingPendingIntent PendingIntentRecord that originated this activity start or + * null if not originated by PendingIntent + */ + public abstract int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents, + String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId, + boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent); + + public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid, + String callingPackage, Intent intent, String resolvedType, IBinder resultTo, + String resultWho, int requestCode, int startFlags, SafeActivityOptions options, + int userId, TaskRecord inTask, String reason, boolean validateIncomingUser, + PendingIntentRecord originatingPendingIntent); + + /** * Start activity {@code intent} without calling user-id check. * * - DO NOT call it with the calling UID cleared. @@ -297,4 +323,13 @@ public abstract class ActivityTaskManagerInternal { * @param displayId The ID of the display showing the IME. */ public abstract void onImeWindowSetOnDisplay(int pid, int displayId); + + public abstract void sendActivityResult(int callingUid, IBinder activityToken, + String resultWho, int requestCode, int resultCode, Intent data); + public abstract void clearPendingResultForActivity( + IBinder activityToken, WeakReference<PendingIntentRecord> pir); + public abstract IIntentSender getIntentSender(int type, String packageName, + int callingUid, int userId, IBinder token, String resultWho, + int requestCode, Intent[] intents, String[] resolvedTypes, int flags, + Bundle bOptions); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index eaaf804d2cf9..236982fd76ac 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2652,6 +2652,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ void setInputMethodWindowLocked(WindowState win) { mInputMethodWindow = win; + // Update display configuration for IME process. + if (mInputMethodWindow != null) { + final int imePid = mInputMethodWindow.mSession.mPid; + mService.mAtmInternal.onImeWindowSetOnDisplay(imePid, + mInputMethodWindow.getDisplayId()); + } computeImeTarget(true /* updateImeTarget */); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index d92818ac611c..a9571be9599d 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -48,7 +48,6 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SU import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE; import static com.android.server.wm.WindowManagerService.logSurface; -import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED; import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION; import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING; @@ -91,7 +90,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1; private static final int SET_USER_ACTIVITY_TIMEOUT = 2; - private boolean mWallpaperForceHidingChanged = false; private Object mLastWindowFreezeSource = null; private Session mHoldScreen = null; private float mScreenBrightness = -1; @@ -626,18 +624,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { recentsAnimationController.checkAnimationReady(mWallpaperController); } - if (mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0 - && !mService.mAppTransition.isReady()) { - // At this point, there was a window with a wallpaper that was force hiding other - // windows behind it, but now it is going away. This may be simple -- just animate away - // the wallpaper and its window -- or it may be hard -- the wallpaper now needs to be - // shown behind something that was hidden. - defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT; - if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats( - "after animateAwayWallpaperLocked", defaultDisplay.pendingLayoutChanges); - } - mWallpaperForceHidingChanged = false; - if (mWallpaperMayChange) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting"); defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; @@ -961,10 +947,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { mWallpaperMayChange = true; doRequest = true; } - if ((bulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0) { - mWallpaperForceHidingChanged = true; - doRequest = true; - } if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) { mOrientationChangeComplete = false; } else { diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 793ffce2466e..a1d6ffd0249e 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -284,9 +284,6 @@ public class WindowAnimator { if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE) != 0) { builder.append(" WALLPAPER_MAY_CHANGE"); } - if ((bulkUpdateParams & WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED) != 0) { - builder.append(" FORCE_HIDING_CHANGED"); - } if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) { builder.append(" ORIENTATION_CHANGE_COMPLETE"); } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 27b623bc0ec3..5410676a49fc 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -340,20 +340,6 @@ public abstract class WindowManagerInternal { public abstract int getInputMethodWindowVisibleHeight(int displayId); /** - * Saves last input method window for transition. - * - * Note that it is assumed that this method is called only by InputMethodManagerService. - */ - public abstract void saveLastInputMethodWindowForTransition(); - - /** - * Clears last input method window for transition. - * - * Note that it is assumed that this method is called only by InputMethodManagerService. - */ - public abstract void clearLastInputMethodWindowForTransition(); - - /** * Notifies WindowManagerService that the current IME window status is being changed. * * <p>Only {@link com.android.server.inputmethod.InputMethodManagerService} is the expected and diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 017f667756bc..7caa7aedb873 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1414,7 +1414,6 @@ public class WindowManagerService extends IWindowManager.Stub win.mToken.addWindow(win); if (type == TYPE_INPUT_METHOD) { - win.mGivenInsetsPending = true; displayContent.setInputMethodWindowLocked(win); imMayMove = false; } else if (type == TYPE_INPUT_METHOD_DIALOG) { @@ -5670,11 +5669,6 @@ public class WindowManagerService extends IWindowManager.Stub mInputManagerCallback.freezeInputDispatchingLw(); - // Clear the last input window -- that is just used for - // clean transitions between IMEs, and if we are freezing - // the screen then the whole world is changing behind the scenes. - mPolicy.setLastInputMethodWindowLw(null, null); - if (mAppTransition.isTransitionSet()) { mAppTransition.freeze(); } @@ -7287,23 +7281,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void saveLastInputMethodWindowForTransition() { - synchronized (mWindowMap) { - final WindowState imeWindow = mRoot.getCurrentInputMethodWindow(); - if (imeWindow != null) { - mPolicy.setLastInputMethodWindowLw(imeWindow, mInputMethodTarget); - } - } - } - - @Override - public void clearLastInputMethodWindowForTransition() { - synchronized (mWindowMap) { - mPolicy.setLastInputMethodWindowLw(null, null); - } - } - - @Override public void updateInputMethodWindowStatus(@NonNull IBinder imeToken, boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) { mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 7161a70b08fe..f7c6d77b4163 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4535,7 +4535,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP updateSurfacePosition(getPendingTransaction()); } - private void updateSurfacePosition(Transaction t) { + @VisibleForTesting + void updateSurfacePosition(Transaction t) { if (mSurfaceControl == null) { return; } diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index d6f9ac3a3ab9..c8d1a8b14e82 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -92,9 +92,8 @@ class WindowSurfacePlacer { static final int SET_UPDATE_ROTATION = 1 << 0; static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1; - static final int SET_FORCE_HIDING_CHANGED = 1 << 2; - static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 3; - static final int SET_WALLPAPER_ACTION_PENDING = 1 << 4; + static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 2; + static final int SET_WALLPAPER_ACTION_PENDING = 1 << 3; private boolean mTraversalScheduled; private int mDeferDepth = 0; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index fb95f5972cbb..b8241d03a78e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -65,6 +65,7 @@ import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.widget.ILockSettings; import com.android.server.am.ActivityManagerService; import com.android.server.am.ActivityTaskManagerService; +import com.android.server.appbinding.AppBindingService; import com.android.server.audio.AudioService; import com.android.server.biometrics.BiometricService; import com.android.server.broadcastradio.BroadcastRadioService; @@ -1710,6 +1711,10 @@ public final class SystemServer { traceEnd(); } + traceBeginAndSlog("AppServiceManager"); + mSystemServiceManager.startService(AppBindingService.Lifecycle.class); + traceEnd(); + // It is now time to start up the app processes... traceBeginAndSlog("MakeVibratorServiceReady"); diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk index 8b5977144db1..78c0be42a448 100644 --- a/services/robotests/Android.mk +++ b/services/robotests/Android.mk @@ -61,6 +61,7 @@ LOCAL_SRC_FILES := \ $(call all-Iaidl-files-under, $(INTERNAL_BACKUP)) \ $(call all-java-files-under, ../../core/java/android/app/backup) \ $(call all-Iaidl-files-under, ../../core/java/android/app/backup) \ + $(call all-java-files-under, ../../core/java/android/util/proto) \ ../../core/java/android/content/pm/PackageInfo.java \ ../../core/java/android/app/IBackupAgent.aidl \ ../../core/java/android/util/KeyValueSettingObserver.java \ diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java index 57ebbfcf8dd6..de915ab0829c 100644 --- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java @@ -122,6 +122,13 @@ public class BackupManagerServiceTest { ShadowAppBackupUtils.reset(); } + @Test + public void testMoreDebug_isFalse() throws Exception { + boolean moreDebug = BackupManagerService.MORE_DEBUG; + + assertThat(moreDebug).isFalse(); + } + /* Tests for destination string */ @Test diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java new file mode 100644 index 000000000000..383bf1d73416 --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkListingTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.encryption.chunk; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; +import com.android.internal.util.Preconditions; +import com.android.server.testing.FrameworkRobolectricTestRunner; +import com.android.server.testing.SystemLoaderPackages; +import com.google.common.base.Charsets; +import java.io.ByteArrayInputStream; +import java.util.Arrays; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +@RunWith(FrameworkRobolectricTestRunner.class) +@Config(manifest = Config.NONE, sdk = 26) +// Include android.util.proto in addition to classes under test because the latest versions of +// android.util.proto.Proto{Input|Output}Stream are not part of Robolectric. +@SystemLoaderPackages({"com.android.server.backup", "android.util.proto"}) +@Presubmit +public class ChunkListingTest { + private static final String CHUNK_A = "CHUNK_A"; + private static final String CHUNK_B = "CHUNK_B"; + private static final String CHUNK_C = "CHUNK_C"; + + private static final int CHUNK_A_LENGTH = 256; + private static final int CHUNK_B_LENGTH = 1024; + private static final int CHUNK_C_LENGTH = 4055; + + private ChunkHash mChunkHashA; + private ChunkHash mChunkHashB; + private ChunkHash mChunkHashC; + + @Before + public void setUp() throws Exception { + mChunkHashA = getHash(CHUNK_A); + mChunkHashB = getHash(CHUNK_B); + mChunkHashC = getHash(CHUNK_C); + } + + @Test + public void testHasChunk_whenChunkInListing_returnsTrue() throws Exception { + byte[] chunkListingProto = + createChunkListingProto( + new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, + new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); + ChunkListing chunkListing = + ChunkListing.readFromProto( + new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); + + boolean chunkAInList = chunkListing.hasChunk(mChunkHashA); + boolean chunkBInList = chunkListing.hasChunk(mChunkHashB); + boolean chunkCInList = chunkListing.hasChunk(mChunkHashC); + + assertThat(chunkAInList).isTrue(); + assertThat(chunkBInList).isTrue(); + assertThat(chunkCInList).isTrue(); + } + + @Test + public void testHasChunk_whenChunkNotInListing_returnsFalse() throws Exception { + byte[] chunkListingProto = + createChunkListingProto( + new ChunkHash[] {mChunkHashA, mChunkHashB}, + new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH}); + ChunkListing chunkListing = + ChunkListing.readFromProto( + new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); + ChunkHash chunkHashEmpty = getHash(""); + + boolean chunkCInList = chunkListing.hasChunk(mChunkHashC); + boolean emptyChunkInList = chunkListing.hasChunk(chunkHashEmpty); + + assertThat(chunkCInList).isFalse(); + assertThat(emptyChunkInList).isFalse(); + } + + @Test + public void testGetChunkEntry_returnsEntryWithCorrectLength() throws Exception { + byte[] chunkListingProto = + createChunkListingProto( + new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, + new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); + ChunkListing chunkListing = + ChunkListing.readFromProto( + new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); + + ChunkListing.Entry entryA = chunkListing.getChunkEntry(mChunkHashA); + ChunkListing.Entry entryB = chunkListing.getChunkEntry(mChunkHashB); + ChunkListing.Entry entryC = chunkListing.getChunkEntry(mChunkHashC); + + assertThat(entryA.getLength()).isEqualTo(CHUNK_A_LENGTH); + assertThat(entryB.getLength()).isEqualTo(CHUNK_B_LENGTH); + assertThat(entryC.getLength()).isEqualTo(CHUNK_C_LENGTH); + } + + @Test + public void testGetChunkEntry_returnsEntryWithCorrectStart() throws Exception { + byte[] chunkListingProto = + createChunkListingProto( + new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, + new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); + ChunkListing chunkListing = + ChunkListing.readFromProto( + new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); + + ChunkListing.Entry entryA = chunkListing.getChunkEntry(mChunkHashA); + ChunkListing.Entry entryB = chunkListing.getChunkEntry(mChunkHashB); + ChunkListing.Entry entryC = chunkListing.getChunkEntry(mChunkHashC); + + assertThat(entryA.getStart()).isEqualTo(0); + assertThat(entryB.getStart()).isEqualTo(CHUNK_A_LENGTH); + assertThat(entryC.getStart()).isEqualTo(CHUNK_A_LENGTH + CHUNK_B_LENGTH); + } + + @Test + public void testGetChunkEntry_returnsNullForNonExistentChunk() throws Exception { + byte[] chunkListingProto = + createChunkListingProto( + new ChunkHash[] {mChunkHashA, mChunkHashB}, + new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH}); + ChunkListing chunkListing = + ChunkListing.readFromProto( + new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); + + ChunkListing.Entry chunkEntryNonexistentChunk = chunkListing.getChunkEntry(mChunkHashC); + + assertThat(chunkEntryNonexistentChunk).isNull(); + } + + @Test + public void testReadFromProto_whenEmptyProto_returnsChunkListingWith0Chunks() throws Exception { + ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {})); + + ChunkListing chunkListing = ChunkListing.readFromProto(emptyProto); + + assertThat(chunkListing.getChunkCount()).isEqualTo(0); + } + + @Test + public void testReadFromProto_returnsChunkListingWithCorrectSize() throws Exception { + byte[] chunkListingProto = + createChunkListingProto( + new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC}, + new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH}); + + ChunkListing chunkListing = + ChunkListing.readFromProto( + new ProtoInputStream(new ByteArrayInputStream(chunkListingProto))); + + assertThat(chunkListing.getChunkCount()).isEqualTo(3); + } + + private byte[] createChunkListingProto(ChunkHash[] hashes, int[] lengths) { + Preconditions.checkArgument(hashes.length == lengths.length); + ProtoOutputStream outputStream = new ProtoOutputStream(); + + for (int i = 0; i < hashes.length; ++i) { + writeToProtoOutputStream(outputStream, hashes[i], lengths[i]); + } + outputStream.flush(); + + return outputStream.getBytes(); + } + + private void writeToProtoOutputStream(ProtoOutputStream out, ChunkHash chunkHash, int length) { + long token = out.start(ChunksMetadataProto.ChunkListing.CHUNKS); + out.write(ChunksMetadataProto.Chunk.HASH, chunkHash.getHash()); + out.write(ChunksMetadataProto.Chunk.LENGTH, length); + out.end(token); + } + + private ChunkHash getHash(String name) { + return new ChunkHash( + Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES)); + } +} diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java new file mode 100644 index 000000000000..1dd7dc834f9e --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.encryption.chunk; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; +import com.android.server.testing.FrameworkRobolectricTestRunner; +import com.android.server.testing.SystemLoaderPackages; +import com.google.common.base.Charsets; +import java.io.ByteArrayInputStream; +import java.util.Arrays; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +@RunWith(FrameworkRobolectricTestRunner.class) +@Config(manifest = Config.NONE, sdk = 26) +// Include android.util.proto in addition to classes under test because the latest versions of +// android.util.proto.Proto{Input|Output}Stream are not part of Robolectric. +@SystemLoaderPackages({"com.android.server.backup", "android.util.proto"}) +@Presubmit +public class ChunkTest { + private static final String CHUNK_A = "CHUNK_A"; + private static final int CHUNK_A_LENGTH = 256; + + private ChunkHash mChunkHashA; + + @Before + public void setUp() throws Exception { + mChunkHashA = getHash(CHUNK_A); + } + + @Test + public void testReadFromProto_readsCorrectly() throws Exception { + ProtoOutputStream out = new ProtoOutputStream(); + out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash()); + out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH); + out.flush(); + byte[] protoBytes = out.getBytes(); + + Chunk chunk = + Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes))); + + assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash()); + assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH); + } + + @Test + public void testReadFromProto_whenFieldsWrittenInReversedOrder_readsCorrectly() + throws Exception { + ProtoOutputStream out = new ProtoOutputStream(); + // Write fields of Chunk proto in reverse order. + out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH); + out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash()); + out.flush(); + byte[] protoBytes = out.getBytes(); + + Chunk chunk = + Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes))); + + assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash()); + assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH); + } + + @Test + public void testReadFromProto_whenEmptyProto_returnsEmptyHash() throws Exception { + ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {})); + + Chunk chunk = Chunk.readFromProto(emptyProto); + + assertThat(chunk.getHash()).asList().hasSize(0); + assertThat(chunk.getLength()).isEqualTo(0); + } + + @Test + public void testReadFromProto_whenOnlyHashSet_returnsChunkWithOnlyHash() throws Exception { + ProtoOutputStream out = new ProtoOutputStream(); + out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash()); + out.flush(); + byte[] protoBytes = out.getBytes(); + + Chunk chunk = + Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes))); + + assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash()); + assertThat(chunk.getLength()).isEqualTo(0); + } + + @Test + public void testReadFromProto_whenOnlyLengthSet_returnsChunkWithOnlyLength() throws Exception { + ProtoOutputStream out = new ProtoOutputStream(); + out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH); + out.flush(); + byte[] protoBytes = out.getBytes(); + + Chunk chunk = + Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes))); + + assertThat(chunk.getHash()).isEqualTo(new byte[] {}); + assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH); + } + + private ChunkHash getHash(String name) { + return new ChunkHash( + Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES)); + } +} diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java b/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java new file mode 100644 index 000000000000..1cd1528b930c --- /dev/null +++ b/services/robotests/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.encryption.chunk; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; +import com.android.server.testing.FrameworkRobolectricTestRunner; +import com.android.server.testing.SystemLoaderPackages; +import com.google.common.primitives.Bytes; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +@RunWith(FrameworkRobolectricTestRunner.class) +@Config(manifest = Config.NONE, sdk = 26) +@SystemLoaderPackages({"com.android.server.backup"}) +@Presubmit +public class EncryptedChunkOrderingTest { + private static final byte[] TEST_BYTE_ARRAY_1 = new byte[] {1, 2, 3, 4, 5}; + private static final byte[] TEST_BYTE_ARRAY_2 = new byte[] {5, 4, 3, 2, 1}; + + @Test + public void testEncryptedChunkOrdering_returnsValue() { + EncryptedChunkOrdering encryptedChunkOrdering = + EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1); + + byte[] bytes = encryptedChunkOrdering.encryptedChunkOrdering(); + + assertThat(bytes) + .asList() + .containsExactlyElementsIn(Bytes.asList(TEST_BYTE_ARRAY_1)) + .inOrder(); + } + + @Test + public void testEquals() { + EncryptedChunkOrdering chunkOrdering1 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1); + EncryptedChunkOrdering equalChunkOrdering1 = + EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1); + EncryptedChunkOrdering chunkOrdering2 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_2); + + assertThat(chunkOrdering1).isEqualTo(equalChunkOrdering1); + assertThat(chunkOrdering1).isNotEqualTo(chunkOrdering2); + } + + @Test + public void testHashCode() { + EncryptedChunkOrdering chunkOrdering1 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1); + EncryptedChunkOrdering equalChunkOrdering1 = + EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_1); + EncryptedChunkOrdering chunkOrdering2 = EncryptedChunkOrdering.create(TEST_BYTE_ARRAY_2); + + int hash1 = chunkOrdering1.hashCode(); + int equalHash1 = equalChunkOrdering1.hashCode(); + int hash2 = chunkOrdering2.hashCode(); + + assertThat(hash1).isEqualTo(equalHash1); + assertThat(hash1).isNotEqualTo(hash2); + } +} diff --git a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java new file mode 100644 index 000000000000..373033500cde --- /dev/null +++ b/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.keyvalue; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import com.android.server.testing.FrameworkRobolectricTestRunner; +import com.android.server.testing.SystemLoaderPackages; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import java.io.IOException; + +@RunWith(FrameworkRobolectricTestRunner.class) +@Config(manifest = Config.NONE, sdk = 26) +@SystemLoaderPackages({"com.android.server.backup"}) +@Presubmit +public class AgentExceptionTest { + @Test + public void testTransitory_isTransitory() throws Exception { + AgentException exception = AgentException.transitory(); + + assertThat(exception.isTransitory()).isTrue(); + } + + @Test + public void testTransitory_withCause() throws Exception { + Exception cause = new IOException(); + + AgentException exception = AgentException.transitory(cause); + + assertThat(exception.isTransitory()).isTrue(); + assertThat(exception.getCause()).isEqualTo(cause); + } + + @Test + public void testPermanent_isNotTransitory() throws Exception { + AgentException exception = AgentException.permanent(); + + assertThat(exception.isTransitory()).isFalse(); + } + + @Test + public void testPermanent_withCause() throws Exception { + Exception cause = new IOException(); + + AgentException exception = AgentException.permanent(cause); + + assertThat(exception.isTransitory()).isFalse(); + assertThat(exception.getCause()).isEqualTo(cause); + } +} diff --git a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java new file mode 100644 index 000000000000..5ea74f163bd6 --- /dev/null +++ b/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.keyvalue; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import com.android.server.testing.FrameworkRobolectricTestRunner; +import com.android.server.testing.SystemLoaderPackages; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import java.io.IOException; + +@RunWith(FrameworkRobolectricTestRunner.class) +@Config(manifest = Config.NONE, sdk = 26) +@SystemLoaderPackages({"com.android.server.backup"}) +@Presubmit +public class BackupExceptionTest { + @Test + public void testConstructor_passesCause() { + Exception cause = new IOException(); + + Exception exception = new BackupException(cause); + + assertThat(exception.getCause()).isEqualTo(cause); + } +} diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java index 21b90f1c2281..31e8333fea89 100644 --- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java +++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java @@ -62,6 +62,13 @@ public class KeyValueBackupReporterTest { } @Test + public void testMoreDebug_isFalse() throws Exception { + boolean moreDebug = KeyValueBackupReporter.MORE_DEBUG; + + assertThat(moreDebug).isFalse(); + } + + @Test public void testOnNewThread_logsCorrectly() throws Exception { KeyValueBackupReporter.onNewThread("foo"); @@ -81,39 +88,4 @@ public class KeyValueBackupReporterTest { assertThat(observer).isEqualTo(mObserver); } - - @Test - public void testOnRevertTask_logsCorrectly() throws Exception { - setMoreDebug(true); - - mReporter.onRevertTask(); - - assertLogcat(TAG, Log.INFO); - } - - @Test - public void testOnRemoteCallReturned_logsCorrectly() throws Exception { - setMoreDebug(true); - - mReporter.onRemoteCallReturned(RemoteResult.of(3), "onFoo()"); - - assertLogcat(TAG, Log.VERBOSE); - ShadowLog.LogItem log = ShadowLog.getLogsForTag(TAG).get(0); - assertThat(log.msg).contains("onFoo()"); - assertThat(log.msg).contains("3"); - } - - /** - * HACK: We actually want {@link KeyValueBackupReporter#MORE_DEBUG} to be a constant to be able - * to strip those lines at build time. So, we have to do this to test :( - */ - private static void setMoreDebug(boolean value) - throws NoSuchFieldException, IllegalAccessException { - if (KeyValueBackupReporter.MORE_DEBUG == value) { - return; - } - Field moreDebugField = KeyValueBackupReporter.class.getDeclaredField("MORE_DEBUG"); - moreDebugField.setAccessible(true); - moreDebugField.set(null, value); - } } diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index b4bc9d199cb0..fb57d68082a2 100644 --- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -155,9 +155,7 @@ import java.util.List; import java.util.concurrent.TimeoutException; import java.util.stream.Stream; -// TODO: When returning to RUNNING_QUEUE vs FINAL, RUNNING_QUEUE sets status = OK. Why? Verify? -// TODO: Check queue in general, behavior w/ multiple packages -// TODO: Test PM invocation +// TODO: Test agents timing out @RunWith(FrameworkRobolectricTestRunner.class) @Config( manifest = Config.NONE, @@ -370,6 +368,47 @@ public class KeyValueBackupTaskTest { } @Test + public void testRunTask_whenOnePackage_cleansUpPmFiles() throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgent(PACKAGE_1); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + + runTask(task); + + assertCleansUpFiles(mTransport, PM_PACKAGE); + } + + @Test + public void testRunTask_whenTransportReturnsTransportErrorForPm_cleansUpPmFiles() + throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + when(transportMock.transport.performBackup( + argThat(packageInfo(PM_PACKAGE)), any(), anyInt())) + .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED); + setUpAgent(PACKAGE_1); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + + runTask(task); + + assertCleansUpFiles(mTransport, PM_PACKAGE); + } + + @Test + public void testRunTask_whenTransportReturnsTransportErrorForPm_resetsBackupState() + throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + when(transportMock.transport.performBackup( + argThat(packageInfo(PM_PACKAGE)), any(), anyInt())) + .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED); + setUpAgent(PACKAGE_1); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + + runTask(task); + + verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile()); + } + + @Test public void testRunTask_whenOnePackage_updatesBookkeeping() throws Exception { // Transport has to be initialized to not reset current token TransportMock transportMock = setUpInitializedTransport(mTransport); @@ -418,7 +457,7 @@ public class KeyValueBackupTaskTest { public void testRunTask_whenNonPmPackageAndNonIncremental_doesNotBackUpPm() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); - PackageManagerBackupAgent pmAgent = spy(createPmAgent()); + BackupAgent pmAgent = spy(createPmAgent()); when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1); @@ -431,7 +470,7 @@ public class KeyValueBackupTaskTest { public void testRunTask_whenNonPmPackageAndPmAndNonIncremental_backsUpPm() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); - PackageManagerBackupAgent pmAgent = spy(createPmAgent()); + BackupAgent pmAgent = spy(createPmAgent()); when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1, PM_PACKAGE); @@ -445,7 +484,7 @@ public class KeyValueBackupTaskTest { public void testRunTask_whenNonPmPackageAndIncremental_backsUpPm() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); - PackageManagerBackupAgent pmAgent = spy(createPmAgent()); + BackupAgent pmAgent = spy(createPmAgent()); when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, false, PACKAGE_1); @@ -529,6 +568,35 @@ public class KeyValueBackupTaskTest { } @Test + public void testRunTask_whenPackageUnknown() throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + // Not calling setUpAgent() for PACKAGE_1 + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + + runTask(task); + + verify(transportMock.transport, never()) + .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()); + verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_PACKAGE_NOT_FOUND); + verify(mObserver).backupFinished(SUCCESS); + assertBackupNotPendingFor(PACKAGE_1); + } + + @Test + public void testRunTask_whenFirstPackageUnknown_callsTransportForSecondPackage() + throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + // Not calling setUpAgent() for PACKAGE_1 + setUpAgentWithData(PACKAGE_2); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2); + + runTask(task); + + verify(transportMock.transport) + .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt()); + } + + @Test public void testRunTask_whenPackageNotEligibleForBackup() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); AgentMock agentMock = setUpAgentWithData(PACKAGE_1.backupNotAllowed()); @@ -545,6 +613,19 @@ public class KeyValueBackupTaskTest { } @Test + public void testRunTask_whenFirstPackageNotEligibleForBackup_callsTransportForSecondPackage() + throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgentsWithData(PACKAGE_1.backupNotAllowed(), PACKAGE_2); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2); + + runTask(task); + + verify(transportMock.transport) + .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt()); + } + + @Test public void testRunTask_whenPackageDoesFullBackup() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); PackageData packageData = fullBackupPackage(1); @@ -561,6 +642,20 @@ public class KeyValueBackupTaskTest { } @Test + public void testRunTask_whenFirstPackageDoesFullBackup_callsTransportForSecondPackage() + throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + PackageData packageData = fullBackupPackage(1); + setUpAgentsWithData(packageData, PACKAGE_2); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, packageData, PACKAGE_2); + + runTask(task); + + verify(transportMock.transport) + .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt()); + } + + @Test public void testRunTask_whenPackageIsStopped() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); AgentMock agentMock = setUpAgentWithData(PACKAGE_1.stopped()); @@ -575,18 +670,16 @@ public class KeyValueBackupTaskTest { } @Test - public void testRunTask_whenPackageUnknown() throws Exception { + public void testRunTask_whenFirstPackageIsStopped_callsTransportForSecondPackage() + throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); - // Not calling setUpAgent() - KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + setUpAgentsWithData(PACKAGE_1.stopped(), PACKAGE_2); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2); runTask(task); - verify(transportMock.transport, never()) - .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()); - verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_PACKAGE_NOT_FOUND); - verify(mObserver).backupFinished(SUCCESS); - assertBackupNotPendingFor(PACKAGE_1); + verify(transportMock.transport) + .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt()); } @Test @@ -629,6 +722,7 @@ public class KeyValueBackupTaskTest { verify(mBackupManagerService).setWorkSource(null); verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE); verify(mObserver).backupFinished(BackupManager.SUCCESS); + assertBackupPendingFor(PACKAGE_1); } @Test @@ -645,6 +739,7 @@ public class KeyValueBackupTaskTest { verify(mBackupManagerService).setWorkSource(null); verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE); verify(mObserver).backupFinished(BackupManager.SUCCESS); + assertBackupPendingFor(PACKAGE_1); } @Test @@ -798,7 +893,7 @@ public class KeyValueBackupTaskTest { runTask(task); - assertBackupNotPendingFor(PACKAGE_1); + assertBackupPendingFor(PACKAGE_1); } @Test @@ -1140,6 +1235,38 @@ public class KeyValueBackupTaskTest { } @Test + public void testRunTask_whenPmAgentWritesData_callsTransportPerformBackupWithAgentData() + throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgent(PACKAGE_1); + Path backupDataPath = createTemporaryFile(); + when(transportMock.transport.performBackup( + argThat(packageInfo(PM_PACKAGE)), any(), anyInt())) + .then(copyBackupDataTo(backupDataPath)); + BackupAgent pmAgent = spy(createPmAgent()); + when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + agentOnBackupDo( + pmAgent, + (oldState, dataOutput, newState) -> { + writeData(dataOutput, "key1", "data1".getBytes()); + writeData(dataOutput, "key2", "data2".getBytes()); + writeState(newState, "newState".getBytes()); + }); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + + runTask(task); + + verify(transportMock.transport) + .performBackup(argThat(packageInfo(PM_PACKAGE)), any(), anyInt()); + try (FileInputStream inputStream = new FileInputStream(backupDataPath.toFile())) { + BackupDataInput backupData = new BackupDataInput(inputStream.getFD()); + assertDataHasKeyValue(backupData, "key1", "data1".getBytes()); + assertDataHasKeyValue(backupData, "key2", "data2".getBytes()); + assertThat(backupData.readNextHeader()).isFalse(); + } + } + + @Test public void testRunTask_whenPerformBackupSucceeds_callsTransportFinishBackup() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); @@ -1176,6 +1303,50 @@ public class KeyValueBackupTaskTest { } @Test + public void testRunTask_whenFinishBackupSucceedsForPm_cleansUp() throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgent(PACKAGE_1); + when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK); + BackupAgent pmAgent = spy(createPmAgent()); + when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + agentOnBackupDo( + pmAgent, + (oldState, dataOutput, newState) -> { + writeData(dataOutput, "key", "data".getBytes()); + writeState(newState, "newState".getBytes()); + }); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + + runTask(task); + + assertThat(Files.readAllBytes(getStateFile(mTransport, PM_PACKAGE))) + .isEqualTo("newState".getBytes()); + assertCleansUpFiles(mTransport, PM_PACKAGE); + // We don't unbind PM + verify(mBackupManagerService, never()).unbindAgent(argThat(applicationInfo(PM_PACKAGE))); + } + + @Test + public void testRunTask_whenFinishBackupSucceedsForPm_doesNotUnbindPm() throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgent(PACKAGE_1); + when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK); + BackupAgent pmAgent = spy(createPmAgent()); + when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + agentOnBackupDo( + pmAgent, + (oldState, dataOutput, newState) -> { + writeData(dataOutput, "key", "data".getBytes()); + writeState(newState, "newState".getBytes()); + }); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + + runTask(task); + + verify(mBackupManagerService, never()).unbindAgent(argThat(applicationInfo(PM_PACKAGE))); + } + + @Test public void testRunTask_whenFinishBackupSucceeds_logsBackupPackageEvent() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); @@ -1354,6 +1525,7 @@ public class KeyValueBackupTaskTest { public void testRunTask_whenTransportReturnsQuotaExceeded_updatesBookkeeping() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgentWithData(PACKAGE_1); when(transportMock.transport.performBackup( argThat(packageInfo(PACKAGE_1)), any(), anyInt())) .thenReturn(BackupTransport.TRANSPORT_QUOTA_EXCEEDED); @@ -1701,9 +1873,9 @@ public class KeyValueBackupTaskTest { } @Test - public void testRunTask_whenPmAgentFails() throws Exception { + public void testRunTask_whenPmAgentFails_reportsCorrectly() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); - PackageManagerBackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); + BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); @@ -1718,6 +1890,75 @@ public class KeyValueBackupTaskTest { } @Test + public void testRunTask_whenPmAgentFails_revertsTask() throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgent(PACKAGE_1); + BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); + when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + + runTask(task); + + assertTaskReverted(transportMock, PACKAGE_1); + } + + @Test + public void testRunTask_whenPmAgentFails_cleansUpFiles() throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgent(PACKAGE_1); + BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); + when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + + runTask(task); + + assertCleansUpFiles(mTransport, PM_PACKAGE); + } + + @Test + public void testRunTask_whenPmAgentFails_resetsBackupState() throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgent(PACKAGE_1); + BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); + when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + + runTask(task); + + verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile()); + } + + @Test + public void testRunTask_whenMarkCancelDuringPmOnBackup_resetsBackupState() throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgent(PACKAGE_1); + BackupAgent pmAgent = spy(createPmAgent()); + when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + agentOnBackupDo( + pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel)); + + runTask(task); + + verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile()); + } + + @Test + public void testRunTask_whenMarkCancelDuringPmOnBackup_cleansUpFiles() throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + setUpAgent(PACKAGE_1); + BackupAgent pmAgent = spy(createPmAgent()); + when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + agentOnBackupDo( + pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel)); + + runTask(task); + + assertCleansUpFiles(mTransport, PM_PACKAGE); + } + + @Test public void testRunTask_whenBackupRunning_doesNotThrow() throws Exception { TransportMock transportMock = setUpInitializedTransport(mTransport); when(mBackupManagerService.isBackupOperationInProgress()).thenReturn(true); @@ -1736,7 +1977,7 @@ public class KeyValueBackupTaskTest { runTask(task); - verify(mReporter).onReadAgentDataError(eq(PACKAGE_1.packageName), any()); + verify(mReporter).onAgentDataError(eq(PACKAGE_1.packageName), any()); } @Test @@ -1779,6 +2020,24 @@ public class KeyValueBackupTaskTest { } @Test + public void testRunTask_whenMarkCancelDuringAgentOnBackup_cleansUpFiles() throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + AgentMock agentMock = setUpAgent(PACKAGE_1); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); + agentOnBackupDo( + agentMock, + (oldState, dataOutput, newState) -> { + writeData(dataOutput, "key", "data".getBytes()); + writeState(newState, "newState".getBytes()); + runInWorkerThread(task::markCancel); + }); + + runTask(task); + + assertCleansUpFiles(mTransport, PACKAGE_1); + } + + @Test public void testRunTask_whenMarkCancelDuringFirstAgentOnBackup_doesNotCallTransportAfterWaitCancel() throws Exception { @@ -2293,20 +2552,28 @@ public class KeyValueBackupTaskTest { */ private static void agentOnBackupDo(AgentMock agentMock, BackupAgentOnBackup function) throws Exception { - doAnswer( - (BackupAgentOnBackup) - (oldState, dataOutput, newState) -> { - ByteArrayOutputStream outputStream = - new ByteArrayOutputStream(); - transferStreamedData( - new FileInputStream(oldState.getFileDescriptor()), - outputStream); - agentMock.oldState = outputStream.toByteArray(); - agentMock.oldStateHistory.add(agentMock.oldState); - function.onBackup(oldState, dataOutput, newState); - }) - .when(agentMock.agent) - .onBackup(any(), any(), any()); + agentOnBackupDo( + agentMock.agent, + (oldState, dataOutput, newState) -> { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + transferStreamedData( + new FileInputStream(oldState.getFileDescriptor()), outputStream); + agentMock.oldState = outputStream.toByteArray(); + agentMock.oldStateHistory.add(agentMock.oldState); + function.onBackup(oldState, dataOutput, newState); + }); + } + + /** + * Implements {@code function} for {@link BackupAgent#onBackup(ParcelFileDescriptor, + * BackupDataOutput, ParcelFileDescriptor)} of {@code agentMock}. + * + * @see #agentOnBackupDo(AgentMock, BackupAgentOnBackup) + * @see #remoteAgentOnBackupThrows(AgentMock, BackupAgentOnBackup) + */ + private static void agentOnBackupDo(BackupAgent backupAgent, BackupAgentOnBackup function) + throws IOException { + doAnswer(function).when(backupAgent).onBackup(any(), any(), any()); } /** @@ -2400,6 +2667,10 @@ public class KeyValueBackupTaskTest { // constructor assertJournalDoesNotContain(mBackupManagerService.getJournal(), packageName); assertThat(mBackupManagerService.getPendingBackups()).doesNotContainKey(packageName); + // Also verifying BMS is never called since for some cases the package wouldn't be + // pending for other reasons (for example it's not eligible for backup). Regardless of + // these reasons, we shouldn't mark them as pending backup (call dataChangedImpl()). + verify(mBackupManagerService, never()).dataChangedImpl(packageName); } } diff --git a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java new file mode 100644 index 000000000000..4b79657b1dae --- /dev/null +++ b/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.backup.keyvalue; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.expectThrows; + +import android.app.backup.BackupTransport; +import android.platform.test.annotations.Presubmit; + +import com.android.server.testing.FrameworkRobolectricTestRunner; +import com.android.server.testing.SystemLoaderPackages; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import java.io.IOException; + +@RunWith(FrameworkRobolectricTestRunner.class) +@Config(manifest = Config.NONE, sdk = 26) +@SystemLoaderPackages({"com.android.server.backup"}) +@Presubmit +public class TaskExceptionTest { + @Test + public void testStateCompromised() { + TaskException exception = TaskException.stateCompromised(); + + assertThat(exception.isStateCompromised()).isTrue(); + assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR); + } + + @Test + public void testStateCompromised_whenCauseInstanceOfTaskException() { + Exception cause = TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED); + + TaskException exception = TaskException.stateCompromised(cause); + + assertThat(exception.isStateCompromised()).isTrue(); + assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED); + assertThat(exception.getCause()).isEqualTo(cause); + } + + @Test + public void testStateCompromised_whenCauseNotInstanceOfTaskException() { + Exception cause = new IOException(); + + TaskException exception = TaskException.stateCompromised(cause); + + assertThat(exception.isStateCompromised()).isTrue(); + assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR); + assertThat(exception.getCause()).isEqualTo(cause); + } + + @Test + public void testForStatus_whenTransportOk_throws() { + expectThrows( + IllegalArgumentException.class, + () -> TaskException.forStatus(BackupTransport.TRANSPORT_OK)); + } + + @Test + public void testForStatus_whenTransportNotInitialized() { + TaskException exception = + TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED); + + assertThat(exception.isStateCompromised()).isFalse(); + assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED); + } + + @Test + public void testCausedBy_whenCauseInstanceOfTaskException_returnsCause() { + Exception cause = TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED); + + TaskException exception = TaskException.causedBy(cause); + + assertThat(exception).isEqualTo(cause); + } + + @Test + public void testCausedBy_whenCauseNotInstanceOfTaskException() { + Exception cause = new IOException(); + + TaskException exception = TaskException.causedBy(cause); + + assertThat(exception).isNotEqualTo(cause); + assertThat(exception.isStateCompromised()).isFalse(); + assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR); + assertThat(exception.getCause()).isEqualTo(cause); + } + + @Test + public void testCreate() { + TaskException exception = TaskException.create(); + + assertThat(exception.isStateCompromised()).isFalse(); + assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR); + } + + @Test + public void testIsStateCompromised_whenStateCompromised_returnsTrue() { + TaskException taskException = TaskException.stateCompromised(); + + boolean stateCompromised = taskException.isStateCompromised(); + + assertThat(stateCompromised).isTrue(); + } + + @Test + public void testIsStateCompromised_whenCreatedWithCreate_returnsFalse() { + TaskException taskException = TaskException.create(); + + boolean stateCompromised = taskException.isStateCompromised(); + + assertThat(stateCompromised).isFalse(); + } + + @Test + public void testGetStatus_whenStatusIsTransportPackageRejected() { + TaskException taskException = + TaskException.forStatus(BackupTransport.TRANSPORT_PACKAGE_REJECTED); + + int status = taskException.getStatus(); + + assertThat(status).isEqualTo(BackupTransport.TRANSPORT_PACKAGE_REJECTED); + } + + @Test + public void testGetStatus_whenStatusIsTransportNotInitialized() { + TaskException taskException = + TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED); + + int status = taskException.getStatus(); + + assertThat(status).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED); + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java index 06c74371eb51..e8a824a12300 100644 --- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java +++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java @@ -16,9 +16,12 @@ package com.android.server.am; +import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE; import static com.android.server.am.MemoryStatUtil.MemoryStat; +import static com.android.server.am.MemoryStatUtil.parseMemoryMaxUsageFromMemCg; import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg; import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs; +import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -32,7 +35,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest public class MemoryStatUtilTest { - private String MEMORY_STAT_CONTENTS = String.join( + private static final String MEMORY_STAT_CONTENTS = String.join( "\n", "cache 96", // keep different from total_cache to catch reading wrong value "rss 97", // keep different from total_rss to catch reading wrong value @@ -67,7 +70,7 @@ public class MemoryStatUtilTest { "total_active_file 81920", "total_unevictable 0"); - private String PROC_STAT_CONTENTS = String.join( + private static final String PROC_STAT_CONTENTS = String.join( " ", "1040", "(system_server)", @@ -122,14 +125,61 @@ public class MemoryStatUtilTest { "3198889956", "0"); + private static final String PROC_STATUS_CONTENTS = "Name:\tandroid.youtube\n" + + "State:\tS (sleeping)\n" + + "Tgid:\t12088\n" + + "Pid:\t12088\n" + + "PPid:\t723\n" + + "TracerPid:\t0\n" + + "Uid:\t10083\t10083\t10083\t10083\n" + + "Gid:\t10083\t10083\t10083\t10083\n" + + "Ngid:\t0\n" + + "FDSize:\t128\n" + + "Groups:\t3003 9997 20083 50083 \n" + + "VmPeak:\t 4546844 kB\n" + + "VmSize:\t 4542636 kB\n" + + "VmLck:\t 0 kB\n" + + "VmPin:\t 0 kB\n" + + "VmHWM:\t 137668 kB\n" // RSS high watermark + + "VmRSS:\t 126776 kB\n" + + "RssAnon:\t 37860 kB\n" + + "RssFile:\t 88764 kB\n" + + "RssShmem:\t 152 kB\n" + + "VmData:\t 4125112 kB\n" + + "VmStk:\t 8192 kB\n" + + "VmExe:\t 24 kB\n" + + "VmLib:\t 102432 kB\n" + + "VmPTE:\t 1300 kB\n" + + "VmPMD:\t 36 kB\n" + + "VmSwap:\t 0 kB\n" + + "Threads:\t95\n" + + "SigQ:\t0/13641\n" + + "SigPnd:\t0000000000000000\n" + + "ShdPnd:\t0000000000000000\n" + + "SigBlk:\t0000000000001204\n" + + "SigIgn:\t0000000000000001\n" + + "SigCgt:\t00000006400084f8\n" + + "CapInh:\t0000000000000000\n" + + "CapPrm:\t0000000000000000\n" + + "CapEff:\t0000000000000000\n" + + "CapBnd:\t0000000000000000\n" + + "CapAmb:\t0000000000000000\n" + + "Seccomp:\t2\n" + + "Cpus_allowed:\tff\n" + + "Cpus_allowed_list:\t0-7\n" + + "Mems_allowed:\t1\n" + + "Mems_allowed_list:\t0\n" + + "voluntary_ctxt_switches:\t903\n" + + "nonvoluntary_ctxt_switches:\t104\n"; + @Test public void testParseMemoryStatFromMemcg_parsesCorrectValues() throws Exception { MemoryStat stat = parseMemoryStatFromMemcg(MEMORY_STAT_CONTENTS); - assertEquals(stat.pgfault, 1); - assertEquals(stat.pgmajfault, 2); - assertEquals(stat.rssInBytes, 3); - assertEquals(stat.cacheInBytes, 4); - assertEquals(stat.swapInBytes, 5); + assertEquals(1, stat.pgfault); + assertEquals(2, stat.pgmajfault); + assertEquals(3, stat.rssInBytes); + assertEquals(4, stat.cacheInBytes); + assertEquals(5, stat.swapInBytes); } @Test @@ -142,6 +192,18 @@ public class MemoryStatUtilTest { } @Test + public void testParseMemoryMaxUsageFromMemCg_parsesCorrectValue() { + assertEquals(1234, parseMemoryMaxUsageFromMemCg("1234")); + } + + @Test + public void testParseMemoryMaxUsageFromMemCg_emptyContents() { + assertEquals(0, parseMemoryMaxUsageFromMemCg("")); + + assertEquals(0, parseMemoryMaxUsageFromMemCg(null)); + } + + @Test public void testParseMemoryStatFromProcfs_parsesCorrectValues() throws Exception { MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS); assertEquals(1, stat.pgfault); @@ -159,4 +221,16 @@ public class MemoryStatUtilTest { stat = parseMemoryStatFromProcfs(null); assertNull(stat); } + + @Test + public void testParseVmHWMFromProcfs_parsesCorrectValue() { + assertEquals(137668, parseVmHWMFromProcfs(PROC_STATUS_CONTENTS) / BYTES_IN_KILOBYTE); + } + + @Test + public void testParseVmHWMFromProcfs_emptyContents() { + assertEquals(0, parseVmHWMFromProcfs("")); + + assertEquals(0, parseVmHWMFromProcfs(null)); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 5fb89977f905..474e5b75e5d8 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -458,12 +458,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public void setLastInputMethodWindowLw(WindowState ime, - WindowState target) { - - } - - @Override public void showRecentApps() { } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java index 6af3ea763c13..b7cc9ce7a619 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java @@ -76,7 +76,8 @@ import androidx.test.runner.AndroidJUnit4; */ @SmallTest @FlakyTest(bugId = 74078662) -@Presubmit +// TODO(b/116597907): Re-enable this test in postsubmit after the bug is fixed. +// @Presubmit @RunWith(AndroidJUnit4.class) public class WindowStateTests extends WindowTestsBase { @@ -369,22 +370,31 @@ public class WindowStateTests extends WindowTestsBase { final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); app.mHasSurface = true; - app.mToken.mSurfaceControl = mock(SurfaceControl.class); + app.mSurfaceControl = mock(SurfaceControl.class); try { app.getFrameLw().set(10, 20, 60, 80); + app.updateSurfacePosition(t); app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true); assertTrue(app.mSeamlesslyRotated); + + // Verify we un-rotate the window state surface. Matrix matrix = new Matrix(); // Un-rotate 90 deg matrix.setRotate(270); // Translate it back to origin matrix.postTranslate(0, mDisplayInfo.logicalWidth); - verify(t).setMatrix(eq(app.mToken.mSurfaceControl), eq(matrix), any(float[].class)); + verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class)); + + // Verify we update the position as well. + float[] currentSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y}; + matrix.mapPoints(currentSurfacePos); + verify(t).setPosition(eq(app.mSurfaceControl), eq(currentSurfacePos[0]), + eq(currentSurfacePos[1])); } finally { + app.mSurfaceControl = null; app.mHasSurface = false; - app.mToken.mSurfaceControl = null; } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java index bbc6550d5753..01b7c4f650e7 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java @@ -59,7 +59,8 @@ import java.nio.charset.StandardCharsets; */ @SmallTest @FlakyTest(bugId = 74078662) -@Presubmit +// TODO(b/116597907): Re-enable this test in postsubmit after the bug is fixed. +// @Presubmit @RunWith(AndroidJUnit4.class) public class WindowTracingTest extends WindowTestsBase { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 4344285e31e7..45d2fa2d16a4 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -97,6 +97,7 @@ import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.provider.MediaStore; import android.provider.Settings.Secure; import android.service.notification.Adjustment; @@ -3382,10 +3383,44 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testIsCallerInstantApp_primaryUser() throws Exception { + ApplicationInfo info = new ApplicationInfo(); + info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT; + when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info); + + assertTrue(mService.isCallerInstantApp("any", 45770, 0)); + + info.privateFlags = 0; + assertFalse(mService.isCallerInstantApp("any", 575370, 0)); + } + + @Test + public void testIsCallerInstantApp_secondaryUser() throws Exception { + ApplicationInfo info = new ApplicationInfo(); + info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT; + when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info); + when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null); + + assertTrue(mService.isCallerInstantApp("any", 68638450, 10)); + } + + @Test + public void testResolveNotificationUid_sameApp_nonSystemUser() throws Exception { + ApplicationInfo info = new ApplicationInfo(); + info.uid = Binder.getCallingUid(); + when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info); + when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null); + + int actualUid = mService.resolveNotificationUid("caller", "caller", info.uid, 10); + + assertEquals(info.uid, actualUid); + } + + @Test public void testResolveNotificationUid_sameApp() throws Exception { ApplicationInfo info = new ApplicationInfo(); info.uid = Binder.getCallingUid(); - when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info); + when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info); int actualUid = mService.resolveNotificationUid("caller", "caller", info.uid, 0); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 26bd4a106ca6..08bc9bcc4003 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -141,6 +141,8 @@ public final class Call { * The caller must specify the {@link #EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE} to indicate to * Telecom which {@link PhoneAccountHandle} the {@link Call} should be handed over to. * @hide + * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated + * APIs instead. */ public static final String EVENT_REQUEST_HANDOVER = "android.telecom.event.REQUEST_HANDOVER"; @@ -149,6 +151,8 @@ public final class Call { * Extra key used with the {@link #EVENT_REQUEST_HANDOVER} call event. Specifies the * {@link PhoneAccountHandle} to which a call should be handed over to. * @hide + * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated + * APIs instead. */ public static final String EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.HANDOVER_PHONE_ACCOUNT_HANDLE"; @@ -161,6 +165,8 @@ public final class Call { * {@link VideoProfile#STATE_BIDIRECTIONAL}, {@link VideoProfile#STATE_RX_ENABLED}, and * {@link VideoProfile#STATE_TX_ENABLED}. * @hide + * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated + * APIs instead. */ public static final String EXTRA_HANDOVER_VIDEO_STATE = "android.telecom.extra.HANDOVER_VIDEO_STATE"; @@ -176,6 +182,8 @@ public final class Call { * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} * is called to initate the handover. * @hide + * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated + * APIs instead. */ public static final String EXTRA_HANDOVER_EXTRAS = "android.telecom.extra.HANDOVER_EXTRAS"; @@ -186,6 +194,8 @@ public final class Call { * <p> * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event. * @hide + * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated + * APIs instead. */ public static final String EVENT_HANDOVER_COMPLETE = "android.telecom.event.HANDOVER_COMPLETE"; @@ -198,6 +208,8 @@ public final class Call { * <p> * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event. * @hide + * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated + * APIs instead. */ public static final String EVENT_HANDOVER_SOURCE_DISCONNECTED = "android.telecom.event.HANDOVER_SOURCE_DISCONNECTED"; @@ -209,6 +221,8 @@ public final class Call { * <p> * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event. * @hide + * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated + * APIs instead. */ public static final String EVENT_HANDOVER_FAILED = "android.telecom.event.HANDOVER_FAILED"; diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index d49469233752..34603a3f056a 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -594,6 +594,8 @@ public abstract class Connection extends Conferenceable { * {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has * successfully completed. * @hide + * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated + * APIs instead. */ public static final String EVENT_HANDOVER_COMPLETE = "android.telecom.event.HANDOVER_COMPLETE"; @@ -603,6 +605,8 @@ public abstract class Connection extends Conferenceable { * {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has failed * to complete. * @hide + * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated + * APIs instead. */ public static final String EVENT_HANDOVER_FAILED = "android.telecom.event.HANDOVER_FAILED"; diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 85901768c79f..995418ee706c 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2115,6 +2115,16 @@ public class CarrierConfigManager { public static final String KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL = "config_show_orig_dial_string_for_cdma"; + /** + * Flag specifying whether to show notification(call blocking disabled) when Enhanced Call + * Blocking(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL) is enabled and making emergency call. + * When true, notification is shown always. + * When false, notification is shown only when any setting of "Enhanced Blocked number" is + * enabled. + */ + public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = + "show_call_blocking_disabled_notification_always_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -2453,6 +2463,7 @@ public class CarrierConfigManager { }); sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, ""); sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false); + sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false); } /** diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java new file mode 100644 index 000000000000..e3737976adf7 --- /dev/null +++ b/telephony/java/android/telephony/MbmsGroupCallSession.java @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2018 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.telephony; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SdkConstant; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.content.ComponentName; +import android.content.Context; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.mbms.GroupCall; +import android.telephony.mbms.GroupCallCallback; +import android.telephony.mbms.InternalGroupCallCallback; +import android.telephony.mbms.InternalGroupCallSessionCallback; +import android.telephony.mbms.MbmsErrors; +import android.telephony.mbms.MbmsGroupCallSessionCallback; +import android.telephony.mbms.MbmsUtils; +import android.telephony.mbms.vendor.IMbmsGroupCallService; +import android.util.ArraySet; +import android.util.Log; + +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +/** + * This class provides functionality for accessing group call functionality over MBMS. + */ +public class MbmsGroupCallSession implements AutoCloseable { + private static final String LOG_TAG = "MbmsGroupCallSession"; + + /** + * Service action which must be handled by the middleware implementing the MBMS group call + * interface. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String MBMS_GROUP_CALL_SERVICE_ACTION = + "android.telephony.action.EmbmsGroupCall"; + + /** + * Metadata key that specifies the component name of the service to bind to for group calls. + * @hide + */ + @TestApi + public static final String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA = + "mbms-group-call-service-override"; + + private static AtomicBoolean sIsInitialized = new AtomicBoolean(false); + + private AtomicReference<IMbmsGroupCallService> mService = new AtomicReference<>(null); + private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + sIsInitialized.set(false); + mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST, + "Received death notification"); + } + }; + + private InternalGroupCallSessionCallback mInternalCallback; + private Set<GroupCall> mKnownActiveGroupCalls = new ArraySet<>(); + + private final Context mContext; + private int mSubscriptionId; + + /** @hide */ + private MbmsGroupCallSession(Context context, Executor executor, int subscriptionId, + MbmsGroupCallSessionCallback callback) { + mContext = context; + mSubscriptionId = subscriptionId; + mInternalCallback = new InternalGroupCallSessionCallback(callback, executor); + } + + /** + * Create a new {@link MbmsGroupCallSession} using the given subscription ID. + * + * You may only have one instance of {@link MbmsGroupCallSession} per UID. If you call this + * method while there is an active instance of {@link MbmsGroupCallSession} in your process + * (in other words, one that has not had {@link #close()} called on it), this method will + * throw an {@link IllegalStateException}. If you call this method in a different process + * running under the same UID, an error will be indicated via + * {@link MbmsGroupCallSessionCallback#onError(int, String)}. + * + * Note that initialization may fail asynchronously. If you wish to try again after you + * receive such an asynchronous error, you must call {@link #close()} on the instance of + * {@link MbmsGroupCallSession} that you received before calling this method again. + * + * @param context The {@link Context} to use. + * @param executor The executor on which you wish to execute callbacks. + * @param subscriptionId The subscription ID to use. + * @param callback A callback object on which you wish to receive results of asynchronous + * operations. + * @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred. + */ + public static @Nullable MbmsGroupCallSession create(@NonNull Context context, + @NonNull Executor executor, int subscriptionId, + final @NonNull MbmsGroupCallSessionCallback callback) { + if (!sIsInitialized.compareAndSet(false, true)) { + throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession"); + } + MbmsGroupCallSession session = new MbmsGroupCallSession(context, executor, + subscriptionId, callback); + + final int result = session.bindAndInitialize(); + if (result != MbmsErrors.SUCCESS) { + sIsInitialized.set(false); + executor.execute(new Runnable() { + @Override + public void run() { + callback.onError(result, null); + } + }); + return null; + } + return session; + } + + /** + * Create a new {@link MbmsGroupCallSession} using the system default data subscription ID. + * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}. + */ + public static MbmsGroupCallSession create(@NonNull Context context, + @NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) { + return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback); + } + + /** + * Terminates this instance. Also terminates + * any group calls spawned from this instance as if + * {@link GroupCall#close()} had been called on them. After this method returns, + * no further callbacks originating from the middleware will be enqueued on the provided + * instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been + * enqueued will still be delivered. + * + * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to + * obtain another instance of {@link MbmsGroupCallSession} immediately after this method + * returns. + * + * May throw an {@link IllegalStateException} + */ + public void close() { + try { + IMbmsGroupCallService groupCallService = mService.get(); + if (groupCallService == null) { + // Ignore and return, assume already disposed. + return; + } + groupCallService.dispose(mSubscriptionId); + for (GroupCall s : mKnownActiveGroupCalls) { + s.getCallback().stop(); + } + mKnownActiveGroupCalls.clear(); + } catch (RemoteException e) { + // Ignore for now + } finally { + mService.set(null); + sIsInitialized.set(false); + mInternalCallback.stop(); + } + } + + /** + * Starts the requested group call, reporting status to the indicated callback. + * Returns an object used to control that call. + * + * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException} + * + * Asynchronous errors through the callback include any of the errors in + * {@link MbmsErrors.GeneralErrors}. + * + * @param executor The executor on which you wish to execute callbacks for this stream. + * @param tmgi The TMGI, an identifier for the group call you want to join. + * @param saiArray An array of SAIs for the group call that should be negotiated separately with + * the carrier. + * @param frequencyArray An array of frequencies for the group call that should be negotiated + * separately with the carrier. + * @param callback The callback that you want to receive information about the call on. + * @return An instance of {@link GroupCall} through which the call can be controlled. + * May be {@code null} if an error occurred. + */ + public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray, + int[] frequencyArray, @NonNull GroupCallCallback callback) { + IMbmsGroupCallService groupCallService = mService.get(); + if (groupCallService == null) { + throw new IllegalStateException("Middleware not yet bound"); + } + + InternalGroupCallCallback serviceCallback = new InternalGroupCallCallback( + callback, executor); + + GroupCall serviceForApp = new GroupCall(mSubscriptionId, + groupCallService, this, tmgi, serviceCallback); + mKnownActiveGroupCalls.add(serviceForApp); + + try { + int returnCode = groupCallService.startGroupCall( + mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback); + if (returnCode == MbmsErrors.UNKNOWN) { + // Unbind and throw an obvious error + close(); + throw new IllegalStateException("Middleware must not return an unknown error code"); + } + if (returnCode != MbmsErrors.SUCCESS) { + mInternalCallback.onError(returnCode, null); + return null; + } + } catch (RemoteException e) { + Log.w(LOG_TAG, "Remote process died"); + mService.set(null); + sIsInitialized.set(false); + mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST, null); + return null; + } + + return serviceForApp; + } + + /** @hide */ + public void onGroupCallStopped(GroupCall service) { + mKnownActiveGroupCalls.remove(service); + } + + private int bindAndInitialize() { + return MbmsUtils.startBinding(mContext, MBMS_GROUP_CALL_SERVICE_ACTION, + new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + IMbmsGroupCallService groupCallService = + IMbmsGroupCallService.Stub.asInterface(service); + int result; + try { + result = groupCallService.initialize(mInternalCallback, + mSubscriptionId); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Service died before initialization"); + mInternalCallback.onError( + MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, + e.toString()); + sIsInitialized.set(false); + return; + } catch (RuntimeException e) { + Log.e(LOG_TAG, "Runtime exception during initialization"); + mInternalCallback.onError( + MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, + e.toString()); + sIsInitialized.set(false); + return; + } + if (result == MbmsErrors.UNKNOWN) { + // Unbind and throw an obvious error + close(); + throw new IllegalStateException("Middleware must not return" + + " an unknown error code"); + } + if (result != MbmsErrors.SUCCESS) { + mInternalCallback.onError(result, + "Error returned during initialization"); + sIsInitialized.set(false); + return; + } + try { + groupCallService.asBinder().linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST, + "Middleware lost during initialization"); + sIsInitialized.set(false); + return; + } + mService.set(groupCallService); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + sIsInitialized.set(false); + mService.set(null); + } + }); + } +} diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 777b850bc9c5..38ee79f06690 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1852,6 +1852,19 @@ public class SubscriptionManager { } /** + * Checks if the supplied subscription ID corresponds to an active subscription. + * + * @param subscriptionId the subscription ID. + * @return {@code true} if the supplied subscription ID corresponds to an active subscription; + * {@code false} if it does not correspond to an active subscription; or throw a + * SecurityException if the caller hasn't got the right permission. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public boolean isActiveSubscriptionId(int subscriptionId) { + return isActiveSubId(subscriptionId); + } + + /** * @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription * and the SIM providing the subscription is present in a slot and in "LOADED" state. * @hide @@ -1861,7 +1874,7 @@ public class SubscriptionManager { try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); if (iSub != null) { - return iSub.isActiveSubId(subId); + return iSub.isActiveSubId(subId, mContext.getOpPackageName()); } } catch (RemoteException ex) { } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index a8bcbe33966a..824533d59bf5 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -16,6 +16,8 @@ package android.telephony; +import static android.content.Context.TELECOM_SERVICE; + import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntDef; @@ -1201,6 +1203,15 @@ public class TelephonyManager { "android.intent.action.DATA_STALL_DETECTED"; /** + * A service action that identifies a {@link android.app.SmsAppService} subclass in the + * AndroidManifest.xml. + * + * <p>See {@link android.app.SmsAppService} for the details. + */ + @SdkConstant(SdkConstantType.SERVICE_ACTION) + public static final String ACTION_SMS_APP_SERVICE = "android.telephony.action.SMS_APP_SERVICE"; + + /** * An int extra used with {@link #ACTION_DATA_STALL_DETECTED} to indicate the * action associated with the data stall recovery. * @@ -2091,10 +2102,37 @@ public class TelephonyManager { /** Max network type number. Update as new types are added. Don't add negative types. {@hide} */ public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_LTE_CA; + + /** @hide */ + @IntDef({ + NETWORK_TYPE_UNKNOWN, + NETWORK_TYPE_GPRS, + NETWORK_TYPE_EDGE, + NETWORK_TYPE_UMTS, + NETWORK_TYPE_CDMA, + NETWORK_TYPE_EVDO_0, + NETWORK_TYPE_EVDO_A, + NETWORK_TYPE_1xRTT, + NETWORK_TYPE_HSDPA, + NETWORK_TYPE_HSUPA, + NETWORK_TYPE_HSPA, + NETWORK_TYPE_IDEN, + NETWORK_TYPE_EVDO_B, + NETWORK_TYPE_LTE, + NETWORK_TYPE_EHRPD, + NETWORK_TYPE_HSPAP, + NETWORK_TYPE_GSM, + NETWORK_TYPE_TD_SCDMA, + NETWORK_TYPE_IWLAN, + NETWORK_TYPE_LTE_CA, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NetworkType{} + /** * @return the NETWORK_TYPE_xxxx for current data connection. */ - public int getNetworkType() { + public @NetworkType int getNetworkType() { try { ITelephony telephony = getITelephony(); if (telephony != null) { @@ -2139,24 +2177,24 @@ public class TelephonyManager { * @hide */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - @UnsupportedAppUsage - public int getNetworkType(int subId) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName()); - } else { - // This can happen when the ITelephony interface is not up yet. - return NETWORK_TYPE_UNKNOWN; - } - } catch(RemoteException ex) { - // This shouldn't happen in the normal case - return NETWORK_TYPE_UNKNOWN; - } catch (NullPointerException ex) { - // This could happen before phone restarts due to crashing - return NETWORK_TYPE_UNKNOWN; - } - } + @UnsupportedAppUsage + public int getNetworkType(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName()); + } else { + // This can happen when the ITelephony interface is not up yet. + return NETWORK_TYPE_UNKNOWN; + } + } catch (RemoteException ex) { + // This shouldn't happen in the normal case + return NETWORK_TYPE_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return NETWORK_TYPE_UNKNOWN; + } + } /** * Returns a constant indicating the radio technology (network type) @@ -2189,7 +2227,7 @@ public class TelephonyManager { */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public int getDataNetworkType() { + public @NetworkType int getDataNetworkType() { return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId())); } @@ -2229,7 +2267,7 @@ public class TelephonyManager { */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public int getVoiceNetworkType() { + public @NetworkType int getVoiceNetworkType() { return getVoiceNetworkType(getSubId()); } @@ -4108,11 +4146,16 @@ public class TelephonyManager { } /** - * Returns the IMS home network domain name that was loaded from the ISIM. - * @return the IMS domain name, or null if not present or not loaded + * Returns the IMS home network domain name that was loaded from the ISIM {@see #APPTYPE_ISIM}. + * @return the IMS domain name. Returns {@code null} if ISIM hasn't been loaded or IMS domain + * hasn't been loaded or isn't present on the ISIM. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} * @hide */ - @UnsupportedAppUsage + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain() { try { IPhoneSubInfo info = getSubscriberInfo(); @@ -4340,7 +4383,7 @@ public class TelephonyManager { * @hide */ private ITelecomService getTelecomService() { - return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE)); + return ITelecomService.Stub.asInterface(ServiceManager.getService(TELECOM_SERVICE)); } private ITelephonyRegistry getTelephonyRegistry() { @@ -5370,7 +5413,19 @@ public class TelephonyManager { } } - // ICC SIM Application Types + /** + * UICC SIM Application Types + * @hide + */ + @IntDef(prefix = { "APPTYPE_" }, value = { + APPTYPE_SIM, + APPTYPE_USIM, + APPTYPE_RUIM, + APPTYPE_CSIM, + APPTYPE_ISIM + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UiccAppType{} /** UICC application type is SIM */ public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM; /** UICC application type is USIM */ @@ -5381,6 +5436,7 @@ public class TelephonyManager { public static final int APPTYPE_CSIM = PhoneConstants.APPTYPE_CSIM; /** UICC application type is ISIM */ public static final int APPTYPE_ISIM = PhoneConstants.APPTYPE_ISIM; + // authContext (parameter P2) when doing UICC challenge, // per 3GPP TS 31.102 (Section 7.1.2) /** Authentication type for UICC challenge is EAP SIM. See RFC 4186 for details. */ @@ -5647,6 +5703,202 @@ public class TelephonyManager { } } + /** @hide */ + @IntDef(prefix = { "NETWORK_MODE_" }, value = { + NETWORK_MODE_WCDMA_PREF, + NETWORK_MODE_GSM_ONLY, + NETWORK_MODE_WCDMA_ONLY, + NETWORK_MODE_GSM_UMTS, + NETWORK_MODE_CDMA_EVDO, + NETWORK_MODE_CDMA_NO_EVDO, + NETWORK_MODE_EVDO_NO_CDMA, + NETWORK_MODE_GLOBAL, + NETWORK_MODE_LTE_CDMA_EVDO, + NETWORK_MODE_LTE_GSM_WCDMA, + NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA, + NETWORK_MODE_LTE_ONLY, + NETWORK_MODE_LTE_WCDMA, + NETWORK_MODE_TDSCDMA_ONLY, + NETWORK_MODE_TDSCDMA_WCDMA, + NETWORK_MODE_LTE_TDSCDMA, + NETWORK_MODE_TDSCDMA_GSM, + NETWORK_MODE_LTE_TDSCDMA_GSM, + NETWORK_MODE_TDSCDMA_GSM_WCDMA, + NETWORK_MODE_LTE_TDSCDMA_WCDMA, + NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA, + NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA, + NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PrefNetworkMode{} + + /** + * preferred network mode is GSM/WCDMA (WCDMA preferred). + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF; + + /** + * preferred network mode is GSM only. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY; + + /** + * preferred network mode is WCDMA only. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY; + + /** + * preferred network mode is GSM/WCDMA (auto mode, according to PRL). + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS; + + /** + * preferred network mode is CDMA and EvDo (auto mode, according to PRL). + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA; + + /** + * preferred network mode is CDMA only. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO; + + /** + * preferred network mode is EvDo only. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA; + + /** + * preferred network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL). + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL; + + /** + * preferred network mode is LTE, CDMA and EvDo. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO; + + /** + * preferred network mode is LTE, GSM/WCDMA. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_LTE_GSM_WCDMA = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA; + + /** + * preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = + RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA; + + /** + * preferred network mode is LTE Only. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_LTE_ONLY = RILConstants.NETWORK_MODE_LTE_ONLY; + + /** + * preferred network mode is LTE/WCDMA. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_LTE_WCDMA = RILConstants.NETWORK_MODE_LTE_WCDMA; + + /** + * preferred network mode is TD-SCDMA only. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_TDSCDMA_ONLY = RILConstants.NETWORK_MODE_TDSCDMA_ONLY; + + /** + * preferred network mode is TD-SCDMA and WCDMA. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_TDSCDMA_WCDMA = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA; + + /** + * preferred network mode is TD-SCDMA and LTE. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_LTE_TDSCDMA = RILConstants.NETWORK_MODE_LTE_TDSCDMA; + + /** + * preferred network mode is TD-SCDMA and GSM. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_TDSCDMA_GSM = RILConstants.NETWORK_MODE_TDSCDMA_GSM; + + /** + * preferred network mode is TD-SCDMA,GSM and LTE. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_LTE_TDSCDMA_GSM = + RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM; + + /** + * preferred network mode is TD-SCDMA, GSM/WCDMA. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA = + RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA; + + /** + * preferred network mode is TD-SCDMA, WCDMA and LTE. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA = + RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA; + + /** + * preferred network mode is TD-SCDMA, GSM/WCDMA and LTE. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = + RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA; + + /** + * preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = + RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA; + /** + * preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo. + * @hide + */ + @SystemApi + public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = + RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA; + /** * Get the preferred network type. * Used for device configuration by some CDMA operators. @@ -5655,11 +5907,12 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). * - * @return the preferred network type, defined in RILConstants.java. + * @return the preferred network type. * @hide */ - @UnsupportedAppUsage - public int getPreferredNetworkType(int subId) { + @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE)) + @SystemApi + public @PrefNetworkMode int getPreferredNetworkType(int subId) { try { ITelephony telephony = getITelephony(); if (telephony != null) @@ -6241,7 +6494,7 @@ public class TelephonyManager { } /** - * @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead. + * @removed Use {@link android.telecom.TelecomManager#endCall()} instead. * @hide * @removed */ @@ -6253,7 +6506,7 @@ public class TelephonyManager { } /** - * @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead + * @removed Use {@link android.telecom.TelecomManager#acceptRingingCall} instead * @hide * @removed */ @@ -6261,26 +6514,22 @@ public class TelephonyManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void answerRingingCall() { - + // No-op } /** - * @deprecated Use {@link android.telecom.TelecomManager#silenceRinger} instead + * @removed Use {@link android.telecom.TelecomManager#silenceRinger} instead * @hide */ @Deprecated @SystemApi @SuppressLint("Doclava125") public void silenceRinger() { - try { - getTelecomService().silenceRinger(getOpPackageName()); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelecomService#silenceRinger", e); - } + // No-op } /** - * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead + * @removed Use {@link android.telecom.TelecomManager#isInCall} instead * @hide */ @Deprecated @@ -6290,18 +6539,11 @@ public class TelephonyManager { android.Manifest.permission.READ_PHONE_STATE }) public boolean isOffhook() { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - return telephony.isOffhook(getOpPackageName()); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#isOffhook", e); - } return false; } /** - * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead + * @removed Use {@link android.telecom.TelecomManager#isRinging} instead * @hide */ @Deprecated @@ -6311,18 +6553,11 @@ public class TelephonyManager { android.Manifest.permission.READ_PHONE_STATE }) public boolean isRinging() { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - return telephony.isRinging(getOpPackageName()); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#isRinging", e); - } return false; } /** - * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead + * @removed Use {@link android.telecom.TelecomManager#isInCall} instead * @hide */ @Deprecated @@ -6332,13 +6567,6 @@ public class TelephonyManager { android.Manifest.permission.READ_PHONE_STATE }) public boolean isIdle() { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) - return telephony.isIdle(getOpPackageName()); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#isIdle", e); - } return true; } @@ -7824,26 +8052,23 @@ public class TelephonyManager { } /** - * Return the application ID for the app type like {@link APPTYPE_CSIM}. - * - * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission + * Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}. + * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220 + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} * - * @param appType the uicc app type like {@link APPTYPE_CSIM} - * @return Application ID for specificied app type or null if no uicc or error. + * @param appType the uicc app type. + * @return Application ID for specified app type or {@code null} if no uicc or error. * @hide */ - public String getAidForAppType(int appType) { + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public String getAidForAppType(@UiccAppType int appType) { return getAidForAppType(getSubId(), appType); } /** - * Return the application ID for the app type like {@link APPTYPE_CSIM}. - * - * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission - * - * @param subId the subscription ID that this request applies to. - * @param appType the uicc app type, like {@link APPTYPE_CSIM} - * @return Application ID for specificied app type or null if no uicc or error. + * same as {@link #getAidForAppType(int)} * @hide */ public String getAidForAppType(int subId, int appType) { diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java new file mode 100644 index 000000000000..9aca18e07812 --- /dev/null +++ b/telephony/java/android/telephony/mbms/GroupCall.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2018 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.telephony.mbms; + +import android.annotation.IntDef; +import android.os.RemoteException; +import android.telephony.MbmsGroupCallSession; +import android.telephony.mbms.vendor.IMbmsGroupCallService; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Class used to represent a single MBMS group call. After a call has been started with + * {@link MbmsGroupCallSession#startGroupCall}, + * this class is used to hold information about the call and control it. + */ +public class GroupCall implements AutoCloseable { + private static final String LOG_TAG = "MbmsGroupCall"; + + /** + * The state of a group call, reported via + * {@link GroupCallCallback#onGroupCallStateChanged(int, int)} + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED}) + public @interface GroupCallState {} + public static final int STATE_STOPPED = 1; + public static final int STATE_STARTED = 2; + public static final int STATE_STALLED = 3; + + /** + * The reason for a call state change, reported via + * {@link GroupCallCallback#onGroupCallStateChanged(int, int)} + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "REASON_" }, + value = {REASON_BY_USER_REQUEST, REASON_FREQUENCY_CONFLICT, + REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE, + REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE}) + public @interface GroupCallStateChangeReason {} + + /** + * Indicates that the middleware does not have a reason to provide for the state change. + */ + public static final int REASON_NONE = 0; + + /** + * State changed due to a call to {@link #close()} or + * {@link MbmsGroupCallSession#startGroupCall} + */ + public static final int REASON_BY_USER_REQUEST = 1; + + // 2 is unused to match up with streaming. + + /** + * State changed due to a frequency conflict with another requested call. + */ + public static final int REASON_FREQUENCY_CONFLICT = 3; + + /** + * State changed due to the middleware running out of memory + */ + public static final int REASON_OUT_OF_MEMORY = 4; + + /** + * State changed due to the device leaving the home carrier's LTE network. + */ + public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5; + + /** + * State changed due to the device leaving the area where this call is being broadcast. + */ + public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; + + private final int mSubscriptionId; + private final long mTmgi; + private final MbmsGroupCallSession mParentSession; + private final InternalGroupCallCallback mCallback; + private IMbmsGroupCallService mService; + + /** + * @hide + */ + public GroupCall(int subscriptionId, + IMbmsGroupCallService service, + MbmsGroupCallSession session, + long tmgi, + InternalGroupCallCallback callback) { + mSubscriptionId = subscriptionId; + mParentSession = session; + mService = service; + mTmgi = tmgi; + mCallback = callback; + } + + /** + * Retrieve the TMGI (Temporary Mobile Group Identity) corresponding to this call. + */ + public long getTmgi() { + return mTmgi; + } + + /** + * Send an update to the middleware when the SAI (Service Area Identifier) list and frequency + * information of the group call has * changed. Callers must obtain this information from the + * wireless carrier independently. + * @param saiArray New array of SAIs that the call is available on. + * @param frequencyArray New array of frequencies that the call is available on. + */ + public void updateGroupCall(int[] saiArray, int[] frequencyArray) { + if (mService == null) { + throw new IllegalStateException("No group call service attached"); + } + + try { + mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Remote process died"); + mService = null; + sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null); + } finally { + mParentSession.onGroupCallStopped(this); + } + } + + /** + * Stop this group call. Further operations on this object will fail with an + * {@link IllegalStateException}. + * + * May throw an {@link IllegalStateException} + */ + @Override + public void close() { + if (mService == null) { + throw new IllegalStateException("No group call service attached"); + } + + try { + mService.stopGroupCall(mSubscriptionId, mTmgi); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Remote process died"); + mService = null; + sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null); + } finally { + mParentSession.onGroupCallStopped(this); + } + } + + /** @hide */ + public InternalGroupCallCallback getCallback() { + return mCallback; + } + + private void sendErrorToApp(int errorCode, String message) { + mCallback.onError(errorCode, message); + } +} + diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java new file mode 100644 index 000000000000..001bb02aad94 --- /dev/null +++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2018 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.telephony.mbms; + +import android.annotation.IntDef; +import android.annotation.Nullable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A callback class for use when the application is in a group call. The middleware + * will provide updates on the status of the call via this callback. + */ +public class GroupCallCallback { + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE, + MbmsErrors.ERROR_MIDDLEWARE_LOST, + MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND, + MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY, + MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY, + MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE, + MbmsErrors.GeneralErrors.ERROR_IN_E911, + MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE, + MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM, + MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" }) + private @interface GroupCallError{} + + /** + * Indicates broadcast signal strength is not available for this call. + * + * This may be due to the call no longer being available due to geography + * or timing (end of service) + */ + public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; + + /** + * Called by the middleware when it has detected an error condition in this group call. The + * possible error codes are listed in {@link MbmsErrors}. + * @param errorCode The error code. + * @param message A human-readable message generated by the middleware for debugging purposes. + */ + public void onError(@GroupCallError int errorCode, @Nullable String message) { + // default implementation empty + } + + /** + * Called to indicate this call has changed state. + * + * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED} + * and {@link GroupCall#STATE_STALLED}. + */ + public void onGroupCallStateChanged(@GroupCall.GroupCallState int state, + @GroupCall.GroupCallStateChangeReason int reason) { + // default implementation empty + } + + /** + * Broadcast Signal Strength updated. + * + * This signal strength is the BROADCAST signal strength which, + * depending on technology in play and it's deployment, may be + * stronger or weaker than the traditional UNICAST signal + * strength. It a simple int from 0-4 for valid levels or + * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available + * for this call due to timing, geography or popularity. + */ + public void onBroadcastSignalStrengthUpdated(int signalStrength) { + // default implementation empty + } +} diff --git a/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl new file mode 100755 index 000000000000..844b6344a34c --- /dev/null +++ b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018 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.telephony.mbms; + +/** + * @hide + */ +oneway interface IGroupCallCallback { + void onError(int errorCode, String message); + void onGroupCallStateChanged(int state, int reason); + void onBroadcastSignalStrengthUpdated(int signalStrength); +} diff --git a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl new file mode 100755 index 000000000000..1a1c7f8af5df --- /dev/null +++ b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl @@ -0,0 +1,33 @@ +/* +** Copyright 2018, 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.telephony.mbms; + +import java.util.List; + +/** + * @hide + */ +oneway interface IMbmsGroupCallSessionCallback +{ + void onError(int errorCode, String message); + + void onAvailableSaisUpdated(in List currentSai, in List availableSais); + + void onServiceInterfaceAvailable(String interfaceName, int index); + + void onMiddlewareReady(); +} diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java new file mode 100644 index 000000000000..2910bb313d84 --- /dev/null +++ b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 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.telephony.mbms; + +import android.os.Binder; + +import java.util.concurrent.Executor; + +/** @hide */ +public class InternalGroupCallCallback extends IGroupCallCallback.Stub { + private final GroupCallCallback mAppCallback; + private final Executor mExecutor; + private volatile boolean mIsStopped = false; + + public InternalGroupCallCallback(GroupCallCallback appCallback, + Executor executor) { + mAppCallback = appCallback; + mExecutor = executor; + } + + @Override + public void onError(final int errorCode, final String message) { + if (mIsStopped) { + return; + } + + mExecutor.execute(new Runnable() { + @Override + public void run() { + long token = Binder.clearCallingIdentity(); + try { + mAppCallback.onError(errorCode, message); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }); + } + + @Override + public void onGroupCallStateChanged(final int state, final int reason) { + if (mIsStopped) { + return; + } + + mExecutor.execute(new Runnable() { + @Override + public void run() { + long token = Binder.clearCallingIdentity(); + try { + mAppCallback.onGroupCallStateChanged(state, reason); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }); + } + + @Override + public void onBroadcastSignalStrengthUpdated(final int signalStrength) { + if (mIsStopped) { + return; + } + + mExecutor.execute(new Runnable() { + @Override + public void run() { + long token = Binder.clearCallingIdentity(); + try { + mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }); + } + + /** Prevents this callback from calling the app */ + public void stop() { + mIsStopped = true; + } +} diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java new file mode 100644 index 000000000000..4c9cf4dd7c92 --- /dev/null +++ b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2018 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.telephony.mbms; + +import android.os.Binder; + +import java.util.List; +import java.util.concurrent.Executor; + +/** @hide */ +public class InternalGroupCallSessionCallback extends IMbmsGroupCallSessionCallback.Stub { + private final Executor mExecutor; + private final MbmsGroupCallSessionCallback mAppCallback; + private volatile boolean mIsStopped = false; + + public InternalGroupCallSessionCallback(MbmsGroupCallSessionCallback appCallback, + Executor executor) { + mAppCallback = appCallback; + mExecutor = executor; + } + + @Override + public void onError(final int errorCode, final String message) { + if (mIsStopped) { + return; + } + + mExecutor.execute(new Runnable() { + @Override + public void run() { + long token = Binder.clearCallingIdentity(); + try { + mAppCallback.onError(errorCode, message); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }); + } + + @Override + public void onAvailableSaisUpdated(final List currentSais, final List availableSais) { + if (mIsStopped) { + return; + } + + mExecutor.execute(new Runnable() { + @Override + public void run() { + long token = Binder.clearCallingIdentity(); + try { + mAppCallback.onAvailableSaisUpdated(currentSais, availableSais); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }); + } + + @Override + public void onServiceInterfaceAvailable(final String interfaceName, final int index) { + if (mIsStopped) { + return; + } + + mExecutor.execute(new Runnable() { + @Override + public void run() { + long token = Binder.clearCallingIdentity(); + try { + mAppCallback.onServiceInterfaceAvailable(interfaceName, index); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }); + } + + @Override + public void onMiddlewareReady() { + if (mIsStopped) { + return; + } + + mExecutor.execute(new Runnable() { + @Override + public void run() { + long token = Binder.clearCallingIdentity(); + try { + mAppCallback.onMiddlewareReady(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + }); + } + + /** Prevents this callback from calling the app */ + public void stop() { + mIsStopped = true; + } +} diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java new file mode 100644 index 000000000000..7da734ee5837 --- /dev/null +++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 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.telephony.mbms; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.content.Context; +import android.telephony.MbmsGroupCallSession; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * A callback class that is used to receive information from the middleware on MBMS group-call + * services. An instance of this object should be passed into + * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}. + */ +public class MbmsGroupCallSessionCallback { + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE, + MbmsErrors.ERROR_MIDDLEWARE_LOST, + MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND, + MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED, + MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE, + MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE, + MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY, + MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY, + MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE, + MbmsErrors.GeneralErrors.ERROR_IN_E911, + MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE, + MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM, + MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" }) + private @interface GroupCallError{} + + /** + * Called by the middleware when it has detected an error condition. The possible error codes + * are listed in {@link MbmsErrors}. + * @param errorCode The error code. + * @param message A human-readable message generated by the middleware for debugging purposes. + */ + public void onError(@GroupCallError int errorCode, @Nullable String message) { + } + + /** + * Indicates that the list of currently available SAIs has been updated. The app may use this + * information to filter the list of group calls when displaying available group calls to + * the user by matching the SAIs with a list of group calls separately negotiated with the + * carrier. The app may also report the aggregate list of SAIs to the group call application + * server so that a network entity can determine when, and where to activate the broadcast of + * particular group calls. + * @param currentSais The available SAIs on the current cell. + * @param availableSais A list of lists of available SAIS in neighboring cells, where each list + * contains the available SAIs in an individual cell. + */ + public void onAvailableSaisUpdated(List<Integer> currentSais, + List<List<Integer>> availableSais) { + } + + /** + * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied + * via this callback may be used to establish a data-link interface with the modem before the + * middleware is ready. + * Note that this method may be called before {@link #onMiddlewareReady()}. + * + * @param interfaceName The interface name for the data link. + * @param index The index for the data link. + */ + public void onServiceInterfaceAvailable(String interfaceName, int index) { + } + + /** + * Called to indicate that the middleware has been initialized and is ready. + * + * Before this method is called, calling any method on an instance of + * {@link MbmsGroupCallSession} will result in an {@link IllegalStateException} or an error + * delivered via {@link #onError(int, String)} with error code + * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}. + */ + public void onMiddlewareReady() { + } +} diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java index 06b2120b59c1..95b4d37e5840 100644 --- a/telephony/java/android/telephony/mbms/MbmsUtils.java +++ b/telephony/java/android/telephony/mbms/MbmsUtils.java @@ -23,6 +23,7 @@ import android.content.ServiceConnection; import android.content.pm.*; import android.content.pm.ServiceInfo; import android.telephony.MbmsDownloadSession; +import android.telephony.MbmsGroupCallSession; import android.telephony.MbmsStreamingSession; import android.util.Log; @@ -59,6 +60,9 @@ public class MbmsUtils { case MbmsStreamingSession.MBMS_STREAMING_SERVICE_ACTION: metaDataKey = MbmsStreamingSession.MBMS_STREAMING_SERVICE_OVERRIDE_METADATA; break; + case MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_ACTION: + metaDataKey = MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA; + break; } if (metaDataKey == null) { return null; diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl new file mode 100755 index 000000000000..721256a95396 --- /dev/null +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl @@ -0,0 +1,39 @@ +/* +** Copyright 2017, 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.telephony.mbms.vendor; + +import android.net.Uri; +import android.telephony.mbms.IMbmsGroupCallSessionCallback; +import android.telephony.mbms.IGroupCallCallback; + +/** + * @hide + */ +interface IMbmsGroupCallService +{ + int initialize(IMbmsGroupCallSessionCallback callback, int subId); + + void stopGroupCall(int subId, long tmgi); + + void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray, + in int[] frequencyArray); + + int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray, + in int[] frequencyArray, IGroupCallCallback callback); + + void dispose(int subId); +} diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java new file mode 100644 index 000000000000..3734ca7d6fc9 --- /dev/null +++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2018 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.telephony.mbms.vendor; + +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.mbms.GroupCallCallback; +import android.telephony.mbms.IGroupCallCallback; +import android.telephony.mbms.IMbmsGroupCallSessionCallback; +import android.telephony.mbms.MbmsErrors; +import android.telephony.mbms.MbmsGroupCallSessionCallback; +import android.telephony.mbms.vendor.IMbmsGroupCallService.Stub; + +import java.util.List; + +/** + * Base class for MBMS group-call services. The middleware should override this class to implement + * its {@link Service} for group calls + * Subclasses should call this class's {@link #onBind} method to obtain an {@link IBinder} if they + * override {@link #onBind}. + * @hide + */ +@SystemApi +@TestApi +public class MbmsGroupCallServiceBase extends Service { + private final IBinder mInterface = new Stub() { + @Override + public int initialize(final IMbmsGroupCallSessionCallback callback, + final int subscriptionId) throws RemoteException { + if (callback == null) { + throw new NullPointerException("Callback must not be null"); + } + + final int uid = Binder.getCallingUid(); + + int result = MbmsGroupCallServiceBase.this.initialize( + new MbmsGroupCallSessionCallback() { + @Override + public void onError(final int errorCode, final String message) { + try { + if (errorCode == MbmsErrors.UNKNOWN) { + throw new IllegalArgumentException( + "Middleware cannot send an unknown error."); + } + callback.onError(errorCode, message); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + @Override + public void onAvailableSaisUpdated(final List currentSais, + final List availableSais) { + try { + callback.onAvailableSaisUpdated(currentSais, availableSais); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + @Override + public void onServiceInterfaceAvailable(final String interfaceName, + final int index) { + try { + callback.onServiceInterfaceAvailable(interfaceName, index); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + @Override + public void onMiddlewareReady() { + try { + callback.onMiddlewareReady(); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + }, subscriptionId); + + if (result == MbmsErrors.SUCCESS) { + callback.asBinder().linkToDeath(new DeathRecipient() { + @Override + public void binderDied() { + onAppCallbackDied(uid, subscriptionId); + } + }, 0); + } + + return result; + } + + @Override + public void stopGroupCall(int subId, long tmgi) { + MbmsGroupCallServiceBase.this.stopGroupCall(subId, tmgi); + } + + @Override + public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray, + int[] frequencyArray) { + MbmsGroupCallServiceBase.this.updateGroupCall( + subscriptionId, tmgi, saiArray, frequencyArray); + } + + @Override + public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray, + final int[] frequencyArray, final IGroupCallCallback callback) + throws RemoteException { + if (callback == null) { + throw new NullPointerException("Callback must not be null"); + } + + final int uid = Binder.getCallingUid(); + + int result = MbmsGroupCallServiceBase.this.startGroupCall( + subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() { + @Override + public void onError(final int errorCode, final String message) { + try { + if (errorCode == MbmsErrors.UNKNOWN) { + throw new IllegalArgumentException( + "Middleware cannot send an unknown error."); + } + callback.onError(errorCode, message); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + public void onGroupCallStateChanged(int state, int reason) { + try { + callback.onGroupCallStateChanged(state, reason); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + + public void onBroadcastSignalStrengthUpdated(int signalStrength) { + try { + callback.onBroadcastSignalStrengthUpdated(signalStrength); + } catch (RemoteException e) { + onAppCallbackDied(uid, subscriptionId); + } + } + }); + + if (result == MbmsErrors.SUCCESS) { + callback.asBinder().linkToDeath(new DeathRecipient() { + @Override + public void binderDied() { + onAppCallbackDied(uid, subscriptionId); + } + }, 0); + } + + return result; + } + + @Override + public void dispose(int subId) throws RemoteException { + MbmsGroupCallServiceBase.this.dispose(subId); + } + }; + + /** + * Initialize the group call service for this app and subscription ID, registering the callback. + * + * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which + * will be intercepted and passed to the app as + * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE} + * + * May return any value from {@link MbmsErrors.InitializationErrors} + * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via + * {@link IMbmsGroupCallSessionCallback#onError(int, String)}. + * + * @param callback The callback to use to communicate with the app. + * @param subscriptionId The subscription ID to use. + */ + public int initialize(MbmsGroupCallSessionCallback callback, int subscriptionId) + throws RemoteException { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * Starts a particular group call. This method may perform asynchronous work. When + * the call is ready for consumption, the middleware should inform the app via + * {@link IGroupCallCallback#onGroupCallStateChanged(int, int)}. + * + * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException} + * + * @param subscriptionId The subscription id to use. + * @param tmgi The TMGI, an identifier for the group call. + * @param saiArray An array of SAIs for the group call. + * @param frequencyArray An array of frequencies for the group call. + * @param callback The callback object on which the app wishes to receive updates. + * @return Any error in {@link MbmsErrors.GeneralErrors} + */ + public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray, + GroupCallCallback callback) { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * Stop the group call identified by {@code tmgi}. + * + * The callback provided via {@link #startGroupCall} should no longer be + * used after this method has called by the app. + * + * May throw an {@link IllegalStateException} + * + * @param subscriptionId The subscription id to use. + * @param tmgi The TMGI for the call to stop. + */ + public void stopGroupCall(int subscriptionId, long tmgi) { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * Called when the app receives new SAI and frequency information for the group call identified + * by {@code tmgi}. + * @param saiArray New array of SAIs that the call is available on. + * @param frequencyArray New array of frequencies that the call is available on. + */ + public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray, + int[] frequencyArray) { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * Signals that the app wishes to dispose of the session identified by the + * {@code subscriptionId} argument and the caller's uid. No notification back to the + * app is required for this operation, and the corresponding callback provided via + * {@link #initialize} should no longer be used + * after this method has been called by the app. + * + * May throw an {@link IllegalStateException} + * + * @param subscriptionId The subscription id to use. + */ + public void dispose(int subscriptionId) throws RemoteException { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * Indicates that the app identified by the given UID and subscription ID has died. + * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}. + * @param subscriptionId The subscription ID the app is using. + */ + public void onAppCallbackDied(int uid, int subscriptionId) { + } + + @Override + public IBinder onBind(Intent intent) { + return mInterface; + } +} diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index 6521f0b41cb2..0ccd748c31df 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -232,5 +232,5 @@ interface ISub { */ int getSimStateForSlotIndex(int slotIndex); - boolean isActiveSubId(int subId); + boolean isActiveSubId(int subId, String callingPackage); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index e1c770c8deec..ca2bcff2f4cd 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -75,116 +75,6 @@ interface ITelephony { void call(String callingPackage, String number); /** - * End call if there is a call in progress, otherwise does nothing. - * - * @return whether it hung up - */ - boolean endCall(); - - /** - * End call on particular subId or go to the Home screen - * @param subId user preferred subId. - * @return whether it hung up - */ - boolean endCallForSubscriber(int subId); - - /** - * Answer the currently-ringing call. - * - * If there's already a current active call, that call will be - * automatically put on hold. If both lines are currently in use, the - * current active call will be ended. - * - * TODO: provide a flag to let the caller specify what policy to use - * if both lines are in use. (The current behavior is hardwired to - * "answer incoming, end ongoing", which is how the CALL button - * is specced to behave.) - * - * TODO: this should be a oneway call (especially since it's called - * directly from the key queue thread). - */ - void answerRingingCall(); - - /** - * Answer the currently-ringing call on particular subId . - * - * If there's already a current active call, that call will be - * automatically put on hold. If both lines are currently in use, the - * current active call will be ended. - * - * TODO: provide a flag to let the caller specify what policy to use - * if both lines are in use. (The current behavior is hardwired to - * "answer incoming, end ongoing", which is how the CALL button - * is specced to behave.) - * - * TODO: this should be a oneway call (especially since it's called - * directly from the key queue thread). - */ - void answerRingingCallForSubscriber(int subId); - - /** - * Silence the ringer if an incoming call is currently ringing. - * (If vibrating, stop the vibrator also.) - * - * It's safe to call this if the ringer has already been silenced, or - * even if there's no incoming call. (If so, this method will do nothing.) - * - * TODO: this should be a oneway call too (see above). - * (Actually *all* the methods here that return void can - * probably be oneway.) - */ - void silenceRinger(); - - /** - * Check if we are in either an active or holding call - * @param callingPackage the name of the package making the call. - * @return true if the phone state is OFFHOOK. - */ - boolean isOffhook(String callingPackage); - - /** - * Check if a particular subId has an active or holding call - * - * @param subId user preferred subId. - * @param callingPackage the name of the package making the call. - * @return true if the phone state is OFFHOOK. - */ - boolean isOffhookForSubscriber(int subId, String callingPackage); - - /** - * Check if an incoming phone call is ringing or call waiting - * on a particular subId. - * - * @param subId user preferred subId. - * @param callingPackage the name of the package making the call. - * @return true if the phone state is RINGING. - */ - boolean isRingingForSubscriber(int subId, String callingPackage); - - /** - * Check if an incoming phone call is ringing or call waiting. - * @param callingPackage the name of the package making the call. - * @return true if the phone state is RINGING. - */ - boolean isRinging(String callingPackage); - - /** - * Check if the phone is idle. - * @param callingPackage the name of the package making the call. - * @return true if the phone state is IDLE. - */ - boolean isIdle(String callingPackage); - - /** - * Check if the phone is idle on a particular subId. - * - * @param subId user preferred subId. - * @param callingPackage the name of the package making the call. - * @return true if the phone state is IDLE. - */ - boolean isIdleForSubscriber(int subId, String callingPackage); - - /** * Check to see if the radio is on or not. * @param callingPackage the name of the package making the call. * @return returns true if the radio is on. diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 4a6fe49f2676..1f60b71c3c0f 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -247,8 +247,14 @@ public class AppLaunch extends InstrumentationTestCase { mIterationCycle = false; // In the "applaunch.txt" file, trail launches is referenced using // "TRIAL_LAUNCH" - String appPkgName = mNameToIntent.get(launch.getApp()) - .getComponent().getPackageName(); + Intent startIntent = mNameToIntent.get(launch.getApp()); + if (startIntent == null) { + Log.w(TAG, "App does not exist: " + launch.getApp()); + mResult.putString(mNameToResultKey.get(launch.getApp()), + "App does not exist"); + continue; + } + String appPkgName = startIntent.getComponent().getPackageName(); if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) { assertTrue(String.format("Not able to compile the app : %s", appPkgName), compileApp(VERIFY_FILTER, appPkgName)); diff --git a/tests/DexLoggerIntegrationTests/AndroidManifest.xml b/tests/DexLoggerIntegrationTests/AndroidManifest.xml index a847e8f3b921..a9f01edbdc41 100644 --- a/tests/DexLoggerIntegrationTests/AndroidManifest.xml +++ b/tests/DexLoggerIntegrationTests/AndroidManifest.xml @@ -17,10 +17,10 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.dexloggertest"> - <!-- Tests feature introduced in P (27) --> + <!-- Tests feature introduced in P (28) --> <uses-sdk - android:minSdkVersion="27" - android:targetSdkVersion="27" /> + android:minSdkVersion="28" + android:targetSdkVersion="28" /> <uses-permission android:name="android.permission.READ_LOGS" /> diff --git a/tools/aapt2/io/FileStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp index c0eaa8e08418..7872738320c3 100644 --- a/tools/aapt2/io/FileStream_test.cpp +++ b/tools/aapt2/io/FileStream_test.cpp @@ -41,46 +41,46 @@ TEST(FileInputStreamTest, NextAndBackup) { ASSERT_FALSE(in.HadError()); EXPECT_THAT(in.ByteCount(), Eq(0u)); - const char* buffer; + const void* buffer; size_t size; - ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size)) << in.GetError(); + ASSERT_TRUE(in.Next(&buffer, &size)) << in.GetError(); ASSERT_THAT(size, Eq(10u)); ASSERT_THAT(buffer, NotNull()); EXPECT_THAT(in.ByteCount(), Eq(10u)); - EXPECT_THAT(StringPiece(buffer, size), Eq("this is a ")); + EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("this is a ")); - ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size)); + ASSERT_TRUE(in.Next(&buffer, &size)); ASSERT_THAT(size, Eq(10u)); ASSERT_THAT(buffer, NotNull()); EXPECT_THAT(in.ByteCount(), Eq(20u)); - EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin")); + EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("cool strin")); in.BackUp(5u); EXPECT_THAT(in.ByteCount(), Eq(15u)); - ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size)); + ASSERT_TRUE(in.Next(&buffer, &size)); ASSERT_THAT(size, Eq(5u)); ASSERT_THAT(buffer, NotNull()); ASSERT_THAT(in.ByteCount(), Eq(20u)); - EXPECT_THAT(StringPiece(buffer, size), Eq("strin")); + EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("strin")); // Backup 1 more than possible. Should clamp. in.BackUp(11u); EXPECT_THAT(in.ByteCount(), Eq(10u)); - ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size)); + ASSERT_TRUE(in.Next(&buffer, &size)); ASSERT_THAT(size, Eq(10u)); ASSERT_THAT(buffer, NotNull()); ASSERT_THAT(in.ByteCount(), Eq(20u)); - EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin")); + EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("cool strin")); - ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size)); + ASSERT_TRUE(in.Next(&buffer, &size)); ASSERT_THAT(size, Eq(1u)); ASSERT_THAT(buffer, NotNull()); ASSERT_THAT(in.ByteCount(), Eq(21u)); - EXPECT_THAT(StringPiece(buffer, size), Eq("g")); + EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("g")); - EXPECT_FALSE(in.Next(reinterpret_cast<const void**>(&buffer), &size)); + EXPECT_FALSE(in.Next(&buffer, &size)); EXPECT_FALSE(in.HadError()); } @@ -93,25 +93,25 @@ TEST(FileOutputStreamTest, NextAndBackup) { ASSERT_FALSE(out.HadError()); EXPECT_THAT(out.ByteCount(), Eq(0u)); - char* buffer; + void* buffer; size_t size; - ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size)); + ASSERT_TRUE(out.Next(&buffer, &size)); ASSERT_THAT(size, Eq(10u)); ASSERT_THAT(buffer, NotNull()); EXPECT_THAT(out.ByteCount(), Eq(10u)); - memcpy(buffer, input.c_str(), size); + memcpy(reinterpret_cast<char*>(buffer), input.c_str(), size); - ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size)); + ASSERT_TRUE(out.Next(&buffer, &size)); ASSERT_THAT(size, Eq(10u)); ASSERT_THAT(buffer, NotNull()); EXPECT_THAT(out.ByteCount(), Eq(20u)); - memcpy(buffer, input.c_str() + 10u, size); + memcpy(reinterpret_cast<char*>(buffer), input.c_str() + 10u, size); - ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size)); + ASSERT_TRUE(out.Next(&buffer, &size)); ASSERT_THAT(size, Eq(10u)); ASSERT_THAT(buffer, NotNull()); EXPECT_THAT(out.ByteCount(), Eq(30u)); - buffer[0] = input[20u]; + reinterpret_cast<char*>(buffer)[0] = input[20u]; out.BackUp(size - 1); EXPECT_THAT(out.ByteCount(), Eq(21u)); |