Merge "MediaPlayer2: allow prepared->pause"
diff --git a/Android.bp b/Android.bp
index a603006..faad6f3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -77,6 +77,7 @@
"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 @@
"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 @@
// Loaded with System.loadLibrary by android.view.textclassifier
required: [
- "libtextclassifier",
"libmedia2_jni",
],
@@ -807,12 +810,16 @@
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 {
+ // 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 {
+ // 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 de83f3e..ff40f75 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,6 +8,11 @@
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 210f956..1b09710 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -37,6 +37,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 1009b67..5785e4a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -950,6 +950,7 @@
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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 9012c33..2246562 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -148,6 +148,10 @@
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 f4d7cbc..9567616 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1188,6 +1188,10 @@
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 @@
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 485b91f..1e9c354 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -80,7 +80,8 @@
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 @@
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 @@
}
/**
+ * 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 @@
// 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 @@
// 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 5a0172b..66392f8 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -169,7 +169,7 @@
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 0472461..ac16fd3 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2076,27 +2076,20 @@
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;->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 d93befd..56ca98f 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1022,6 +1022,7 @@
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$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 2ee266d..2acae1c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1588,11 +1588,13 @@
* @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 @@
*/
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 @@
@UnsupportedAppUsage
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
+ getApplication().dispatchActivityPreCreated(this, icicle);
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
@@ -7209,6 +7214,7 @@
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 @@
}
final void performStart(String reason) {
+ getApplication().dispatchActivityPreStarted(this);
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
mFragments.noteStateNotSaved();
mCalled = false;
@@ -7284,6 +7291,7 @@
}
mActivityTransitionState.enterReady(this);
+ getApplication().dispatchActivityPostStarted(this);
}
/**
@@ -7338,6 +7346,7 @@
}
final void performResume(boolean followedByPause, String reason) {
+ getApplication().dispatchActivityPreResumed(this);
performRestart(true /* start */, reason);
mFragments.execPendingActions();
@@ -7387,9 +7396,11 @@
"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 @@
"Activity " + mComponent.toShortString() +
" did not call through to super.onPause()");
}
+ getApplication().dispatchActivityPostPaused(this);
}
final void performUserLeaving() {
@@ -7417,6 +7429,7 @@
mCanEnterPictureInPicture = false;
if (!mStopped) {
+ getApplication().dispatchActivityPreStopped(this);
if (mWindow != null) {
mWindow.closeAllPanels();
}
@@ -7451,11 +7464,13 @@
}
mStopped = true;
+ getApplication().dispatchActivityPostStopped(this);
}
mResumed = false;
}
final void performDestroy() {
+ getApplication().dispatchActivityPreDestroyed(this);
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
@@ -7465,6 +7480,7 @@
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 be1f2db..294a3ec 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -19,11 +19,14 @@
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 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 68b745d..e12942f 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -65,13 +65,144 @@
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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
}
+ @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
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
rename to core/java/android/app/ISmsAppService.aidl
index c50d6d6..1ac2ec6 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 39db16d..9bfdae0 100644
--- a/core/java/android/app/ProcessMemoryState.java
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -23,19 +23,20 @@
* 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 @@
this.rssInBytes = rssInBytes;
this.cacheInBytes = cacheInBytes;
this.swapInBytes = swapInBytes;
+ this.rssHighWatermarkInBytes = rssHighWatermarkInBytes;
}
private ProcessMemoryState(Parcel in) {
@@ -55,6 +57,7 @@
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 @@
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 0000000..3f2b025
--- /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 41dec8f..0044005 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -134,6 +134,7 @@
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 @@
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 183be5f..559a59b 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -73,6 +73,8 @@
/** 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 @@
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 bd1e6a4..a64eead 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -51,6 +51,8 @@
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.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 @@
* <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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 @@
@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 @@
@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 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 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 @@
@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 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 @@
@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 @@
@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 @@
@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 @@
*/
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 @@
}
/** @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 @@
}
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 e3d9b19..7dc4577 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -101,13 +101,9 @@
}
/** @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 @@
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 981be83..d711574 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3088,6 +3088,7 @@
//@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 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 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 03a3d1f..1fa5190 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.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.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 @@
}
}
- /** @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 @@
};
/**
- * 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 @@
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 d967fba..7810e6c 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1889,7 +1889,7 @@
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 b948402..ae12f93 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -410,14 +410,6 @@
@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 @@
mShowInputFlags = 0;
mShowInputRequested = false;
doHideWindow();
- clearInsetOfPreviousIme();
if (resultReceiver != null) {
resultReceiver.send(wasVis != isInputViewShown()
? InputMethodManager.RESULT_HIDDEN
@@ -601,7 +592,6 @@
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 @@
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 @@
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 @@
}
/**
- * 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 @@
+ " 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 0000000..aa44eb7
--- /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 119f5d0..4f4d62a 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 @@
}
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 26223f7..163e3d5 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -115,6 +115,12 @@
*/
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 b49a949..44dfd11 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -1230,7 +1230,7 @@
// 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 @@
}
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 7bcc685..d5aad33 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 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 @@
}
/**
- * 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 f8e8762..c4ef77a 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -189,6 +189,8 @@
final ViewGroup sceneRoot = scene.getSceneRoot();
if (!sPendingTransitions.contains(sceneRoot)) {
if (transition == null) {
+ exitPreviousScene(sceneRoot);
+
scene.enter();
} else {
sPendingTransitions.add(sceneRoot);
@@ -210,6 +212,14 @@
}
}
+ 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 @@
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 553511d..edd09f8 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -40,7 +40,7 @@
*
* @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 @@
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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
}
@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 @@
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 4b946d7..667fab5 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -19,6 +19,7 @@
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 @@
*
* @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 0000000..b0556a3
--- /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 982e5c2..8ae9127 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -24,7 +24,6 @@
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 @@
* @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 @@
private RenderNode(long nativePtr) {
mNativeRenderNode = nativePtr;
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode);
- mOwningView = null;
+ mAnimationHost = null;
}
/**
@@ -174,8 +173,8 @@
* @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 @@
}
/**
+ * 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 @@
// 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 @@
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 514a11e..e71182c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -209,7 +209,7 @@
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 @@
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 6737839..0986b89 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -25,7 +25,6 @@
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 @@
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 f4be9f2..1eb35c5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4864,7 +4864,7 @@
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 @@
@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 @@
*/
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 0000000..58f555d
--- /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 2ee7ab9..5bc44ce 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -52,7 +52,6 @@
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 @@
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 ViewRootImpl(Context context, Display display) {
mContext = context;
+ mDisplayContext = context.createDisplayContext(display);
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
@@ -984,6 +990,9 @@
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 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 @@
} else {
mDisplay = preferredDisplay;
}
+ mDisplayContext = mContext.createDisplayContext(mDisplay);
}
void pokeDrawLockIfNeeded() {
@@ -2579,7 +2591,7 @@
.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 @@
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 @@
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 @@
@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 29339e9..3e240cf 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.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 @@
@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 @@
&& 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 @@
} 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 @@
&& 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 @@
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 @@
? 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 @@
}
}
- private TextClassifierImplNative getNative(LocaleList localeList)
+ private AnnotatorModel getNative(LocaleList localeList)
throws FileNotFoundException {
synchronized (mLock) {
localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
@@ -310,7 +312,7 @@
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 @@
}
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 @@
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 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 3d4c8f2..0000000
--- 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 ae6a2fd..23d1237 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -25,6 +25,13 @@
* 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 0843e26..9beeaa5 100644
--- a/core/java/android/webkit/RenderProcessGoneDetail.java
+++ b/core/java/android/webkit/RenderProcessGoneDetail.java
@@ -22,6 +22,13 @@
**/
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 7839a00..ca33a0c 100644
--- a/core/java/android/webkit/SafeBrowsingResponse.java
+++ b/core/java/android/webkit/SafeBrowsingResponse.java
@@ -27,6 +27,12 @@
* {@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 3517c74..d7e0715 100644
--- a/core/java/android/webkit/ServiceWorkerController.java
+++ b/core/java/android/webkit/ServiceWorkerController.java
@@ -38,6 +38,14 @@
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 30f465c..9908182 100644
--- a/core/java/android/webkit/TracingController.java
+++ b/core/java/android/webkit/TracingController.java
@@ -43,6 +43,13 @@
* </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 f6166c5..f346c60 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -31,6 +31,14 @@
*/
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 8dd30f6..9d74c98 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -388,7 +388,8 @@
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 8e2786d..16ddd0f 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -119,8 +119,9 @@
*
* @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 bbfac44..f95b3ce 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -321,6 +321,12 @@
* @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 44867c7..0c203ab 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 @@
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 f6e99ba..e7ac566 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 @@
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 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 @@
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 ((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 (!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())) {
+ if (!procMatch) {
continue;
}
- if (activeOnly && !proc.isInUse()) {
- pw.print(" (Not active: ");
- pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")");
- 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(" 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.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 ((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);
}
- if (activeOnly && !proc.isInUse()) {
- 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);
}
- 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 ((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);
+ }
}
- 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 (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);
}
}
}
}
- 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 ((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);
}
- 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(" Total procs: "); pw.print(numShownProcs);
+ pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
}
if (dumpAll) {
@@ -1598,8 +1646,7 @@
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 @@
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 @@
}
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 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 @@
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 @@
}
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);
- }
- }
-
- 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_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);
}
}
}
- proto.end(token);
+ 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);
+ }
+ }
+ }
+ }
}
final public static class ProcessStateHolder {
@@ -2110,30 +2175,39 @@
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 c32894d..2671781 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -28,7 +28,6 @@
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 f0e8dc5..cdb986a 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -135,22 +135,6 @@
}
/**
- * 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 31f13c3..33b9ff7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -337,6 +337,9 @@
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 eba4c50..343aef2 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -674,7 +674,7 @@
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 0701f3e..63b0046 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -473,8 +473,8 @@
// 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 @@
return;
}
- env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod,
+ env->CallVoidMethod(localref, gPositionListener_PositionLostMethod,
info ? info->canvasContext.getFrameNumber() : 0);
env->DeleteLocalRef(localref);
}
@@ -555,7 +555,7 @@
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 @@
{ "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 @@
};
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 9d5f0bc..ab50ad1 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -130,6 +130,13 @@
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 0000000..a375f02
--- /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 5a1f2e8..ade0b11 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 7325a41..938e36d 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 0712cbc..48b59c7 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 227972e..fdd0624 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.xml b/core/res/res/values-watch/themes.xml
deleted file mode 100644
index 1be47ba..0000000
--- a/core/res/res/values-watch/themes.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<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>
- </style>
-</resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index a7736e7c..bfba312 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 @@
<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 6053728..32671ac8 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_background">@color/black</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_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 1404383..9aebf6c 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 7d14f86..a0c02ed 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 0000000..f6ff1b6
--- /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 cf85986..9f2256a 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 f951f27..3385527 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1681,11 +1681,14 @@
</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 @@
<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 0000000..2142f27
--- /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 53e9826..fa37bed 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -28,11 +28,10 @@
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
similarity index 97%
rename from core/java/android/view/RecordingCanvas.java
rename to graphics/java/android/graphics/BaseRecordingCanvas.java
index 3364483..6e93691 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 @@
*
* @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 33caa00..69ff3bc 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -592,49 +592,6 @@
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 @@
}
/**
+ * 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 76f2cfb..6c1372f 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -51,6 +51,7 @@
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 @@
/**
* @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 @@
}
}
+ @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 1458c66..bd1ac25 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -422,9 +422,10 @@
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 @@
}
/**
- * 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 f4a2199..5e80749 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -45,7 +45,7 @@
/**
* 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 @@
/**
* 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 e60d43e..285a1a5 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 @@
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 @@
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 @@
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 @@
&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 @@
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 7a539ae..c211f5d 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -139,6 +139,8 @@
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 @@
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 @@
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 0000000..7e7335d
--- /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 86f9032..1b1bf8d 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -30,6 +30,7 @@
"SystemUIPluginLib",
"SystemUISharedLib",
"SettingsLib",
+ "android.car.user",
"androidx.car_car",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
@@ -51,7 +52,6 @@
libs: [
"telephony-common",
"android.car",
- "android.car.user",
],
manifest: "AndroidManifest.xml",
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 943dd84..ee4c954 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -14,6 +14,7 @@
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 0000000..b56181d
--- /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 0000000..7e71308
--- /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 0000000..6d35550
--- /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 0000000..e2a7a19
--- /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 0000000..593b6f5
--- /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 a710410..58feef5 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 @@
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 e13e566..988062d 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 @@
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 b9f7323..750a843 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.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 @@
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 @@
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 @@
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 @@
*/
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 @@
}
return;
}
- mProfileConnectionState.put(profile, newProfileState);
if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
if (profile instanceof MapProfile) {
profile.setPreferred(mDevice, true);
@@ -161,18 +158,6 @@
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 @@
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 @@
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 @@
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 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 @@
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 5a64e02..21cf0c2 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 @@
{
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 @@
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 2dd8eaf..62507f5 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 @@
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 1eeb4f0..8bc0acf 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 @@
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 4b6a22c..4879144 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 @@
}
@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 c8d4fc8..61e5b6b 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 @@
}
@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 fe6b222..75d16db 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 @@
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 0447f37..4b0ca74 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 @@
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 7000f9d..0c29f43 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 @@
}
}
- 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 7ad2e28c..1e22f44 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 @@
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 caea04f..7582024 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 @@
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 dfd1622..e1e5dbe 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 @@
// 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 02afe8d..7b81162 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 @@
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 8fefb2f..1f15601 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 @@
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 e9d8cb5..adef0841 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 @@
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 61602c6..9a6f104 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 @@
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 c3241bb..74bd97f 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 @@
/**
* 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 0000000..2d8c0de
--- /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 0000000..80e1356
--- /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
similarity index 77%
rename from packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java
rename to packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java
index a070b2a..34e6097 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.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 @@
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 @@
@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 @@
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 @@
return this;
}
- public Builder setSubscriptionId(int subId) {
+ public Builder setSubscriberId(String subId) {
mSubId = subId;
return this;
}
@@ -110,8 +105,8 @@
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 0ca2e87..ede248b 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 @@
@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 7baded8..62b5688 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 c18db11..f6201dd 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 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 @@
}
@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 @@
"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 @@
"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 @@
}
@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 @@
// 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 @@
// 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 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 @@
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 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 @@
"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 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 @@
"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 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 @@
// 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 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 @@
// 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 @@
// 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 @@
}
@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 @@
}
@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 @@
}
@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 @@
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 @@
}
@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 @@
}
@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 @@
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 c23ad79..887c1d5 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.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 @@
@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 @@
@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 @@
@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 0000000..4c4207b
--- /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 0000000..10c9dfb
--- /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 03ed62b..75f43f9 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/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
new file mode 100644
index 0000000..3ab6b56
--- /dev/null
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -0,0 +1,27 @@
+<?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 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 ab89043..e253360 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.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 @@
* 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 b04d047..c7910f9 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 @@
}
/**
+ * 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 d83b36d..3191d14 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 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 5ffdc7b..2daa33b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -377,6 +377,7 @@
@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 b159b39..b8df3c06 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -342,12 +342,11 @@
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 10c8ec0..f1b53fe 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.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 @@
}
}
- 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 77f4bf5..408e599 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -256,12 +256,6 @@
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 bd2b7a5..1af2156 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.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.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 @@
}
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 @@
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 f9dbf4a..fb343f9 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -22,27 +22,10 @@
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 36b2347..c566460 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 @@
/**
* 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 @@
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 @@
resetBrightnessToDefault();
break;
case FINISH:
- setLightSensorEnabled(false);
+ onDestroy();
break;
}
if (newState != DozeMachine.State.FINISH) {
@@ -95,6 +120,13 @@
}
}
+ 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 @@
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 @@
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 @@
}
}
+ @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 745f312..d833c16 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 @@
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 757c821..4988f07 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 @@
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 0cedf98..74f7706 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 @@
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.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 @@
}
}
- 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 b7164cb..864a6f9 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 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 @@
}
// 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 @@
mPipManager.initialize(mContext);
getComponent(CommandQueue.class).addCallbacks(this);
+ putComponent(PipUI.class, this);
}
@Override
@@ -65,6 +67,10 @@
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
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/recents/misc/ForegroundThread.java
rename to packages/SystemUI/src/com/android/systemui/pip/phone/ForegroundThread.java
index 784ac4e..9bf46bb 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 5547e2d..9ce2606 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 @@
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 @@
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 ee15655..04746c1 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.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 @@
/**
* 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 @@
mMenuController.onActivityPinned();
mAppOpsListener.onActivityPinned(packageName);
- SystemServicesProxy.getInstance(mContext).setPipVisibility(true);
+ Dependency.get(UiOffloadThread.class).submit(() -> {
+ WindowManagerWrapper.getInstance().setPipVisibility(true);
+ });
}
@Override
@@ -93,7 +94,9 @@
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 f0ab046..ce7da79 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.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.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 @@
*/
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 020c550..43e9db7 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.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 @@
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 @@
}
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 c36cdf6..79e5086 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 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 @@
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 @@
.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 7a57fdd..8b2e1d5 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 @@
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 12daff1..9edd65e1 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.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 @@
@Override
protected void handleClick() {
+ if (getAdapter() == null) {
+ return;
+ }
if (!getAdapter().isEnabled()) {
getAdapter().enable();
} else {
@@ -96,13 +100,13 @@
@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 fc1831d..90c1099 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 @@
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 58d8d8f..e977144 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 @@
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 8bb3c02..74f6c2d 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.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 @@
mImpl.onBootCompleted();
}
+ public void growRecents() {
+ EventBus.getDefault().send(new RecentsGrowingEvent());
+ }
+
/**
* Shows the Recents.
*/
@@ -463,7 +468,7 @@
}
@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 @@
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 @@
}
}
- @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 @@
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 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 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 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 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 63a65d0..d95c731 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 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.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 @@
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 beec4b3..a1da785 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 @@
}
@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 @@
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 ff1f7dc..c5e9f04 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 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 @@
}
@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 f1bc214..9e3ced3 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 @@
*/
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 1178725..3ed5f70 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 @@
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 @@
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 750002c..64fa8f8 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 @@
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.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 @@
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 da0a435..ea194a7 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 android.content.res.Configuration;
-import android.os.RemoteException;
-import android.view.IDockedStackListener;
-import android.view.LayoutInflater;
-import android.view.View;
-
-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 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 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 @@
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 @@
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 @@
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 98925b9..fa01af6 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.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.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 @@
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 @@
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 @@
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 @@
@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 @@
}
}
- @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 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 @@
mWindowManager.setSlippery(false);
liftBackground();
}
- EventBus.getDefault().send(new StartedDragingEvent());
+ if (mCallback != null) {
+ mCallback.onDraggingStart();
+ }
return mDockSide != WindowManager.DOCKED_INVALID;
}
@@ -617,7 +613,9 @@
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 @@
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 @@
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 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 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 @@
mSnapAlgorithm.getMiddleTarget());
}
- public void onRecentsDrawn() {
+ void onRecentsDrawn() {
updateDockSide();
final int position = calculatePositionForInsetBounds();
if (mState.animateAfterRecentsDrawn) {
@@ -1314,13 +1310,15 @@
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 4415bd7..02f7505 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 @@
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 826fa6c..f66db48 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.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 @@
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 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 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 @@
}
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 247e3d3..00e0b95 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.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.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 @@
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 d6886f5..89a842e 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 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 @@
* 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 @@
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 @@
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 @@
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 @@
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 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 304a00f..2450e44 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.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 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 67e512c..618a4c1 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 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 935eaac..a3e982e 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.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.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 @@
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 @@
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 @@
} 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 @@
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 @@
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 9e2331f..903c272 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 @@
};
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 @@
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 @@
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 4f957bf..c7ab27b 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 @@
}
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 cbbb0e3..9c579da 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.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.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 @@
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 @@
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 @@
}
};
- 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 8c02e1f..62d2099 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 {
+public class NavigationBarGestureHelper implements 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;
+ private static final String TAG = "NavigationBarGestureHelper";
- /**
- * When dragging from the navigation bar, we drag in recents.
- */
- public static final int DRAG_MODE_RECENTS = 0;
-
- /**
- * When dragging from the navigation bar, we drag the divider.
- */
- public static final int DRAG_MODE_DIVIDER = 1;
-
- 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 @@
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 @@
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 aebcb9f..e5c9100 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 @@
private Configuration mConfiguration;
private NavigationBarInflaterView mNavigationInflaterView;
- private RecentsComponent mRecentsComponent;
- private Divider mDivider;
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
@@ -314,14 +312,10 @@
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 @@
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 @@
@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 09833d4..1524f80 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 @@
}
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 1afdc66b..553165b 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.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 @@
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 45bcc4e..226b645 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.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.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.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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
}
}
updateDozingState();
- updatePublicMode();
- mEntryManager.updateNotifications();
checkBarModes();
updateScrimController();
updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
@@ -4053,8 +4025,6 @@
@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 @@
@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 @@
protected WindowManager mWindowManager;
protected IWindowManager mWindowManagerService;
+ private IDreamManager mDreamManager;
protected Display mDisplay;
@@ -4999,7 +4970,13 @@
}
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 e9efaa1..0a72c3f 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 @@
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 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 @@
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
synchronized (mCallbacks) {
mCallbacks.remove(callback);
-
updateWifiStateListeners(!mCallbacks.isEmpty());
}
}
@@ -112,7 +109,6 @@
* @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 @@
@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 @@
}
/**
- * 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
+ * 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) {
- 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
- */
- 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;
+ }
+
+ fireHotspotChangedCallback();
+ }
+
+ private void maybeResetSoftApState() {
+ if (!mWaitingForTerminalState) {
+ return; // Only reset soft AP state if enabled from this controller.
+ }
+ 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;
+ }
}
@Override
public void onNumClientsChanged(int numConnectedDevices) {
mNumConnectedDevices = numConnectedDevices;
- fireHotspotChangedCallback(isHotspotEnabled(), numConnectedDevices);
- }
-
- 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.
- }
-
- @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.
- }
- }
-
- /**
- * 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());
- }
+ 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 0000000..cfe9818
--- /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 eaa0dcf..1cf73b0 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.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 @@
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 @@
}
@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 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 f8aa28d..199c4c2 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.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 @@
private @Mock IActivityTaskManager mIActivityTaskManager;
private WorkLockActivityController mController;
- private SysUiTaskStackChangeListener mTaskStackListener;
+ private TaskStackChangeListener mTaskStackListener;
@Before
public void setUp() throws Exception {
@@ -75,8 +75,8 @@
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 a7758a6..515c109 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.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 @@
// 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 @@
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 c236fbe..ca968a8 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 @@
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 @@
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 @@
}
@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 @@
}, 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 @@
}, 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 1e3d42b..b545e61 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.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 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 @@
@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 @@
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 5006b0b..da93327 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.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 @@
@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 @@
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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 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 de85dcb..90c10fd 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6524,6 +6524,31 @@
// 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 5962406..fe86ab3 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 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 @@
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 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 @@
}
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 @@
}
mMetricsLogger.write(log);
}
- }, isUpdate, compatMode);
+ }, mUiModeMgr.isNightMode(), isUpdate, compatMode);
});
}
@@ -368,6 +374,7 @@
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 68a495f..742d494 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 9d3d3cb..89b442e 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 @@
*/
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 @@
private final String mServicePackageName;
private final ComponentName mComponentName;
private final boolean mCompatMode;
+ private final int mThemeId;
private boolean mDestroyed;
@@ -152,7 +155,9 @@
@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 @@
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 @@
}
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 @@
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 @@
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 0000000..5bec1a9
--- /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 0000000..2d2e88a
--- /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 0000000..3a6d1f6
--- /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 0000000..e2ca351
--- /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/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java b/services/backup/java/com/android/server/backup/keyvalue/BackupException.java
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
copy to services/backup/java/com/android/server/backup/keyvalue/BackupException.java
index 5d19851..27b2d35 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/BackupException.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.
@@ -14,12 +14,20 @@
* limitations under the License
*/
-package com.android.systemui.stackdivider.events;
+package com.android.server.backup.keyvalue;
-import com.android.systemui.recents.events.EventBus;
+import android.util.AndroidException;
/**
- * Sent when the divider is being draged either manually or by an animation.
+ * Key-value backup task exception.
+ *
+ * @see AgentException
+ * @see TaskException
*/
-public class StartedDragingEvent extends EventBus.Event {
+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 54e6b1d..bb8a1d1 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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
}
}
- 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 @@
/* 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 e915ce1..6904b3f 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.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.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.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 @@
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 @@
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 @@
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 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;
+ /** 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);
}
- 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;
- }
- return sendDataToTransport();
}
@Override
@@ -378,11 +377,10 @@
@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 @@
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 @@
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;
+ throw TaskException.stateCompromised();
}
-
- if (mStatus != BackupTransport.TRANSPORT_OK) {
- mBackupManagerService.resetBackupState(mStateDirectory);
- return false;
- }
-
- return true;
}
private PerformFullTransportBackupTask createFullBackupTask(List<String> packages) {
@@ -446,120 +440,82 @@
mUserInitiated);
}
- /** Returns whether to consume next queue package. */
- private boolean backupPm() {
- RemoteResult agentResult = null;
+ private void backupPm() throws TaskException {
+ mReporter.onStartPackageBackup(PM_PACKAGE);
+ mCurrentPackage = new PackageInfo();
+ mCurrentPackage.packageName = PM_PACKAGE;
+
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());
-
- Pair<Integer, RemoteResult> statusAndResult = extractAgentData(PM_PACKAGE, mAgent);
- mStatus = statusAndResult.first;
- agentResult = statusAndResult.second;
- } catch (Exception e) {
+ extractPmAgentData(mCurrentPackage);
+ int status = sendDataToTransport(mCurrentPackage);
+ cleanUpAgentForTransportStatus(status);
+ } catch (AgentException | TaskException e) {
mReporter.onExtractPmAgentDataError(e);
- mStatus = BackupTransport.TRANSPORT_ERROR;
+ cleanUpAgentForError(e);
+ // PM agent failure is task failure.
+ throw TaskException.stateCompromised(e);
}
-
- 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;
- }
-
- 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;
- }
-
- 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;
- }
- } catch (PackageManager.NameNotFoundException e) {
- mStatus = BackupTransport.AGENT_UNKNOWN;
- } finally {
- mBackupManagerService.setWorkSource(null);
+ extractAgentData(mCurrentPackage);
+ int status = sendDataToTransport(mCurrentPackage);
+ cleanUpAgentForTransportStatus(status);
+ } catch (AgentException | TaskException e) {
+ cleanUpAgentForError(e);
+ throw e;
}
-
- 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) {
- mReporter.onAgentError(packageName);
- mBackupManagerService.dataChangedImpl(packageName);
- mStatus = BackupTransport.TRANSPORT_OK;
- return true;
- }
-
- if (mStatus == BackupTransport.AGENT_UNKNOWN) {
- mReporter.onAgentUnknown(packageName);
- mStatus = BackupTransport.TRANSPORT_OK;
- return true;
- }
-
- // Transport-level failure, re-enqueue everything.
- revertTask();
- return false;
- }
-
- Preconditions.checkNotNull(agentResult);
- return handleAgentResult(mCurrentPackage, agentResult);
}
- private void finishTask() {
+ private PackageInfo getPackageForBackup(String packageName) throws AgentException {
+ final PackageInfo packageInfo;
+ try {
+ packageInfo =
+ mPackageManager.getPackageInfo(
+ packageName, PackageManager.GET_SIGNING_CERTIFICATES);
+ } catch (PackageManager.NameNotFoundException e) {
+ 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;
+ }
+
+ 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);
+ throw AgentException.transitory();
+ }
+ } catch (SecurityException e) {
+ mReporter.onBindAgentError(packageName, e);
+ throw AgentException.transitory(e);
+ }
+ return agent;
+ }
+
+ 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 @@
// 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 @@
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 @@
}
if (!mCancelled
- && mStatus == BackupTransport.TRANSPORT_OK
+ && status == BackupTransport.TRANSPORT_OK
&& mFullBackupTask != null
&& !mPendingFullBackups.isEmpty()) {
mReporter.onStartFullBackup(mPendingFullBackups);
@@ -621,7 +582,7 @@
mFullBackupTask.unregisterTask();
}
mTaskFinishedListener.onFinished(callerLogString);
- mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, mStatus));
+ mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, status));
mBackupManagerService.getWakelock().release();
}
@@ -642,17 +603,12 @@
}
@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 @@
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 @@
"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 @@
}
}
- /** 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;
-
- 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;
- }
+ writeWidgetPayloadIfAppropriate(mBackupData.getFileDescriptor(), packageName);
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);
-
- 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;
- }
- } else {
- mReporter.onEmptyData(mCurrentPackage);
- mStatus = BackupTransport.TRANSPORT_OK;
- }
-
- if (nonIncremental
- && mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
- mReporter.onNonIncrementalAndNonIncrementalRequired();
- mStatus = BackupTransport.TRANSPORT_ERROR;
- }
-
-
- 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;
+ int status = transportPerformBackup(mCurrentPackage, mBackupDataFile, nonIncremental);
+ handleTransportStatus(status, packageName, mBackupDataFile.length());
+ return status;
}
- /** Returns whether to consume next queue package. */
- private boolean handleTransportStatus(int status, String packageName, long size) {
+ 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;
+ }
+
+ 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);
+
+ status = transport.performBackup(packageInfo, backupData, flags);
+ if (status == BackupTransport.TRANSPORT_OK) {
+ status = transport.finishBackup();
+ }
+ } catch (Exception e) {
+ mReporter.onPackageBackupTransportError(packageName, e);
+ throw TaskException.causedBy(e);
+ }
+
+ if (nonIncremental && status == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
+ mReporter.onPackageBackupNonIncrementalAndNonIncrementalRequired(packageName);
+ throw TaskException.create();
+ }
+
+ return status;
+ }
+
+ 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 @@
}
/**
- * 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 @@
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 @@
}
}
- /** Cleans-up after having called the agent. */
+ /**
+ * 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);
+ }
+
+ /**
+ * Cleans up agent resources opened by {@link #extractAgentData(PackageInfo)} according to
+ * transport status returned in {@link #sendDataToTransport(PackageInfo)}.
+ */
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();
- }
-
- private void updateFiles(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 @@
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 @@
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 0000000..08d2895
--- /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/StoppedDragingEvent.java b/services/core/java/com/android/server/UiModeManagerInternal.java
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
copy to services/core/java/com/android/server/UiModeManagerInternal.java
index c50d6d6..ef29069 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.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 isn't draging anymore.
+ * UiModeManager local system service interface.
+ *
+ * @hide Only for use within the system server.
*/
-public class StoppedDragingEvent 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 f436286..5538e72 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -108,6 +108,8 @@
private PowerManager.WakeLock mWakeLock;
+ private final LocalService mLocalService = new LocalService();
+
public UiModeManagerService(Context context) {
super(context);
}
@@ -242,6 +244,7 @@
}, TAG + ".onStart");
publishBinderService(Context.UI_MODE_SERVICE, mService);
+ publishLocalService(UiModeManagerInternal.class, mLocalService);
}
private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
@@ -367,7 +370,8 @@
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 @@
}
}
}
+
+ 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 510d333..461d39d 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 @@
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 01421c7..fab967c 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 @@
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 8c7fc84..692f9cf 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_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 @@
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 @@
String mDeviceOwnerName;
final UserController mUserController;
+ final PendingIntentController mPendingIntentController;
final AppErrors mAppErrors;
@@ -821,12 +820,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
mSystemThread = null;
mUiHandler = injector.getUiHandler(null);
mUserController = null;
+ mPendingIntentController = null;
mProcStartHandlerThread = null;
mProcStartHandler = null;
mHiddenApiBlacklist = null;
@@ -2407,7 +2381,8 @@
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 @@
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 @@
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 @@
}
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 @@
}
}
- 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 @@
@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 @@
pw.println("-------------------------------------------------------------------------------");
}
- dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+ mPendingIntentController.dumpPendingIntents(pw, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
@@ -11159,7 +10983,7 @@
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 @@
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 @@
}
}
- 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 @@
memoryStat.pgmajfault,
memoryStat.rssInBytes,
memoryStat.cacheInBytes,
- memoryStat.swapInBytes);
+ memoryStat.swapInBytes,
+ memoryStat.rssHighWatermarkInBytes);
processMemoryStates.add(processMemoryState);
}
}
@@ -21091,6 +20843,46 @@
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 263c34f..78b42f2 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 @@
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_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_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_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.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 35a1eb8..9f59bd8 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 @@
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 @@
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 177e2f5..1fb8f87 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 @@
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 7da0519..890aafe 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 @@
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 @@
}
}
- 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 4dc2851..add9f2a 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.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 @@
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 @@
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 @@
}
+ 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 @@
}
@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 @@
}
});
}
+
+ @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 16c3235..e2035f6 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 @@
// 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 ee4e36f..cfe2829 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.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 @@
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 @@
* 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 @@
* 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 @@
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 @@
// 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 @@
}
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();
- }
- }
+ final KeyguardDisplayState state = getDisplay(display.mDisplayId);
+ state.visibilitiesUpdated(this, display);
+ requestDismissKeyguard |= state.mRequestDismissKeyguard;
}
- mOccluded |= mWindowManager.isShowingDream();
- if (mOccluded != lastOccluded) {
- handleOccludedChanged();
- }
- if (mDismissingKeyguardActivity != lastDismissingKeyguardActivity) {
+
+ // Dismissing Keyguard happens globally using the information from all displays.
+ if (requestDismissKeyguard) {
handleDismissKeyguard();
}
}
@@ -305,7 +281,7 @@
* 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 @@
}
/**
- * 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 @@
}
}
+ private boolean isDisplayOccluded(int displayId) {
+ return getDisplay(displayId).mOccluded;
+ }
+
/**
* @return true if Keyguard can be currently dismissed without entering credentials.
*/
@@ -355,12 +334,14 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 aad890b..228c71d 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 @@
* 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 @@
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 @@
*/
@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 @@
*/
@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 @@
/**
* 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 @@
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 @@
}
/**
+ * 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 @@
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 0000000..a9c00a7
--- /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 ee1166e..b9c6fa6 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.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 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 @@
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 @@
// + Integer.toHexString(hashCode));
}
+ @Override
public boolean equals(Object otherObj) {
if (otherObj == null) {
return false;
@@ -188,11 +190,11 @@
}
}
- 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 @@
}
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;
}
-
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
-
- // Extract options before clearing calling identity
- SafeActivityOptions mergedOptions = key.options;
- if (mergedOptions == null) {
- mergedOptions = SafeActivityOptions.fromBundle(options);
- } else {
- mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
- }
-
- 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);
- }
- }
- }
-
- 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);
- }
- 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;
- }
- break;
- }
-
- if (sendFinish && res != ActivityManager.START_CANCELED) {
- try {
- finishedReceiver.performReceive(new Intent(finalIntent), 0,
- null, null, false, false, key.userId);
- } catch (RemoteException e) {
- }
- }
-
- Binder.restoreCallingIdentity(origId);
-
- return res;
+ flagsMask &= ~Intent.IMMUTABLE_FLAGS;
+ flagsValues &= flagsMask;
+ finalIntent.setFlags((finalIntent.getFlags() & ~flagsMask) | flagsValues);
+ } else {
+ resolvedType = key.requestResolvedType;
}
+
+ // Extract options before clearing calling identity
+ 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;
+ }
+
}
- return ActivityManager.START_CANCELED;
+ // 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 = 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 */);
+ }
+ } 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;
+ }
+ } 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 {
+ 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;
+ }
+ break;
+ }
+
+ 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 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 8ce650c..6ffd8a9 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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
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 @@
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 @@
// 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 @@
if (!isCheckin) {
synchronized (mAm) {
if (isCompact) {
- mProcessStats.dumpCheckinLocked(pw, reqPackage);
+ mProcessStats.dumpCheckinLocked(pw, reqPackage, section);
} else {
if (sepNeeded) {
pw.println();
@@ -1062,7 +1175,7 @@
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 @@
}
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 @@
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 @@
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 f7de7f4..fa0cb47 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 @@
* 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 9b42d65..ef8cb1c 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.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 0000000..91b3b21
--- /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 8caa702..66c7c43 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 @@
// 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 @@
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 @@
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 6f5f90a..a9b0d5c 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 @@
throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
}
- unbindCurrentMethodLocked(true);
+ unbindCurrentMethodLocked();
mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
mCurIntent.setComponent(info.getComponent());
@@ -2020,7 +2020,7 @@
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 @@
channel.dispose();
}
- void unbindCurrentMethodLocked(boolean savePosition) {
+ void unbindCurrentMethodLocked() {
if (mVisibleBound) {
mContext.unbindService(mVisibleConnection);
mVisibleBound = false;
@@ -2076,10 +2076,6 @@
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 @@
void resetCurrentMethodAndClient(
/* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
mCurMethodId = null;
- unbindCurrentMethodLocked(false);
+ unbindCurrentMethodLocked();
unbindCurrentClientLocked(unbindClientReason);
}
@@ -2865,7 +2861,7 @@
final int newFocusDisplayId =
mWindowManagerInternal.getDisplayIdForWindow(windowToken);
if (newFocusDisplayId != mCurTokenDisplayId) {
- unbindCurrentMethodLocked(false);
+ unbindCurrentMethodLocked();
}
}
} else if (isTextEditor && doAutoShow && (softInputMode &
@@ -3237,19 +3233,6 @@
}
@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 @@
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 @@
@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 e81b32c..cade07c 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 @@
// 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 @@
}
}
} 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 @@
@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 @@
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 @@
}
}
- 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 @@
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 @@
}
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 @@
}
}
- 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 9bf21c8..760f155 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 @@
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 91af0ec..e10827b 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.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 @@
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 @@
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 @@
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 @@
// 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 @@
//
// 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 21daa39..910ea73 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 @@
* - 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 @@
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 @@
/**
* 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 e3e1590..602ce3b 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 @@
@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 @@
* @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 @@
* 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 @@
* 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 @@
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 @@
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 3c9dd63..6f644dd 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.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.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 @@
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 91fd8d0..2557f46 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 @@
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 @@
// 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 @@
}
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 @@
// 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 @@
}
@Override
- public void setLastInputMethodWindowLw(WindowState ime, WindowState target) {
- mLastInputMethodWindow = ime;
- mLastInputMethodTargetWindow = target;
- }
-
- @Override
public void setDismissImeOnBackKeyPressed(boolean newValue) {
mDismissImeOnBackKeyPressed = newValue;
}
@@ -7845,7 +7831,6 @@
if (statusBar != null) {
statusBar.setCurrentUser(newUserId);
}
- setLastInputMethodWindowLw(null, null);
}
@Override
@@ -8020,14 +8005,6 @@
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 1fcdd63c..27ab3ef 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 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 68e636a..5adc248 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 @@
try {
mBatteryStats.noteInteractive(true);
} catch (RemoteException ex) { }
+ StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
+ StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON);
}
/**
@@ -401,6 +403,9 @@
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 bfa03ca..c6e6449 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 @@
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 7d2fc15..bcf9212 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.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.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 @@
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 @@
* @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 eaaf804..236982f 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 @@
*/
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 d92818a..a9571be 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_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 @@
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 @@
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 @@
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 793ffce..a1d6ffd 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 @@
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 27b623b..5410676 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 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 017f667..7caa7ae 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 @@
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 @@
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 @@
}
@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 7161a70..f7c6d77 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 @@
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 d6f9ac3..c8d1a8b 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 @@
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 fb95f59..b8241d0 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.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 @@
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 8b59771..78c0be4 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -61,6 +61,7 @@
$(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 57ebbfc..de915ab 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 @@
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 0000000..383bf1d
--- /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 0000000..1dd7dc8
--- /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 0000000..1cd1528
--- /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 0000000..3730335
--- /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 0000000..5ea74f1
--- /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 21b90f1..31e8333 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 @@
}
@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 @@
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 b4bc9d1..fb57d68 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.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 @@
}
@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 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 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 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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
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 @@
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 @@
runTask(task);
- assertBackupNotPendingFor(PACKAGE_1);
+ assertBackupPendingFor(PACKAGE_1);
}
@Test
@@ -1140,6 +1235,38 @@
}
@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 @@
}
@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 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 @@
}
@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 @@
}
@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 @@
runTask(task);
- verify(mReporter).onReadAgentDataError(eq(PACKAGE_1.packageName), any());
+ verify(mReporter).onAgentDataError(eq(PACKAGE_1.packageName), any());
}
@Test
@@ -1779,6 +2020,24 @@
}
@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 @@
*/
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 @@
// 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 0000000..4b79657
--- /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 06c7437..e8a824a 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 @@
@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 @@
"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 @@
"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 @@
}
@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 @@
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 5fb8997..474e5b7 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 @@
}
@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 6af3ea7..b7cc9ce 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 @@
*/
@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 @@
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 bbc6550..01b7c4f 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 @@
*/
@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 4344285..45d2fa2 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.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 @@
}
@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 26bd4a1..08bc9bc 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -141,6 +141,8 @@
* 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 @@
* 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 @@
* {@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 @@
* {@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 @@
* <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 @@
* <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 @@
* <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 d494692..34603a3 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -594,6 +594,8 @@
* {@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 @@
* {@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 8590176..995418e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2115,6 +2115,16 @@
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 @@
});
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 0000000..e373797
--- /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 777b850..38ee79f 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1852,6 +1852,19 @@
}
/**
+ * 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 @@
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 a8bcbe3..824533d 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 @@
"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 @@
/** 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 @@
* @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 @@
*/
@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 @@
*/
@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 @@
}
/**
- * 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 @@
* @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 @@
}
}
- // 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 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 @@
}
}
+ /** @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 @@
* {@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 @@
}
/**
- * @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead.
+ * @removed Use {@link android.telecom.TelecomManager#endCall()} instead.
* @hide
* @removed
*/
@@ -6253,7 +6506,7 @@
}
/**
- * @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
+ * @removed Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
* @hide
* @removed
*/
@@ -6261,26 +6514,22 @@
@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 @@
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 @@
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 @@
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 @@
}
/**
- * Return the application ID for the app type like {@link APPTYPE_CSIM}.
+ * 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}
*
- * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
- *
- * @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 0000000..9aca18e
--- /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 0000000..001bb02
--- /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/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
old mode 100644
new mode 100755
similarity index 61%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
rename to telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
index 5d19851..844b634
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
+++ b/telephony/java/android/telephony/mbms/IGroupCallCallback.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,16 @@
* 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.telephony.mbms;
/**
- * Sent when the divider is being draged either manually or by an animation.
+ * @hide
*/
-public class StartedDragingEvent extends EventBus.Event {
+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 0000000..1a1c7f8
--- /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 0000000..2910bb3
--- /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 0000000..4c9cf4d
--- /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 0000000..7da734e
--- /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 06b2120..95b4d37 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -23,6 +23,7 @@
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 @@
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 0000000..721256a
--- /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 0000000..3734ca7
--- /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 6521f0b..0ccd748 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -232,5 +232,5 @@
*/
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 e1c770c..ca2bcff 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -75,116 +75,6 @@
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 4a6fe49..1f60b71 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 @@
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 a847e8f..a9f01ed 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 c0eaa8e..7872738 100644
--- a/tools/aapt2/io/FileStream_test.cpp
+++ b/tools/aapt2/io/FileStream_test.cpp
@@ -41,46 +41,46 @@
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 @@
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));