summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp5
-rwxr-xr-xapi/current.txt45
-rw-r--r--api/system-current.txt31
-rw-r--r--api/test-current.txt15
-rw-r--r--cmds/statsd/src/atoms.proto69
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp2
-rw-r--r--core/java/android/app/ProcessMemoryState.java6
-rw-r--r--core/java/android/app/SystemServiceRegistry.java8
-rw-r--r--core/java/android/bluetooth/BluetoothMapClient.java19
-rw-r--r--core/java/android/content/ContentProvider.java54
-rw-r--r--core/java/android/content/ContentProviderOperation.java16
-rw-r--r--core/java/android/content/Context.java9
-rw-r--r--core/java/android/content/pm/PackageParser.java59
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java43
-rw-r--r--core/java/android/permission/PermissionManager.java136
-rw-r--r--core/java/android/text/TextLine.java40
-rw-r--r--core/java/android/text/TextPaint.java27
-rw-r--r--core/java/android/view/ViewRootImpl.java17
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl1
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java16
-rw-r--r--core/res/res/drawable/ic_ab_back_material_settings.xml9
-rw-r--r--core/res/res/values-television/themes.xml1
-rw-r--r--core/res/res/values-television/themes_device_defaults.xml1
-rw-r--r--core/res/res/values-watch/themes.xml23
-rw-r--r--core/res/res/values-watch/themes_device_defaults.xml7
-rw-r--r--core/tests/coretests/src/android/content/ContentProviderTest.java88
-rw-r--r--graphics/java/android/graphics/Paint.java75
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/AppPreference/Android.bp13
-rw-r--r--packages/SettingsLib/AppPreference/AndroidManifest.xml23
-rw-r--r--packages/SettingsLib/AppPreference/res/layout/preference_app.xml100
-rw-r--r--packages/SettingsLib/AppPreference/res/values/dimens.xml20
-rw-r--r--packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java63
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java70
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java1
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java20
-rw-r--r--proto/src/metrics_constants/metrics_constants.proto25
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/AgentException.java63
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/BackupException.java33
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java37
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java602
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/TaskException.java83
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java3
-rw-r--r--services/core/java/com/android/server/am/MemoryStatUtil.java63
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java35
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java24
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java33
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java18
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java16
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java24
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java7
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java1
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java23
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java3
-rw-r--r--services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java7
-rw-r--r--services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java70
-rw-r--r--services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java45
-rw-r--r--services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java42
-rw-r--r--services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java335
-rw-r--r--services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java152
-rw-r--r--services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java88
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java36
-rw-r--r--telecomm/java/android/telecom/Call.java14
-rw-r--r--telecomm/java/android/telecom/Connection.java4
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java11
-rw-r--r--telephony/java/android/telephony/MbmsGroupCallSession.java300
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java80
-rw-r--r--telephony/java/android/telephony/mbms/GroupCall.java176
-rw-r--r--telephony/java/android/telephony/mbms/GroupCallCallback.java87
-rwxr-xr-xtelephony/java/android/telephony/mbms/IGroupCallCallback.aidl26
-rwxr-xr-xtelephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl33
-rw-r--r--telephony/java/android/telephony/mbms/InternalGroupCallCallback.java96
-rw-r--r--telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java116
-rw-r--r--telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java99
-rw-r--r--telephony/java/android/telephony/mbms/MbmsUtils.java4
-rwxr-xr-xtelephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl39
-rw-r--r--telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java275
101 files changed, 3719 insertions, 865 deletions
diff --git a/Android.bp b/Android.bp
index ba9156efe4af..47ad24a01924 100644
--- a/Android.bp
+++ b/Android.bp
@@ -515,11 +515,14 @@ java_defaults {
"telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
+ "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl",
"telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl",
"telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
+ "telephony/java/android/telephony/mbms/IGroupCallCallback.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
"telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
+ "telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl",
"telephony/java/android/telephony/INetworkService.aidl",
"telephony/java/android/telephony/INetworkServiceCallback.aidl",
"telephony/java/com/android/ims/internal/IImsCallSession.aidl",
@@ -807,7 +810,7 @@ gensrcs {
java_library {
name: "ext",
installable: true,
- no_framework_libs: true,
+ sdk_version: "core_current",
static_libs: [
"libphonenumber-platform",
"nist-sip",
diff --git a/api/current.txt b/api/current.txt
index bb3cb0b2f5f4..52257c0f7529 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -14007,6 +14007,10 @@ package android.graphics {
method public float getRunAdvance(char[], int, int, int, int, boolean, int);
method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int);
method public android.graphics.Shader getShader();
+ method public int getShadowLayerColor();
+ method public float getShadowLayerDx();
+ method public float getShadowLayerDy();
+ method public float getShadowLayerRadius();
method public float getStrikeThruPosition();
method public float getStrikeThruThickness();
method public android.graphics.Paint.Cap getStrokeCap();
@@ -42266,6 +42270,7 @@ package android.telephony {
field public static final deprecated java.lang.String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
field public static final java.lang.String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
field public static final java.lang.String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
+ field public static final java.lang.String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool";
field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
@@ -42527,6 +42532,13 @@ package android.telephony {
field public static final int STATUS_UNKNOWN = 0; // 0x0
}
+ public class MbmsGroupCallSession implements java.lang.AutoCloseable {
+ method public void close();
+ method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback);
+ method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
+ method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ }
+
public class MbmsStreamingSession implements java.lang.AutoCloseable {
method public void close();
method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback);
@@ -43476,6 +43488,29 @@ package android.telephony.mbms {
field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
}
+ public class GroupCall implements java.lang.AutoCloseable {
+ method public void close();
+ method public long getTmgi();
+ method public void updateGroupCall(int[], int[]);
+ field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
+ field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3
+ field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6
+ field public static final int REASON_NONE = 0; // 0x0
+ field public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5; // 0x5
+ field public static final int REASON_OUT_OF_MEMORY = 4; // 0x4
+ field public static final int STATE_STALLED = 3; // 0x3
+ field public static final int STATE_STARTED = 2; // 0x2
+ field public static final int STATE_STOPPED = 1; // 0x1
+ }
+
+ public class GroupCallCallback {
+ ctor public GroupCallCallback();
+ method public void onBroadcastSignalStrengthUpdated(int);
+ method public void onError(int, java.lang.String);
+ method public void onGroupCallStateChanged(int, int);
+ field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
+ }
+
public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
ctor public MbmsDownloadReceiver();
method public void onReceive(android.content.Context, android.content.Intent);
@@ -43524,6 +43559,14 @@ package android.telephony.mbms {
field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
}
+ public class MbmsGroupCallSessionCallback {
+ ctor public MbmsGroupCallSessionCallback();
+ method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
+ method public void onError(int, java.lang.String);
+ method public void onMiddlewareReady();
+ method public void onServiceInterfaceAvailable(java.lang.String, int);
+ }
+
public class MbmsStreamingSessionCallback {
ctor public MbmsStreamingSessionCallback();
method public void onError(int, java.lang.String);
@@ -44158,6 +44201,8 @@ package android.text {
field public float density;
field public int[] drawableState;
field public int linkColor;
+ field public int underlineColor;
+ field public float underlineThickness;
}
public class TextUtils {
diff --git a/api/system-current.txt b/api/system-current.txt
index 1009b672cf65..1f74cbf6312c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -950,6 +950,7 @@ package android.content {
field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final java.lang.String NETWORK_SCORE_SERVICE = "network_score";
field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
+ field public static final java.lang.String PERMISSION_SERVICE = "permission";
field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
field public static final java.lang.String SECURE_ELEMENT_SERVICE = "secure_element";
field public static final java.lang.String STATS_MANAGER = "stats";
@@ -4192,6 +4193,20 @@ package android.os.storage {
}
+package android.permission {
+
+ public final class PermissionManager {
+ method public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
+ }
+
+ public static final class PermissionManager.SplitPermissionInfo {
+ method public java.lang.String[] getNewPermissions();
+ method public java.lang.String getRootPermission();
+ method public int getTargetSdk();
+ }
+
+}
+
package android.permissionpresenterservice {
public abstract class RuntimePermissionPresenterService extends android.app.Service {
@@ -5257,6 +5272,10 @@ package android.telephony {
field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
}
+ public class MbmsGroupCallSession implements java.lang.AutoCloseable {
+ field public static final java.lang.String MBMS_GROUP_CALL_SERVICE_ACTION = "android.telephony.action.EmbmsGroupCall";
+ }
+
public class MbmsStreamingSession implements java.lang.AutoCloseable {
field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
@@ -5434,6 +5453,7 @@ package android.telephony {
method public deprecated boolean getDataEnabled();
method public deprecated boolean getDataEnabled(int);
method public boolean getEmergencyCallbackMode();
+ method public java.lang.String getIsimDomain();
method public int getSimApplicationState();
method public int getSimCardState();
method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
@@ -6558,6 +6578,17 @@ package android.telephony.mbms.vendor {
method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
}
+ public class MbmsGroupCallServiceBase extends android.app.Service {
+ ctor public MbmsGroupCallServiceBase();
+ method public void dispose(int) throws android.os.RemoteException;
+ method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
+ method public void onAppCallbackDied(int, int);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public void stopGroupCall(int, long);
+ method public void updateGroupCall(int, long, int[], int[]);
+ }
+
public class MbmsStreamingServiceBase extends android.os.Binder {
ctor public MbmsStreamingServiceBase();
method public void dispose(int) throws android.os.RemoteException;
diff --git a/api/test-current.txt b/api/test-current.txt
index f4d7cbcab6dc..956761615254 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1188,6 +1188,10 @@ package android.telephony {
field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override";
}
+ public class MbmsGroupCallSession implements java.lang.AutoCloseable {
+ field public static final java.lang.String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA = "mbms-group-call-service-override";
+ }
+
public class MbmsStreamingSession implements java.lang.AutoCloseable {
field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
}
@@ -1257,6 +1261,17 @@ package android.telephony.mbms.vendor {
method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
}
+ public class MbmsGroupCallServiceBase extends android.app.Service {
+ ctor public MbmsGroupCallServiceBase();
+ method public void dispose(int) throws android.os.RemoteException;
+ method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
+ method public void onAppCallbackDied(int, int);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+ method public void stopGroupCall(int, long);
+ method public void updateGroupCall(int, long, int[], int[]);
+ }
+
public class MbmsStreamingServiceBase extends android.os.Binder {
ctor public MbmsStreamingServiceBase();
method public void dispose(int) throws android.os.RemoteException;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 485b91fb8ece..448608e9ece7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -136,6 +136,7 @@ message Atom {
FingerprintAcquired fingerprint_acquired = 87;
FingerprintAuthenticated fingerprint_authenticated = 88;
FingerprintErrorOccurred fingerprint_error_occurred = 89;
+ Notification notification = 90;
}
// Pulled events will start at field 10000.
@@ -1891,6 +1892,69 @@ message FingerprintErrorOccurred {
// The type of error.
optional Error error = 3;
}
+
+message Notification {
+
+ // Type of notification event.
+ enum Type {
+ TYPE_UNKNOWN = 0;
+ // Notification became visible to the user.
+ TYPE_OPEN = 1;
+ // Notification became hidden.
+ TYPE_CLOSE = 2;
+ // Notification switched to detail mode.
+ TYPE_DETAIL = 3;
+ // Notification was clicked.
+ TYPE_ACTION = 4;
+ // Notification was dismissed.
+ TYPE_DISMISS = 5;
+ // Notification switched to summary mode. The enum value of 14 is to
+ // match that of metrics_constants.
+ TYPE_COLLAPSE = 14;
+ }
+ optional Type type = 1;
+
+ // Package name associated with the notification.
+ optional string package_name = 2;
+
+ // Tag associated with notification.
+ optional string tag = 3;
+
+ // Application-supplied ID associated with the notification.
+ optional int32 id = 4;
+
+ // Index of notification in the notification panel.
+ optional int32 shade_index = 5;
+
+ // The number of notifications in the notification panel.
+ optional int32 shade_count = 6;
+
+ // Importance for the notification.
+ optional int32 importance = 7;
+
+ // ID for the notification channel.
+ optional int32 channel_id = 8;
+
+ // Importance for the notification channel.
+ optional int32 channel_importance = 9;
+
+ // Whether notification was a group summary.
+ optional bool group_summary = 10;
+
+ // Time since notification was created in milliseconds.
+ optional int64 since_create_millis = 11;
+
+ // Time since notification was interrupted in milliseconds.
+ optional int64 since_interruption_millis = 12;
+
+ // Time since notification was updated in milliseconds.
+ optional int64 since_update_millis = 13;
+
+ // Time since notification was visible in milliseconds.
+ optional int64 since_visible_millis = 14;
+}
+
+
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
@@ -2156,6 +2220,11 @@ message ProcessMemoryState {
// SWAP
optional int64 swap_in_bytes = 8;
+
+ // RSS high watermark.
+ // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
+ // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
+ optional int64 rss_high_watermark_in_bytes = 9;
}
/*
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 5a0172b22301..66392f80f1fe 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -169,7 +169,7 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
- {{4, 5, 6, 7, 8},
+ {{4, 5, 6, 7, 8, 9},
{2, 3},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java
index e0aed16b8abf..9bfdae0d8c03 100644
--- a/core/java/android/app/ProcessMemoryState.java
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -32,10 +32,11 @@ public final class ProcessMemoryState implements Parcelable {
public final long rssInBytes;
public final long cacheInBytes;
public final long swapInBytes;
+ public final long rssHighWatermarkInBytes;
public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
long pgmajfault, long rssInBytes, long cacheInBytes,
- long swapInBytes) {
+ long swapInBytes, long rssHighWatermarkInBytes) {
this.uid = uid;
this.processName = processName;
this.oomScore = oomScore;
@@ -44,6 +45,7 @@ public final class ProcessMemoryState implements Parcelable {
this.rssInBytes = rssInBytes;
this.cacheInBytes = cacheInBytes;
this.swapInBytes = swapInBytes;
+ this.rssHighWatermarkInBytes = rssHighWatermarkInBytes;
}
private ProcessMemoryState(Parcel in) {
@@ -55,6 +57,7 @@ public final class ProcessMemoryState implements Parcelable {
rssInBytes = in.readLong();
cacheInBytes = in.readLong();
swapInBytes = in.readLong();
+ rssHighWatermarkInBytes = in.readLong();
}
public static final Creator<ProcessMemoryState> CREATOR = new Creator<ProcessMemoryState>() {
@@ -84,5 +87,6 @@ public final class ProcessMemoryState implements Parcelable {
parcel.writeLong(rssInBytes);
parcel.writeLong(cacheInBytes);
parcel.writeLong(swapInBytes);
+ parcel.writeLong(rssHighWatermarkInBytes);
}
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 41dec8f10937..0044005c51f2 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -134,6 +134,7 @@ import android.os.UserManager;
import android.os.Vibrator;
import android.os.health.SystemHealthManager;
import android.os.storage.StorageManager;
+import android.permission.PermissionManager;
import android.print.IPrintManager;
import android.print.PrintManager;
import android.service.oemlock.IOemLockService;
@@ -1064,6 +1065,13 @@ final class SystemServiceRegistry {
throws ServiceNotFoundException {
return new TimeZoneDetector();
}});
+
+ registerService(Context.PERMISSION_SERVICE, PermissionManager.class,
+ new CachedServiceFetcher<PermissionManager>() {
+ @Override
+ public PermissionManager createService(ContextImpl ctx) {
+ return new PermissionManager(ctx.getOuterContext());
+ }});
}
/**
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 183be5f38bd1..559a59b68b4e 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -73,6 +73,8 @@ public final class BluetoothMapClient implements BluetoothProfile {
/** Connection canceled before completion. */
public static final int RESULT_CANCELED = 2;
+ private static final int UPLOADING_FEATURE_BITMASK = 0x08;
+
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
public void onBluetoothStateChange(boolean up) {
@@ -395,6 +397,23 @@ public final class BluetoothMapClient implements BluetoothProfile {
return false;
}
+ /**
+ * Returns the "Uploading" feature bit value from the SDP record's
+ * MapSupportedFeatures field (see Bluetooth MAP 1.4 spec, page 114).
+ * @param device The Bluetooth device to get this value for.
+ * @return Returns true if the Uploading bit value in SDP record's
+ * MapSupportedFeatures field is set. False is returned otherwise.
+ */
+ public boolean isUploadingSupported(BluetoothDevice device) {
+ try {
+ return (mService != null && isEnabled() && isValidDevice(device))
+ && ((mService.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ return false;
+ }
+
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index bd1e6a462805..8d9533e09cbf 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -51,6 +51,8 @@ import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -58,6 +60,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Objects;
/**
* Content providers are one of the primary building blocks of Android applications, providing
@@ -99,6 +102,7 @@ import java.util.Arrays;
* <p>For more information about using content providers, read the
* <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
* developer guide.</p>
+ * </div>
*/
public abstract class ContentProvider implements ComponentCallbacks2 {
@@ -220,7 +224,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
// The caller has no access to the data, so return an empty cursor with
@@ -268,7 +272,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public String getType(Uri uri) {
// getCallingPackage() isn't available in getType(), as the javadoc states.
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
Trace.traceBegin(TRACE_TAG_DATABASE, "getType");
try {
@@ -280,7 +284,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
@@ -303,7 +307,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
@@ -327,11 +331,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
for (int i = 0; i < numOperations; i++) {
ContentProviderOperation operation = operations.get(i);
Uri uri = operation.getUri();
- validateIncomingUri(uri);
- userIds[i] = getUserIdFromUri(uri);
- if (userIds[i] != UserHandle.USER_CURRENT) {
- // Removing the user id from the uri.
- operation = new ContentProviderOperation(operation, true);
+ uri = validateIncomingUri(uri);
+ uri = maybeGetUriWithoutUserId(uri);
+ // Rebuild operation if we changed the Uri above
+ if (!Objects.equals(operation.getUri(), uri)) {
+ operation = new ContentProviderOperation(operation, uri);
operations.set(i, operation);
}
if (operation.isReadOperation()) {
@@ -368,7 +372,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
@@ -386,7 +390,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public int update(String callingPkg, Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return 0;
@@ -405,7 +409,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public ParcelFileDescriptor openFile(
String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal,
IBinder callerToken) throws FileNotFoundException {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(callingPkg, uri, mode, callerToken);
Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
@@ -423,7 +427,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public AssetFileDescriptor openAssetFile(
String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(callingPkg, uri, mode, null);
Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
@@ -454,7 +458,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
// getCallingPackage() isn't available in getType(), as the javadoc states.
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
Trace.traceBegin(TRACE_TAG_DATABASE, "getStreamTypes");
try {
@@ -468,7 +472,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
Bundle.setDefusable(opts, true);
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(callingPkg, uri, "r", null);
Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
@@ -489,7 +493,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public Uri canonicalize(String callingPkg, Uri uri) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
@@ -507,7 +511,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public Uri uncanonicalize(String callingPkg, Uri uri) {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
@@ -526,7 +530,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public boolean refresh(String callingPkg, Uri uri, Bundle args,
ICancellationSignal cancellationSignal) throws RemoteException {
- validateIncomingUri(uri);
+ uri = validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
return false;
@@ -1965,7 +1969,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
*/
if (mContext == null) {
mContext = context;
- if (context != null) {
+ if (context != null && mTransport != null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
@@ -2074,7 +2078,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
/** @hide */
- private void validateIncomingUri(Uri uri) throws SecurityException {
+ @VisibleForTesting
+ public Uri validateIncomingUri(Uri uri) throws SecurityException {
String auth = uri.getAuthority();
if (!mSingleUser) {
int userId = getUserIdFromAuthority(auth, UserHandle.USER_CURRENT);
@@ -2093,6 +2098,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
throw new SecurityException(message);
}
+
+ // Normalize the path by removing any empty path segments, which can be
+ // a source of security issues.
+ final String encodedPath = uri.getEncodedPath();
+ if (encodedPath != null && encodedPath.indexOf("//") != -1) {
+ return uri.buildUpon().encodedPath(encodedPath.replaceAll("//+", "/")).build();
+ } else {
+ return uri;
+ }
}
/** @hide */
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index e3d9b1931faa..7dc45776715c 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -101,13 +101,9 @@ public class ContentProviderOperation implements Parcelable {
}
/** @hide */
- public ContentProviderOperation(ContentProviderOperation cpo, boolean removeUserIdFromUri) {
+ public ContentProviderOperation(ContentProviderOperation cpo, Uri withUri) {
mType = cpo.mType;
- if (removeUserIdFromUri) {
- mUri = ContentProvider.getUriWithoutUserId(cpo.mUri);
- } else {
- mUri = cpo.mUri;
- }
+ mUri = withUri;
mValues = cpo.mValues;
mSelection = cpo.mSelection;
mSelectionArgs = cpo.mSelectionArgs;
@@ -117,14 +113,6 @@ public class ContentProviderOperation implements Parcelable {
mYieldAllowed = cpo.mYieldAllowed;
}
- /** @hide */
- public ContentProviderOperation getWithoutUserIdInUri() {
- if (ContentProvider.uriHasUserId(mUri)) {
- return new ContentProviderOperation(this, true);
- }
- return this;
- }
-
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
Uri.writeToParcel(dest, mUri);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 981be833f3c0..caaf4af247e9 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3088,6 +3088,7 @@ public abstract class Context {
//@hide: SYSTEM_UPDATE_SERVICE,
//@hide: TIME_DETECTOR_SERVICE,
//@hide: TIME_ZONE_DETECTOR_SERVICE,
+ PERMISSION_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -3860,6 +3861,14 @@ public abstract class Context {
*/
public static final String SOUND_TRIGGER_SERVICE = "soundtrigger";
+ /**
+ * Official published name of the (internal) permission service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ @SystemApi
+ public static final String PERMISSION_SERVICE = "permission";
/**
* Use with {@link #getSystemService(String)} to retrieve an
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 03a3d1f438ff..1fa5190ef8df 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -40,7 +40,6 @@ import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -73,6 +72,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.permission.PermissionManager;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.system.StructStat;
@@ -258,19 +258,6 @@ public class PackageParser {
}
}
- /** @hide */
- public static class SplitPermissionInfo {
- public final String rootPerm;
- public final String[] newPerms;
- public final int targetSdk;
-
- public SplitPermissionInfo(String rootPerm, String[] newPerms, int targetSdk) {
- this.rootPerm = rootPerm;
- this.newPerms = newPerms;
- this.targetSdk = targetSdk;
- }
- }
-
/**
* List of new permissions that have been added since 1.0.
* NOTE: These must be declared in SDK version order, with permissions
@@ -290,34 +277,6 @@ public class PackageParser {
};
/**
- * List of permissions that have been split into more granular or dependent
- * permissions.
- * @hide
- */
- public static final PackageParser.SplitPermissionInfo SPLIT_PERMISSIONS[] =
- new PackageParser.SplitPermissionInfo[] {
- // READ_EXTERNAL_STORAGE is always required when an app requests
- // WRITE_EXTERNAL_STORAGE, because we can't have an app that has
- // write access without read access. The hack here with the target
- // target SDK version ensures that this grant is always done.
- new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
- new String[] { android.Manifest.permission.READ_EXTERNAL_STORAGE },
- android.os.Build.VERSION_CODES.CUR_DEVELOPMENT+1),
- new PackageParser.SplitPermissionInfo(android.Manifest.permission.READ_CONTACTS,
- new String[] { android.Manifest.permission.READ_CALL_LOG },
- android.os.Build.VERSION_CODES.JELLY_BEAN),
- new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_CONTACTS,
- new String[] { android.Manifest.permission.WRITE_CALL_LOG },
- android.os.Build.VERSION_CODES.JELLY_BEAN),
- new PackageParser.SplitPermissionInfo(Manifest.permission.ACCESS_FINE_LOCATION,
- new String[] { android.Manifest.permission.ACCESS_BACKGROUND_LOCATION },
- android.os.Build.VERSION_CODES.P0),
- new PackageParser.SplitPermissionInfo(Manifest.permission.ACCESS_COARSE_LOCATION,
- new String[] { android.Manifest.permission.ACCESS_BACKGROUND_LOCATION },
- android.os.Build.VERSION_CODES.P0),
- };
-
- /**
* @deprecated callers should move to explicitly passing around source path.
*/
@Deprecated
@@ -2474,16 +2433,18 @@ public class PackageParser {
Slog.i(TAG, implicitPerms.toString());
}
- final int NS = PackageParser.SPLIT_PERMISSIONS.length;
+
+ final int NS = PermissionManager.SPLIT_PERMISSIONS.length;
for (int is=0; is<NS; is++) {
- final PackageParser.SplitPermissionInfo spi
- = PackageParser.SPLIT_PERMISSIONS[is];
- if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk
- || !pkg.requestedPermissions.contains(spi.rootPerm)) {
+ final PermissionManager.SplitPermissionInfo spi =
+ PermissionManager.SPLIT_PERMISSIONS[is];
+ if (pkg.applicationInfo.targetSdkVersion >= spi.getTargetSdk()
+ || !pkg.requestedPermissions.contains(spi.getRootPermission())) {
continue;
}
- for (int in=0; in<spi.newPerms.length; in++) {
- final String perm = spi.newPerms[in];
+ final String[] newPerms = spi.getNewPermissions();
+ for (int in = 0; in < newPerms.length; in++) {
+ final String perm = newPerms[in];
if (!pkg.requestedPermissions.contains(perm)) {
pkg.requestedPermissions.add(perm);
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b94840279cea..ae12f93285a8 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -410,14 +410,6 @@ public class InputMethodService extends AbstractInputMethodService {
@GuardedBy("mLock")
private boolean mNotifyUserActionSent;
- /**
- * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we
- * have not shown our own window yet. In this situation, the previous inset continues to be
- * shown as an empty region until it is explicitly updated. Basically we can trigger the update
- * by calling 1) {@code mWindow.show()} or 2) {@link #clearInsetOfPreviousIme()}.
- */
- boolean mShouldClearInsetOfPreviousIme;
-
@UnsupportedAppUsage
final Insets mTmpInsets = new Insets();
final int[] mTmpLocation = new int[2];
@@ -581,7 +573,6 @@ public class InputMethodService extends AbstractInputMethodService {
mShowInputFlags = 0;
mShowInputRequested = false;
doHideWindow();
- clearInsetOfPreviousIme();
if (resultReceiver != null) {
resultReceiver.send(wasVis != isInputViewShown()
? InputMethodManager.RESULT_HIDDEN
@@ -601,7 +592,6 @@ public class InputMethodService extends AbstractInputMethodService {
if (dispatchOnShowInputRequested(flags, false)) {
showWindow(true);
}
- clearInsetOfPreviousIme();
// If user uses hard keyboard, IME button should always be shown.
setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
@@ -946,9 +936,6 @@ public class InputMethodService extends AbstractInputMethodService {
super.onCreate();
mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
mSettingsObserver = SettingsObserver.createAndRegister(this);
- // If the previous IME has occupied non-empty inset in the screen, we need to decide whether
- // we continue to use the same size of the inset or update it
- mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
// TODO(b/111364446) Need to address context lifecycle issue if need to re-create
// for update resources & configuration correctly when show soft input
// in non-default display.
@@ -1882,9 +1869,6 @@ public class InputMethodService extends AbstractInputMethodService {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
onWindowShown();
mWindow.show();
- // Put here rather than in onWindowShown() in case people forget to call
- // super.onWindowShown().
- mShouldClearInsetOfPreviousIme = false;
}
}
@@ -1934,32 +1918,6 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
- * Reset the inset occupied the previous IME when and only when
- * {@link #mShouldClearInsetOfPreviousIme} is {@code true}.
- */
- private void clearInsetOfPreviousIme() {
- if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme() "
- + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
- if (!mShouldClearInsetOfPreviousIme) return;
-
- clearLastInputMethodWindowForTransition();
- mShouldClearInsetOfPreviousIme = false;
- }
-
- /**
- * Tells the system that the IME decided to not show a window and the system no longer needs to
- * use the previous IME's inset.
- *
- * <p>Caveat: {@link android.inputmethodservice.InputMethodService#clearInsetOfPreviousIme()}
- * is the only expected caller of this method. Do not depend on this anywhere else.</p>
- *
- * <p>TODO: We probably need to reconsider how IME should be handled.</p>
- */
- private void clearLastInputMethodWindowForTransition() {
- mPrivOps.clearLastInputMethodWindowForTransition();
- }
-
- /**
* Called when a new client has bound to the input method. This
* may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
* and {@link #onFinishInput()} calls as the user navigates through its
@@ -2980,7 +2938,6 @@ public class InputMethodService extends AbstractInputMethodService {
+ " visibleTopInsets=" + mTmpInsets.visibleTopInsets
+ " touchableInsets=" + mTmpInsets.touchableInsets
+ " touchableRegion=" + mTmpInsets.touchableRegion);
- p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
p.println(" mSettingsObserver=" + mSettingsObserver);
}
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
new file mode 100644
index 000000000000..aa44eb7e3a26
--- /dev/null
+++ b/core/java/android/permission/PermissionManager.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+
+import com.android.internal.annotations.Immutable;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * System level service for accessing the permission capabilities of the platform.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.PERMISSION_SERVICE)
+public final class PermissionManager {
+ /**
+ * {@link android.content.pm.PackageParser} needs access without having a {@link Context}.
+ *
+ * @hide
+ */
+ public static final SplitPermissionInfo[] SPLIT_PERMISSIONS = new SplitPermissionInfo[]{
+ // READ_EXTERNAL_STORAGE is always required when an app requests
+ // WRITE_EXTERNAL_STORAGE, because we can't have an app that has
+ // write access without read access. The hack here with the target
+ // target SDK version ensures that this grant is always done.
+ new SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE},
+ android.os.Build.VERSION_CODES.CUR_DEVELOPMENT + 1),
+ new SplitPermissionInfo(android.Manifest.permission.READ_CONTACTS,
+ new String[]{android.Manifest.permission.READ_CALL_LOG},
+ android.os.Build.VERSION_CODES.JELLY_BEAN),
+ new SplitPermissionInfo(android.Manifest.permission.WRITE_CONTACTS,
+ new String[]{android.Manifest.permission.WRITE_CALL_LOG},
+ android.os.Build.VERSION_CODES.JELLY_BEAN),
+ new SplitPermissionInfo(Manifest.permission.ACCESS_FINE_LOCATION,
+ new String[]{android.Manifest.permission.ACCESS_BACKGROUND_LOCATION},
+ android.os.Build.VERSION_CODES.P0),
+ new SplitPermissionInfo(Manifest.permission.ACCESS_COARSE_LOCATION,
+ new String[]{android.Manifest.permission.ACCESS_BACKGROUND_LOCATION},
+ android.os.Build.VERSION_CODES.P0)};
+
+ private final @NonNull Context mContext;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context The current context in which to operate.
+ * @hide
+ */
+ public PermissionManager(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Get list of permissions that have been split into more granular or dependent permissions.
+ *
+ * <p>E.g. before {@link android.os.Build.VERSION_CODES#P0} an app that was granted
+ * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access he location while it was in
+ * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#P0}
+ * the location permission only grants location access while the app is in foreground. This
+ * would break apps that target before {@link android.os.Build.VERSION_CODES#P0}. Hence whenever
+ * such an old app asks for a location permission (i.e. the
+ * {@link SplitPermissionInfo#getRootPermission()}), then the
+ * {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside
+ * {@{@link SplitPermissionInfo#getNewPermissions}) is added.
+ *
+ * <p>Note: Regular apps do not have to worry about this. The platform and permission controller
+ * automatically add the new permissions where needed.
+ *
+ * @return All permissions that are split.
+ */
+ public @NonNull List<SplitPermissionInfo> getSplitPermissions() {
+ return Arrays.asList(SPLIT_PERMISSIONS);
+ }
+
+ /**
+ * A permission that was added in a previous API level might have split into several
+ * permissions. This object describes one such split.
+ */
+ @Immutable
+ public static final class SplitPermissionInfo {
+ private final @NonNull String mRootPerm;
+ private final @NonNull String[] mNewPerms;
+ private final int mTargetSdk;
+
+ /**
+ * Get the permission that is split.
+ */
+ public @NonNull String getRootPermission() {
+ return mRootPerm;
+ }
+
+ /**
+ * Get the permissions that are added.
+ */
+ public @NonNull String[] getNewPermissions() {
+ return mNewPerms;
+ }
+
+ /**
+ * Get the target API level when the permission was split.
+ */
+ public int getTargetSdk() {
+ return mTargetSdk;
+ }
+
+ private SplitPermissionInfo(@NonNull String rootPerm, @NonNull String[] newPerms,
+ int targetSdk) {
+ mRootPerm = rootPerm;
+ mNewPerms = newPerms;
+ mTargetSdk = targetSdk;
+ }
+ }
+}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index b49a949a36e7..44dfd115f7f2 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -1230,7 +1230,7 @@ public class TextLine {
// with the next chunk. So we just save the TextPaint for future comparisons
// and use.
activePaint.set(wp);
- } else if (!wp.hasEqualAttributes(activePaint)) {
+ } else if (!equalAttributes(wp, activePaint)) {
// The style of the present chunk of text is substantially different from the
// style of the previous chunk. We need to handle the active piece of text
// and restart with the present chunk.
@@ -1335,4 +1335,42 @@ public class TextLine {
}
private static final int TAB_INCREMENT = 20;
+
+ private static boolean equalAttributes(@NonNull TextPaint lp, @NonNull TextPaint rp) {
+ return lp.getColorFilter() == rp.getColorFilter()
+ && lp.getMaskFilter() == rp.getMaskFilter()
+ && lp.getShader() == rp.getShader()
+ && lp.getTypeface() == rp.getTypeface()
+ && lp.getXfermode() == rp.getXfermode()
+ && lp.getTextLocales().equals(rp.getTextLocales())
+ && TextUtils.equals(lp.getFontFeatureSettings(), rp.getFontFeatureSettings())
+ && TextUtils.equals(lp.getFontVariationSettings(), rp.getFontVariationSettings())
+ && lp.getShadowLayerRadius() == rp.getShadowLayerRadius()
+ && lp.getShadowLayerDx() == rp.getShadowLayerDx()
+ && lp.getShadowLayerDy() == rp.getShadowLayerDy()
+ && lp.getShadowLayerColor() == rp.getShadowLayerColor()
+ && lp.getFlags() == rp.getFlags()
+ && lp.getHinting() == rp.getHinting()
+ && lp.getStyle() == rp.getStyle()
+ && lp.getColor() == rp.getColor()
+ && lp.getStrokeWidth() == rp.getStrokeWidth()
+ && lp.getStrokeMiter() == rp.getStrokeMiter()
+ && lp.getStrokeCap() == rp.getStrokeCap()
+ && lp.getStrokeJoin() == rp.getStrokeJoin()
+ && lp.getTextAlign() == rp.getTextAlign()
+ && lp.isElegantTextHeight() == rp.isElegantTextHeight()
+ && lp.getTextSize() == rp.getTextSize()
+ && lp.getTextScaleX() == rp.getTextScaleX()
+ && lp.getTextSkewX() == rp.getTextSkewX()
+ && lp.getLetterSpacing() == rp.getLetterSpacing()
+ && lp.getWordSpacing() == rp.getWordSpacing()
+ && lp.getHyphenEdit() == rp.getHyphenEdit()
+ && lp.bgColor == rp.bgColor
+ && lp.baselineShift == rp.baselineShift
+ && lp.linkColor == rp.linkColor
+ && lp.drawableState == rp.drawableState
+ && lp.density == rp.density
+ && lp.underlineColor == rp.underlineColor
+ && lp.underlineThickness == rp.underlineThickness;
+ }
}
diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java
index 7bcc6859b8fc..d5aad33a85e7 100644
--- a/core/java/android/text/TextPaint.java
+++ b/core/java/android/text/TextPaint.java
@@ -17,7 +17,7 @@
package android.text;
import android.annotation.ColorInt;
-import android.annotation.NonNull;
+import android.annotation.Px;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
@@ -37,17 +37,14 @@ public class TextPaint extends Paint {
public float density = 1.0f;
/**
* Special value 0 means no custom underline
- * @hide
*/
@ColorInt
- @UnsupportedAppUsage
public int underlineColor = 0;
+
/**
* Thickness of the underline, in pixels.
- * @hide
*/
- @UnsupportedAppUsage
- public float underlineThickness;
+ public @Px float underlineThickness;
public TextPaint() {
super();
@@ -78,24 +75,6 @@ public class TextPaint extends Paint {
}
/**
- * Returns true if all attributes, including the attributes inherited from Paint, are equal.
- *
- * The caller is expected to have checked the trivial cases, like the pointers being equal,
- * the objects having different classes, or the parameter being null.
- * @hide
- */
- public boolean hasEqualAttributes(@NonNull TextPaint other) {
- return bgColor == other.bgColor
- && baselineShift == other.baselineShift
- && linkColor == other.linkColor
- && drawableState == other.drawableState
- && density == other.density
- && underlineColor == other.underlineColor
- && underlineThickness == other.underlineThickness
- && super.hasEqualAttributes((Paint) other);
- }
-
- /**
* Defines a custom underline for this Paint.
* @param color underline solid color
* @param thickness underline thickness
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2ee7ab939bd9..9be9ed04d049 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -239,6 +239,12 @@ public final class ViewRootImpl implements ViewParent,
final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>();
@UnsupportedAppUsage
final Context mContext;
+ /**
+ * TODO(b/116349163): Check if we can merge this into {@link #mContext}.
+ */
+ @NonNull
+ private Context mDisplayContext;
+
@UnsupportedAppUsage
final IWindowSession mWindowSession;
@NonNull Display mDisplay;
@@ -532,6 +538,7 @@ public final class ViewRootImpl implements ViewParent,
public ViewRootImpl(Context context, Display display) {
mContext = context;
+ mDisplayContext = context.createDisplayContext(display);
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
@@ -1249,6 +1256,7 @@ public final class ViewRootImpl implements ViewParent,
} else {
mDisplay = preferredDisplay;
}
+ mDisplayContext = mContext.createDisplayContext(mDisplay);
}
void pokeDrawLockIfNeeded() {
@@ -2579,7 +2587,7 @@ public final class ViewRootImpl implements ViewParent,
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
if (imm != null && imTarget) {
imm.onPreWindowFocus(mView, hasWindowFocus);
imm.onPostWindowFocus(mView, mView.findFocus(),
@@ -2695,7 +2703,7 @@ public final class ViewRootImpl implements ViewParent,
mLastWasImTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onPreWindowFocus(mView, hasWindowFocus);
}
@@ -4329,7 +4337,8 @@ public final class ViewRootImpl implements ViewParent,
enqueueInputEvent(event, null, 0, true);
} break;
case MSG_CHECK_FOCUS: {
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm =
+ mDisplayContext.getSystemService(InputMethodManager.class);
if (imm != null) {
imm.checkFocus();
}
@@ -4871,7 +4880,7 @@ public final class ViewRootImpl implements ViewParent,
@Override
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+ InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
if (imm != null) {
final InputEvent event = q.mEvent;
if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index c32894dd04f9..2671781a60c1 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -28,7 +28,6 @@ import com.android.internal.inputmethod.IInputContentUriToken;
interface IInputMethodPrivilegedOperations {
void setImeWindowStatus(int vis, int backDisposition);
void reportStartInput(in IBinder startInputToken);
- void clearLastInputMethodWindowForTransition();
IInputContentUriToken createInputContentUriToken(in Uri contentUri, in String packageName);
void reportFullscreenMode(boolean fullscreen);
void setInputMethod(String id);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index f0e8dc59d09a..cdb986ab1a78 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -135,22 +135,6 @@ public final class InputMethodPrivilegedOperations {
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#clearLastInputMethodWindowForTransition()}.
- */
- @AnyThread
- public void clearLastInputMethodWindowForTransition() {
- final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return;
- }
- try {
- ops.clearLastInputMethodWindowForTransition();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String)}.
*
* @param contentUri Content URI to which a temporary read permission should be granted
diff --git a/core/res/res/drawable/ic_ab_back_material_settings.xml b/core/res/res/drawable/ic_ab_back_material_settings.xml
index 7325a4173a7f..938e36d4ecb5 100644
--- a/core/res/res/drawable/ic_ab_back_material_settings.xml
+++ b/core/res/res/drawable/ic_ab_back_material_settings.xml
@@ -18,11 +18,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
android:autoMirrored="true"
- android:tint="?attr/colorControlNormal">
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
<path
android:fillColor="@color/white"
- android:pathData="M20,11H7.62l4.88,-4.88a0.996,0.996 0,1 0,-1.41 -1.41l-6.94,6.94c-0.2,0.2 -0.2,0.51 0,0.71l6.94,6.94a0.996,0.996 0,1 0,1.41 -1.41L7.62,13H20c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1z"/>
+ android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8l8,8l1.41,-1.41L7.83,13H20V11z"/>
</vector>
+
diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml
index 0712cbcfc024..48b59c70a61e 100644
--- a/core/res/res/values-television/themes.xml
+++ b/core/res/res/values-television/themes.xml
@@ -15,7 +15,6 @@
-->
<resources>
<style name="Theme.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
- <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" />
<style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
<style name="Theme.Material.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml
index b92c44e9f444..fdd06242c1e0 100644
--- a/core/res/res/values-television/themes_device_defaults.xml
+++ b/core/res/res/values-television/themes_device_defaults.xml
@@ -15,6 +15,7 @@
-->
<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" />
<!-- TODO(b/116457731): remove colorBackground from colors_material.xml if not used anymore -->
diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml
deleted file mode 100644
index 1be47baf4e7f..000000000000
--- 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 a7736e7c3e82..bfba312da016 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -422,6 +422,13 @@ a similar way.
<item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
</style>
+ <!-- Theme for the dialog shown when an app crashes or ANRs. Override to make it dark. -->
+ <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert">
+ <item name="windowContentTransitions">false</item>
+ <item name="windowActivityTransitions">false</item>
+ <item name="windowCloseOnTouchOutside">false</item>
+ </style>
+
<style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
<!-- Color palette Dark -->
<item name="colorPrimary">@color/primary_device_default_dark</item>
diff --git a/core/tests/coretests/src/android/content/ContentProviderTest.java b/core/tests/coretests/src/android/content/ContentProviderTest.java
new file mode 100644
index 000000000000..2142f27ff6d8
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContentProviderTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.withSettings;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentProviderTest {
+
+ private Context mContext;
+ private ContentProvider mCp;
+
+ private ApplicationInfo mProviderApp;
+ private ProviderInfo mProvider;
+
+ @Before
+ public void setUp() {
+ mProviderApp = new ApplicationInfo();
+ mProviderApp.uid = 10001;
+
+ mProvider = new ProviderInfo();
+ mProvider.authority = "com.example";
+ mProvider.applicationInfo = mProviderApp;
+
+ mContext = mock(Context.class);
+
+ mCp = mock(ContentProvider.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS));
+ mCp.attachInfo(mContext, mProvider);
+ }
+
+ @Test
+ public void testValidateIncomingUri_Normal() throws Exception {
+ assertEquals(Uri.parse("content://com.example/"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/")));
+ assertEquals(Uri.parse("content://com.example/foo/bar"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/foo/bar")));
+ assertEquals(Uri.parse("content://com.example/foo%2Fbar"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/foo%2Fbar")));
+ assertEquals(Uri.parse("content://com.example/foo%2F%2Fbar"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/foo%2F%2Fbar")));
+ }
+
+ @Test
+ public void testValidateIncomingUri_Shady() throws Exception {
+ assertEquals(Uri.parse("content://com.example/"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example//")));
+ assertEquals(Uri.parse("content://com.example/foo/bar/"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example//foo//bar//")));
+ assertEquals(Uri.parse("content://com.example/foo/bar/"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/foo///bar/")));
+ assertEquals(Uri.parse("content://com.example/foo%2F%2Fbar/baz"),
+ mCp.validateIncomingUri(Uri.parse("content://com.example/foo%2F%2Fbar//baz")));
+ }
+
+ @Test
+ public void testValidateIncomingUri_NonPath() throws Exception {
+ // We only touch paths; queries and fragments are left intact
+ assertEquals(Uri.parse("content://com.example/foo/bar?foo=b//ar#foo=b//ar"),
+ mCp.validateIncomingUri(
+ Uri.parse("content://com.example/foo/bar?foo=b//ar#foo=b//ar")));
+ }
+}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 33caa00db208..69ff3bca6528 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -592,49 +592,6 @@ public class Paint {
mShadowLayerColor = paint.mShadowLayerColor;
}
- /**
- * Returns true if all attributes are equal.
- *
- * The caller is expected to have checked the trivial cases, like the pointers being equal,
- * the objects having different classes, or the parameter being null.
- * @hide
- */
- public boolean hasEqualAttributes(@NonNull Paint other) {
- return mColorFilter == other.mColorFilter
- && mMaskFilter == other.mMaskFilter
- && mPathEffect == other.mPathEffect
- && mShader == other.mShader
- && mTypeface == other.mTypeface
- && mXfermode == other.mXfermode
- && mHasCompatScaling == other.mHasCompatScaling
- && mCompatScaling == other.mCompatScaling
- && mInvCompatScaling == other.mInvCompatScaling
- && mBidiFlags == other.mBidiFlags
- && mLocales.equals(other.mLocales)
- && TextUtils.equals(mFontFeatureSettings, other.mFontFeatureSettings)
- && TextUtils.equals(mFontVariationSettings, other.mFontVariationSettings)
- && mShadowLayerRadius == other.mShadowLayerRadius
- && mShadowLayerDx == other.mShadowLayerDx
- && mShadowLayerDy == other.mShadowLayerDy
- && mShadowLayerColor == other.mShadowLayerColor
- && getFlags() == other.getFlags()
- && getHinting() == other.getHinting()
- && getStyle() == other.getStyle()
- && getColor() == other.getColor()
- && getStrokeWidth() == other.getStrokeWidth()
- && getStrokeMiter() == other.getStrokeMiter()
- && getStrokeCap() == other.getStrokeCap()
- && getStrokeJoin() == other.getStrokeJoin()
- && getTextAlign() == other.getTextAlign()
- && isElegantTextHeight() == other.isElegantTextHeight()
- && getTextSize() == other.getTextSize()
- && getTextScaleX() == other.getTextScaleX()
- && getTextSkewX() == other.getTextSkewX()
- && getLetterSpacing() == other.getLetterSpacing()
- && getWordSpacing() == other.getWordSpacing()
- && getHyphenEdit() == other.getHyphenEdit();
- }
-
/** @hide */
@UnsupportedAppUsage
public void setCompatibilityScaling(float factor) {
@@ -1395,6 +1352,38 @@ public class Paint {
}
/**
+ * Returns the blur radius of the shadow layer.
+ * @see #setShadowLayer(float,float,float,int)
+ */
+ public float getShadowLayerRadius() {
+ return mShadowLayerRadius;
+ }
+
+ /**
+ * Returns the x offset of the shadow layer.
+ * @see #setShadowLayer(float,float,float,int)
+ */
+ public float getShadowLayerDx() {
+ return mShadowLayerDx;
+ }
+
+ /**
+ * Returns the y offset of the shadow layer.
+ * @see #setShadowLayer(float,float,float,int)
+ */
+ public float getShadowLayerDy() {
+ return mShadowLayerDy;
+ }
+
+ /**
+ * Returns the color of the shadow layer.
+ * @see #setShadowLayer(float,float,float,int)
+ */
+ public @ColorInt int getShadowLayerColor() {
+ return mShadowLayerColor;
+ }
+
+ /**
* Return the paint's Align value for drawing text. This controls how the
* text is positioned relative to its origin. LEFT align means that all of
* the text will be drawn to the right of its origin (i.e. the origin
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 943dd84eccf9..ee4c95445bff 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -14,6 +14,7 @@ android_library {
static_libs: [
"SettingsLibHelpUtils",
"SettingsLibRestrictedLockUtils",
+ "SettingsLibAppPreference",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/AppPreference/Android.bp b/packages/SettingsLib/AppPreference/Android.bp
new file mode 100644
index 000000000000..b56181d75e6a
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLibAppPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ libs: [
+ "androidx.annotation_annotation",
+ "androidx.preference_preference",
+ ],
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/AppPreference/AndroidManifest.xml b/packages/SettingsLib/AppPreference/AndroidManifest.xml
new file mode 100644
index 000000000000..7e71308444cd
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget.apppreference">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
new file mode 100644
index 000000000000..6d35550f1b77
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="56dp"
+ android:orientation="horizontal"
+ android:paddingEnd="8dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/secondary_app_icon_size"
+ android:layout_height="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"/>
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView android:id="@android:id/summary"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="@android:style/TextAppearance.Material.Small"
+ android:textAlignment="viewStart"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView android:id="@+id/appendix"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="@android:style/TextAppearance.Material.Small"
+ android:textAlignment="viewEnd"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ <ProgressBar
+ android:id="@android:id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:max="100"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:minWidth="64dp"
+ android:orientation="vertical"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/values/dimens.xml b/packages/SettingsLib/AppPreference/res/values/dimens.xml
new file mode 100644
index 000000000000..e2a7a191f9de
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="secondary_app_icon_size">32dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java
new file mode 100644
index 000000000000..593b6f5b35f4
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/apppreference/AppPreference.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget.apppreference;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ProgressBar;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+public class AppPreference extends Preference {
+
+ private int mProgress;
+ private boolean mProgressVisible;
+
+ public AppPreference(Context context) {
+ super(context);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ public AppPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setLayoutResource(R.layout.preference_app);
+ }
+
+ public void setProgress(int amount) {
+ mProgress = amount;
+ mProgressVisible = true;
+ notifyChanged();
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+
+ view.findViewById(R.id.summary_container)
+ .setVisibility(TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE);
+ final ProgressBar progress = (ProgressBar) view.findViewById(android.R.id.progress);
+ if (mProgressVisible) {
+ progress.setProgress(mProgress);
+ progress.setVisibility(View.VISIBLE);
+ } else {
+ progress.setVisibility(View.GONE);
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index a71041045df2..58feef55bd29 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -102,7 +102,7 @@ public class A2dpProfile implements LocalBluetoothProfile {
BluetoothProfile.A2DP);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index e13e566ec901..988062de0a37 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -96,7 +96,7 @@ final class A2dpSinkProfile implements LocalBluetoothProfile {
BluetoothProfile.A2DP_SINK);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index b9f7323b1a9e..2887a08cad06 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -29,8 +29,6 @@ import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
-import androidx.annotation.VisibleForTesting;
-
import com.android.settingslib.R;
import java.util.ArrayList;
@@ -39,6 +37,8 @@ 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 +48,10 @@ import java.util.List;
public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
private static final String TAG = "CachedBluetoothDevice";
+ // See mConnectAttempted
+ private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
+ private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
+
private final Context mContext;
private final BluetoothAdapter mLocalAdapter;
private final LocalBluetoothProfileManager mProfileManager;
@@ -78,17 +82,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
private final static String MESSAGE_REJECTION_COUNT_PREFS_NAME = "bluetooth_message_reject";
- public long getHiSyncId() {
- return mHiSyncId;
- }
-
- public void setHiSyncId(long id) {
- if (BluetoothUtils.D) {
- Log.d(TAG, "setHiSyncId: mDevice " + mDevice + ", id " + id);
- }
- mHiSyncId = id;
- }
-
/**
* Last time a bt profile auto-connect was attempted.
* If an ACTION_UUID intent comes in within
@@ -97,14 +90,22 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
*/
private long mConnectAttempted;
- // See mConnectAttempted
- private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
- private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
-
// Active device state
private boolean mIsActiveDeviceA2dp = false;
private boolean mIsActiveDeviceHeadset = false;
private boolean mIsActiveDeviceHearingAid = false;
+
+ CachedBluetoothDevice(Context context, LocalBluetoothProfileManager profileManager,
+ BluetoothDevice device) {
+ mContext = context;
+ mLocalAdapter = BluetoothAdapter.getDefaultAdapter();
+ mProfileManager = profileManager;
+ mDevice = device;
+ mProfileConnectionState = new HashMap<>();
+ fillData();
+ mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID;
+ }
+
/**
* Describes the current device and profile for logging.
*
@@ -161,18 +162,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
fetchActiveDevices();
}
- CachedBluetoothDevice(Context context,
- LocalBluetoothProfileManager profileManager,
- BluetoothDevice device) {
- mContext = context;
- mLocalAdapter = BluetoothAdapter.getDefaultAdapter();
- mProfileManager = profileManager;
- mDevice = device;
- mProfileConnectionState = new HashMap<LocalBluetoothProfile, Integer>();
- fillData();
- mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID;
- }
-
public void disconnect() {
for (LocalBluetoothProfile profile : mProfiles) {
disconnect(profile);
@@ -204,6 +193,17 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
connectWithoutResettingTimer(connectAllProfiles);
}
+ public long getHiSyncId() {
+ return mHiSyncId;
+ }
+
+ public void setHiSyncId(long id) {
+ if (BluetoothUtils.D) {
+ Log.d(TAG, "setHiSyncId: mDevice " + mDevice + ", id " + id);
+ }
+ mHiSyncId = id;
+ }
+
void onBondingDockConnect() {
// Attempt to connect if UUIDs are available. Otherwise,
// we will connect when the ACTION_UUID intent arrives.
@@ -226,7 +226,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
int preferredProfiles = 0;
for (LocalBluetoothProfile profile : mProfiles) {
- if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
+ if (connectAllProfiles ? profile.accessProfileEnabled() : profile.isAutoConnectable()) {
if (profile.isPreferred(mDevice)) {
++preferredProfiles;
connectInt(profile);
@@ -300,14 +300,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
return true;
}
- /**
- * Return true if user initiated pairing on this device. The message text is
- * slightly different for local vs. remote initiated pairing dialogs.
- */
- boolean isUserInitiatedPairing() {
- return mDevice.isBondingInitiatedLocally();
- }
-
public void unpair() {
int state = getBondState();
@@ -669,7 +661,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
List<LocalBluetoothProfile> connectableProfiles =
new ArrayList<LocalBluetoothProfile>();
for (LocalBluetoothProfile profile : mProfiles) {
- if (profile.isConnectable()) {
+ if (profile.accessProfileEnabled()) {
connectableProfiles.add(profile);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 2dd8eaf7021d..62507f58426f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -103,7 +103,7 @@ public class HeadsetProfile implements LocalBluetoothProfile {
BluetoothProfile.HEADSET);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 1eeb4f058d1d..8bc0acf5f824 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -100,7 +100,7 @@ public class HearingAidProfile implements LocalBluetoothProfile {
new HearingAidServiceListener(), BluetoothProfile.HEARING_AID);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 4b6a22c9b2c8..4879144a5994 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -104,7 +104,7 @@ final class HfpClientProfile implements LocalBluetoothProfile {
}
@Override
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index c8d4fc84f4b4..61e5b6b3e125 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -94,7 +94,7 @@ public class HidDeviceProfile implements LocalBluetoothProfile {
}
@Override
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index fe6b22224819..75d16db13efe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -93,7 +93,7 @@ public class HidProfile implements LocalBluetoothProfile {
BluetoothProfile.HID_HOST);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
index 0447f378fca1..4b0ca7434f9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
@@ -26,9 +26,9 @@ import android.bluetooth.BluetoothDevice;
public interface LocalBluetoothProfile {
/**
- * Returns true if the user can initiate a connection, false otherwise.
+ * Return {@code true} if the user can initiate a connection for this profile in UI.
*/
- boolean isConnectable();
+ boolean accessProfileEnabled();
/**
* Returns true if the user can enable auto connection for this profile.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 7ad2e28c84b5..1e22f440b5f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -105,7 +105,7 @@ public final class MapClientProfile implements LocalBluetoothProfile {
new MapClientServiceListener(), BluetoothProfile.MAP_CLIENT);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index caea04f87a50..758202412c93 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -104,7 +104,7 @@ public class MapProfile implements LocalBluetoothProfile {
BluetoothProfile.MAP);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
index dfd16224ef8f..e1e5dbe29a1a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
@@ -32,7 +32,7 @@ final class OppProfile implements LocalBluetoothProfile {
// Order of this profile in device profiles list
private static final int ORDINAL = 2;
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 02afe8db7201..7b811624a6f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -78,7 +78,7 @@ public class PanProfile implements LocalBluetoothProfile {
BluetoothProfile.PAN);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 8fefb2fc01b8..1f15601f2756 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -107,7 +107,7 @@ public final class PbapClientProfile implements LocalBluetoothProfile {
new PbapClientServiceListener(), BluetoothProfile.PBAP_CLIENT);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index e9d8cb5a4b2b..adef0841cb2a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -80,7 +80,7 @@ public class PbapServerProfile implements LocalBluetoothProfile {
BluetoothPbap pbap = new BluetoothPbap(context, new PbapServiceListener());
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 61602c6463ee..9a6f104fadd5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -106,7 +106,7 @@ final class SapProfile implements LocalBluetoothProfile {
BluetoothProfile.SAP);
}
- public boolean isConnectable() {
+ public boolean accessProfileEnabled() {
return true;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index 0ca2e87de336..ede248b85083 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -52,6 +52,7 @@ public class SettingsLibRobolectricTestRunner extends RobolectricTestRunner {
@Override
public List<ResourcePath> getIncludedResourcePaths() {
final List<ResourcePath> paths = super.getIncludedResourcePaths();
+ paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/AppPreference/res"));
paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
new file mode 100644
index 000000000000..10c9dfbe6067
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget.apppreference;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class AppPreferenceTest {
+
+ private Context mContext;
+ private View mRootView;
+ private AppPreference mPref;
+ private PreferenceViewHolder mHolder;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mRootView = View.inflate(mContext, R.layout.preference_app, null /* parent */);
+ mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+ mPref = new AppPreference(mContext);
+ }
+
+ @Test
+ public void setProgress_showProgress() {
+ mPref.setProgress(1);
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(android.R.id.progress).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void setSummary_showSummaryContainer() {
+ mPref.setSummary("test");
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(R.id.summary_container).getVisibility())
+ .isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void noSummary_hideSummaryContainer() {
+ mPref.setSummary(null);
+ mPref.onBindViewHolder(mHolder);
+
+ assertThat(mHolder.findViewById(R.id.summary_container).getVisibility())
+ .isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void foobar_testName() {
+ float iconSize = mContext.getResources().getDimension(R.dimen.secondary_app_icon_size);
+ assertThat(Float.floatToIntBits(iconSize)).isEqualTo(Float.floatToIntBits(32));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 36b234704592..c5664605dcef 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -16,11 +16,15 @@
package com.android.systemui.doze;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.os.Build;
import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
@@ -31,7 +35,12 @@ import com.android.internal.annotations.VisibleForTesting;
/**
* Controls the screen brightness when dozing.
*/
-public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListener {
+public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part,
+ SensorEventListener {
+ protected static final String ACTION_AOD_BRIGHTNESS =
+ "com.android.systemui.doze.AOD_BRIGHTNESS";
+ protected static final String BRIGHTNESS_BUCKET = "brightness_bucket";
+
private final Context mContext;
private final DozeMachine.Service mDozeService;
private final DozeHost mDozeHost;
@@ -40,36 +49,52 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
private final Sensor mLightSensor;
private final int[] mSensorToBrightness;
private final int[] mSensorToScrimOpacity;
+ private final boolean mDebuggable;
private boolean mRegistered;
private int mDefaultDozeBrightness;
private boolean mPaused = false;
private int mLastSensorValue = -1;
+ /**
+ * Debug value used for emulating various display brightness buckets:
+ *
+ * {@code am broadcast -p com.android.systemui -a com.android.systemui.doze.AOD_BRIGHTNESS
+ * --ei brightness_bucket 1}
+ */
+ private int mDebugBrightnessBucket = -1;
+
+ @VisibleForTesting
public DozeScreenBrightness(Context context, DozeMachine.Service service,
SensorManager sensorManager, Sensor lightSensor, DozeHost host,
Handler handler, int defaultDozeBrightness, int[] sensorToBrightness,
- int[] sensorToScrimOpacity) {
+ int[] sensorToScrimOpacity, boolean debuggable) {
mContext = context;
mDozeService = service;
mSensorManager = sensorManager;
mLightSensor = lightSensor;
mDozeHost = host;
mHandler = handler;
+ mDebuggable = debuggable;
mDefaultDozeBrightness = defaultDozeBrightness;
mSensorToBrightness = sensorToBrightness;
mSensorToScrimOpacity = sensorToScrimOpacity;
+
+ if (mDebuggable) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_AOD_BRIGHTNESS);
+ mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null);
+ }
}
- @VisibleForTesting
public DozeScreenBrightness(Context context, DozeMachine.Service service,
SensorManager sensorManager, Sensor lightSensor, DozeHost host,
Handler handler, AlwaysOnDisplayPolicy policy) {
this(context, service, sensorManager, lightSensor, host, handler,
context.getResources().getInteger(
com.android.internal.R.integer.config_screenBrightnessDoze),
- policy.screenBrightnessArray, policy.dimmingScrimArray);
+ policy.screenBrightnessArray, policy.dimmingScrimArray, Build.IS_DEBUGGABLE);
}
@Override
@@ -87,7 +112,7 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
resetBrightnessToDefault();
break;
case FINISH:
- setLightSensorEnabled(false);
+ onDestroy();
break;
}
if (newState != DozeMachine.State.FINISH) {
@@ -95,6 +120,13 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
}
}
+ private void onDestroy() {
+ setLightSensorEnabled(false);
+ if (mDebuggable) {
+ mContext.unregisterReceiver(this);
+ }
+ }
+
@Override
public void onSensorChanged(SensorEvent event) {
Trace.beginSection("DozeScreenBrightness.onSensorChanged" + event.values[0]);
@@ -110,7 +142,9 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
private void updateBrightnessAndReady() {
if (mRegistered) {
- int brightness = computeBrightness(mLastSensorValue);
+ int sensorValue = mDebugBrightnessBucket == -1
+ ? mLastSensorValue : mDebugBrightnessBucket;
+ int brightness = computeBrightness(sensorValue);
boolean brightnessReady = brightness > 0;
if (brightnessReady) {
mDozeService.setDozeScreenBrightness(clampToUserSetting(brightness));
@@ -125,7 +159,7 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
scrimOpacity = 255;
} else if (brightnessReady) {
// Only unblank scrim once brightness is ready.
- scrimOpacity = computeScrimOpacity(mLastSensorValue);
+ scrimOpacity = computeScrimOpacity(sensorValue);
}
if (scrimOpacity >= 0) {
mDozeHost.setAodDimmingScrim(scrimOpacity / 255f);
@@ -184,4 +218,9 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
}
}
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mDebugBrightnessBucket = intent.getIntExtra(BRIGHTNESS_BUCKET, -1);
+ updateBrightnessAndReady();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 757c821f37e3..4988f07ca3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1938,6 +1938,12 @@ public class KeyguardViewMediator extends SystemUI {
mStatusBarManager = (StatusBarManager)
mContext.getSystemService(Context.STATUS_BAR_SERVICE);
}
+
+ // TODO(b/113914868): investigation log for disappearing home button
+ Log.d(TAG, "adjustStatusBarLocked (b/113914868): mShowing=" + mShowing
+ + " mStatusBarManager=" + mStatusBarManager + " mOccluded="
+ + mOccluded + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons);
+
if (mStatusBarManager == null) {
Log.w(TAG, "Could not get status bar manager");
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index aebcb9f27e24..71b35e043f77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -595,6 +595,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
boolean disableHome = ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
+ // TODO(b/113914868): investigation log for disappearing home button
+ Log.i(TAG, "updateNavButtonIcons (b/113914868): home disabled=" + disableHome
+ + " mDisabledFlags=" + mDisabledFlags);
+
// Always disable recents when alternate car mode UI is active.
boolean disableRecent = mUseCarModeUi || !isOverviewEnabled();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index eaa0dcf88588..1cf73b00d219 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -33,6 +33,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import android.content.Intent;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
@@ -71,7 +72,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mSensor = mSensorManager.getFakeLightSensor();
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
mSensor.getSensor(), mHostFake, null /* handler */,
- DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY);
+ DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
+ true /* debuggable */);
}
@Test
@@ -93,6 +95,19 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
}
@Test
+ public void testAod_usesDebugValue() throws Exception {
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+
+ Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS);
+ intent.putExtra(DozeScreenBrightness.BRIGHTNESS_BUCKET, 1);
+ mScreen.onReceive(mContext, intent);
+ mSensor.sendSensorEvent(3);
+
+ assertEquals(1, mServiceFake.screenBrightness);
+ }
+
+ @Test
public void testAod_usesLightSensorRespectingUserSetting() throws Exception {
int maxBrightness = 3;
Settings.System.putIntForUser(mContext.getContentResolver(),
@@ -160,7 +175,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
null /* sensor */, mHostFake, null /* handler */,
- DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY);
+ DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
+ true /* debuggable */);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index de85dcb16adb..90c10fdcbfde 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6524,6 +6524,31 @@ message MetricsEvent {
// Tag of a field for the length of a text
FIELD_AUTOFILL_TEXT_LEN = 1572;
+ // Action: the notification assistant is changing a notification
+ // OS: Q
+ NOTIFICATION_ASSISTANT_ADJUSTMENT = 1573;
+
+ // Subtype: The people attached to a notification was changed
+ ADJUSTMENT_KEY_PEOPLE = 1574;
+
+ // Subtype: The snooze options attached to a notification was changed
+ ADJUSTMENT_KEY_SNOOZE_CRITERIA = 1575;
+
+ // Subtype: The group of a notification was changed
+ ADJUSTMENT_KEY_GROUP_KEY = 1576;
+
+ // Subtype: The user sentiment of a notification was changed
+ ADJUSTMENT_KEY_USER_SENTIMENT = 1577;
+
+ // Subtype: New actions have been added to a notification
+ ADJUSTMENT_KEY_SMART_ACTIONS = 1578;
+
+ // Subtype: New smart replies have been added to a notification
+ ADJUSTMENT_KEY_SMART_REPLIES = 1579;
+
+ // Subtype: The importance of a notification has been changed
+ ADJUSTMENT_KEY_IMPORTANCE = 1580;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/backup/java/com/android/server/backup/keyvalue/AgentException.java b/services/backup/java/com/android/server/backup/keyvalue/AgentException.java
new file mode 100644
index 000000000000..e2ca35116bdc
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/keyvalue/AgentException.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.keyvalue;
+
+/**
+ * This represents something wrong with a specific package. For example:
+ * <ul>
+ * <li>Package unknown.
+ * <li>Package is not eligible for backup anymore.
+ * <li>Backup agent timed out.
+ * <li>Backup agent wrote protected keys.
+ * <li>...
+ * </ul>
+ *
+ * @see KeyValueBackupTask
+ * @see TaskException
+ */
+class AgentException extends BackupException {
+ static AgentException transitory() {
+ return new AgentException(/* transitory */ true);
+ }
+
+ static AgentException transitory(Exception cause) {
+ return new AgentException(/* transitory */ true, cause);
+ }
+
+ static AgentException permanent() {
+ return new AgentException(/* transitory */ false);
+ }
+
+ static AgentException permanent(Exception cause) {
+ return new AgentException(/* transitory */ false, cause);
+ }
+
+ private final boolean mTransitory;
+
+ private AgentException(boolean transitory) {
+ mTransitory = transitory;
+ }
+
+ private AgentException(boolean transitory, Exception cause) {
+ super(cause);
+ mTransitory = transitory;
+ }
+
+ boolean isTransitory() {
+ return mTransitory;
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/BackupException.java b/services/backup/java/com/android/server/backup/keyvalue/BackupException.java
new file mode 100644
index 000000000000..27b2d35b13ca
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/keyvalue/BackupException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.keyvalue;
+
+import android.util.AndroidException;
+
+/**
+ * Key-value backup task exception.
+ *
+ * @see AgentException
+ * @see TaskException
+ */
+class BackupException extends AndroidException {
+ BackupException() {}
+
+ BackupException(Exception cause) {
+ super(cause);
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
index 54e6b1d82f74..bb8a1d1339a7 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
@@ -54,7 +54,7 @@ import java.util.List;
public class KeyValueBackupReporter {
@VisibleForTesting static final String TAG = "KeyValueBackupTask";
private static final boolean DEBUG = BackupManagerService.DEBUG;
- @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || true;
+ @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || false;
static void onNewThread(String threadName) {
if (DEBUG) {
@@ -153,16 +153,18 @@ public class KeyValueBackupReporter {
mObserver, packageName, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
}
- void onBindAgentError(SecurityException e) {
- Slog.d(TAG, "Error in bind/backup", e);
- }
-
void onAgentUnknown(String packageName) {
Slog.d(TAG, "Package does not exist, skipping");
BackupObserverUtils.sendBackupOnPackageResult(
mObserver, packageName, BackupManager.ERROR_PACKAGE_NOT_FOUND);
}
+ void onBindAgentError(String packageName, SecurityException e) {
+ Slog.d(TAG, "Error in bind/backup", e);
+ BackupObserverUtils.sendBackupOnPackageResult(
+ mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE);
+ }
+
void onAgentError(String packageName) {
if (MORE_DEBUG) {
Slog.i(TAG, "Agent failure for " + packageName + ", re-staging");
@@ -190,6 +192,8 @@ public class KeyValueBackupReporter {
void onCallAgentDoBackupError(String packageName, boolean callingAgent, Exception e) {
if (callingAgent) {
Slog.e(TAG, "Error invoking agent on " + packageName + ": " + e);
+ BackupObserverUtils.sendBackupOnPackageResult(
+ mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE);
} else {
Slog.e(TAG, "Error before invoking agent on " + packageName + ": " + e);
}
@@ -220,12 +224,8 @@ public class KeyValueBackupReporter {
}
}
- void onReadAgentDataError(String packageName, IOException e) {
- Slog.w(TAG, "Unable read backup data for " + packageName + ": " + e);
- }
-
- void onWriteWidgetDataError(String packageName, IOException e) {
- Slog.w(TAG, "Unable to save widget data for " + packageName + ": " + e);
+ void onAgentDataError(String packageName, IOException e) {
+ Slog.w(TAG, "Unable to read/write agent data for " + packageName + ": " + e);
}
void onDigestError(NoSuchAlgorithmException e) {
@@ -243,16 +243,12 @@ public class KeyValueBackupReporter {
}
}
- void onSendDataToTransport(String packageName) {
+ void onTransportPerformBackup(String packageName) {
if (MORE_DEBUG) {
Slog.v(TAG, "Sending non-empty data to transport for " + packageName);
}
}
- void onNonIncrementalAndNonIncrementalRequired() {
- Slog.e(TAG, "Transport requested non-incremental but already the case");
- }
-
void onEmptyData(PackageInfo packageInfo) {
if (MORE_DEBUG) {
Slog.i(TAG, "No backup data written, not calling transport");
@@ -302,13 +298,20 @@ public class KeyValueBackupReporter {
/* extras */ null);
}
+ void onPackageBackupNonIncrementalAndNonIncrementalRequired(String packageName) {
+ Slog.e(TAG, "Transport requested non-incremental but already the case");
+ BackupObserverUtils.sendBackupOnPackageResult(
+ mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
+ }
+
void onPackageBackupTransportFailure(String packageName) {
BackupObserverUtils.sendBackupOnPackageResult(
mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
}
- void onPackageBackupError(String packageName, Exception e) {
+ void onPackageBackupTransportError(String packageName, Exception e) {
Slog.e(TAG, "Transport error backing up " + packageName, e);
BackupObserverUtils.sendBackupOnPackageResult(
mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index e915ce16a2ef..6904b3fc6b9c 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -16,6 +16,7 @@
package com.android.server.backup.keyvalue;
+import static android.app.ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL;
import static android.os.ParcelFileDescriptor.MODE_CREATE;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
@@ -25,8 +26,8 @@ import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
import static com.android.server.backup.BackupManagerService.OP_PENDING;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
+import android.annotation.IntDef;
import android.annotation.Nullable;
-import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
@@ -47,7 +48,6 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -77,6 +77,8 @@ import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@@ -173,10 +175,8 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
private static final String BLANK_STATE_FILE_NAME = "blank_state";
private static final String PM_PACKAGE = BackupManagerService.PACKAGE_MANAGER_SENTINEL;
- @VisibleForTesting
- public static final String STAGING_FILE_SUFFIX = ".data";
- @VisibleForTesting
- public static final String NEW_STATE_FILE_SUFFIX = ".new";
+ @VisibleForTesting public static final String STAGING_FILE_SUFFIX = ".data";
+ @VisibleForTesting public static final String NEW_STATE_FILE_SUFFIX = ".new";
/**
* Creates a new {@link KeyValueBackupTask} for key-value backup operation, spins up a new
@@ -244,13 +244,13 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
private final int mCurrentOpToken;
private final File mStateDirectory;
private final File mDataDirectory;
+ private final File mBlankStateFile;
private final List<String> mOriginalQueue;
private final List<String> mQueue;
private final List<String> mPendingFullBackups;
private final Object mQueueLock;
@Nullable private final DataChangedJournal mJournal;
- private int mStatus;
@Nullable private PerformFullTransportBackupTask mFullBackupTask;
@Nullable private IBackupAgent mAgent;
@Nullable private PackageInfo mCurrentPackage;
@@ -316,6 +316,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
mDataDirectory = mBackupManagerService.getDataDir();
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mQueueLock = mBackupManagerService.getQueueLock();
+ mBlankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME);
}
private void registerTask() {
@@ -331,45 +332,43 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
public void run() {
Process.setThreadPriority(THREAD_PRIORITY);
- boolean processQueue = startTask();
- while (processQueue && !mQueue.isEmpty() && !mCancelled) {
- String packageName = mQueue.remove(0);
- if (PM_PACKAGE.equals(packageName)) {
- processQueue = backupPm();
- } else {
- processQueue = backupPackage(packageName);
+ int status = BackupTransport.TRANSPORT_OK;
+ try {
+ startTask();
+ while (!mQueue.isEmpty() && !mCancelled) {
+ String packageName = mQueue.remove(0);
+ try {
+ if (PM_PACKAGE.equals(packageName)) {
+ backupPm();
+ } else {
+ backupPackage(packageName);
+ }
+ } catch (AgentException e) {
+ if (e.isTransitory()) {
+ // We try again this package in the next backup pass.
+ mBackupManagerService.dataChangedImpl(packageName);
+ }
+ }
}
+ } catch (TaskException e) {
+ if (e.isStateCompromised()) {
+ mBackupManagerService.resetBackupState(mStateDirectory);
+ }
+ revertTask();
+ status = e.getStatus();
}
- finishTask();
+ finishTask(status);
}
- /** Returns whether to consume next queue package. */
- private boolean handleAgentResult(@Nullable PackageInfo packageInfo, RemoteResult result) {
- if (result == RemoteResult.FAILED_THREAD_INTERRUPTED) {
- // Not an explicit cancel, we need to flag it.
- mCancelled = true;
- mReporter.onAgentCancelled(packageInfo);
- cleanUpAgentForAgentError();
- return false;
- }
- if (result == RemoteResult.FAILED_CANCELLED) {
- mReporter.onAgentCancelled(packageInfo);
- cleanUpAgentForAgentError();
- return false;
- }
- if (result == RemoteResult.FAILED_TIMED_OUT) {
- mReporter.onAgentTimedOut(packageInfo);
- cleanUpAgentForAgentError();
- return true;
- }
- Preconditions.checkState(result.isPresent());
- long agentResult = result.get();
- if (agentResult == BackupAgent.RESULT_ERROR) {
- mReporter.onAgentResultError(packageInfo);
- cleanUpAgentForAgentError();
- return true;
+ /** Returns transport status. */
+ private int sendDataToTransport(@Nullable PackageInfo packageInfo)
+ throws AgentException, TaskException {
+ try {
+ return sendDataToTransport();
+ } catch (IOException e) {
+ mReporter.onAgentDataError(packageInfo.packageName, e);
+ throw TaskException.causedBy(e);
}
- return sendDataToTransport();
}
@Override
@@ -378,11 +377,10 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
@Override
public void operationComplete(long unusedResult) {}
- /** Returns whether to consume next queue package. */
- private boolean startTask() {
+ private void startTask() throws TaskException {
if (mBackupManagerService.isBackupOperationInProgress()) {
mReporter.onSkipBackup();
- return false;
+ throw TaskException.create();
}
// Unfortunately full backup task constructor registers the task with BMS, so we have to
@@ -390,11 +388,9 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
mFullBackupTask = createFullBackupTask(mPendingFullBackups);
registerTask();
- mStatus = BackupTransport.TRANSPORT_OK;
-
if (mQueue.isEmpty() && mPendingFullBackups.isEmpty()) {
mReporter.onEmptyQueueAtStart();
- return false;
+ return;
}
// We only backup PM if it was explicitly in the queue or if it's incremental.
boolean backupPm = mQueue.remove(PM_PACKAGE) || !mNonIncremental;
@@ -415,20 +411,18 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
if (pmState.length() <= 0) {
mReporter.onInitializeTransport(transportName);
mBackupManagerService.resetBackupState(mStateDirectory);
- mStatus = transport.initializeDevice();
- mReporter.onTransportInitialized(mStatus);
+ int status = transport.initializeDevice();
+ mReporter.onTransportInitialized(status);
+ if (status != BackupTransport.TRANSPORT_OK) {
+ throw TaskException.stateCompromised();
+ }
}
+ } catch (TaskException e) {
+ throw e;
} catch (Exception e) {
mReporter.onInitializeTransportError(e);
- mStatus = BackupTransport.TRANSPORT_ERROR;
- }
-
- if (mStatus != BackupTransport.TRANSPORT_OK) {
- mBackupManagerService.resetBackupState(mStateDirectory);
- return false;
+ throw TaskException.stateCompromised();
}
-
- return true;
}
private PerformFullTransportBackupTask createFullBackupTask(List<String> packages) {
@@ -446,120 +440,82 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
mUserInitiated);
}
- /** Returns whether to consume next queue package. */
- private boolean backupPm() {
- RemoteResult agentResult = null;
- try {
- mCurrentPackage = new PackageInfo();
- mCurrentPackage.packageName = PM_PACKAGE;
-
- // Since PM is running in the system process we can set up its agent directly.
- BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent();
- mAgent = IBackupAgent.Stub.asInterface(pmAgent.onBind());
+ private void backupPm() throws TaskException {
+ mReporter.onStartPackageBackup(PM_PACKAGE);
+ mCurrentPackage = new PackageInfo();
+ mCurrentPackage.packageName = PM_PACKAGE;
- Pair<Integer, RemoteResult> statusAndResult = extractAgentData(PM_PACKAGE, mAgent);
- mStatus = statusAndResult.first;
- agentResult = statusAndResult.second;
- } catch (Exception e) {
+ try {
+ extractPmAgentData(mCurrentPackage);
+ int status = sendDataToTransport(mCurrentPackage);
+ cleanUpAgentForTransportStatus(status);
+ } catch (AgentException | TaskException e) {
mReporter.onExtractPmAgentDataError(e);
- mStatus = BackupTransport.TRANSPORT_ERROR;
- }
-
- if (mStatus != BackupTransport.TRANSPORT_OK) {
- // In this case either extractAgentData() already made the agent clean-up or we haven't
- // prepared the state for calling the agent, in either case we don't need to clean-up.
- mBackupManagerService.resetBackupState(mStateDirectory);
- return false;
+ cleanUpAgentForError(e);
+ // PM agent failure is task failure.
+ throw TaskException.stateCompromised(e);
}
-
- Preconditions.checkNotNull(agentResult);
- return handleAgentResult(mCurrentPackage, agentResult);
}
- /** Returns whether to consume next queue package. */
- private boolean backupPackage(String packageName) {
+ private void backupPackage(String packageName) throws AgentException, TaskException {
mReporter.onStartPackageBackup(packageName);
- mStatus = BackupTransport.TRANSPORT_OK;
+ mCurrentPackage = getPackageForBackup(packageName);
- // Verify that the requested app is eligible for key-value backup.
- RemoteResult agentResult = null;
try {
- mCurrentPackage = mPackageManager.getPackageInfo(
- packageName, PackageManager.GET_SIGNING_CERTIFICATES);
- ApplicationInfo applicationInfo = mCurrentPackage.applicationInfo;
- if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mPackageManager)) {
- // The manifest has changed. This won't happen again because the app won't be
- // requesting further backups.
- mReporter.onPackageNotEligibleForBackup(packageName);
- return true;
- }
-
- if (AppBackupUtils.appGetsFullBackup(mCurrentPackage)) {
- // Initially enqueued for key-value backup, but only supports full-backup now.
- mReporter.onPackageEligibleForFullBackup(packageName);
- return true;
- }
-
- if (AppBackupUtils.appIsStopped(applicationInfo)) {
- // Just as it won't receive broadcasts, we won't run it for backup.
- mReporter.onPackageStopped(packageName);
- return true;
- }
+ extractAgentData(mCurrentPackage);
+ int status = sendDataToTransport(mCurrentPackage);
+ cleanUpAgentForTransportStatus(status);
+ } catch (AgentException | TaskException e) {
+ cleanUpAgentForError(e);
+ throw e;
+ }
+ }
- try {
- mBackupManagerService.setWorkSource(new WorkSource(applicationInfo.uid));
- IBackupAgent agent =
- mBackupManagerService.bindToAgentSynchronous(
- applicationInfo,
- ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
- if (agent != null) {
- mAgent = agent;
- Pair<Integer, RemoteResult> statusAndResult =
- extractAgentData(packageName, agent);
- mStatus = statusAndResult.first;
- agentResult = statusAndResult.second;
- } else {
- // Timeout waiting for the agent to bind.
- mStatus = BackupTransport.AGENT_ERROR;
- }
- } catch (SecurityException e) {
- mReporter.onBindAgentError(e);
- mStatus = BackupTransport.AGENT_ERROR;
- }
+ private PackageInfo getPackageForBackup(String packageName) throws AgentException {
+ final PackageInfo packageInfo;
+ try {
+ packageInfo =
+ mPackageManager.getPackageInfo(
+ packageName, PackageManager.GET_SIGNING_CERTIFICATES);
} catch (PackageManager.NameNotFoundException e) {
- mStatus = BackupTransport.AGENT_UNKNOWN;
- } finally {
- mBackupManagerService.setWorkSource(null);
+ mReporter.onAgentUnknown(packageName);
+ throw AgentException.permanent(e);
}
+ ApplicationInfo applicationInfo = packageInfo.applicationInfo;
+ if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mPackageManager)) {
+ mReporter.onPackageNotEligibleForBackup(packageName);
+ throw AgentException.permanent();
+ }
+ if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
+ mReporter.onPackageEligibleForFullBackup(packageName);
+ throw AgentException.permanent();
+ }
+ if (AppBackupUtils.appIsStopped(applicationInfo)) {
+ mReporter.onPackageStopped(packageName);
+ throw AgentException.permanent();
+ }
+ return packageInfo;
+ }
- if (mStatus != BackupTransport.TRANSPORT_OK) {
- // In this case either extractAgentData() already made the agent clean-up or we haven't
- // prepared the state for calling the agent, in either case we don't need to clean-up.
- Preconditions.checkState(mAgent == null);
-
- if (mStatus == BackupTransport.AGENT_ERROR) {
+ private IBackupAgent bindAgent(PackageInfo packageInfo) throws AgentException {
+ String packageName = packageInfo.packageName;
+ final IBackupAgent agent;
+ try {
+ agent =
+ mBackupManagerService.bindToAgentSynchronous(
+ packageInfo.applicationInfo, BACKUP_MODE_INCREMENTAL);
+ if (agent == null) {
mReporter.onAgentError(packageName);
- mBackupManagerService.dataChangedImpl(packageName);
- mStatus = BackupTransport.TRANSPORT_OK;
- return true;
+ throw AgentException.transitory();
}
-
- if (mStatus == BackupTransport.AGENT_UNKNOWN) {
- mReporter.onAgentUnknown(packageName);
- mStatus = BackupTransport.TRANSPORT_OK;
- return true;
- }
-
- // Transport-level failure, re-enqueue everything.
- revertTask();
- return false;
+ } catch (SecurityException e) {
+ mReporter.onBindAgentError(packageName, e);
+ throw AgentException.transitory(e);
}
-
- Preconditions.checkNotNull(agentResult);
- return handleAgentResult(mCurrentPackage, agentResult);
+ return agent;
}
- private void finishTask() {
+ private void finishTask(int status) {
// Mark packages that we couldn't backup as pending backup.
for (String packageName : mQueue) {
mBackupManagerService.dataChangedImpl(packageName);
@@ -576,7 +532,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
// If we succeeded and this is the first time we've done a backup, we can record the current
// backup dataset token.
long currentToken = mBackupManagerService.getCurrentToken();
- if ((mStatus == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) {
+ if ((status == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) {
try {
IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString);
mBackupManagerService.setCurrentToken(transport.getCurrentRestoreSet());
@@ -589,9 +545,14 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
synchronized (mQueueLock) {
mBackupManagerService.setBackupRunning(false);
- if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
+ if (status == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
mReporter.onTransportNotInitialized();
- triggerTransportInitializationLocked();
+ try {
+ triggerTransportInitializationLocked();
+ } catch (Exception e) {
+ mReporter.onPendingInitializeTransportError(e);
+ status = BackupTransport.TRANSPORT_ERROR;
+ }
}
}
@@ -605,7 +566,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
}
if (!mCancelled
- && mStatus == BackupTransport.TRANSPORT_OK
+ && status == BackupTransport.TRANSPORT_OK
&& mFullBackupTask != null
&& !mPendingFullBackups.isEmpty()) {
mReporter.onStartFullBackup(mPendingFullBackups);
@@ -621,7 +582,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
mFullBackupTask.unregisterTask();
}
mTaskFinishedListener.onFinished(callerLogString);
- mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, mStatus));
+ mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, status));
mBackupManagerService.getWakelock().release();
}
@@ -642,17 +603,12 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
}
@GuardedBy("mQueueLock")
- private void triggerTransportInitializationLocked() {
- try {
- IBackupTransport transport =
- mTransportClient.connectOrThrow("KVBT.triggerTransportInitializationLocked");
- mBackupManagerService.getPendingInits().add(transport.name());
- deletePmStateFile();
- mBackupManagerService.backupNow();
- } catch (Exception e) {
- mReporter.onPendingInitializeTransportError(e);
- mStatus = BackupTransport.TRANSPORT_ERROR;
- }
+ private void triggerTransportInitializationLocked() throws Exception {
+ IBackupTransport transport =
+ mTransportClient.connectOrThrow("KVBT.triggerTransportInitializationLocked");
+ mBackupManagerService.getPendingInits().add(transport.name());
+ deletePmStateFile();
+ mBackupManagerService.backupNow();
}
/** Removes PM state, triggering initialization in the next key-value task. */
@@ -660,35 +616,69 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
new File(mStateDirectory, PM_PACKAGE).delete();
}
+ /** Same as {@link #extractAgentData(PackageInfo)}, but only for PM package. */
+ private void extractPmAgentData(PackageInfo packageInfo) throws AgentException, TaskException {
+ Preconditions.checkArgument(packageInfo.packageName.equals(PM_PACKAGE));
+ BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent();
+ mAgent = IBackupAgent.Stub.asInterface(pmAgent.onBind());
+ extractAgentData(packageInfo, mAgent);
+ }
+
/**
- * Returns a {@link Pair}. The first of the pair contains the status. In case the status is
- * {@link BackupTransport#TRANSPORT_OK}, the second of the pair contains the agent result,
- * otherwise {@code null}.
+ * Binds to the agent and extracts its backup data. If this method returns, the data in {@code
+ * mBackupData} is ready to be sent to the transport, otherwise it will throw.
+ *
+ * <p>This method leaves agent resources (agent binder, files and file-descriptors) opened that
+ * need to be cleaned up after terminating, either successfully or exceptionally. This clean-up
+ * can be done with methods {@link #cleanUpAgentForTransportStatus(int)} and {@link
+ * #cleanUpAgentForError(BackupException)}, depending on whether data was successfully sent to
+ * the transport or not. It's the caller responsibility to do the clean-up or delegate it.
*/
- private Pair<Integer, RemoteResult> extractAgentData(String packageName, IBackupAgent agent) {
+ private void extractAgentData(PackageInfo packageInfo) throws AgentException, TaskException {
+ mBackupManagerService.setWorkSource(new WorkSource(packageInfo.applicationInfo.uid));
+ try {
+ mAgent = bindAgent(packageInfo);
+ extractAgentData(packageInfo, mAgent);
+ } finally {
+ mBackupManagerService.setWorkSource(null);
+ }
+ }
+
+ /**
+ * Calls agent {@link IBackupAgent#doBackup(ParcelFileDescriptor, ParcelFileDescriptor,
+ * ParcelFileDescriptor, long, IBackupCallback, int)} and waits for the result. If this method
+ * returns, the data in {@code mBackupData} is ready to be sent to the transport, otherwise it
+ * will throw.
+ *
+ * <p>This method creates files and file-descriptors for the agent that need to be deleted and
+ * closed after terminating, either successfully or exceptionally. This clean-up can be done
+ * with methods {@link #cleanUpAgentForTransportStatus(int)} and {@link
+ * #cleanUpAgentForError(BackupException)}, depending on whether data was successfully sent to
+ * the transport or not. It's the caller responsibility to do the clean-up or delegate it.
+ */
+ private void extractAgentData(PackageInfo packageInfo, IBackupAgent agent)
+ throws AgentException, TaskException {
+ String packageName = packageInfo.packageName;
mReporter.onExtractAgentData(packageName);
- File blankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME);
mSavedStateFile = new File(mStateDirectory, packageName);
mBackupDataFile = new File(mDataDirectory, packageName + STAGING_FILE_SUFFIX);
mNewStateFile = new File(mStateDirectory, packageName + NEW_STATE_FILE_SUFFIX);
mReporter.onAgentFilesReady(mBackupDataFile);
- mSavedState = null;
- mBackupData = null;
- mNewState = null;
-
boolean callingAgent = false;
final RemoteResult agentResult;
try {
- File savedStateFileForAgent = (mNonIncremental) ? blankStateFile : mSavedStateFile;
+ File savedStateFileForAgent = (mNonIncremental) ? mBlankStateFile : mSavedStateFile;
// MODE_CREATE to make an empty file if necessary
- mSavedState = ParcelFileDescriptor.open(
- savedStateFileForAgent, MODE_READ_ONLY | MODE_CREATE);
- mBackupData = ParcelFileDescriptor.open(
- mBackupDataFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
- mNewState = ParcelFileDescriptor.open(
- mNewStateFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
+ mSavedState =
+ ParcelFileDescriptor.open(savedStateFileForAgent, MODE_READ_ONLY | MODE_CREATE);
+ mBackupData =
+ ParcelFileDescriptor.open(
+ mBackupDataFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
+ mNewState =
+ ParcelFileDescriptor.open(
+ mNewStateFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
if (!SELinux.restorecon(mBackupDataFile)) {
mReporter.onRestoreconFailed(mBackupDataFile);
@@ -713,15 +703,40 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
"doBackup()");
} catch (Exception e) {
mReporter.onCallAgentDoBackupError(packageName, callingAgent, e);
- cleanUpAgentForAgentError();
- // TODO: Remove the check on callingAgent when RemoteCall supports local agent calls.
- int status =
- callingAgent ? BackupTransport.AGENT_ERROR : BackupTransport.TRANSPORT_ERROR;
- return Pair.create(status, null);
+ if (callingAgent) {
+ throw AgentException.transitory(e);
+ } else {
+ throw TaskException.create();
+ }
+ } finally {
+ mBlankStateFile.delete();
}
- blankStateFile.delete();
+ checkAgentResult(packageInfo, agentResult);
+ }
- return Pair.create(BackupTransport.TRANSPORT_OK, agentResult);
+ private void checkAgentResult(PackageInfo packageInfo, RemoteResult result)
+ throws AgentException, TaskException {
+ if (result == RemoteResult.FAILED_THREAD_INTERRUPTED) {
+ // Not an explicit cancel, we need to flag it.
+ mCancelled = true;
+ mReporter.onAgentCancelled(packageInfo);
+ throw TaskException.create();
+ }
+ if (result == RemoteResult.FAILED_CANCELLED) {
+ mReporter.onAgentCancelled(packageInfo);
+ throw TaskException.create();
+ }
+ if (result == RemoteResult.FAILED_TIMED_OUT) {
+ mReporter.onAgentTimedOut(packageInfo);
+ throw AgentException.transitory();
+ }
+ Preconditions.checkState(result.isPresent());
+ long resultCode = result.get();
+ if (resultCode == BackupAgent.RESULT_ERROR) {
+ mReporter.onAgentResultError(packageInfo);
+ throw AgentException.transitory();
+ }
+ Preconditions.checkState(resultCode == BackupAgent.RESULT_SUCCESS);
}
private void agentFail(IBackupAgent agent, String message) {
@@ -801,94 +816,79 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
}
}
- /** Returns whether to consume next queue package. */
- private boolean sendDataToTransport() {
+ /** Returns transport status. */
+ private int sendDataToTransport() throws AgentException, TaskException, IOException {
Preconditions.checkState(mBackupData != null);
+ checkBackupData(mCurrentPackage.applicationInfo, mBackupDataFile);
String packageName = mCurrentPackage.packageName;
- ApplicationInfo applicationInfo = mCurrentPackage.applicationInfo;
+ writeWidgetPayloadIfAppropriate(mBackupData.getFileDescriptor(), packageName);
- boolean writingWidgetData = false;
- try {
- if (!validateBackupData(applicationInfo, mBackupDataFile)) {
- cleanUpAgentForAgentError();
- return true;
- }
- writingWidgetData = true;
- writeWidgetPayloadIfAppropriate(mBackupData.getFileDescriptor(), packageName);
- } catch (IOException e) {
- if (writingWidgetData) {
- mReporter.onWriteWidgetDataError(packageName, e);
- } else {
- mReporter.onReadAgentDataError(packageName, e);
- }
- cleanUpAgentForAgentError();
- revertTask();
- return false;
+ boolean nonIncremental = mSavedStateFile.length() == 0;
+ int status = transportPerformBackup(mCurrentPackage, mBackupDataFile, nonIncremental);
+ handleTransportStatus(status, packageName, mBackupDataFile.length());
+ return status;
+ }
+
+ private int transportPerformBackup(
+ PackageInfo packageInfo, File backupDataFile, boolean nonIncremental)
+ throws TaskException {
+ String packageName = packageInfo.packageName;
+ long size = backupDataFile.length();
+ if (size <= 0) {
+ mReporter.onEmptyData(packageInfo);
+ return BackupTransport.TRANSPORT_OK;
}
- boolean nonIncremental = mSavedStateFile.length() == 0;
- long size = mBackupDataFile.length();
- if (size > 0) {
- try (ParcelFileDescriptor backupData =
- ParcelFileDescriptor.open(mBackupDataFile, MODE_READ_ONLY)) {
- IBackupTransport transport =
- mTransportClient.connectOrThrow("KVBT.sendDataToTransport()");
- mReporter.onSendDataToTransport(packageName);
- int flags = getPerformBackupFlags(mUserInitiated, nonIncremental);
+ int status;
+ try (ParcelFileDescriptor backupData =
+ ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
+ IBackupTransport transport =
+ mTransportClient.connectOrThrow("KVBT.transportPerformBackup()");
+ mReporter.onTransportPerformBackup(packageName);
+ int flags = getPerformBackupFlags(mUserInitiated, nonIncremental);
- mStatus = transport.performBackup(mCurrentPackage, backupData, flags);
- if (mStatus == BackupTransport.TRANSPORT_OK) {
- mStatus = transport.finishBackup();
- }
- } catch (Exception e) {
- mReporter.onPackageBackupError(packageName, e);
- mStatus = BackupTransport.TRANSPORT_ERROR;
+ status = transport.performBackup(packageInfo, backupData, flags);
+ if (status == BackupTransport.TRANSPORT_OK) {
+ status = transport.finishBackup();
}
- } else {
- mReporter.onEmptyData(mCurrentPackage);
- mStatus = BackupTransport.TRANSPORT_OK;
+ } catch (Exception e) {
+ mReporter.onPackageBackupTransportError(packageName, e);
+ throw TaskException.causedBy(e);
}
- if (nonIncremental
- && mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
- mReporter.onNonIncrementalAndNonIncrementalRequired();
- mStatus = BackupTransport.TRANSPORT_ERROR;
+ if (nonIncremental && status == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
+ mReporter.onPackageBackupNonIncrementalAndNonIncrementalRequired(packageName);
+ throw TaskException.create();
}
-
- boolean processQueue = handleTransportStatus(mStatus, packageName, size);
- // We might report quota exceeded to the agent in handleTransportStatus() above, so we
- // only clean-up after it.
- cleanUpAgentForTransportStatus(mStatus);
- return processQueue;
+ return status;
}
- /** Returns whether to consume next queue package. */
- private boolean handleTransportStatus(int status, String packageName, long size) {
+ private void handleTransportStatus(int status, String packageName, long size)
+ throws TaskException, AgentException {
if (status == BackupTransport.TRANSPORT_OK) {
mReporter.onPackageBackupComplete(packageName, size);
- return true;
- }
- if (status == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
- mReporter.onPackageBackupRejected(packageName);
- return true;
+ return;
}
if (status == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
mReporter.onPackageBackupNonIncrementalRequired(mCurrentPackage);
// Immediately retry the current package.
mQueue.add(0, packageName);
- return true;
+ return;
+ }
+ if (status == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+ mReporter.onPackageBackupRejected(packageName);
+ throw AgentException.permanent();
}
if (status == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
mReporter.onPackageBackupQuotaExceeded(packageName);
agentDoQuotaExceeded(mAgent, packageName, size);
- return true;
+ throw AgentException.permanent();
}
// Any other error here indicates a transport-level failure.
mReporter.onPackageBackupTransportFailure(packageName);
- revertTask();
- return false;
+ throw TaskException.forStatus(status);
}
private void agentDoQuotaExceeded(@Nullable IBackupAgent agent, String packageName, long size) {
@@ -908,19 +908,17 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
}
/**
- * For system apps and pseudo-apps always return {@code true}. For regular apps returns whether
- * {@code backupDataFile} doesn't have any protected keys.
- *
- * <p>If the app has attempted to write any protected keys we also crash them.
+ * For system apps and pseudo-apps never throws. For regular apps throws {@link AgentException}
+ * if {@code backupDataFile} has any protected keys, also crashing the app.
*/
- private boolean validateBackupData(
- @Nullable ApplicationInfo applicationInfo, File backupDataFile) throws IOException {
+ private void checkBackupData(@Nullable ApplicationInfo applicationInfo, File backupDataFile)
+ throws IOException, AgentException {
if (applicationInfo == null || (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
// System apps and pseudo-apps can write what they want.
- return true;
+ return;
}
try (ParcelFileDescriptor backupData =
- ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
+ ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
BackupDataInput backupDataInput = new BackupDataInput(backupData.getFileDescriptor());
while (backupDataInput.readNextHeader()) {
String key = backupDataInput.getKey();
@@ -928,12 +926,11 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
mReporter.onAgentIllegalKey(mCurrentPackage, key);
// Crash them if they wrote any protected keys.
agentFail(mAgent, "Illegal backup key: " + key);
- return false;
+ throw AgentException.permanent();
}
backupDataInput.skipEntityData();
}
}
- return true;
}
private int getPerformBackupFlags(boolean userInitiated, boolean nonIncremental) {
@@ -1009,44 +1006,39 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
}
}
- /** Cleans-up after having called the agent. */
- private void cleanUpAgentForTransportStatus(int status) {
- updateFiles(status);
- cleanUpAgent();
- }
-
- /** Cleans-up if we failed to call the agent. */
- private void cleanUpAgentForAgentError() {
- mBackupDataFile.delete();
- mNewStateFile.delete();
- cleanUpAgent();
+ /**
+ * Cleans up agent resources opened by {@link #extractAgentData(PackageInfo)} for exceptional
+ * case.
+ *
+ * <p>Note: Declaring exception parameter so that the caller only calls this when an exception
+ * is thrown.
+ */
+ private void cleanUpAgentForError(BackupException exception) {
+ cleanUpAgent(StateTransaction.DISCARD_NEW);
}
- private void updateFiles(int status) {
+ /**
+ * Cleans up agent resources opened by {@link #extractAgentData(PackageInfo)} according to
+ * transport status returned in {@link #sendDataToTransport(PackageInfo)}.
+ */
+ private void cleanUpAgentForTransportStatus(int status) {
switch (status) {
case BackupTransport.TRANSPORT_OK:
- mBackupDataFile.delete();
- mNewStateFile.renameTo(mSavedStateFile);
+ cleanUpAgent(StateTransaction.COMMIT_NEW);
break;
case BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED:
- mSavedStateFile.delete();
- mBackupDataFile.delete();
- mNewStateFile.delete();
+ cleanUpAgent(StateTransaction.DISCARD_ALL);
break;
default:
- // Includes:
- // * BackupTransport.TRANSPORT_PACKAGE_REJECTED
- // * BackupTransport.TRANSPORT_QUOTA_EXCEEDED
- // * BackupTransport.TRANSPORT_ERROR
- mBackupDataFile.delete();
- mNewStateFile.delete();
- break;
+ // All other transport statuses are properly converted to agent or task exceptions.
+ throw new AssertionError();
}
}
- /** Cleans-up file-descriptors and unbinds agent. */
- private void cleanUpAgent() {
- mAgent = null;
+ private void cleanUpAgent(@StateTransaction int stateTransaction) {
+ applyStateTransaction(stateTransaction);
+ mBackupDataFile.delete();
+ mBlankStateFile.delete();
tryCloseFileDescriptor(mSavedState, "old state");
tryCloseFileDescriptor(mBackupData, "backup data");
tryCloseFileDescriptor(mNewState, "new state");
@@ -1058,6 +1050,24 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
if (mCurrentPackage.applicationInfo != null) {
mBackupManagerService.unbindAgent(mCurrentPackage.applicationInfo);
}
+ mAgent = null;
+ }
+
+ private void applyStateTransaction(@StateTransaction int stateTransaction) {
+ switch (stateTransaction) {
+ case StateTransaction.COMMIT_NEW:
+ mNewStateFile.renameTo(mSavedStateFile);
+ break;
+ case StateTransaction.DISCARD_NEW:
+ mNewStateFile.delete();
+ break;
+ case StateTransaction.DISCARD_ALL:
+ mSavedStateFile.delete();
+ mNewStateFile.delete();
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown state transaction " + stateTransaction);
+ }
}
private void tryCloseFileDescriptor(@Nullable Closeable closeable, String logName) {
@@ -1079,4 +1089,16 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
mPendingCall = null;
return result;
}
+
+ @IntDef({
+ StateTransaction.COMMIT_NEW,
+ StateTransaction.DISCARD_NEW,
+ StateTransaction.DISCARD_ALL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface StateTransaction {
+ int COMMIT_NEW = 0;
+ int DISCARD_NEW = 1;
+ int DISCARD_ALL = 2;
+ }
}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/TaskException.java b/services/backup/java/com/android/server/backup/keyvalue/TaskException.java
new file mode 100644
index 000000000000..08d289556ca3
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/keyvalue/TaskException.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.keyvalue;
+
+import android.app.backup.BackupTransport;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * The key-value backup task has failed, no more packages will be processed and we shouldn't attempt
+ * any more backups now. These can be caused by transport failures (as opposed to agent failures).
+ *
+ * @see KeyValueBackupTask
+ * @see AgentException
+ */
+class TaskException extends BackupException {
+ private static final int DEFAULT_STATUS = BackupTransport.TRANSPORT_ERROR;
+
+ static TaskException stateCompromised() {
+ return new TaskException(/* stateCompromised */ true, DEFAULT_STATUS);
+ }
+
+ static TaskException stateCompromised(Exception cause) {
+ if (cause instanceof TaskException) {
+ TaskException exception = (TaskException) cause;
+ return new TaskException(cause, /* stateCompromised */ true, exception.getStatus());
+ }
+ return new TaskException(cause, /* stateCompromised */ true, DEFAULT_STATUS);
+ }
+
+ static TaskException forStatus(int status) {
+ Preconditions.checkArgument(
+ status != BackupTransport.TRANSPORT_OK, "Exception based on TRANSPORT_OK");
+ return new TaskException(/* stateCompromised */ false, status);
+ }
+
+ static TaskException causedBy(Exception cause) {
+ if (cause instanceof TaskException) {
+ return (TaskException) cause;
+ }
+ return new TaskException(cause, /* stateCompromised */ false, DEFAULT_STATUS);
+ }
+
+ static TaskException create() {
+ return new TaskException(/* stateCompromised */ false, DEFAULT_STATUS);
+ }
+
+ private final boolean mStateCompromised;
+ private final int mStatus;
+
+ private TaskException(Exception cause, boolean stateCompromised, int status) {
+ super(cause);
+ mStateCompromised = stateCompromised;
+ mStatus = status;
+ }
+
+ private TaskException(boolean stateCompromised, int status) {
+ mStateCompromised = stateCompromised;
+ mStatus = status;
+ }
+
+ boolean isStateCompromised() {
+ return mStateCompromised;
+ }
+
+ int getStatus() {
+ return mStatus;
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8c7fc849b79e..6e3ea9fad599 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20892,7 +20892,8 @@ public class ActivityManagerService extends IActivityManager.Stub
memoryStat.pgmajfault,
memoryStat.rssInBytes,
memoryStat.cacheInBytes,
- memoryStat.swapInBytes);
+ memoryStat.swapInBytes,
+ memoryStat.rssHighWatermarkInBytes);
processMemoryStates.add(processMemoryState);
}
}
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index aad890b8bd74..228c71d91b8f 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -37,18 +37,25 @@ import java.util.regex.Pattern;
* Static utility methods related to {@link MemoryStat}.
*/
final class MemoryStatUtil {
+ static final int BYTES_IN_KILOBYTE = 1024;
+
private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
/** True if device has per-app memcg */
- private static final Boolean DEVICE_HAS_PER_APP_MEMCG =
+ private static final boolean DEVICE_HAS_PER_APP_MEMCG =
SystemProperties.getBoolean("ro.config.per_app_memcg", false);
/** Path to check if device has memcg */
private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
/** Path to memory stat file for logging app start memory state */
private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat";
+ /** Path to memory max usage file for logging app memory state */
+ private static final String MEMORY_MAX_USAGE_FILE_FMT =
+ "/dev/memcg/apps/uid_%d/pid_%d/memory.max_usage_in_bytes";
/** Path to procfs stat file for logging app start memory state */
private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
+ /** Path to procfs status file for logging app memory state */
+ private static final String PROC_STATUS_FILE_FMT = "/proc/%d/status";
private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
@@ -56,6 +63,9 @@ final class MemoryStatUtil {
private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)");
private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)");
+ private static final Pattern RSS_HIGH_WATERMARK_IN_BYTES =
+ Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
+
private static final int PGFAULT_INDEX = 9;
private static final int PGMAJFAULT_INDEX = 11;
private static final int RSS_IN_BYTES_INDEX = 23;
@@ -80,8 +90,15 @@ final class MemoryStatUtil {
*/
@Nullable
static MemoryStat readMemoryStatFromMemcg(int uid, int pid) {
- final String path = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid);
- return parseMemoryStatFromMemcg(readFileContents(path));
+ final String statPath = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid);
+ MemoryStat stat = parseMemoryStatFromMemcg(readFileContents(statPath));
+ if (stat == null) {
+ return null;
+ }
+ String maxUsagePath = String.format(Locale.US, MEMORY_MAX_USAGE_FILE_FMT, uid, pid);
+ stat.rssHighWatermarkInBytes = parseMemoryMaxUsageFromMemCg(
+ readFileContents(maxUsagePath));
+ return stat;
}
/**
@@ -91,8 +108,14 @@ final class MemoryStatUtil {
*/
@Nullable
static MemoryStat readMemoryStatFromProcfs(int pid) {
- final String path = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
- return parseMemoryStatFromProcfs(readFileContents(path));
+ final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
+ MemoryStat stat = parseMemoryStatFromProcfs(readFileContents(statPath));
+ if (stat == null) {
+ return null;
+ }
+ final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid);
+ stat.rssHighWatermarkInBytes = parseVmHWMFromProcfs(readFileContents(statusPath));
+ return stat;
}
private static String readFileContents(String path) {
@@ -113,7 +136,7 @@ final class MemoryStatUtil {
/**
* Parses relevant statistics out from the contents of a memory.stat file in memcg.
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @VisibleForTesting
@Nullable
static MemoryStat parseMemoryStatFromMemcg(String memoryStatContents) {
if (memoryStatContents == null || memoryStatContents.isEmpty()) {
@@ -135,10 +158,18 @@ final class MemoryStatUtil {
return memoryStat;
}
+ @VisibleForTesting
+ static long parseMemoryMaxUsageFromMemCg(String memoryMaxUsageContents) {
+ if (memoryMaxUsageContents == null || memoryMaxUsageContents.isEmpty()) {
+ return 0;
+ }
+ return Long.valueOf(memoryMaxUsageContents);
+ }
+
/**
- * Parses relevant statistics out from the contents of a /proc/pid/stat file in procfs.
+ * Parses relevant statistics out from the contents of the /proc/pid/stat file in procfs.
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @VisibleForTesting
@Nullable
static MemoryStat parseMemoryStatFromProcfs(String procStatContents) {
if (procStatContents == null || procStatContents.isEmpty()) {
@@ -158,6 +189,20 @@ final class MemoryStatUtil {
}
/**
+ * Parses RSS high watermark out from the contents of the /proc/pid/status file in procfs. The
+ * returned value is in bytes.
+ */
+ @VisibleForTesting
+ static long parseVmHWMFromProcfs(String procStatusContents) {
+ if (procStatusContents == null || procStatusContents.isEmpty()) {
+ return 0;
+ }
+ Matcher m = RSS_HIGH_WATERMARK_IN_BYTES.matcher(procStatusContents);
+ // Convert value read from /proc/pid/status from kilobytes to bytes.
+ return m.find() ? Long.valueOf(m.group(1)) * BYTES_IN_KILOBYTE : 0;
+ }
+
+ /**
* Returns whether per-app memcg is available on device.
*/
static boolean hasMemcg() {
@@ -175,5 +220,7 @@ final class MemoryStatUtil {
long cacheInBytes;
/** Number of bytes of swap usage */
long swapInBytes;
+ /** Number of bytes of peak anonymous and swap cache memory */
+ long rssHighWatermarkInBytes;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6f5f90a781e3..a9b0d5c42f73 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1976,7 +1976,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
}
- unbindCurrentMethodLocked(true);
+ unbindCurrentMethodLocked();
mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
mCurIntent.setComponent(info.getComponent());
@@ -2020,7 +2020,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mCurMethod = IInputMethod.Stub.asInterface(service);
if (mCurToken == null) {
Slog.w(TAG, "Service connected without a token!");
- unbindCurrentMethodLocked(false);
+ unbindCurrentMethodLocked();
return;
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
@@ -2059,7 +2059,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
channel.dispose();
}
- void unbindCurrentMethodLocked(boolean savePosition) {
+ void unbindCurrentMethodLocked() {
if (mVisibleBound) {
mContext.unbindService(mVisibleConnection);
mVisibleBound = false;
@@ -2076,10 +2076,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
+ mCurTokenDisplayId);
}
- if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && savePosition) {
- // The current IME is shown. Hence an IME switch (transition) is happening.
- mWindowManagerInternal.saveLastInputMethodWindowForTransition();
- }
mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId);
} catch (RemoteException e) {
}
@@ -2094,7 +2090,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
void resetCurrentMethodAndClient(
/* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
mCurMethodId = null;
- unbindCurrentMethodLocked(false);
+ unbindCurrentMethodLocked();
unbindCurrentClientLocked(unbindClientReason);
}
@@ -2865,7 +2861,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final int newFocusDisplayId =
mWindowManagerInternal.getDisplayIdForWindow(windowToken);
if (newFocusDisplayId != mCurTokenDisplayId) {
- unbindCurrentMethodLocked(false);
+ unbindCurrentMethodLocked();
}
}
} else if (isTextEditor && doAutoShow && (softInputMode &
@@ -3237,19 +3233,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
- private void clearLastInputMethodWindowForTransition(IBinder token) {
- if (!calledFromValidUser()) {
- return;
- }
- synchronized (mMethodMap) {
- if (!calledWithValidToken(token)) {
- return;
- }
- }
- mWindowManagerInternal.clearLastInputMethodWindowForTransition();
- }
-
- @BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
Slog.d(TAG, "Got the notification of a user action.");
@@ -4957,7 +4940,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
try {
synchronized (mMethodMap) {
hideCurrentInputLocked(0, null);
- unbindCurrentMethodLocked(false);
+ unbindCurrentMethodLocked();
// Reset the current IME
resetSelectedInputMethodAndSubtypeLocked(null);
// Also reset the settings of the current IME
@@ -5030,12 +5013,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
- public void clearLastInputMethodWindowForTransition() {
- mImms.clearLastInputMethodWindowForTransition(mToken);
- }
-
- @BinderThread
- @Override
public IInputContentUriToken createInputContentUriToken(Uri contentUri,
String packageName) {
return mImms.createInputContentUriToken(mToken, contentUri, packageName);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 67c5e0034bf6..cade07cfb821 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4284,8 +4284,12 @@ public class NotificationManagerService extends SystemService {
@VisibleForTesting
int resolveNotificationUid(String callingPkg, String targetPkg,
int callingUid, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ userId = USER_SYSTEM;
+ }
// posted from app A on behalf of app A
- if (isCallerSameApp(targetPkg, callingUid) && TextUtils.equals(callingPkg, targetPkg)) {
+ if (isCallerSameApp(targetPkg, callingUid, userId)
+ && TextUtils.equals(callingPkg, targetPkg)) {
return callingUid;
}
@@ -4322,7 +4326,7 @@ public class NotificationManagerService extends SystemService {
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationLock) {
if (mNotificationsByKey.get(r.sbn.getKey()) == null
- && isCallerInstantApp(pkg, callingUid)) {
+ && isCallerInstantApp(pkg, callingUid, r.getUserId())) {
// Ephemeral apps have some special constraints for notifications.
// They are not allowed to create new notifications however they are allowed to
// update notifications created by the system (e.g. a foreground service
@@ -6416,7 +6420,8 @@ public class NotificationManagerService extends SystemService {
}
}
- private boolean isCallerInstantApp(String pkg, int callingUid) {
+ @VisibleForTesting
+ boolean isCallerInstantApp(String pkg, int callingUid, int userId) {
// System is always allowed to act for ephemeral apps.
if (isUidSystemOrPhone(callingUid)) {
return false;
@@ -6425,8 +6430,7 @@ public class NotificationManagerService extends SystemService {
mAppOps.checkPackage(callingUid, pkg);
try {
- ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
- UserHandle.getCallingUserId());
+ ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId);
if (ai == null) {
throw new SecurityException("Unknown package " + pkg);
}
@@ -6438,13 +6442,13 @@ public class NotificationManagerService extends SystemService {
}
private void checkCallerIsSameApp(String pkg) {
- checkCallerIsSameApp(pkg, Binder.getCallingUid());
+ checkCallerIsSameApp(pkg, Binder.getCallingUid(), UserHandle.getCallingUserId());
}
- private void checkCallerIsSameApp(String pkg, int uid) {
+ private void checkCallerIsSameApp(String pkg, int uid, int userId) {
try {
ApplicationInfo ai = mPackageManager.getApplicationInfo(
- pkg, 0, UserHandle.getCallingUserId());
+ pkg, 0, userId);
if (ai == null) {
throw new SecurityException("Unknown package " + pkg);
}
@@ -6466,9 +6470,9 @@ public class NotificationManagerService extends SystemService {
}
}
- private boolean isCallerSameApp(String pkg, int uid) {
+ private boolean isCallerSameApp(String pkg, int uid, int userId) {
try {
- checkCallerIsSameApp(pkg, uid);
+ checkCallerIsSameApp(pkg, uid, userId);
return true;
} catch (SecurityException e) {
return false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 91af0eca8707..e10827bc6101 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -232,6 +232,7 @@ import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
+import android.permission.PermissionManager;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.security.KeyStore;
@@ -2912,13 +2913,15 @@ public class PackageManagerService extends IPackageManager.Stub
if (mIsUpgrade) {
final int callingUid = getCallingUid();
- final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length;
+ final List<PermissionManager.SplitPermissionInfo> splitPermissions =
+ mContext.getSystemService(PermissionManager.class).getSplitPermissions();
+ final int numSplitPerms = splitPermissions.size();
for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
- final PackageParser.SplitPermissionInfo splitPerm =
- PackageParser.SPLIT_PERMISSIONS[splitPermNum];
- final String rootPerm = splitPerm.rootPerm;
+ final PermissionManager.SplitPermissionInfo splitPerm =
+ splitPermissions.get(splitPermNum);
+ final String rootPerm = splitPerm.getRootPermission();
- if (preUpgradeSdkVersion >= splitPerm.targetSdk) {
+ if (preUpgradeSdkVersion >= splitPerm.getTargetSdk()) {
continue;
}
@@ -2926,7 +2929,7 @@ public class PackageManagerService extends IPackageManager.Stub
for (int packageNum = 0; packageNum < numPackages; packageNum++) {
final PackageParser.Package pkg = mPackages.valueAt(packageNum);
- if (pkg.applicationInfo.targetSdkVersion >= splitPerm.targetSdk
+ if (pkg.applicationInfo.targetSdkVersion >= splitPerm.getTargetSdk()
|| !pkg.requestedPermissions.contains(rootPerm)) {
continue;
}
@@ -2938,7 +2941,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
- final String[] newPerms = splitPerm.newPerms;
+ final String[] newPerms = splitPerm.getNewPermissions();
final int numNewPerms = newPerms.length;
for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
@@ -15336,7 +15339,9 @@ public class PackageManagerService extends IPackageManager.Stub
// This needs to be done before invoking dexopt so that any install-time profile
// can be used for optimizations.
mArtManagerService.prepareAppProfiles(
- pkg, resolveUserIds(reconciledPkg.installForUser.getIdentifier()));
+ pkg,
+ resolveUserIds(reconciledPkg.installForUser.getIdentifier()),
+ /* updateReferenceProfileContent= */ true);
// Check whether we need to dexopt the app.
//
@@ -21184,8 +21189,18 @@ public class PackageManagerService extends IPackageManager.Stub
//
// We also have to cover non system users because we do not call the usual install package
// methods for them.
+ //
+ // NOTE: in order to speed up first boot time we only create the current profile and do not
+ // update the content of the reference profile. A system image should already be configured
+ // with the right profile keys and the profiles for the speed-profile prebuilds should
+ // already be copied. That's done in #performDexOptUpgrade.
+ //
+ // TODO(calin, mathieuc): We should use .dm files for prebuilds profiles instead of
+ // manually copying them in #performDexOptUpgrade. When we do that we should have a more
+ // granular check here and only update the existing profiles.
if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
- mArtManagerService.prepareAppProfiles(pkg, userId);
+ mArtManagerService.prepareAppProfiles(pkg, userId,
+ /* updateReferenceProfileContent= */ false);
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 21daa39e05e0..910ea738acd8 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -389,7 +389,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
* - create the current primary profile to save time at app startup time.
* - copy the profiles from the associated dex metadata file to the reference profile.
*/
- public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user) {
+ public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user,
+ boolean updateReferenceProfileContent) {
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
if (user < 0) {
Slog.wtf(TAG, "Invalid user id: " + user);
@@ -404,8 +405,14 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) {
String codePath = codePathsProfileNames.keyAt(i);
String profileName = codePathsProfileNames.valueAt(i);
- File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
- String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
+ String dexMetadataPath = null;
+ // Passing the dex metadata file to the prepare method will update the reference
+ // profile content. As such, we look for the dex metadata file only if we need to
+ // perform an update.
+ if (updateReferenceProfileContent) {
+ File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
+ dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
+ }
synchronized (mInstaller) {
boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,
profileName, codePath, dexMetadataPath);
@@ -423,9 +430,10 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
/**
* Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}.
*/
- public void prepareAppProfiles(PackageParser.Package pkg, int[] user) {
+ public void prepareAppProfiles(PackageParser.Package pkg, int[] user,
+ boolean updateReferenceProfileContent) {
for (int i = 0; i < user.length; i++) {
- prepareAppProfiles(pkg, user[i]);
+ prepareAppProfiles(pkg, user[i], updateReferenceProfileContent);
}
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 3c9dd636e12b..6f644dd70339 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -34,7 +34,6 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackagesProvider;
import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
-import android.content.pm.PackageParser;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.media.RingtoneManager;
@@ -48,6 +47,7 @@ import android.os.Message;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.permission.PermissionManager;
import android.print.PrintManager;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
@@ -1024,15 +1024,17 @@ public final class DefaultPermissionGrantPolicy {
ApplicationInfo applicationInfo = pkg.applicationInfo;
// Automatically attempt to grant split permissions to older APKs
- final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length;
+ final List<PermissionManager.SplitPermissionInfo> splitPermissions =
+ mContext.getSystemService(PermissionManager.class).getSplitPermissions();
+ final int numSplitPerms = splitPermissions.size();
for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
- final PackageParser.SplitPermissionInfo splitPerm =
- PackageParser.SPLIT_PERMISSIONS[splitPermNum];
+ final PermissionManager.SplitPermissionInfo splitPerm =
+ splitPermissions.get(splitPermNum);
if (applicationInfo != null
- && applicationInfo.targetSdkVersion < splitPerm.targetSdk
- && permissionsWithoutSplits.contains(splitPerm.rootPerm)) {
- Collections.addAll(permissions, splitPerm.newPerms);
+ && applicationInfo.targetSdkVersion < splitPerm.getTargetSdk()
+ && permissionsWithoutSplits.contains(splitPerm.getRootPermission())) {
+ Collections.addAll(permissions, splitPerm.getNewPermissions());
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 91fd8d0e960d..deeae26d034c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -533,8 +533,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
GlobalActions mGlobalActions;
Handler mHandler;
- WindowState mLastInputMethodWindow = null;
- WindowState mLastInputMethodTargetWindow = null;
// FIXME This state is shared between the input reader and handler thread.
// Technically it's broken and buggy but it has been like this for many years
@@ -4732,12 +4730,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
final WindowManager.LayoutParams attrs = win.getAttrs();
final boolean isDefaultDisplay = win.isDefaultDisplay();
- final boolean needsToOffsetInputMethodTarget =
- (win == mLastInputMethodTargetWindow) && (mLastInputMethodWindow != null);
- if (needsToOffsetInputMethodTarget) {
- if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
- offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames);
- }
final int type = attrs.type;
final int fl = PolicyControl.getWindowFlags(win, attrs);
@@ -5191,7 +5183,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// can't appear underneath them.
if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
&& !win.getGivenInsetsPendingLw()) {
- setLastInputMethodWindowLw(null, null);
offsetInputMethodWindowLw(win, displayFrames);
}
if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
@@ -7822,12 +7813,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public void setLastInputMethodWindowLw(WindowState ime, WindowState target) {
- mLastInputMethodWindow = ime;
- mLastInputMethodTargetWindow = target;
- }
-
- @Override
public void setDismissImeOnBackKeyPressed(boolean newValue) {
mDismissImeOnBackKeyPressed = newValue;
}
@@ -7845,7 +7830,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (statusBar != null) {
statusBar.setCurrentUser(newUserId);
}
- setLastInputMethodWindowLw(null, null);
}
@Override
@@ -8020,14 +8004,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
- if (mLastInputMethodWindow != null) {
- pw.print(prefix); pw.print("mLastInputMethodWindow=");
- pw.println(mLastInputMethodWindow);
- }
- if (mLastInputMethodTargetWindow != null) {
- pw.print(prefix); pw.print("mLastInputMethodTargetWindow=");
- pw.println(mLastInputMethodTargetWindow);
- }
if (mStatusBar != null) {
pw.print(prefix); pw.print("mStatusBar=");
pw.print(mStatusBar); pw.print(" isStatusBarKeyguard=");
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 1fcdd63c4488..27ab3efb3fb1 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1536,13 +1536,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
public void lockNow(Bundle options);
/**
- * Set the last used input method window state. This state is used to make IME transition
- * smooth.
- * @hide
- */
- public void setLastInputMethodWindowLw(WindowState ime, WindowState target);
-
- /**
* An internal callback (from InputMethodManagerService) to notify a state change regarding
* whether the back key should dismiss the software keyboard (IME) or not.
*
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index bfa03ca9f2be..c6e6449313c0 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -966,6 +966,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
e.writeLong(processMemoryState.rssInBytes);
e.writeLong(processMemoryState.cacheInBytes);
e.writeLong(processMemoryState.swapInBytes);
+ e.writeLong(processMemoryState.rssHighWatermarkInBytes);
pulledData.add(e);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index eaaf804d2cf9..236982fd76ac 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2652,6 +2652,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*/
void setInputMethodWindowLocked(WindowState win) {
mInputMethodWindow = win;
+ // Update display configuration for IME process.
+ if (mInputMethodWindow != null) {
+ final int imePid = mInputMethodWindow.mSession.mPid;
+ mService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
+ mInputMethodWindow.getDisplayId());
+ }
computeImeTarget(true /* updateImeTarget */);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 27b623bc0ec3..5410676a49fc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -340,20 +340,6 @@ public abstract class WindowManagerInternal {
public abstract int getInputMethodWindowVisibleHeight(int displayId);
/**
- * Saves last input method window for transition.
- *
- * Note that it is assumed that this method is called only by InputMethodManagerService.
- */
- public abstract void saveLastInputMethodWindowForTransition();
-
- /**
- * Clears last input method window for transition.
- *
- * Note that it is assumed that this method is called only by InputMethodManagerService.
- */
- public abstract void clearLastInputMethodWindowForTransition();
-
- /**
* Notifies WindowManagerService that the current IME window status is being changed.
*
* <p>Only {@link com.android.server.inputmethod.InputMethodManagerService} is the expected and
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 017f667756bc..7caa7aedb873 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1414,7 +1414,6 @@ public class WindowManagerService extends IWindowManager.Stub
win.mToken.addWindow(win);
if (type == TYPE_INPUT_METHOD) {
- win.mGivenInsetsPending = true;
displayContent.setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
@@ -5670,11 +5669,6 @@ public class WindowManagerService extends IWindowManager.Stub
mInputManagerCallback.freezeInputDispatchingLw();
- // Clear the last input window -- that is just used for
- // clean transitions between IMEs, and if we are freezing
- // the screen then the whole world is changing behind the scenes.
- mPolicy.setLastInputMethodWindowLw(null, null);
-
if (mAppTransition.isTransitionSet()) {
mAppTransition.freeze();
}
@@ -7287,23 +7281,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void saveLastInputMethodWindowForTransition() {
- synchronized (mWindowMap) {
- final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
- if (imeWindow != null) {
- mPolicy.setLastInputMethodWindowLw(imeWindow, mInputMethodTarget);
- }
- }
- }
-
- @Override
- public void clearLastInputMethodWindowForTransition() {
- synchronized (mWindowMap) {
- mPolicy.setLastInputMethodWindowLw(null, null);
- }
- }
-
- @Override
public void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) {
mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7161a70b08fe..f7c6d77b4163 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4535,7 +4535,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
updateSurfacePosition(getPendingTransaction());
}
- private void updateSurfacePosition(Transaction t) {
+ @VisibleForTesting
+ void updateSurfacePosition(Transaction t) {
if (mSurfaceControl == null) {
return;
}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index 57ebbfcf8dd6..de915ab0829c 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -122,6 +122,13 @@ public class BackupManagerServiceTest {
ShadowAppBackupUtils.reset();
}
+ @Test
+ public void testMoreDebug_isFalse() throws Exception {
+ boolean moreDebug = BackupManagerService.MORE_DEBUG;
+
+ assertThat(moreDebug).isFalse();
+ }
+
/* Tests for destination string */
@Test
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
new file mode 100644
index 000000000000..373033500cde
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.keyvalue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class AgentExceptionTest {
+ @Test
+ public void testTransitory_isTransitory() throws Exception {
+ AgentException exception = AgentException.transitory();
+
+ assertThat(exception.isTransitory()).isTrue();
+ }
+
+ @Test
+ public void testTransitory_withCause() throws Exception {
+ Exception cause = new IOException();
+
+ AgentException exception = AgentException.transitory(cause);
+
+ assertThat(exception.isTransitory()).isTrue();
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+
+ @Test
+ public void testPermanent_isNotTransitory() throws Exception {
+ AgentException exception = AgentException.permanent();
+
+ assertThat(exception.isTransitory()).isFalse();
+ }
+
+ @Test
+ public void testPermanent_withCause() throws Exception {
+ Exception cause = new IOException();
+
+ AgentException exception = AgentException.permanent(cause);
+
+ assertThat(exception.isTransitory()).isFalse();
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
new file mode 100644
index 000000000000..5ea74f163bd6
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.keyvalue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class BackupExceptionTest {
+ @Test
+ public void testConstructor_passesCause() {
+ Exception cause = new IOException();
+
+ Exception exception = new BackupException(cause);
+
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
index 21b90f1c2281..31e8333fea89 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
@@ -62,6 +62,13 @@ public class KeyValueBackupReporterTest {
}
@Test
+ public void testMoreDebug_isFalse() throws Exception {
+ boolean moreDebug = KeyValueBackupReporter.MORE_DEBUG;
+
+ assertThat(moreDebug).isFalse();
+ }
+
+ @Test
public void testOnNewThread_logsCorrectly() throws Exception {
KeyValueBackupReporter.onNewThread("foo");
@@ -81,39 +88,4 @@ public class KeyValueBackupReporterTest {
assertThat(observer).isEqualTo(mObserver);
}
-
- @Test
- public void testOnRevertTask_logsCorrectly() throws Exception {
- setMoreDebug(true);
-
- mReporter.onRevertTask();
-
- assertLogcat(TAG, Log.INFO);
- }
-
- @Test
- public void testOnRemoteCallReturned_logsCorrectly() throws Exception {
- setMoreDebug(true);
-
- mReporter.onRemoteCallReturned(RemoteResult.of(3), "onFoo()");
-
- assertLogcat(TAG, Log.VERBOSE);
- ShadowLog.LogItem log = ShadowLog.getLogsForTag(TAG).get(0);
- assertThat(log.msg).contains("onFoo()");
- assertThat(log.msg).contains("3");
- }
-
- /**
- * HACK: We actually want {@link KeyValueBackupReporter#MORE_DEBUG} to be a constant to be able
- * to strip those lines at build time. So, we have to do this to test :(
- */
- private static void setMoreDebug(boolean value)
- throws NoSuchFieldException, IllegalAccessException {
- if (KeyValueBackupReporter.MORE_DEBUG == value) {
- return;
- }
- Field moreDebugField = KeyValueBackupReporter.class.getDeclaredField("MORE_DEBUG");
- moreDebugField.setAccessible(true);
- moreDebugField.set(null, value);
- }
}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index b4bc9d199cb0..fb57d68082a2 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -155,9 +155,7 @@ import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
-// TODO: When returning to RUNNING_QUEUE vs FINAL, RUNNING_QUEUE sets status = OK. Why? Verify?
-// TODO: Check queue in general, behavior w/ multiple packages
-// TODO: Test PM invocation
+// TODO: Test agents timing out
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
manifest = Config.NONE,
@@ -370,6 +368,47 @@ public class KeyValueBackupTaskTest {
}
@Test
+ public void testRunTask_whenOnePackage_cleansUpPmFiles() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PM_PACKAGE);
+ }
+
+ @Test
+ public void testRunTask_whenTransportReturnsTransportErrorForPm_cleansUpPmFiles()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ when(transportMock.transport.performBackup(
+ argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
+ .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+ setUpAgent(PACKAGE_1);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PM_PACKAGE);
+ }
+
+ @Test
+ public void testRunTask_whenTransportReturnsTransportErrorForPm_resetsBackupState()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ when(transportMock.transport.performBackup(
+ argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
+ .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+ setUpAgent(PACKAGE_1);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile());
+ }
+
+ @Test
public void testRunTask_whenOnePackage_updatesBookkeeping() throws Exception {
// Transport has to be initialized to not reset current token
TransportMock transportMock = setUpInitializedTransport(mTransport);
@@ -418,7 +457,7 @@ public class KeyValueBackupTaskTest {
public void testRunTask_whenNonPmPackageAndNonIncremental_doesNotBackUpPm() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
- PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+ BackupAgent pmAgent = spy(createPmAgent());
when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1);
@@ -431,7 +470,7 @@ public class KeyValueBackupTaskTest {
public void testRunTask_whenNonPmPackageAndPmAndNonIncremental_backsUpPm() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
- PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+ BackupAgent pmAgent = spy(createPmAgent());
when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
KeyValueBackupTask task =
createKeyValueBackupTask(transportMock, true, PACKAGE_1, PM_PACKAGE);
@@ -445,7 +484,7 @@ public class KeyValueBackupTaskTest {
public void testRunTask_whenNonPmPackageAndIncremental_backsUpPm() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
- PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+ BackupAgent pmAgent = spy(createPmAgent());
when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, false, PACKAGE_1);
@@ -529,6 +568,35 @@ public class KeyValueBackupTaskTest {
}
@Test
+ public void testRunTask_whenPackageUnknown() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ // Not calling setUpAgent() for PACKAGE_1
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ verify(transportMock.transport, never())
+ .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+ verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_PACKAGE_NOT_FOUND);
+ verify(mObserver).backupFinished(SUCCESS);
+ assertBackupNotPendingFor(PACKAGE_1);
+ }
+
+ @Test
+ public void testRunTask_whenFirstPackageUnknown_callsTransportForSecondPackage()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ // Not calling setUpAgent() for PACKAGE_1
+ setUpAgentWithData(PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
+
+ runTask(task);
+
+ verify(transportMock.transport)
+ .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
+ }
+
+ @Test
public void testRunTask_whenPackageNotEligibleForBackup() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
AgentMock agentMock = setUpAgentWithData(PACKAGE_1.backupNotAllowed());
@@ -545,6 +613,19 @@ public class KeyValueBackupTaskTest {
}
@Test
+ public void testRunTask_whenFirstPackageNotEligibleForBackup_callsTransportForSecondPackage()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgentsWithData(PACKAGE_1.backupNotAllowed(), PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
+
+ runTask(task);
+
+ verify(transportMock.transport)
+ .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
+ }
+
+ @Test
public void testRunTask_whenPackageDoesFullBackup() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
PackageData packageData = fullBackupPackage(1);
@@ -561,6 +642,20 @@ public class KeyValueBackupTaskTest {
}
@Test
+ public void testRunTask_whenFirstPackageDoesFullBackup_callsTransportForSecondPackage()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ PackageData packageData = fullBackupPackage(1);
+ setUpAgentsWithData(packageData, PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, packageData, PACKAGE_2);
+
+ runTask(task);
+
+ verify(transportMock.transport)
+ .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
+ }
+
+ @Test
public void testRunTask_whenPackageIsStopped() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
AgentMock agentMock = setUpAgentWithData(PACKAGE_1.stopped());
@@ -575,18 +670,16 @@ public class KeyValueBackupTaskTest {
}
@Test
- public void testRunTask_whenPackageUnknown() throws Exception {
+ public void testRunTask_whenFirstPackageIsStopped_callsTransportForSecondPackage()
+ throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
- // Not calling setUpAgent()
- KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+ setUpAgentsWithData(PACKAGE_1.stopped(), PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
runTask(task);
- verify(transportMock.transport, never())
- .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
- verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_PACKAGE_NOT_FOUND);
- verify(mObserver).backupFinished(SUCCESS);
- assertBackupNotPendingFor(PACKAGE_1);
+ verify(transportMock.transport)
+ .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
}
@Test
@@ -629,6 +722,7 @@ public class KeyValueBackupTaskTest {
verify(mBackupManagerService).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
verify(mObserver).backupFinished(BackupManager.SUCCESS);
+ assertBackupPendingFor(PACKAGE_1);
}
@Test
@@ -645,6 +739,7 @@ public class KeyValueBackupTaskTest {
verify(mBackupManagerService).setWorkSource(null);
verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
verify(mObserver).backupFinished(BackupManager.SUCCESS);
+ assertBackupPendingFor(PACKAGE_1);
}
@Test
@@ -798,7 +893,7 @@ public class KeyValueBackupTaskTest {
runTask(task);
- assertBackupNotPendingFor(PACKAGE_1);
+ assertBackupPendingFor(PACKAGE_1);
}
@Test
@@ -1140,6 +1235,38 @@ public class KeyValueBackupTaskTest {
}
@Test
+ public void testRunTask_whenPmAgentWritesData_callsTransportPerformBackupWithAgentData()
+ throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ Path backupDataPath = createTemporaryFile();
+ when(transportMock.transport.performBackup(
+ argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
+ .then(copyBackupDataTo(backupDataPath));
+ BackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ agentOnBackupDo(
+ pmAgent,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key1", "data1".getBytes());
+ writeData(dataOutput, "key2", "data2".getBytes());
+ writeState(newState, "newState".getBytes());
+ });
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ verify(transportMock.transport)
+ .performBackup(argThat(packageInfo(PM_PACKAGE)), any(), anyInt());
+ try (FileInputStream inputStream = new FileInputStream(backupDataPath.toFile())) {
+ BackupDataInput backupData = new BackupDataInput(inputStream.getFD());
+ assertDataHasKeyValue(backupData, "key1", "data1".getBytes());
+ assertDataHasKeyValue(backupData, "key2", "data2".getBytes());
+ assertThat(backupData.readNextHeader()).isFalse();
+ }
+ }
+
+ @Test
public void testRunTask_whenPerformBackupSucceeds_callsTransportFinishBackup()
throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
@@ -1176,6 +1303,50 @@ public class KeyValueBackupTaskTest {
}
@Test
+ public void testRunTask_whenFinishBackupSucceedsForPm_cleansUp() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
+ BackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ agentOnBackupDo(
+ pmAgent,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key", "data".getBytes());
+ writeState(newState, "newState".getBytes());
+ });
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ assertThat(Files.readAllBytes(getStateFile(mTransport, PM_PACKAGE)))
+ .isEqualTo("newState".getBytes());
+ assertCleansUpFiles(mTransport, PM_PACKAGE);
+ // We don't unbind PM
+ verify(mBackupManagerService, never()).unbindAgent(argThat(applicationInfo(PM_PACKAGE)));
+ }
+
+ @Test
+ public void testRunTask_whenFinishBackupSucceedsForPm_doesNotUnbindPm() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
+ BackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ agentOnBackupDo(
+ pmAgent,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key", "data".getBytes());
+ writeState(newState, "newState".getBytes());
+ });
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ verify(mBackupManagerService, never()).unbindAgent(argThat(applicationInfo(PM_PACKAGE)));
+ }
+
+ @Test
public void testRunTask_whenFinishBackupSucceeds_logsBackupPackageEvent() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
@@ -1354,6 +1525,7 @@ public class KeyValueBackupTaskTest {
public void testRunTask_whenTransportReturnsQuotaExceeded_updatesBookkeeping()
throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
when(transportMock.transport.performBackup(
argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
.thenReturn(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
@@ -1701,9 +1873,9 @@ public class KeyValueBackupTaskTest {
}
@Test
- public void testRunTask_whenPmAgentFails() throws Exception {
+ public void testRunTask_whenPmAgentFails_reportsCorrectly() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
- PackageManagerBackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+ BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
@@ -1718,6 +1890,75 @@ public class KeyValueBackupTaskTest {
}
@Test
+ public void testRunTask_whenPmAgentFails_revertsTask() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ assertTaskReverted(transportMock, PACKAGE_1);
+ }
+
+ @Test
+ public void testRunTask_whenPmAgentFails_cleansUpFiles() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PM_PACKAGE);
+ }
+
+ @Test
+ public void testRunTask_whenPmAgentFails_resetsBackupState() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ runTask(task);
+
+ verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile());
+ }
+
+ @Test
+ public void testRunTask_whenMarkCancelDuringPmOnBackup_resetsBackupState() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ BackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+ agentOnBackupDo(
+ pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel));
+
+ runTask(task);
+
+ verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile());
+ }
+
+ @Test
+ public void testRunTask_whenMarkCancelDuringPmOnBackup_cleansUpFiles() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgent(PACKAGE_1);
+ BackupAgent pmAgent = spy(createPmAgent());
+ when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+ agentOnBackupDo(
+ pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel));
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PM_PACKAGE);
+ }
+
+ @Test
public void testRunTask_whenBackupRunning_doesNotThrow() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
when(mBackupManagerService.isBackupOperationInProgress()).thenReturn(true);
@@ -1736,7 +1977,7 @@ public class KeyValueBackupTaskTest {
runTask(task);
- verify(mReporter).onReadAgentDataError(eq(PACKAGE_1.packageName), any());
+ verify(mReporter).onAgentDataError(eq(PACKAGE_1.packageName), any());
}
@Test
@@ -1779,6 +2020,24 @@ public class KeyValueBackupTaskTest {
}
@Test
+ public void testRunTask_whenMarkCancelDuringAgentOnBackup_cleansUpFiles() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ AgentMock agentMock = setUpAgent(PACKAGE_1);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+ agentOnBackupDo(
+ agentMock,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key", "data".getBytes());
+ writeState(newState, "newState".getBytes());
+ runInWorkerThread(task::markCancel);
+ });
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PACKAGE_1);
+ }
+
+ @Test
public void
testRunTask_whenMarkCancelDuringFirstAgentOnBackup_doesNotCallTransportAfterWaitCancel()
throws Exception {
@@ -2293,20 +2552,28 @@ public class KeyValueBackupTaskTest {
*/
private static void agentOnBackupDo(AgentMock agentMock, BackupAgentOnBackup function)
throws Exception {
- doAnswer(
- (BackupAgentOnBackup)
- (oldState, dataOutput, newState) -> {
- ByteArrayOutputStream outputStream =
- new ByteArrayOutputStream();
- transferStreamedData(
- new FileInputStream(oldState.getFileDescriptor()),
- outputStream);
- agentMock.oldState = outputStream.toByteArray();
- agentMock.oldStateHistory.add(agentMock.oldState);
- function.onBackup(oldState, dataOutput, newState);
- })
- .when(agentMock.agent)
- .onBackup(any(), any(), any());
+ agentOnBackupDo(
+ agentMock.agent,
+ (oldState, dataOutput, newState) -> {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ transferStreamedData(
+ new FileInputStream(oldState.getFileDescriptor()), outputStream);
+ agentMock.oldState = outputStream.toByteArray();
+ agentMock.oldStateHistory.add(agentMock.oldState);
+ function.onBackup(oldState, dataOutput, newState);
+ });
+ }
+
+ /**
+ * Implements {@code function} for {@link BackupAgent#onBackup(ParcelFileDescriptor,
+ * BackupDataOutput, ParcelFileDescriptor)} of {@code agentMock}.
+ *
+ * @see #agentOnBackupDo(AgentMock, BackupAgentOnBackup)
+ * @see #remoteAgentOnBackupThrows(AgentMock, BackupAgentOnBackup)
+ */
+ private static void agentOnBackupDo(BackupAgent backupAgent, BackupAgentOnBackup function)
+ throws IOException {
+ doAnswer(function).when(backupAgent).onBackup(any(), any(), any());
}
/**
@@ -2400,6 +2667,10 @@ public class KeyValueBackupTaskTest {
// constructor
assertJournalDoesNotContain(mBackupManagerService.getJournal(), packageName);
assertThat(mBackupManagerService.getPendingBackups()).doesNotContainKey(packageName);
+ // Also verifying BMS is never called since for some cases the package wouldn't be
+ // pending for other reasons (for example it's not eligible for backup). Regardless of
+ // these reasons, we shouldn't mark them as pending backup (call dataChangedImpl()).
+ verify(mBackupManagerService, never()).dataChangedImpl(packageName);
}
}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
new file mode 100644
index 000000000000..4b79657b1dae
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.keyvalue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.app.backup.BackupTransport;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class TaskExceptionTest {
+ @Test
+ public void testStateCompromised() {
+ TaskException exception = TaskException.stateCompromised();
+
+ assertThat(exception.isStateCompromised()).isTrue();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testStateCompromised_whenCauseInstanceOfTaskException() {
+ Exception cause = TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+ TaskException exception = TaskException.stateCompromised(cause);
+
+ assertThat(exception.isStateCompromised()).isTrue();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+
+ @Test
+ public void testStateCompromised_whenCauseNotInstanceOfTaskException() {
+ Exception cause = new IOException();
+
+ TaskException exception = TaskException.stateCompromised(cause);
+
+ assertThat(exception.isStateCompromised()).isTrue();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+
+ @Test
+ public void testForStatus_whenTransportOk_throws() {
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> TaskException.forStatus(BackupTransport.TRANSPORT_OK));
+ }
+
+ @Test
+ public void testForStatus_whenTransportNotInitialized() {
+ TaskException exception =
+ TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+ assertThat(exception.isStateCompromised()).isFalse();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+ }
+
+ @Test
+ public void testCausedBy_whenCauseInstanceOfTaskException_returnsCause() {
+ Exception cause = TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+ TaskException exception = TaskException.causedBy(cause);
+
+ assertThat(exception).isEqualTo(cause);
+ }
+
+ @Test
+ public void testCausedBy_whenCauseNotInstanceOfTaskException() {
+ Exception cause = new IOException();
+
+ TaskException exception = TaskException.causedBy(cause);
+
+ assertThat(exception).isNotEqualTo(cause);
+ assertThat(exception.isStateCompromised()).isFalse();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ assertThat(exception.getCause()).isEqualTo(cause);
+ }
+
+ @Test
+ public void testCreate() {
+ TaskException exception = TaskException.create();
+
+ assertThat(exception.isStateCompromised()).isFalse();
+ assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testIsStateCompromised_whenStateCompromised_returnsTrue() {
+ TaskException taskException = TaskException.stateCompromised();
+
+ boolean stateCompromised = taskException.isStateCompromised();
+
+ assertThat(stateCompromised).isTrue();
+ }
+
+ @Test
+ public void testIsStateCompromised_whenCreatedWithCreate_returnsFalse() {
+ TaskException taskException = TaskException.create();
+
+ boolean stateCompromised = taskException.isStateCompromised();
+
+ assertThat(stateCompromised).isFalse();
+ }
+
+ @Test
+ public void testGetStatus_whenStatusIsTransportPackageRejected() {
+ TaskException taskException =
+ TaskException.forStatus(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+
+ int status = taskException.getStatus();
+
+ assertThat(status).isEqualTo(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+ }
+
+ @Test
+ public void testGetStatus_whenStatusIsTransportNotInitialized() {
+ TaskException taskException =
+ TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+ int status = taskException.getStatus();
+
+ assertThat(status).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 06c74371eb51..e8a824a12300 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -16,9 +16,12 @@
package com.android.server.am;
+import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE;
import static com.android.server.am.MemoryStatUtil.MemoryStat;
+import static com.android.server.am.MemoryStatUtil.parseMemoryMaxUsageFromMemCg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
+import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -32,7 +35,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MemoryStatUtilTest {
- private String MEMORY_STAT_CONTENTS = String.join(
+ private static final String MEMORY_STAT_CONTENTS = String.join(
"\n",
"cache 96", // keep different from total_cache to catch reading wrong value
"rss 97", // keep different from total_rss to catch reading wrong value
@@ -67,7 +70,7 @@ public class MemoryStatUtilTest {
"total_active_file 81920",
"total_unevictable 0");
- private String PROC_STAT_CONTENTS = String.join(
+ private static final String PROC_STAT_CONTENTS = String.join(
" ",
"1040",
"(system_server)",
@@ -122,14 +125,61 @@ public class MemoryStatUtilTest {
"3198889956",
"0");
+ private static final String PROC_STATUS_CONTENTS = "Name:\tandroid.youtube\n"
+ + "State:\tS (sleeping)\n"
+ + "Tgid:\t12088\n"
+ + "Pid:\t12088\n"
+ + "PPid:\t723\n"
+ + "TracerPid:\t0\n"
+ + "Uid:\t10083\t10083\t10083\t10083\n"
+ + "Gid:\t10083\t10083\t10083\t10083\n"
+ + "Ngid:\t0\n"
+ + "FDSize:\t128\n"
+ + "Groups:\t3003 9997 20083 50083 \n"
+ + "VmPeak:\t 4546844 kB\n"
+ + "VmSize:\t 4542636 kB\n"
+ + "VmLck:\t 0 kB\n"
+ + "VmPin:\t 0 kB\n"
+ + "VmHWM:\t 137668 kB\n" // RSS high watermark
+ + "VmRSS:\t 126776 kB\n"
+ + "RssAnon:\t 37860 kB\n"
+ + "RssFile:\t 88764 kB\n"
+ + "RssShmem:\t 152 kB\n"
+ + "VmData:\t 4125112 kB\n"
+ + "VmStk:\t 8192 kB\n"
+ + "VmExe:\t 24 kB\n"
+ + "VmLib:\t 102432 kB\n"
+ + "VmPTE:\t 1300 kB\n"
+ + "VmPMD:\t 36 kB\n"
+ + "VmSwap:\t 0 kB\n"
+ + "Threads:\t95\n"
+ + "SigQ:\t0/13641\n"
+ + "SigPnd:\t0000000000000000\n"
+ + "ShdPnd:\t0000000000000000\n"
+ + "SigBlk:\t0000000000001204\n"
+ + "SigIgn:\t0000000000000001\n"
+ + "SigCgt:\t00000006400084f8\n"
+ + "CapInh:\t0000000000000000\n"
+ + "CapPrm:\t0000000000000000\n"
+ + "CapEff:\t0000000000000000\n"
+ + "CapBnd:\t0000000000000000\n"
+ + "CapAmb:\t0000000000000000\n"
+ + "Seccomp:\t2\n"
+ + "Cpus_allowed:\tff\n"
+ + "Cpus_allowed_list:\t0-7\n"
+ + "Mems_allowed:\t1\n"
+ + "Mems_allowed_list:\t0\n"
+ + "voluntary_ctxt_switches:\t903\n"
+ + "nonvoluntary_ctxt_switches:\t104\n";
+
@Test
public void testParseMemoryStatFromMemcg_parsesCorrectValues() throws Exception {
MemoryStat stat = parseMemoryStatFromMemcg(MEMORY_STAT_CONTENTS);
- assertEquals(stat.pgfault, 1);
- assertEquals(stat.pgmajfault, 2);
- assertEquals(stat.rssInBytes, 3);
- assertEquals(stat.cacheInBytes, 4);
- assertEquals(stat.swapInBytes, 5);
+ assertEquals(1, stat.pgfault);
+ assertEquals(2, stat.pgmajfault);
+ assertEquals(3, stat.rssInBytes);
+ assertEquals(4, stat.cacheInBytes);
+ assertEquals(5, stat.swapInBytes);
}
@Test
@@ -142,6 +192,18 @@ public class MemoryStatUtilTest {
}
@Test
+ public void testParseMemoryMaxUsageFromMemCg_parsesCorrectValue() {
+ assertEquals(1234, parseMemoryMaxUsageFromMemCg("1234"));
+ }
+
+ @Test
+ public void testParseMemoryMaxUsageFromMemCg_emptyContents() {
+ assertEquals(0, parseMemoryMaxUsageFromMemCg(""));
+
+ assertEquals(0, parseMemoryMaxUsageFromMemCg(null));
+ }
+
+ @Test
public void testParseMemoryStatFromProcfs_parsesCorrectValues() throws Exception {
MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS);
assertEquals(1, stat.pgfault);
@@ -159,4 +221,16 @@ public class MemoryStatUtilTest {
stat = parseMemoryStatFromProcfs(null);
assertNull(stat);
}
+
+ @Test
+ public void testParseVmHWMFromProcfs_parsesCorrectValue() {
+ assertEquals(137668, parseVmHWMFromProcfs(PROC_STATUS_CONTENTS) / BYTES_IN_KILOBYTE);
+ }
+
+ @Test
+ public void testParseVmHWMFromProcfs_emptyContents() {
+ assertEquals(0, parseVmHWMFromProcfs(""));
+
+ assertEquals(0, parseVmHWMFromProcfs(null));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 5fb89977f905..474e5b75e5d8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -458,12 +458,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public void setLastInputMethodWindowLw(WindowState ime,
- WindowState target) {
-
- }
-
- @Override
public void showRecentApps() {
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 6af3ea763c13..b7cc9ce7a619 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -76,7 +76,8 @@ import androidx.test.runner.AndroidJUnit4;
*/
@SmallTest
@FlakyTest(bugId = 74078662)
-@Presubmit
+// TODO(b/116597907): Re-enable this test in postsubmit after the bug is fixed.
+// @Presubmit
@RunWith(AndroidJUnit4.class)
public class WindowStateTests extends WindowTestsBase {
@@ -369,22 +370,31 @@ public class WindowStateTests extends WindowTestsBase {
final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
app.mHasSurface = true;
- app.mToken.mSurfaceControl = mock(SurfaceControl.class);
+ app.mSurfaceControl = mock(SurfaceControl.class);
try {
app.getFrameLw().set(10, 20, 60, 80);
+ app.updateSurfacePosition(t);
app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true);
assertTrue(app.mSeamlesslyRotated);
+
+ // Verify we un-rotate the window state surface.
Matrix matrix = new Matrix();
// Un-rotate 90 deg
matrix.setRotate(270);
// Translate it back to origin
matrix.postTranslate(0, mDisplayInfo.logicalWidth);
- verify(t).setMatrix(eq(app.mToken.mSurfaceControl), eq(matrix), any(float[].class));
+ verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class));
+
+ // Verify we update the position as well.
+ float[] currentSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y};
+ matrix.mapPoints(currentSurfacePos);
+ verify(t).setPosition(eq(app.mSurfaceControl), eq(currentSurfacePos[0]),
+ eq(currentSurfacePos[1]));
} finally {
+ app.mSurfaceControl = null;
app.mHasSurface = false;
- app.mToken.mSurfaceControl = null;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
index bbc6550d5753..01b7c4f650e7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
@@ -59,7 +59,8 @@ import java.nio.charset.StandardCharsets;
*/
@SmallTest
@FlakyTest(bugId = 74078662)
-@Presubmit
+// TODO(b/116597907): Re-enable this test in postsubmit after the bug is fixed.
+// @Presubmit
@RunWith(AndroidJUnit4.class)
public class WindowTracingTest extends WindowTestsBase {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index cbf6c6ecd331..45d2fa2d16a4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3383,10 +3383,44 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testIsCallerInstantApp_primaryUser() throws Exception {
+ ApplicationInfo info = new ApplicationInfo();
+ info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+
+ assertTrue(mService.isCallerInstantApp("any", 45770, 0));
+
+ info.privateFlags = 0;
+ assertFalse(mService.isCallerInstantApp("any", 575370, 0));
+ }
+
+ @Test
+ public void testIsCallerInstantApp_secondaryUser() throws Exception {
+ ApplicationInfo info = new ApplicationInfo();
+ info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+
+ assertTrue(mService.isCallerInstantApp("any", 68638450, 10));
+ }
+
+ @Test
+ public void testResolveNotificationUid_sameApp_nonSystemUser() throws Exception {
+ ApplicationInfo info = new ApplicationInfo();
+ info.uid = Binder.getCallingUid();
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+
+ int actualUid = mService.resolveNotificationUid("caller", "caller", info.uid, 10);
+
+ assertEquals(info.uid, actualUid);
+ }
+
+ @Test
public void testResolveNotificationUid_sameApp() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.uid = Binder.getCallingUid();
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info);
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
int actualUid = mService.resolveNotificationUid("caller", "caller", info.uid, 0);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 26bd4a106ca6..08bc9bcc4003 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -141,6 +141,8 @@ public final class Call {
* The caller must specify the {@link #EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE} to indicate to
* Telecom which {@link PhoneAccountHandle} the {@link Call} should be handed over to.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_REQUEST_HANDOVER =
"android.telecom.event.REQUEST_HANDOVER";
@@ -149,6 +151,8 @@ public final class Call {
* Extra key used with the {@link #EVENT_REQUEST_HANDOVER} call event. Specifies the
* {@link PhoneAccountHandle} to which a call should be handed over to.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE =
"android.telecom.extra.HANDOVER_PHONE_ACCOUNT_HANDLE";
@@ -161,6 +165,8 @@ public final class Call {
* {@link VideoProfile#STATE_BIDIRECTIONAL}, {@link VideoProfile#STATE_RX_ENABLED}, and
* {@link VideoProfile#STATE_TX_ENABLED}.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EXTRA_HANDOVER_VIDEO_STATE =
"android.telecom.extra.HANDOVER_VIDEO_STATE";
@@ -176,6 +182,8 @@ public final class Call {
* {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}
* is called to initate the handover.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EXTRA_HANDOVER_EXTRAS = "android.telecom.extra.HANDOVER_EXTRAS";
@@ -186,6 +194,8 @@ public final class Call {
* <p>
* A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_HANDOVER_COMPLETE =
"android.telecom.event.HANDOVER_COMPLETE";
@@ -198,6 +208,8 @@ public final class Call {
* <p>
* A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_HANDOVER_SOURCE_DISCONNECTED =
"android.telecom.event.HANDOVER_SOURCE_DISCONNECTED";
@@ -209,6 +221,8 @@ public final class Call {
* <p>
* A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_HANDOVER_FAILED =
"android.telecom.event.HANDOVER_FAILED";
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index d49469233752..34603a3f056a 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -594,6 +594,8 @@ public abstract class Connection extends Conferenceable {
* {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has
* successfully completed.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_HANDOVER_COMPLETE =
"android.telecom.event.HANDOVER_COMPLETE";
@@ -603,6 +605,8 @@ public abstract class Connection extends Conferenceable {
* {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has failed
* to complete.
* @hide
+ * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+ * APIs instead.
*/
public static final String EVENT_HANDOVER_FAILED =
"android.telecom.event.HANDOVER_FAILED";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 85901768c79f..995418ee706c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2115,6 +2115,16 @@ public class CarrierConfigManager {
public static final String KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL =
"config_show_orig_dial_string_for_cdma";
+ /**
+ * Flag specifying whether to show notification(call blocking disabled) when Enhanced Call
+ * Blocking(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL) is enabled and making emergency call.
+ * When true, notification is shown always.
+ * When false, notification is shown only when any setting of "Enhanced Blocked number" is
+ * enabled.
+ */
+ public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL =
+ "show_call_blocking_disabled_notification_always_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -2453,6 +2463,7 @@ public class CarrierConfigManager {
});
sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "");
sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
new file mode 100644
index 000000000000..e3737976adf7
--- /dev/null
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.GroupCall;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.InternalGroupCallCallback;
+import android.telephony.mbms.InternalGroupCallSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsGroupCallSessionCallback;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.vendor.IMbmsGroupCallService;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This class provides functionality for accessing group call functionality over MBMS.
+ */
+public class MbmsGroupCallSession implements AutoCloseable {
+ private static final String LOG_TAG = "MbmsGroupCallSession";
+
+ /**
+ * Service action which must be handled by the middleware implementing the MBMS group call
+ * interface.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String MBMS_GROUP_CALL_SERVICE_ACTION =
+ "android.telephony.action.EmbmsGroupCall";
+
+ /**
+ * Metadata key that specifies the component name of the service to bind to for group calls.
+ * @hide
+ */
+ @TestApi
+ public static final String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA =
+ "mbms-group-call-service-override";
+
+ private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+ private AtomicReference<IMbmsGroupCallService> mService = new AtomicReference<>(null);
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ sIsInitialized.set(false);
+ mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Received death notification");
+ }
+ };
+
+ private InternalGroupCallSessionCallback mInternalCallback;
+ private Set<GroupCall> mKnownActiveGroupCalls = new ArraySet<>();
+
+ private final Context mContext;
+ private int mSubscriptionId;
+
+ /** @hide */
+ private MbmsGroupCallSession(Context context, Executor executor, int subscriptionId,
+ MbmsGroupCallSessionCallback callback) {
+ mContext = context;
+ mSubscriptionId = subscriptionId;
+ mInternalCallback = new InternalGroupCallSessionCallback(callback, executor);
+ }
+
+ /**
+ * Create a new {@link MbmsGroupCallSession} using the given subscription ID.
+ *
+ * You may only have one instance of {@link MbmsGroupCallSession} per UID. If you call this
+ * method while there is an active instance of {@link MbmsGroupCallSession} in your process
+ * (in other words, one that has not had {@link #close()} called on it), this method will
+ * throw an {@link IllegalStateException}. If you call this method in a different process
+ * running under the same UID, an error will be indicated via
+ * {@link MbmsGroupCallSessionCallback#onError(int, String)}.
+ *
+ * Note that initialization may fail asynchronously. If you wish to try again after you
+ * receive such an asynchronous error, you must call {@link #close()} on the instance of
+ * {@link MbmsGroupCallSession} that you received before calling this method again.
+ *
+ * @param context The {@link Context} to use.
+ * @param executor The executor on which you wish to execute callbacks.
+ * @param subscriptionId The subscription ID to use.
+ * @param callback A callback object on which you wish to receive results of asynchronous
+ * operations.
+ * @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred.
+ */
+ public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
+ @NonNull Executor executor, int subscriptionId,
+ final @NonNull MbmsGroupCallSessionCallback callback) {
+ if (!sIsInitialized.compareAndSet(false, true)) {
+ throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
+ }
+ MbmsGroupCallSession session = new MbmsGroupCallSession(context, executor,
+ subscriptionId, callback);
+
+ final int result = session.bindAndInitialize();
+ if (result != MbmsErrors.SUCCESS) {
+ sIsInitialized.set(false);
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ callback.onError(result, null);
+ }
+ });
+ return null;
+ }
+ return session;
+ }
+
+ /**
+ * Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
+ * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ */
+ public static MbmsGroupCallSession create(@NonNull Context context,
+ @NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
+ return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+ }
+
+ /**
+ * Terminates this instance. Also terminates
+ * any group calls spawned from this instance as if
+ * {@link GroupCall#close()} had been called on them. After this method returns,
+ * no further callbacks originating from the middleware will be enqueued on the provided
+ * instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been
+ * enqueued will still be delivered.
+ *
+ * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to
+ * obtain another instance of {@link MbmsGroupCallSession} immediately after this method
+ * returns.
+ *
+ * May throw an {@link IllegalStateException}
+ */
+ public void close() {
+ try {
+ IMbmsGroupCallService groupCallService = mService.get();
+ if (groupCallService == null) {
+ // Ignore and return, assume already disposed.
+ return;
+ }
+ groupCallService.dispose(mSubscriptionId);
+ for (GroupCall s : mKnownActiveGroupCalls) {
+ s.getCallback().stop();
+ }
+ mKnownActiveGroupCalls.clear();
+ } catch (RemoteException e) {
+ // Ignore for now
+ } finally {
+ mService.set(null);
+ sIsInitialized.set(false);
+ mInternalCallback.stop();
+ }
+ }
+
+ /**
+ * Starts the requested group call, reporting status to the indicated callback.
+ * Returns an object used to control that call.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * Asynchronous errors through the callback include any of the errors in
+ * {@link MbmsErrors.GeneralErrors}.
+ *
+ * @param executor The executor on which you wish to execute callbacks for this stream.
+ * @param tmgi The TMGI, an identifier for the group call you want to join.
+ * @param saiArray An array of SAIs for the group call that should be negotiated separately with
+ * the carrier.
+ * @param frequencyArray An array of frequencies for the group call that should be negotiated
+ * separately with the carrier.
+ * @param callback The callback that you want to receive information about the call on.
+ * @return An instance of {@link GroupCall} through which the call can be controlled.
+ * May be {@code null} if an error occurred.
+ */
+ public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray,
+ int[] frequencyArray, @NonNull GroupCallCallback callback) {
+ IMbmsGroupCallService groupCallService = mService.get();
+ if (groupCallService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ InternalGroupCallCallback serviceCallback = new InternalGroupCallCallback(
+ callback, executor);
+
+ GroupCall serviceForApp = new GroupCall(mSubscriptionId,
+ groupCallService, this, tmgi, serviceCallback);
+ mKnownActiveGroupCalls.add(serviceForApp);
+
+ try {
+ int returnCode = groupCallService.startGroupCall(
+ mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback);
+ if (returnCode == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an unknown error code");
+ }
+ if (returnCode != MbmsErrors.SUCCESS) {
+ mInternalCallback.onError(returnCode, null);
+ return null;
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService.set(null);
+ sIsInitialized.set(false);
+ mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return null;
+ }
+
+ return serviceForApp;
+ }
+
+ /** @hide */
+ public void onGroupCallStopped(GroupCall service) {
+ mKnownActiveGroupCalls.remove(service);
+ }
+
+ private int bindAndInitialize() {
+ return MbmsUtils.startBinding(mContext, MBMS_GROUP_CALL_SERVICE_ACTION,
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMbmsGroupCallService groupCallService =
+ IMbmsGroupCallService.Stub.asInterface(service);
+ int result;
+ try {
+ result = groupCallService.initialize(mInternalCallback,
+ mSubscriptionId);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Service died before initialization");
+ mInternalCallback.onError(
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "Runtime exception during initialization");
+ mInternalCallback.onError(
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ }
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return"
+ + " an unknown error code");
+ }
+ if (result != MbmsErrors.SUCCESS) {
+ mInternalCallback.onError(result,
+ "Error returned during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ try {
+ groupCallService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Middleware lost during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ mService.set(groupCallService);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ sIsInitialized.set(false);
+ mService.set(null);
+ }
+ });
+ }
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a8bcbe33966a..d58b932f2851 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2091,10 +2091,37 @@ public class TelephonyManager {
/** Max network type number. Update as new types are added. Don't add negative types. {@hide} */
public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_LTE_CA;
+
+ /** @hide */
+ @IntDef({
+ NETWORK_TYPE_UNKNOWN,
+ NETWORK_TYPE_GPRS,
+ NETWORK_TYPE_EDGE,
+ NETWORK_TYPE_UMTS,
+ NETWORK_TYPE_CDMA,
+ NETWORK_TYPE_EVDO_0,
+ NETWORK_TYPE_EVDO_A,
+ NETWORK_TYPE_1xRTT,
+ NETWORK_TYPE_HSDPA,
+ NETWORK_TYPE_HSUPA,
+ NETWORK_TYPE_HSPA,
+ NETWORK_TYPE_IDEN,
+ NETWORK_TYPE_EVDO_B,
+ NETWORK_TYPE_LTE,
+ NETWORK_TYPE_EHRPD,
+ NETWORK_TYPE_HSPAP,
+ NETWORK_TYPE_GSM,
+ NETWORK_TYPE_TD_SCDMA,
+ NETWORK_TYPE_IWLAN,
+ NETWORK_TYPE_LTE_CA,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkType{}
+
/**
* @return the NETWORK_TYPE_xxxx for current data connection.
*/
- public int getNetworkType() {
+ public @NetworkType int getNetworkType() {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -2139,24 +2166,24 @@ public class TelephonyManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @UnsupportedAppUsage
- public int getNetworkType(int subId) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName());
- } else {
- // This can happen when the ITelephony interface is not up yet.
- return NETWORK_TYPE_UNKNOWN;
- }
- } catch(RemoteException ex) {
- // This shouldn't happen in the normal case
- return NETWORK_TYPE_UNKNOWN;
- } catch (NullPointerException ex) {
- // This could happen before phone restarts due to crashing
- return NETWORK_TYPE_UNKNOWN;
- }
- }
+ @UnsupportedAppUsage
+ public int getNetworkType(int subId) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName());
+ } else {
+ // This can happen when the ITelephony interface is not up yet.
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ } catch (RemoteException ex) {
+ // This shouldn't happen in the normal case
+ return NETWORK_TYPE_UNKNOWN;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return NETWORK_TYPE_UNKNOWN;
+ }
+ }
/**
* Returns a constant indicating the radio technology (network type)
@@ -2189,7 +2216,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public int getDataNetworkType() {
+ public @NetworkType int getDataNetworkType() {
return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
}
@@ -2229,7 +2256,7 @@ public class TelephonyManager {
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public int getVoiceNetworkType() {
+ public @NetworkType int getVoiceNetworkType() {
return getVoiceNetworkType(getSubId());
}
@@ -4108,11 +4135,16 @@ public class TelephonyManager {
}
/**
- * Returns the IMS home network domain name that was loaded from the ISIM.
- * @return the IMS domain name, or null if not present or not loaded
+ * Returns the IMS home network domain name that was loaded from the ISIM {@see #APPTYPE_ISIM}.
+ * @return the IMS domain name. Returns {@code null} if ISIM hasn't been loaded or IMS domain
+ * hasn't been loaded or isn't present on the ISIM.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getIsimDomain() {
try {
IPhoneSubInfo info = getSubscriberInfo();
diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java
new file mode 100644
index 000000000000..9aca18e07812
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/GroupCall.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.os.RemoteException;
+import android.telephony.MbmsGroupCallSession;
+import android.telephony.mbms.vendor.IMbmsGroupCallService;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class used to represent a single MBMS group call. After a call has been started with
+ * {@link MbmsGroupCallSession#startGroupCall},
+ * this class is used to hold information about the call and control it.
+ */
+public class GroupCall implements AutoCloseable {
+ private static final String LOG_TAG = "MbmsGroupCall";
+
+ /**
+ * The state of a group call, reported via
+ * {@link GroupCallCallback#onGroupCallStateChanged(int, int)}
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+ public @interface GroupCallState {}
+ public static final int STATE_STOPPED = 1;
+ public static final int STATE_STARTED = 2;
+ public static final int STATE_STALLED = 3;
+
+ /**
+ * The reason for a call state change, reported via
+ * {@link GroupCallCallback#onGroupCallStateChanged(int, int)}
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "REASON_" },
+ value = {REASON_BY_USER_REQUEST, REASON_FREQUENCY_CONFLICT,
+ REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
+ REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
+ public @interface GroupCallStateChangeReason {}
+
+ /**
+ * Indicates that the middleware does not have a reason to provide for the state change.
+ */
+ public static final int REASON_NONE = 0;
+
+ /**
+ * State changed due to a call to {@link #close()} or
+ * {@link MbmsGroupCallSession#startGroupCall}
+ */
+ public static final int REASON_BY_USER_REQUEST = 1;
+
+ // 2 is unused to match up with streaming.
+
+ /**
+ * State changed due to a frequency conflict with another requested call.
+ */
+ public static final int REASON_FREQUENCY_CONFLICT = 3;
+
+ /**
+ * State changed due to the middleware running out of memory
+ */
+ public static final int REASON_OUT_OF_MEMORY = 4;
+
+ /**
+ * State changed due to the device leaving the home carrier's LTE network.
+ */
+ public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5;
+
+ /**
+ * State changed due to the device leaving the area where this call is being broadcast.
+ */
+ public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6;
+
+ private final int mSubscriptionId;
+ private final long mTmgi;
+ private final MbmsGroupCallSession mParentSession;
+ private final InternalGroupCallCallback mCallback;
+ private IMbmsGroupCallService mService;
+
+ /**
+ * @hide
+ */
+ public GroupCall(int subscriptionId,
+ IMbmsGroupCallService service,
+ MbmsGroupCallSession session,
+ long tmgi,
+ InternalGroupCallCallback callback) {
+ mSubscriptionId = subscriptionId;
+ mParentSession = session;
+ mService = service;
+ mTmgi = tmgi;
+ mCallback = callback;
+ }
+
+ /**
+ * Retrieve the TMGI (Temporary Mobile Group Identity) corresponding to this call.
+ */
+ public long getTmgi() {
+ return mTmgi;
+ }
+
+ /**
+ * Send an update to the middleware when the SAI (Service Area Identifier) list and frequency
+ * information of the group call has * changed. Callers must obtain this information from the
+ * wireless carrier independently.
+ * @param saiArray New array of SAIs that the call is available on.
+ * @param frequencyArray New array of frequencies that the call is available on.
+ */
+ public void updateGroupCall(int[] saiArray, int[] frequencyArray) {
+ if (mService == null) {
+ throw new IllegalStateException("No group call service attached");
+ }
+
+ try {
+ mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService = null;
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ } finally {
+ mParentSession.onGroupCallStopped(this);
+ }
+ }
+
+ /**
+ * Stop this group call. Further operations on this object will fail with an
+ * {@link IllegalStateException}.
+ *
+ * May throw an {@link IllegalStateException}
+ */
+ @Override
+ public void close() {
+ if (mService == null) {
+ throw new IllegalStateException("No group call service attached");
+ }
+
+ try {
+ mService.stopGroupCall(mSubscriptionId, mTmgi);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService = null;
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ } finally {
+ mParentSession.onGroupCallStopped(this);
+ }
+ }
+
+ /** @hide */
+ public InternalGroupCallCallback getCallback() {
+ return mCallback;
+ }
+
+ private void sendErrorToApp(int errorCode, String message) {
+ mCallback.onError(errorCode, message);
+ }
+}
+
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
new file mode 100644
index 000000000000..001bb02aad94
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A callback class for use when the application is in a group call. The middleware
+ * will provide updates on the status of the call via this callback.
+ */
+public class GroupCallCallback {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+ MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+ MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+ MbmsErrors.GeneralErrors.ERROR_IN_E911,
+ MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+ MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+ MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
+ private @interface GroupCallError{}
+
+ /**
+ * Indicates broadcast signal strength is not available for this call.
+ *
+ * This may be due to the call no longer being available due to geography
+ * or timing (end of service)
+ */
+ public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+
+ /**
+ * Called by the middleware when it has detected an error condition in this group call. The
+ * possible error codes are listed in {@link MbmsErrors}.
+ * @param errorCode The error code.
+ * @param message A human-readable message generated by the middleware for debugging purposes.
+ */
+ public void onError(@GroupCallError int errorCode, @Nullable String message) {
+ // default implementation empty
+ }
+
+ /**
+ * Called to indicate this call has changed state.
+ *
+ * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
+ * and {@link GroupCall#STATE_STALLED}.
+ */
+ public void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+ @GroupCall.GroupCallStateChangeReason int reason) {
+ // default implementation empty
+ }
+
+ /**
+ * Broadcast Signal Strength updated.
+ *
+ * This signal strength is the BROADCAST signal strength which,
+ * depending on technology in play and it's deployment, may be
+ * stronger or weaker than the traditional UNICAST signal
+ * strength. It a simple int from 0-4 for valid levels or
+ * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
+ * for this call due to timing, geography or popularity.
+ */
+ public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+ // default implementation empty
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
new file mode 100755
index 000000000000..844b6344a34c
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+/**
+ * @hide
+ */
+oneway interface IGroupCallCallback {
+ void onError(int errorCode, String message);
+ void onGroupCallStateChanged(int state, int reason);
+ void onBroadcastSignalStrengthUpdated(int signalStrength);
+}
diff --git a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
new file mode 100755
index 000000000000..1a1c7f8af5df
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
@@ -0,0 +1,33 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony.mbms;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+oneway interface IMbmsGroupCallSessionCallback
+{
+ void onError(int errorCode, String message);
+
+ void onAvailableSaisUpdated(in List currentSai, in List availableSais);
+
+ void onServiceInterfaceAvailable(String interfaceName, int index);
+
+ void onMiddlewareReady();
+}
diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
new file mode 100644
index 000000000000..2910bb313d84
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.os.Binder;
+
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalGroupCallCallback extends IGroupCallCallback.Stub {
+ private final GroupCallCallback mAppCallback;
+ private final Executor mExecutor;
+ private volatile boolean mIsStopped = false;
+
+ public InternalGroupCallCallback(GroupCallCallback appCallback,
+ Executor executor) {
+ mAppCallback = appCallback;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onError(final int errorCode, final String message) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onError(errorCode, message);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onGroupCallStateChanged(final int state, final int reason) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onGroupCallStateChanged(state, reason);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onBroadcastSignalStrengthUpdated(final int signalStrength) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ /** Prevents this callback from calling the app */
+ public void stop() {
+ mIsStopped = true;
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
new file mode 100644
index 000000000000..4c9cf4dd7c92
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.os.Binder;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalGroupCallSessionCallback extends IMbmsGroupCallSessionCallback.Stub {
+ private final Executor mExecutor;
+ private final MbmsGroupCallSessionCallback mAppCallback;
+ private volatile boolean mIsStopped = false;
+
+ public InternalGroupCallSessionCallback(MbmsGroupCallSessionCallback appCallback,
+ Executor executor) {
+ mAppCallback = appCallback;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onError(final int errorCode, final String message) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onError(errorCode, message);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onAvailableSaisUpdated(final List currentSais, final List availableSais) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onAvailableSaisUpdated(currentSais, availableSais);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onServiceInterfaceAvailable(final String interfaceName, final int index) {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onServiceInterfaceAvailable(interfaceName, index);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onMiddlewareReady() {
+ if (mIsStopped) {
+ return;
+ }
+
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ mAppCallback.onMiddlewareReady();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ }
+
+ /** Prevents this callback from calling the app */
+ public void stop() {
+ mIsStopped = true;
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
new file mode 100644
index 000000000000..7da734ee5837
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.telephony.MbmsGroupCallSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class that is used to receive information from the middleware on MBMS group-call
+ * services. An instance of this object should be passed into
+ * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ */
+public class MbmsGroupCallSessionCallback {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+ MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+ MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+ MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+ MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+ MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+ MbmsErrors.GeneralErrors.ERROR_IN_E911,
+ MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+ MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+ MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
+ private @interface GroupCallError{}
+
+ /**
+ * Called by the middleware when it has detected an error condition. The possible error codes
+ * are listed in {@link MbmsErrors}.
+ * @param errorCode The error code.
+ * @param message A human-readable message generated by the middleware for debugging purposes.
+ */
+ public void onError(@GroupCallError int errorCode, @Nullable String message) {
+ }
+
+ /**
+ * Indicates that the list of currently available SAIs has been updated. The app may use this
+ * information to filter the list of group calls when displaying available group calls to
+ * the user by matching the SAIs with a list of group calls separately negotiated with the
+ * carrier. The app may also report the aggregate list of SAIs to the group call application
+ * server so that a network entity can determine when, and where to activate the broadcast of
+ * particular group calls.
+ * @param currentSais The available SAIs on the current cell.
+ * @param availableSais A list of lists of available SAIS in neighboring cells, where each list
+ * contains the available SAIs in an individual cell.
+ */
+ public void onAvailableSaisUpdated(List<Integer> currentSais,
+ List<List<Integer>> availableSais) {
+ }
+
+ /**
+ * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
+ * via this callback may be used to establish a data-link interface with the modem before the
+ * middleware is ready.
+ * Note that this method may be called before {@link #onMiddlewareReady()}.
+ *
+ * @param interfaceName The interface name for the data link.
+ * @param index The index for the data link.
+ */
+ public void onServiceInterfaceAvailable(String interfaceName, int index) {
+ }
+
+ /**
+ * Called to indicate that the middleware has been initialized and is ready.
+ *
+ * Before this method is called, calling any method on an instance of
+ * {@link MbmsGroupCallSession} will result in an {@link IllegalStateException} or an error
+ * delivered via {@link #onError(int, String)} with error code
+ * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
+ */
+ public void onMiddlewareReady() {
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index 06b2120b59c1..95b4d37e5840 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -23,6 +23,7 @@ import android.content.ServiceConnection;
import android.content.pm.*;
import android.content.pm.ServiceInfo;
import android.telephony.MbmsDownloadSession;
+import android.telephony.MbmsGroupCallSession;
import android.telephony.MbmsStreamingSession;
import android.util.Log;
@@ -59,6 +60,9 @@ public class MbmsUtils {
case MbmsStreamingSession.MBMS_STREAMING_SERVICE_ACTION:
metaDataKey = MbmsStreamingSession.MBMS_STREAMING_SERVICE_OVERRIDE_METADATA;
break;
+ case MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_ACTION:
+ metaDataKey = MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA;
+ break;
}
if (metaDataKey == null) {
return null;
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
new file mode 100755
index 000000000000..721256a95396
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
@@ -0,0 +1,39 @@
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony.mbms.vendor;
+
+import android.net.Uri;
+import android.telephony.mbms.IMbmsGroupCallSessionCallback;
+import android.telephony.mbms.IGroupCallCallback;
+
+/**
+ * @hide
+ */
+interface IMbmsGroupCallService
+{
+ int initialize(IMbmsGroupCallSessionCallback callback, int subId);
+
+ void stopGroupCall(int subId, long tmgi);
+
+ void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
+ in int[] frequencyArray);
+
+ int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
+ in int[] frequencyArray, IGroupCallCallback callback);
+
+ void dispose(int subId);
+}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
new file mode 100644
index 000000000000..3734ca7d6fc9
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms.vendor;
+
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.IGroupCallCallback;
+import android.telephony.mbms.IMbmsGroupCallSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsGroupCallSessionCallback;
+import android.telephony.mbms.vendor.IMbmsGroupCallService.Stub;
+
+import java.util.List;
+
+/**
+ * Base class for MBMS group-call services. The middleware should override this class to implement
+ * its {@link Service} for group calls
+ * Subclasses should call this class's {@link #onBind} method to obtain an {@link IBinder} if they
+ * override {@link #onBind}.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class MbmsGroupCallServiceBase extends Service {
+ private final IBinder mInterface = new Stub() {
+ @Override
+ public int initialize(final IMbmsGroupCallSessionCallback callback,
+ final int subscriptionId) throws RemoteException {
+ if (callback == null) {
+ throw new NullPointerException("Callback must not be null");
+ }
+
+ final int uid = Binder.getCallingUid();
+
+ int result = MbmsGroupCallServiceBase.this.initialize(
+ new MbmsGroupCallSessionCallback() {
+ @Override
+ public void onError(final int errorCode, final String message) {
+ try {
+ if (errorCode == MbmsErrors.UNKNOWN) {
+ throw new IllegalArgumentException(
+ "Middleware cannot send an unknown error.");
+ }
+ callback.onError(errorCode, message);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onAvailableSaisUpdated(final List currentSais,
+ final List availableSais) {
+ try {
+ callback.onAvailableSaisUpdated(currentSais, availableSais);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onServiceInterfaceAvailable(final String interfaceName,
+ final int index) {
+ try {
+ callback.onServiceInterfaceAvailable(interfaceName, index);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ @Override
+ public void onMiddlewareReady() {
+ try {
+ callback.onMiddlewareReady();
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+ }, subscriptionId);
+
+ if (result == MbmsErrors.SUCCESS) {
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+ }
+
+ return result;
+ }
+
+ @Override
+ public void stopGroupCall(int subId, long tmgi) {
+ MbmsGroupCallServiceBase.this.stopGroupCall(subId, tmgi);
+ }
+
+ @Override
+ public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
+ int[] frequencyArray) {
+ MbmsGroupCallServiceBase.this.updateGroupCall(
+ subscriptionId, tmgi, saiArray, frequencyArray);
+ }
+
+ @Override
+ public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray,
+ final int[] frequencyArray, final IGroupCallCallback callback)
+ throws RemoteException {
+ if (callback == null) {
+ throw new NullPointerException("Callback must not be null");
+ }
+
+ final int uid = Binder.getCallingUid();
+
+ int result = MbmsGroupCallServiceBase.this.startGroupCall(
+ subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() {
+ @Override
+ public void onError(final int errorCode, final String message) {
+ try {
+ if (errorCode == MbmsErrors.UNKNOWN) {
+ throw new IllegalArgumentException(
+ "Middleware cannot send an unknown error.");
+ }
+ callback.onError(errorCode, message);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ public void onGroupCallStateChanged(int state, int reason) {
+ try {
+ callback.onGroupCallStateChanged(state, reason);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+
+ public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+ try {
+ callback.onBroadcastSignalStrengthUpdated(signalStrength);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }
+ });
+
+ if (result == MbmsErrors.SUCCESS) {
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+ }
+
+ return result;
+ }
+
+ @Override
+ public void dispose(int subId) throws RemoteException {
+ MbmsGroupCallServiceBase.this.dispose(subId);
+ }
+ };
+
+ /**
+ * Initialize the group call service for this app and subscription ID, registering the callback.
+ *
+ * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
+ * will be intercepted and passed to the app as
+ * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+ *
+ * May return any value from {@link MbmsErrors.InitializationErrors}
+ * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
+ * {@link IMbmsGroupCallSessionCallback#onError(int, String)}.
+ *
+ * @param callback The callback to use to communicate with the app.
+ * @param subscriptionId The subscription ID to use.
+ */
+ public int initialize(MbmsGroupCallSessionCallback callback, int subscriptionId)
+ throws RemoteException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * Starts a particular group call. This method may perform asynchronous work. When
+ * the call is ready for consumption, the middleware should inform the app via
+ * {@link IGroupCallCallback#onGroupCallStateChanged(int, int)}.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ * @param tmgi The TMGI, an identifier for the group call.
+ * @param saiArray An array of SAIs for the group call.
+ * @param frequencyArray An array of frequencies for the group call.
+ * @param callback The callback object on which the app wishes to receive updates.
+ * @return Any error in {@link MbmsErrors.GeneralErrors}
+ */
+ public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray,
+ GroupCallCallback callback) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * Stop the group call identified by {@code tmgi}.
+ *
+ * The callback provided via {@link #startGroupCall} should no longer be
+ * used after this method has called by the app.
+ *
+ * May throw an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ * @param tmgi The TMGI for the call to stop.
+ */
+ public void stopGroupCall(int subscriptionId, long tmgi) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * Called when the app receives new SAI and frequency information for the group call identified
+ * by {@code tmgi}.
+ * @param saiArray New array of SAIs that the call is available on.
+ * @param frequencyArray New array of frequencies that the call is available on.
+ */
+ public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
+ int[] frequencyArray) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * Signals that the app wishes to dispose of the session identified by the
+ * {@code subscriptionId} argument and the caller's uid. No notification back to the
+ * app is required for this operation, and the corresponding callback provided via
+ * {@link #initialize} should no longer be used
+ * after this method has been called by the app.
+ *
+ * May throw an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ */
+ public void dispose(int subscriptionId) throws RemoteException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ /**
+ * Indicates that the app identified by the given UID and subscription ID has died.
+ * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+ * @param subscriptionId The subscription ID the app is using.
+ */
+ public void onAppCallbackDied(int uid, int subscriptionId) {
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mInterface;
+ }
+}