diff options
94 files changed, 2766 insertions, 1075 deletions
diff --git a/Android.bp b/Android.bp index dc9c4d4b5a85..4e7a7b4d7ae2 100644 --- a/Android.bp +++ b/Android.bp @@ -521,6 +521,8 @@ java_defaults { "telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl", "telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl", "telecomm/java/com/android/internal/telecom/IInCallService.aidl", + "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl", + "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl", "telecomm/java/com/android/internal/telecom/ITelecomService.aidl", "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl", "telephony/java/android/telephony/data/IDataService.aidl", diff --git a/api/current.txt b/api/current.txt index 86684c8cbaea..b272b9b9292a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5730,7 +5730,6 @@ package android.app { public final class NotificationChannelGroup implements android.os.Parcelable { ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence); - method public boolean canOverlayApps(); method public android.app.NotificationChannelGroup clone(); method public int describeContents(); method public java.util.List<android.app.NotificationChannel> getChannels(); @@ -5738,7 +5737,6 @@ package android.app { method public java.lang.String getId(); method public java.lang.CharSequence getName(); method public boolean isBlocked(); - method public void setAllowAppOverlay(boolean); method public void setDescription(java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR; @@ -5746,6 +5744,7 @@ package android.app { public class NotificationManager { method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule); + method public boolean areAppOverlaysAllowed(); method public boolean areNotificationsEnabled(); method public boolean canNotifyAsPackage(java.lang.String); method public void cancel(int); @@ -10268,6 +10267,7 @@ package android.content { field public static final java.lang.String CATEGORY_OPENABLE = "android.intent.category.OPENABLE"; field public static final java.lang.String CATEGORY_PREFERENCE = "android.intent.category.PREFERENCE"; field public static final java.lang.String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE"; + field public static final java.lang.String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME"; field public static final java.lang.String CATEGORY_SELECTED_ALTERNATIVE = "android.intent.category.SELECTED_ALTERNATIVE"; field public static final java.lang.String CATEGORY_TAB = "android.intent.category.TAB"; field public static final java.lang.String CATEGORY_TEST = "android.intent.category.TEST"; @@ -43638,6 +43638,10 @@ package android.telephony { field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int"; field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array"; field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool"; + field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT = "opportunistic_network_entry_threshold_rsrp_int"; + field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = "opportunistic_network_entry_threshold_rssnr_int"; + field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int"; + field public static final java.lang.String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int"; field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool"; field public static final java.lang.String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array"; field public static final java.lang.String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string"; diff --git a/api/system-current.txt b/api/system-current.txt index 4278003f4601..5014abfd79e7 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -27,6 +27,7 @@ package android { field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET"; field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"; + field public static final java.lang.String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE"; field public static final java.lang.String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE"; field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"; @@ -5649,6 +5650,14 @@ package android.telecom { ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean); } + public class PhoneAccountSuggestionService extends android.app.Service { + ctor public PhoneAccountSuggestionService(); + method public void onAccountSuggestionRequest(java.lang.String); + method public android.os.IBinder onBind(android.content.Intent); + method public final void suggestPhoneAccounts(java.lang.String, java.util.List<android.telecom.PhoneAccountSuggestion>); + field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService"; + } + public final class RemoteConference { method public deprecated void setAudioState(android.telecom.AudioState); } @@ -6141,6 +6150,7 @@ package android.telephony { method public boolean getEmergencyCallbackMode(); method public java.lang.String getIsimDomain(); method public int getPreferredNetworkType(int); + method public int getPreferredNetworkTypeBitmap(); method public int getRadioPowerState(); method public int getSimApplicationState(); method public int getSimCardState(); @@ -6169,6 +6179,7 @@ package android.telephony { method public void setDataActivationState(int); method public deprecated void setDataEnabled(int, boolean); method public void setDataRoamingEnabled(boolean); + method public boolean setPreferredNetworkTypeBitmap(int); method public boolean setRadio(boolean); method public boolean setRadioPower(boolean); method public void setSimPowerState(int); diff --git a/api/test-current.txt b/api/test-current.txt index 46e7683c3cb7..1e15792fd92c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -182,7 +182,6 @@ package android.app { method public int getUserLockedFields(); method public void lockFields(int); method public void setBlocked(boolean); - field public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 2; // 0x2 } public class NotificationManager { @@ -1282,6 +1281,14 @@ package android.telecom { ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean); } + public class PhoneAccountSuggestionService extends android.app.Service { + ctor public PhoneAccountSuggestionService(); + method public void onAccountSuggestionRequest(java.lang.String); + method public android.os.IBinder onBind(android.content.Intent); + method public final void suggestPhoneAccounts(java.lang.String, java.util.List<android.telecom.PhoneAccountSuggestion>); + field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService"; + } + } package android.telephony { diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index a9819972cfc7..69cb264ef237 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -293,7 +293,7 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTim // Then, check stats-data directory to see there's any file containing // ConfigMetricsReport from previous shutdowns to concatenate to reports. - StorageManager::appendConfigMetricsReport(key, proto); + StorageManager::appendConfigMetricsReport(key, proto, erase_data); auto it = mMetricsManagers.find(key); if (it != mMetricsManagers.end()) { diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 04173b217dcb..50b64b986866 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -296,6 +296,7 @@ void StatsService::dumpIncidentSection(int out) { ADB_DUMP, &proto); proto.end(reportsListToken); proto.flush(out); + proto.clear(); } } @@ -466,23 +467,12 @@ status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) { name.assign(args[1].c_str(), args[1].size()); good = true; } else if (argCount == 3) { - // If it's a userdebug or eng build, then the shell user can - // impersonate other uids. - if (mEngBuild) { - const char* s = args[1].c_str(); - if (*s != '\0') { - char* end = NULL; - uid = strtol(s, &end, 0); - if (*end == '\0') { - name.assign(args[2].c_str(), args[2].size()); - good = true; - } - } - } else { - dprintf(out, - "The metrics can only be dumped for other UIDs on eng or userdebug " - "builds.\n"); + good = getUidFromArgs(args, 1, uid); + if (!good) { + dprintf(out, "Invalid UID. Note that the metrics can only be dumped for " + "other UIDs on eng or userdebug builds.\n"); } + name.assign(args[2].c_str(), args[2].size()); } if (!good) { print_cmd_help(out); @@ -518,23 +508,12 @@ status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& arg name.assign(args[2].c_str(), args[2].size()); good = true; } else if (argCount == 4) { - // If it's a userdebug or eng build, then the shell user can - // impersonate other uids. - if (mEngBuild) { - const char* s = args[2].c_str(); - if (*s != '\0') { - char* end = NULL; - uid = strtol(s, &end, 0); - if (*end == '\0') { - name.assign(args[3].c_str(), args[3].size()); - good = true; - } - } - } else { - dprintf(err, - "The config can only be set for other UIDs on eng or userdebug " - "builds.\n"); + good = getUidFromArgs(args, 2, uid); + if (!good) { + dprintf(err, "Invalid UID. Note that the config can only be set for " + "other UIDs on eng or userdebug builds.\n"); } + name.assign(args[3].c_str(), args[3].size()); } else if (argCount == 2 && args[1] == "remove") { good = true; } @@ -612,23 +591,12 @@ status_t StatsService::cmd_dump_report(int out, const Vector<String8>& args) { name.assign(args[1].c_str(), args[1].size()); good = true; } else if (argCount == 3) { - // If it's a userdebug or eng build, then the shell user can - // impersonate other uids. - if (mEngBuild) { - const char* s = args[1].c_str(); - if (*s != '\0') { - char* end = NULL; - uid = strtol(s, &end, 0); - if (*end == '\0') { - name.assign(args[2].c_str(), args[2].size()); - good = true; - } - } - } else { - dprintf(out, - "The metrics can only be dumped for other UIDs on eng or userdebug " - "builds.\n"); + good = getUidFromArgs(args, 1, uid); + if (!good) { + dprintf(out, "Invalid UID. Note that the metrics can only be dumped for " + "other UIDs on eng or userdebug builds.\n"); } + name.assign(args[2].c_str(), args[2].size()); } if (good) { vector<uint8_t> data; @@ -714,18 +682,14 @@ status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& ar state = atoi(args[2].c_str()); good = true; } else if (argCount == 4) { - uid = atoi(args[1].c_str()); - // If it's a userdebug or eng build, then the shell user can impersonate other uids. - // Otherwise, the uid must match the actual caller's uid. - if (mEngBuild || (uid >= 0 && (uid_t)uid == IPCThreadState::self()->getCallingUid())) { - label = atoi(args[2].c_str()); - state = atoi(args[3].c_str()); - good = true; - } else { + good = getUidFromArgs(args, 1, uid); + if (!good) { dprintf(out, - "Selecting a UID for writing AppBreadcrumb can only be done for other UIDs " - "on eng or userdebug builds.\n"); + "Invalid UID. Note that selecting a UID for writing AppBreadcrumb can only be " + "done for other UIDs on eng or userdebug builds.\n"); } + label = atoi(args[2].c_str()); + state = atoi(args[3].c_str()); } if (good) { dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state); @@ -792,6 +756,28 @@ status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) { } } +bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) { + const char* s = args[uidArgIndex].c_str(); + if (*s == '\0') { + return false; + } + char* endc = NULL; + int64_t longUid = strtol(s, &endc, 0); + if (*endc != '\0') { + return false; + } + int32_t goodUid = static_cast<int32_t>(longUid); + if (longUid < 0 || static_cast<uint64_t>(longUid) != static_cast<uid_t>(goodUid)) { + return false; // It was not of uid_t type. + } + uid = goodUid; + + int32_t callingUid = IPCThreadState::self()->getCallingUid(); + return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids. + || (callingUid == goodUid) // Anyone can 'impersonate' themselves. + || (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL. +} + Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version, const vector<String16>& version_string, const vector<String16>& app, diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index cd4d601a606f..135a3c9cde51 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -291,6 +291,15 @@ private: status_t cmd_print_logs(int outFd, const Vector<String8>& args); /** + * Writes the value of args[uidArgIndex] into uid. + * Returns whether the uid is reasonable (type uid_t) and whether + * 1. it is equal to the calling uid, or + * 2. the device is mEngBuild, or + * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL). + */ + bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid); + + /** * Adds a configuration after checking permissions and obtaining UID from binder call. */ bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config); @@ -340,6 +349,7 @@ private: FRIEND_TEST(StatsServiceTest, TestAddConfig_simple); FRIEND_TEST(StatsServiceTest, TestAddConfig_empty); FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid); + FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 0c05be1170d1..bdcdc536759d 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3670,7 +3670,7 @@ message AppCompacted { SOME = 1; FULL = 2; } - optional Action action = 3 [default = UNKNOWN]; + optional Action action = 3; // Total RSS in kilobytes consumed by the process prior to compaction. optional int64 before_rss_total_kilobytes = 4; @@ -3702,12 +3702,12 @@ message AppCompacted { // The last compaction action performed for this app. optional Action last_action = 13; - // The last time that compaction was attempted on this process in seconds - // since boot. - optional int64 last_compact_timestamp = 14; + // The last time that compaction was attempted on this process in milliseconds + // since boot, not including sleep (see SystemClock.uptimeMillis()). + optional int64 last_compact_timestamp_ms_since_boot = 14; - // The oom_adj at the time of compaction. - optional int32 oom_adj = 15; + // The oom_score_adj at the time of compaction. + optional int32 oom_score_adj = 15; // The process state at the time of compaction. optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN]; diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 2f19a02ecafe..90f641a34b85 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -188,7 +188,9 @@ bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) { return false; } -void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto) { +void StorageManager::appendConfigMetricsReport(const ConfigKey& key, + ProtoOutputStream* proto, + bool erasa_data) { unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir); if (dir == NULL) { VLOG("Path %s does not exist", STATS_DATA_DIR); @@ -224,8 +226,9 @@ void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutput close(fd); } - // Remove file from disk after reading. - remove(file_name.c_str()); + if (erasa_data) { + remove(file_name.c_str()); + } } } } diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index 8fbc89e4f657..dcf3bb607380 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -68,10 +68,12 @@ public: static bool hasConfigMetricsReport(const ConfigKey& key); /** - * Appends ConfigMetricsReport found on disk to the specific proto and - * delete it. + * Appends the ConfigMetricsReport found on disk to the specifid proto + * and, if erase_data, deletes it from disk. */ - static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto); + static void appendConfigMetricsReport(const ConfigKey& key, + ProtoOutputStream* proto, + bool erase_data); /** * Call to load the saved configs from disk. diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp index a7b413666266..560fb9f02174 100644 --- a/cmds/statsd/tests/StatsService_test.cpp +++ b/cmds/statsd/tests/StatsService_test.cpp @@ -58,6 +58,45 @@ TEST(StatsServiceTest, TestAddConfig_invalid) { service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()})); } +TEST(StatsServiceTest, TestGetUidFromArgs) { + Vector<String8> args; + args.push(String8("-1")); + args.push(String8("0")); + args.push(String8("1")); + args.push(String8("9999999999999999999999999999999999")); + args.push(String8("a1")); + args.push(String8("")); + + int32_t uid; + + StatsService service(nullptr); + service.mEngBuild = true; + + // "-1" + EXPECT_FALSE(service.getUidFromArgs(args, 0, uid)); + + // "0" + EXPECT_TRUE(service.getUidFromArgs(args, 1, uid)); + EXPECT_EQ(0, uid); + + // "1" + EXPECT_TRUE(service.getUidFromArgs(args, 2, uid)); + EXPECT_EQ(1, uid); + + // "999999999999999999" + EXPECT_FALSE(service.getUidFromArgs(args, 3, uid)); + + // "a1" + EXPECT_FALSE(service.getUidFromArgs(args, 4, uid)); + + // "" + EXPECT_FALSE(service.getUidFromArgs(args, 5, uid)); + + // For a non-userdebug, uid "1" cannot be impersonated. + service.mEngBuild = false; + EXPECT_FALSE(service.getUidFromArgs(args, 2, uid)); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index a39f5e30bd51..4174ad7cd586 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -51,6 +51,8 @@ public final class Telecom extends BaseCommand { private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP = "add-or-remove-call-companion-app"; private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app"; + private static final String COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT = + "set-phone-acct-suggestion-component"; private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account"; private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer"; private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer"; @@ -64,36 +66,37 @@ public final class Telecom extends BaseCommand { @Override public void onShowUsage(PrintStream out) { - out.println( - "usage: telecom [subcommand] [options]\n" + - "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" + - "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" + - "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" + - "usage: telecom set-test-call-redirection-app <PACKAGE>\n" + - "usage: telecom set-test-call-screening-app <PACKAGE>\n" + - "usage: telecom set-test-auto-mode-app <PACKAGE>\n" + - "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" + - "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN> <LABEL> <ADDRESS>\n" + - "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n" + - "usage: telecom set-default-dialer <PACKAGE>\n" + - "usage: telecom get-default-dialer\n" + - "usage: telecom get-system-dialer\n" + - "usage: telecom wait-on-handlers\n" + - "\n" + - "telecom set-phone-account-enabled: Enables the given phone account, if it has \n" + - " already been registered with Telecom.\n" + - "\n" + - "telecom set-phone-account-disabled: Disables the given phone account, if it \n" + - " has already been registered with telecom.\n" + - "\n" + - "telecom set-default-dialer: Sets the default dialer to the given component. \n" + - "\n" + - "telecom get-default-dialer: Displays the current default dialer. \n" + - "\n" + - "telecom get-system-dialer: Displays the current system dialer. \n" + - "\n" + - "telecom wait-on-handlers: Wait until all handlers finish their work. \n" - ); + out.println("usage: telecom [subcommand] [options]\n" + + "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" + + "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" + + "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" + + "usage: telecom set-test-call-redirection-app <PACKAGE>\n" + + "usage: telecom set-test-call-screening-app <PACKAGE>\n" + + "usage: telecom set-test-auto-mode-app <PACKAGE>\n" + + "usage: telecom set-phone-acct-suggestion-component <COMPONENT>\n" + + "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" + + "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN>" + + " <LABEL> <ADDRESS>\n" + + "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n" + + "usage: telecom set-default-dialer <PACKAGE>\n" + + "usage: telecom get-default-dialer\n" + + "usage: telecom get-system-dialer\n" + + "usage: telecom wait-on-handlers\n" + + "\n" + + "telecom set-phone-account-enabled: Enables the given phone account, if it has \n" + + " already been registered with Telecom.\n" + + "\n" + + "telecom set-phone-account-disabled: Disables the given phone account, if it \n" + + " has already been registered with telecom.\n" + + "\n" + + "telecom set-default-dialer: Sets the default dialer to the given component. \n" + + "\n" + + "telecom get-default-dialer: Displays the current default dialer. \n" + + "\n" + + "telecom get-system-dialer: Displays the current system dialer. \n" + + "\n" + + "telecom wait-on-handlers: Wait until all handlers finish their work. \n" + ); } @Override @@ -134,6 +137,9 @@ public final class Telecom extends BaseCommand { case COMMAND_SET_TEST_AUTO_MODE_APP: runSetTestAutoModeApp(); break; + case COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT: + runSetTestPhoneAcctSuggestionComponent(); + break; case COMMAND_REGISTER_SIM_PHONE_ACCOUNT: runRegisterSimPhoneAccount(); break; @@ -216,6 +222,11 @@ public final class Telecom extends BaseCommand { mTelecomService.setTestAutoModeApp(packageName); } + private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException { + final String componentName = nextArg(); + mTelecomService.setTestPhoneAcctSuggestionComponent(componentName); + } + private void runUnregisterPhoneAccount() throws RemoteException { final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs(); mTelecomService.unregisterPhoneAccount(handle); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 00567523e393..163be8efc8fd 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -65,6 +65,10 @@ interface INotificationManager boolean areNotificationsEnabled(String pkg); int getPackageImportance(String pkg); + void setAppOverlaysAllowed(String pkg, int uid, boolean allowed); + boolean areAppOverlaysAllowed(String pkg); + boolean areAppOverlaysAllowedForPackage(String pkg, int uid); + void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList); void createNotificationChannels(String pkg, in ParceledListSlice channelsList); void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index b9d590741263..e066f06542c3 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3130,6 +3130,10 @@ public class Notification implements Parcelable return mAppOverlayIntent; } + /** + * Returns whether the platform is allowed (by the app developer) to generate contextual actions + * for this notification. + */ public boolean getAllowSystemGeneratedContextualActions() { return mAllowSystemGeneratedContextualActions; } diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java index 2322a42c93d5..34cd9f029746 100644 --- a/core/java/android/app/NotificationChannelGroup.java +++ b/core/java/android/app/NotificationChannelGroup.java @@ -50,20 +50,12 @@ public final class NotificationChannelGroup implements Parcelable { private static final String ATT_DESC = "desc"; private static final String ATT_ID = "id"; private static final String ATT_BLOCKED = "blocked"; - private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay"; private static final String ATT_USER_LOCKED = "locked"; - private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true; - /** * @hide */ public static final int USER_LOCKED_BLOCKED_STATE = 0x00000001; - /** - * @hide - */ - @TestApi - public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000002; /** * @see #getId() @@ -74,7 +66,6 @@ public final class NotificationChannelGroup implements Parcelable { private String mDescription; private boolean mBlocked; private List<NotificationChannel> mChannels = new ArrayList<>(); - private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY; // Bitwise representation of fields that have been changed by the user private int mUserLockedFields; @@ -110,7 +101,6 @@ public final class NotificationChannelGroup implements Parcelable { } in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader()); mBlocked = in.readBoolean(); - mAllowAppOverlay = in.readBoolean(); mUserLockedFields = in.readInt(); } @@ -138,7 +128,6 @@ public final class NotificationChannelGroup implements Parcelable { } dest.writeParcelableList(mChannels, flags); dest.writeBoolean(mBlocked); - dest.writeBoolean(mAllowAppOverlay); dest.writeInt(mUserLockedFields); } @@ -181,15 +170,6 @@ public final class NotificationChannelGroup implements Parcelable { } /** - * Returns whether notifications posted to this channel group can display outside of the - * notification shade, in a floating window on top of other apps. These may additionally be - * blocked at the notification channel level, see {@link NotificationChannel#canOverlayApps()}. - */ - public boolean canOverlayApps() { - return mAllowAppOverlay; - } - - /** * Sets the user visible description of this group. * * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too @@ -200,21 +180,6 @@ public final class NotificationChannelGroup implements Parcelable { } /** - * Sets whether notifications posted to this channel group can appear outside of the - * notification shade, floating over other apps' content. - * - * <p>This value will be ignored for notifications that are posted to channels that do not - * allow app overlays ({@link NotificationChannel#canOverlayApps()}. - * - * <p>Only modifiable before the channel is submitted to - * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.</p> - * @see Notification#getAppOverlayIntent() - */ - public void setAllowAppOverlay(boolean allowAppOverlay) { - mAllowAppOverlay = allowAppOverlay; - } - - /** * @hide */ @TestApi @@ -266,7 +231,6 @@ public final class NotificationChannelGroup implements Parcelable { // Name, id, and importance are set in the constructor. setDescription(parser.getAttributeValue(null, ATT_DESC)); setBlocked(safeBool(parser, ATT_BLOCKED, false)); - setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY)); } private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) { @@ -289,9 +253,6 @@ public final class NotificationChannelGroup implements Parcelable { out.attribute(null, ATT_DESC, getDescription().toString()); } out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked())); - if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) { - out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps())); - } out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields)); out.endTag(null, TAG_GROUP); @@ -307,7 +268,6 @@ public final class NotificationChannelGroup implements Parcelable { record.put(ATT_NAME, getName()); record.put(ATT_DESC, getDescription()); record.put(ATT_BLOCKED, isBlocked()); - record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps()); record.put(ATT_USER_LOCKED, mUserLockedFields); return record; } @@ -336,7 +296,6 @@ public final class NotificationChannelGroup implements Parcelable { if (o == null || getClass() != o.getClass()) return false; NotificationChannelGroup that = (NotificationChannelGroup) o; return isBlocked() == that.isBlocked() && - mAllowAppOverlay == that.mAllowAppOverlay && mUserLockedFields == that.mUserLockedFields && Objects.equals(getId(), that.getId()) && Objects.equals(getName(), that.getName()) && @@ -347,7 +306,7 @@ public final class NotificationChannelGroup implements Parcelable { @Override public int hashCode() { return Objects.hash(getId(), getName(), getDescription(), isBlocked(), getChannels(), - mAllowAppOverlay, mUserLockedFields); + mUserLockedFields); } @Override @@ -356,7 +315,6 @@ public final class NotificationChannelGroup implements Parcelable { cloned.setDescription(getDescription()); cloned.setBlocked(isBlocked()); cloned.setChannels(getChannels()); - cloned.setAllowAppOverlay(canOverlayApps()); cloned.lockFields(mUserLockedFields); return cloned; } @@ -369,7 +327,6 @@ public final class NotificationChannelGroup implements Parcelable { + ", mDescription=" + (!TextUtils.isEmpty(mDescription) ? "hasDescription " : "") + ", mBlocked=" + mBlocked + ", mChannels=" + mChannels - + ", mAllowAppOverlay=" + mAllowAppOverlay + ", mUserLockedFields=" + mUserLockedFields + '}'; } @@ -385,7 +342,6 @@ public final class NotificationChannelGroup implements Parcelable { for (NotificationChannel channel : mChannels) { channel.writeToProto(proto, NotificationChannelGroupProto.CHANNELS); } - proto.write(NotificationChannelGroupProto.ALLOW_APP_OVERLAY, mAllowAppOverlay); proto.end(token); } } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 606b00bddc2f..a782ced7f9f8 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1074,6 +1074,25 @@ public class NotificationManager { } } + + /** + * Sets whether notifications posted by this app can appear outside of the + * notification shade, floating over other apps' content. + * + * <p>This value will be ignored for notifications that are posted to channels that do not + * allow app overlays ({@link NotificationChannel#canOverlayApps()}. + * + * @see Notification#getAppOverlayIntent() + */ + public boolean areAppOverlaysAllowed() { + INotificationManager service = getService(); + try { + return service.areAppOverlaysAllowed(mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Checks the ability to modify notification do not disturb policy for the calling package. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 2f0618cf64b3..d5c6c63243f6 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4222,6 +4222,11 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_HOME_MAIN = "android.intent.category.HOME_MAIN"; /** + * The home activity shown on secondary displays that support showing home activities. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_SECONDARY_HOME = "android.intent.category.SECONDARY_HOME"; + /** * This is the setup wizard activity, that is the first activity that is displayed * when the user sets up the device for the first time. * @hide diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 124d7b174739..8a0d916187b7 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -17,8 +17,10 @@ package android.os; import android.content.Context; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.opengl.EGL14; @@ -57,9 +59,9 @@ public class GraphicsEnvironment { private static final String TAG = "GraphicsEnvironment"; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0"; - private static final String ANGLE_PACKAGE_NAME = "com.google.android.angle"; private static final String ANGLE_RULES_FILE = "a4a_rules.json"; private static final String ANGLE_TEMP_RULES = "debug.angle.rules"; + private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID"; private ClassLoader mClassLoader; private String mLayerPath; @@ -276,6 +278,27 @@ public class GraphicsEnvironment { } /** + * Get the ANGLE package name. + */ + private String getAnglePackageName(Context context) { + Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID); + + List<ResolveInfo> resolveInfos = context.getPackageManager() + .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY); + if (resolveInfos.size() != 1) { + Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: " + + resolveInfos.size()); + for (ResolveInfo resolveInfo : resolveInfos) { + Log.e(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName); + } + return ""; + } + + // Must be exactly 1 ANGLE PKG found to get here. + return resolveInfos.get(0).activityInfo.packageName; + } + + /** * Pass ANGLE details down to trigger enable logic */ private void setupAngle(Context context, Bundle bundle, String packageName) { @@ -286,12 +309,18 @@ public class GraphicsEnvironment { + "set to: '" + devOptIn + "'"); } + String anglePkgName = getAnglePackageName(context); + if (anglePkgName.isEmpty()) { + Log.e(TAG, "Failed to find ANGLE package."); + return; + } + ApplicationInfo angleInfo; try { - angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME, + angleInfo = context.getPackageManager().getApplicationInfo(anglePkgName, PackageManager.MATCH_SYSTEM_ONLY); } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed"); + Log.w(TAG, "ANGLE package '" + anglePkgName + "' not installed"); return; } @@ -351,7 +380,7 @@ public class GraphicsEnvironment { angleAssets = context.getPackageManager().getResourcesForApplication(angleInfo).getAssets(); } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Failed to get AssetManager for '" + ANGLE_PACKAGE_NAME + "'"); + Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "'"); return; } @@ -360,7 +389,7 @@ public class GraphicsEnvironment { assetsFd = angleAssets.openFd(ANGLE_RULES_FILE); } catch (IOException e) { Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from " - + "'" + ANGLE_PACKAGE_NAME + "'"); + + "'" + anglePkgName + "'"); return; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 40f194693e8b..a39f696aed73 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12949,14 +12949,21 @@ public final class Settings { /** * Property used by {@code com.android.server.SystemServer} on start to decide whether - * the Content Capture service should be created or not + * the Content Capture service should be created or not. * - * <p>By default it should *NOT* be set (in which case the decision is based on whether - * the OEM provides an implementation for the service), but it can be overridden to: + * <p>Possible values are: * * <ul> - * <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency. - * <li>Enable the CTS tests to be run on AOSP builds + * <li>If set to {@link #CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_DEFAULT}, it will only + * be set if the OEM provides and defines the service name by overlaying + * {@code config_defaultContentCaptureService} (this is the "default" mode) + * <li>If set to {@link #CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_ALWAYS}, it will + * always be enabled, even when the resource is not overlaid (this is useful during + * development and/or to run the CTS tests on AOSP builds). + * <li>Otherwise, it's explicitly disabled (this could work as a "kill switch" so OEMs + * can disable it remotely in case of emergency by setting to something else (like + * {@code "false"}); notice that it's also disabled if the OEM doesn't explicitly set one + * of the values above). * </ul> * * @hide @@ -12964,6 +12971,11 @@ public final class Settings { public static final String CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED = "content_capture_service_explicitly_enabled"; + /** @hide */ + public static final String CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_DEFAULT = "default"; + /** @hide */ + public static final String CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_ALWAYS = "always"; + /** {@hide} */ public static final String ISOLATED_STORAGE_LOCAL = "isolated_storage_local"; /** {@hide} */ diff --git a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java index aaea45e4adbf..26cf1809313e 100644 --- a/services/core/java/com/android/server/infra/AbstractMultiplePendingRequestsRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.infra; +package com.android.internal.infra; import android.annotation.NonNull; import android.content.ComponentName; diff --git a/services/core/java/com/android/server/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java index 41dcf89a0b04..5ab258283785 100644 --- a/services/core/java/com/android/server/infra/AbstractRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractRemoteService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.infra; +package com.android.internal.infra; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; @@ -27,13 +27,13 @@ import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.IInterface; +import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.util.Slog; import com.android.internal.annotations.GuardedBy; -import com.android.server.FgThread; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -109,7 +109,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I mComponentName = componentName; mIntent = new Intent(serviceInterface).setComponent(mComponentName); mUserId = userId; - mHandler = new Handler(FgThread.getHandler().getLooper()); + mHandler = new Handler(Looper.getMainLooper()); mBindInstantServiceAllowed = bindInstantServiceAllowed; } diff --git a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java index d32f13b5d71b..f0c223388137 100644 --- a/services/core/java/com/android/server/infra/AbstractSinglePendingRequestRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.infra; +package com.android.internal.infra; import android.annotation.NonNull; import android.content.ComponentName; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index cc8927fdf730..8b6f33fd97e7 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2003,6 +2003,15 @@ <permission android:name="android.permission.BIND_SCREENING_SERVICE" android:protectionLevel="signature|privileged" /> + <!-- Must be required by a {@link android.telecom.PhoneAccountSuggestionService}, + to ensure that only the system can bind to it. + <p>Protection level: signature|privileged + @SystemApi + @hide + --> + <permission android:name="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE" + android:protectionLevel="signature|privileged" /> + <!-- Must be required by a {@link android.telecom.CallRedirectionService}, to ensure that only the system can bind to it. <p>Protection level: signature|privileged diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3f1f9cd062a6..9f5eab557026 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2615,6 +2615,11 @@ <!-- Package name for default network scorer app; overridden by product overlays. --> <string name="config_defaultNetworkScorerPackageName"></string> + <!-- Feature flag to enable memory efficient task snapshots that are used in recents optimized + for low memory devices and replace the app transition starting window with the splash + screen. --> + <bool name="config_lowRamTaskSnapshotsAndRecents">false</bool> + <!-- Determines whether recent tasks are provided to the user. Default device has recents property. If this is false, then the following recents config flags are ignored. --> <bool name="config_hasRecents">true</bool> @@ -3642,4 +3647,11 @@ <!-- Component name for the default module metadata provider on this device --> <string name="config_defaultModuleMetadataProvider">com.android.modulemetadata</string> + + <!-- This is the default launcher component to use on secondary displays that support system + decorations. + This launcher activity must support multiple instances and have corresponding launch mode + set in AndroidManifest. + {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} --> + <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ed9f3b11b67b..4ed050117541 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -329,6 +329,7 @@ <java-symbol type="bool" name="config_enableMultiUserUI"/> <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/> <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/> + <java-symbol type="bool" name="config_lowRamTaskSnapshotsAndRecents" /> <java-symbol type="bool" name="config_hasRecents" /> <java-symbol type="string" name="config_recentsComponentName" /> <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" /> @@ -3520,4 +3521,7 @@ <java-symbol type="dimen" name="rounded_corner_radius_bottom" /> <java-symbol type="string" name="config_defaultModuleMetadataProvider" /> + + <!-- For Secondary Launcher --> + <java-symbol type="string" name="config_secondaryHomeComponent" /> </resources> diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index c84c0354a7c4..f7541e0678d5 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -365,7 +365,7 @@ <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font> </family> <family lang="und-Cakm"> - <font weight="400" style="normal">NotoSansChakma-Regular.ttf</font> + <font weight="400" style="normal">NotoSansChakma-Regular.otf</font> </family> <family lang="und-Cher"> <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font> diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml index 1e0ff506cb20..a05a219b917b 100644 --- a/packages/PackageInstaller/res/values/strings.xml +++ b/packages/PackageInstaller/res/values/strings.xml @@ -121,7 +121,7 @@ <string name="uninstall_update_text_multiuser">Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.</string> <!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] --> <string name="uninstall_remove_contributed_files">Also remove <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of associated media files.</string> - <!-- Label of a checkbox that allows to remove the files contributed by app during uninstall [CHAR LIMIT=none] --> + <!-- Label of a checkbox that allows to keep the data (e.g. files, settings) of the app on uninstall [CHAR LIMIT=none] --> <string name="uninstall_keep_data">Keep <xliff:g id="size" example="1.5MB">%1$s</xliff:g> of app data.</string> <!-- Label for the notification channel containing notifications for current uninstall operations [CHAR LIMIT=40] --> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 2b7babd06b47..7914a0b9d6fd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -321,6 +321,11 @@ public class BluetoothEventManager { // Dispatch device add callback to show bonded but // not connected devices in discovery mode dispatchDeviceAdded(cachedDevice); + Log.d(TAG, "DeviceFoundHandler found bonded and not connected device:" + + cachedDevice); + } else { + Log.d(TAG, "DeviceFoundHandler found existing CachedBluetoothDevice:" + + cachedDevice); } cachedDevice.setRssi(rssi); cachedDevice.setJustDiscovered(true); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 1bffff753513..e28c894ff8f3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -116,8 +116,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) { if (BluetoothUtils.D) { - Log.d(TAG, "onProfileStateChanged: profile " + profile + - " newProfileState " + newProfileState); + Log.d(TAG, "onProfileStateChanged: profile " + profile + ", device=" + mDevice + + ", newProfileState " + newProfileState); } if (mLocalAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF) { @@ -570,7 +570,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } if (BluetoothUtils.D) { - Log.e(TAG, "updating profiles for " + mDevice.getAliasName()); + Log.e(TAG, "updating profiles for " + mDevice.getAliasName() + ", " + mDevice); BluetoothClass bluetoothClass = mDevice.getBluetoothClass(); if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString()); diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java index 9572fb3c629d..20fe495f1afa 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java @@ -1,6 +1,8 @@ package com.android.settingslib.core; import android.content.Context; +import android.text.TextUtils; +import android.util.Log; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; @@ -11,6 +13,8 @@ import androidx.preference.PreferenceScreen; */ public abstract class AbstractPreferenceController { + private static final String TAG = "AbstractPrefController"; + protected final Context mContext; public AbstractPreferenceController(Context context) { @@ -22,6 +26,10 @@ public abstract class AbstractPreferenceController { */ public void displayPreference(PreferenceScreen screen) { final String prefKey = getPreferenceKey(); + if (TextUtils.isEmpty(prefKey)) { + Log.w(TAG, "Skipping displayPreference because key is empty:" + getClass().getName()); + return; + } if (isAvailable()) { setVisible(screen, prefKey, true /* visible */); if (this instanceof Preference.OnPreferenceChangeListener) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java index 28de1914838f..f695e0c35df6 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java @@ -35,6 +35,8 @@ import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class AbstractPreferenceControllerTest { + private static final String KEY_PREF = "test_pref"; + @Mock private PreferenceScreen mScreen; @@ -47,9 +49,9 @@ public class AbstractPreferenceControllerTest { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mPreference = new Preference(mContext); - mPreference.setKey(TestPrefController.KEY_PREF); - when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference); - mTestPrefController = new TestPrefController(mContext); + mPreference.setKey(KEY_PREF); + when(mScreen.findPreference(KEY_PREF)).thenReturn(mPreference); + mTestPrefController = new TestPrefController(mContext, KEY_PREF); } @Test @@ -62,15 +64,24 @@ public class AbstractPreferenceControllerTest { } @Test + public void displayPref_noKey_shouldDoNothing() { + mTestPrefController.isAvailable = true; + + mTestPrefController.displayPreference(mScreen); + + assertThat(mPreference.isVisible()).isTrue(); + } + + @Test public void setVisible_prefIsVisible_shouldSetToVisible() { - mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, true /* visible */); + mTestPrefController.setVisible(mScreen, KEY_PREF, true /* visible */); assertThat(mPreference.isVisible()).isTrue(); } @Test public void setVisible_prefNotVisible_shouldSetToInvisible() { - mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, false /* visible */); + mTestPrefController.setVisible(mScreen, KEY_PREF, false /* visible */); assertThat(mPreference.isVisible()).isFalse(); } @@ -92,13 +103,14 @@ public class AbstractPreferenceControllerTest { } private static class TestPrefController extends AbstractPreferenceController { - private static final String KEY_PREF = "test_pref"; private static final CharSequence TEST_SUMMARY = "Test"; public boolean isAvailable; + private final String mPrefKey; - public TestPrefController(Context context) { + TestPrefController(Context context, String key) { super(context); + mPrefKey = key; } @Override @@ -113,7 +125,7 @@ public class AbstractPreferenceControllerTest { @Override public String getPreferenceKey() { - return KEY_PREF; + return mPrefKey; } @Override diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md index 565d765f5556..8bfa1c28d985 100644 --- a/packages/SystemUI/docs/dagger.md +++ b/packages/SystemUI/docs/dagger.md @@ -125,6 +125,28 @@ public class Dependency { } ``` +### Using injection with Fragments + +Fragments are created as part of the FragmentManager, so they need to be +setup so the manager knows how to create them. To do that, add a method +to com.android.systemui.fragments.FragmentService$FragmentCreator that +returns your fragment class. Thats all thats required, once the method +exists, FragmentService will automatically pick it up and use injection +whenever your fragment needs to be created. + +```java +public interface FragmentCreator { ++ NavigationBarFragment createNavigationBar(); +} +``` + +If you need to create your fragment (i.e. for the add or replace transaction), +then the FragmentHostManager can do this for you. + +```java +FragmentHostManager.get(view).create(NavigationBarFragment.class); +``` + ## TODO List - Eliminate usages of Depndency#get diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index cc6848f323e9..d57fe8a36d61 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -28,4 +28,7 @@ -keep class com.android.systemui.plugins.** { *; } +-keep class com.android.systemui.fragments.FragmentService$FragmentCreator { + *; +} -keep class androidx.core.app.CoreComponentFactory diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index c41ef0ede89e..576660431d82 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -24,6 +24,7 @@ import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.annotation.ColorInt; +import android.annotation.StyleRes; import android.app.PendingIntent; import android.content.Context; import android.graphics.Color; @@ -444,9 +445,11 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe static class KeyguardSliceButton extends Button implements ConfigurationController.ConfigurationListener { + @StyleRes + private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary; + public KeyguardSliceButton(Context context) { - super(context, null /* attrs */, 0 /* styleAttr */, - com.android.keyguard.R.style.TextAppearance_Keyguard_Secondary); + super(context, null /* attrs */, 0 /* styleAttr */, sStyleId); onDensityOrFontScaleChanged(); setEllipsize(TruncateAt.END); } @@ -469,6 +472,11 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe } @Override + public void onOverlayChanged() { + setTextAppearance(sStyleId); + } + + @Override public void setText(CharSequence text, BufferType type) { super.setText(text, type); updatePadding(); diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 38dadd4b961d..874cdccb8794 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -325,6 +325,17 @@ public class BatteryMeterView extends LinearLayout implements .inflate(R.layout.battery_percentage_view, null); } + /** + * Updates percent view by removing old one and reinflating if necessary + */ + public void updatePercentView() { + if (mBatteryPercentView != null) { + removeView(mBatteryPercentView); + mBatteryPercentView = null; + } + updateShowPercent(); + } + private void updatePercentText() { if (mBatteryPercentView != null) { mBatteryPercentView.setText( diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 4e5af15dbfed..445d156d289c 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; @@ -254,6 +255,7 @@ public class Dependency extends SystemUI { @Inject Lazy<NotificationListener> mNotificationListener; @Inject Lazy<NotificationLogger> mNotificationLogger; @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager; + @Inject Lazy<NotificationRowBinder> mNotificationRowBinder; @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil; @Inject Lazy<SmartReplyController> mSmartReplyController; @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler; @@ -422,6 +424,7 @@ public class Dependency extends SystemUI { mProviders.put(NotificationLogger.class, mNotificationLogger::get); mProviders.put(NotificationViewHierarchyManager.class, mNotificationViewHierarchyManager::get); + mProviders.put(NotificationRowBinder.class, mNotificationRowBinder::get); mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get); mProviders.put(SmartReplyController.class, mSmartReplyController::get); mProviders.put(RemoteInputQuickSettingsDisabler.class, diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java index e828b2346b81..76336bb8127f 100644 --- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java @@ -44,7 +44,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.appops.AppOpsController; import com.android.systemui.appops.AppOpsControllerImpl; import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.ActivityStarter; @@ -405,12 +404,6 @@ public class DependencyProvider { @Singleton @Provides - public FragmentService provideFragmentService() { - return new FragmentService(); - } - - @Singleton - @Provides public ExtensionController provideExtensionController(Context context) { return new ExtensionControllerImpl(context); } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 9bc91eef52d0..1a2473e6b858 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -30,6 +30,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.assist.AssistManager; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.power.EnhancedEstimates; import com.android.systemui.power.EnhancedEstimatesImpl; @@ -41,6 +42,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; @@ -215,5 +217,11 @@ public class SystemUIFactory { public interface SystemUIRootComponent { @Singleton Dependency.DependencyInjector createDependency(); + + /** + * FragmentCreator generates all Fragments that need injection. + */ + @Singleton + FragmentService.FragmentCreator createFragmentCreator(); } } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 906a210b6b31..52d1260b4221 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -39,25 +39,39 @@ import java.util.Set; * NotificationPresenter to be displayed to the user. */ public class AppOpsControllerImpl implements AppOpsController, - AppOpsManager.OnOpActiveChangedListener { + AppOpsManager.OnOpActiveChangedListener, + AppOpsManager.OnOpNotedListener { - private static final long LOCATION_TIME_DELAY_MS = 5000; + private static final long NOTED_OP_TIME_DELAY_MS = 5000; private static final String TAG = "AppOpsControllerImpl"; private static final boolean DEBUG = false; private final Context mContext; - protected final AppOpsManager mAppOps; - private final H mBGHandler; + private final AppOpsManager mAppOps; + private H mBGHandler; private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>(); private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>(); + @GuardedBy("mActiveItems") private final List<AppOpItem> mActiveItems = new ArrayList<>(); + @GuardedBy("mNotedItems") + private final List<AppOpItem> mNotedItems = new ArrayList<>(); + + protected static final int[] OPS; + protected static final String[] OPS_STRING = new String[] { + AppOpsManager.OPSTR_CAMERA, + AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, + AppOpsManager.OPSTR_RECORD_AUDIO, + AppOpsManager.OPSTR_COARSE_LOCATION, + AppOpsManager.OPSTR_FINE_LOCATION}; - protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA, - AppOpsManager.OP_SYSTEM_ALERT_WINDOW, - AppOpsManager.OP_RECORD_AUDIO, - AppOpsManager.OP_COARSE_LOCATION, - AppOpsManager.OP_FINE_LOCATION}; + static { + int numOps = OPS_STRING.length; + OPS = new int[numOps]; + for (int i = 0; i < numOps; i++) { + OPS[i] = AppOpsManager.strOpToOp(OPS_STRING[i]); + } + } public AppOpsControllerImpl(Context context, Looper bgLooper) { mContext = context; @@ -70,11 +84,18 @@ public class AppOpsControllerImpl implements AppOpsController, } @VisibleForTesting + protected void setBGHandler(H handler) { + mBGHandler = handler; + } + + @VisibleForTesting protected void setListening(boolean listening) { if (listening) { mAppOps.startWatchingActive(OPS, this); + mAppOps.startWatchingNoted(OPS_STRING, this); } else { mAppOps.stopWatchingActive(this); + mAppOps.stopWatchingNoted(this); } } @@ -124,10 +145,11 @@ public class AppOpsControllerImpl implements AppOpsController, if (mCallbacks.isEmpty()) setListening(false); } - private AppOpItem getAppOpItem(int code, int uid, String packageName) { - final int itemsQ = mActiveItems.size(); + private AppOpItem getAppOpItem(List<AppOpItem> appOpList, int code, int uid, + String packageName) { + final int itemsQ = appOpList.size(); for (int i = 0; i < itemsQ; i++) { - AppOpItem item = mActiveItems.get(i); + AppOpItem item = appOpList.get(i); if (item.getCode() == code && item.getUid() == uid && item.getPackageName().equals(packageName)) { return item; @@ -138,39 +160,59 @@ public class AppOpsControllerImpl implements AppOpsController, private boolean updateActives(int code, int uid, String packageName, boolean active) { synchronized (mActiveItems) { - AppOpItem item = getAppOpItem(code, uid, packageName); + AppOpItem item = getAppOpItem(mActiveItems, code, uid, packageName); if (item == null && active) { item = new AppOpItem(code, uid, packageName, System.currentTimeMillis()); mActiveItems.add(item); - if (code == AppOpsManager.OP_COARSE_LOCATION - || code == AppOpsManager.OP_FINE_LOCATION) { - mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS); - } if (DEBUG) Log.w(TAG, "Added item: " + item.toString()); return true; } else if (item != null && !active) { mActiveItems.remove(item); if (DEBUG) Log.w(TAG, "Removed item: " + item.toString()); return true; - } else if (item != null && active - && (code == AppOpsManager.OP_COARSE_LOCATION - || code == AppOpsManager.OP_FINE_LOCATION)) { - mBGHandler.scheduleRemoval(item, LOCATION_TIME_DELAY_MS); - return true; } return false; } } + private void removeNoted(int code, int uid, String packageName) { + AppOpItem item; + synchronized (mNotedItems) { + item = getAppOpItem(mNotedItems, code, uid, packageName); + if (item == null) return; + mNotedItems.remove(item); + if (DEBUG) Log.w(TAG, "Removed item: " + item.toString()); + } + notifySuscribers(code, uid, packageName, false); + } + + private void addNoted(int code, int uid, String packageName) { + AppOpItem item; + synchronized (mNotedItems) { + item = getAppOpItem(mNotedItems, code, uid, packageName); + if (item == null) { + item = new AppOpItem(code, uid, packageName, System.currentTimeMillis()); + mNotedItems.add(item); + if (DEBUG) Log.w(TAG, "Added item: " + item.toString()); + } + } + mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS); + } + /** * Returns a copy of the list containing all the active AppOps that the controller tracks. * * @return List of active AppOps information */ public List<AppOpItem> getActiveAppOps() { + ArrayList<AppOpItem> active; synchronized (mActiveItems) { - return new ArrayList<>(mActiveItems); + active = new ArrayList<>(mActiveItems); + } + synchronized (mNotedItems) { + active.addAll(mNotedItems); } + return active; } /** @@ -192,19 +234,45 @@ public class AppOpsControllerImpl implements AppOpsController, } } } + synchronized (mNotedItems) { + final int numNotedItems = mNotedItems.size(); + for (int i = 0; i < numNotedItems; i++) { + AppOpItem item = mNotedItems.get(i); + if (UserHandle.getUserId(item.getUid()) == userId) { + list.add(item); + } + } + } return list; } @Override public void onOpActiveChanged(int code, int uid, String packageName, boolean active) { if (updateActives(code, uid, packageName, active)) { + notifySuscribers(code, uid, packageName, active); + } + } + + @Override + public void onOpNoted(String code, int uid, String packageName, int result) { + if (DEBUG) { + Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]); + } + if (result != AppOpsManager.MODE_ALLOWED) return; + int op_code = AppOpsManager.strOpToOp(code); + addNoted(op_code, uid, packageName); + notifySuscribers(op_code, uid, packageName, true); + } + + private void notifySuscribers(int code, int uid, String packageName, boolean active) { + if (mCallbacksByCode.containsKey(code)) { for (Callback cb: mCallbacksByCode.get(code)) { cb.onActiveStateChanged(code, uid, packageName, active); } } } - private final class H extends Handler { + protected final class H extends Handler { H(Looper looper) { super(looper); } @@ -214,8 +282,7 @@ public class AppOpsControllerImpl implements AppOpsController, postDelayed(new Runnable() { @Override public void run() { - onOpActiveChanged(item.getCode(), item.getUid(), - item.getPackageName(), false); + removeNoted(item.getCode(), item.getUid(), item.getPackageName()); } }, item, timeToRemoval); } diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java index 60e39b21680b..30f6e1affe0a 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java @@ -41,6 +41,8 @@ import com.android.systemui.util.leak.LeakDetector; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; @@ -186,6 +188,13 @@ public class FragmentHostManager { mFragments.dispatchDestroy(); } + /** + * Creates a fragment that requires injection. + */ + public <T> T create(Class<T> fragmentCls) { + return (T) mPlugins.instantiate(mContext, fragmentCls.getName(), null); + } + public interface FragmentListener { void onFragmentViewCreated(String tag, Fragment fragment); @@ -294,13 +303,36 @@ public class FragmentHostManager { Fragment instantiate(Context context, String className, Bundle arguments) { Context extensionContext = mExtensionLookup.get(className); if (extensionContext != null) { - Fragment f = Fragment.instantiate(extensionContext, className, arguments); + Fragment f = instantiateWithInjections(extensionContext, className, arguments); if (f instanceof Plugin) { ((Plugin) f).onCreate(mContext, extensionContext); } return f; } - return Fragment.instantiate(context, className, arguments); + return instantiateWithInjections(context, className, arguments); + } + + private Fragment instantiateWithInjections(Context context, String className, + Bundle args) { + Method method = mManager.getInjectionMap().get(className); + if (method != null) { + try { + Fragment f = (Fragment) method.invoke(mManager.getFragmentCreator()); + // Setup the args, taken from Fragment#instantiate. + if (args != null) { + args.setClassLoader(f.getClass().getClassLoader()); + f.setArguments(args); + } + return f; + } catch (IllegalAccessException e) { + throw new Fragment.InstantiationException("Unable to instantiate " + className, + e); + } catch (InvocationTargetException e) { + throw new Fragment.InstantiationException("Unable to instantiate " + className, + e); + } + } + return Fragment.instantiate(context, className, args); } } diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java index bf7d629c5d7a..8dbaf0f681cf 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java @@ -14,6 +14,7 @@ package com.android.systemui.fragments; +import android.app.Fragment; import android.content.res.Configuration; import android.os.Handler; import android.util.ArrayMap; @@ -21,20 +22,56 @@ import android.view.View; import com.android.systemui.ConfigurationChangedReceiver; import com.android.systemui.Dumpable; +import com.android.systemui.SystemUIFactory; +import com.android.systemui.qs.QSFragment; +import com.android.systemui.statusbar.phone.NavigationBarFragment; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.Subcomponent; /** * Holds a map of root views to FragmentHostStates and generates them as needed. * Also dispatches the configuration changes to all current FragmentHostStates. */ +@Singleton public class FragmentService implements ConfigurationChangedReceiver, Dumpable { private static final String TAG = "FragmentService"; private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>(); + private final ArrayMap<String, Method> mInjectionMap = new ArrayMap<>(); private final Handler mHandler = new Handler(); + private final FragmentCreator mFragmentCreator; + + @Inject + public FragmentService(SystemUIFactory.SystemUIRootComponent rootComponent) { + mFragmentCreator = rootComponent.createFragmentCreator(); + initInjectionMap(); + } + + ArrayMap<String, Method> getInjectionMap() { + return mInjectionMap; + } + + FragmentCreator getFragmentCreator() { + return mFragmentCreator; + } + + private void initInjectionMap() { + for (Method method : FragmentCreator.class.getDeclaredMethods()) { + if (Fragment.class.isAssignableFrom(method.getReturnType()) + && (method.getModifiers() & Modifier.PUBLIC) != 0) { + mInjectionMap.put(method.getReturnType().getName(), method); + } + } + } public FragmentHostManager getFragmentHostManager(View view) { View root = view.getRootView(); @@ -74,6 +111,21 @@ public class FragmentService implements ConfigurationChangedReceiver, Dumpable { } } + /** + * The subcomponent of dagger that holds all fragments that need injection. + */ + @Subcomponent + public interface FragmentCreator { + /** + * Inject a NavigationBarFragment. + */ + NavigationBarFragment createNavigationBarFragment(); + /** + * Inject a QSFragment. + */ + QSFragment createQSFragment(); + } + private class FragmentHostState { private final View mView; diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index 268462ea526e..a87d634451cd 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -115,7 +115,7 @@ class PrivacyItemController(val context: Context, val callback: Callback) { private fun updatePrivacyList() { privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) } - .mapNotNull { toPrivacyItem(it) } + .mapNotNull { toPrivacyItem(it) }.distinct() } private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 2acbea45a235..fa775c0429b6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -35,7 +35,6 @@ import android.widget.FrameLayout.LayoutParams; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.R.id; @@ -47,6 +46,8 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; +import javax.inject.Inject; + public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { private static final String TAG = "QS"; private static final boolean DEBUG = false; @@ -74,8 +75,12 @@ public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks { private float mLastQSExpansion = -1; private boolean mQsDisabled; - private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler = - Dependency.get(RemoteInputQuickSettingsDisabler.class); + private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; + + @Inject + public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler) { + mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler; + } @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java index 9d2be39b28fc..4dcacd4bffdc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java @@ -87,6 +87,8 @@ public class TileQueryHelper { if (current != null) { // The setting QS_TILES is not populated immediately upon Factory Reset possibleTiles.addAll(Arrays.asList(current.split(","))); + } else { + current = ""; } String[] stockSplit = stock.split(","); for (String spec : stockSplit) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java index a5c0a2d3b4d4..ee0d1a2d5a51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -28,7 +29,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow */ public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener, ActivatableNotificationView.OnActivatedListener, - NotificationEntryManager.Callback { + NotificationEntryManager.Callback, + NotificationRowBinder.BindRowCallback { /** * Returns true if the presenter is not visible. For example, it may not be necessary to do * animations if this returns true. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java index ae9f323c2ebf..ef7e0fe5791f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java @@ -105,6 +105,7 @@ public class NotificationData { public NotificationChannel channel; public long lastAudiblyAlertedMs; public boolean noisy; + public boolean ambient; public int importance; public StatusBarIconView icon; public StatusBarIconView expandedIcon; @@ -174,6 +175,7 @@ public class NotificationData { channel = ranking.getChannel(); lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis(); importance = ranking.getImportance(); + ambient = ranking.isAmbient(); snoozeCriteria = ranking.getSnoozeCriteria(); userSentiment = ranking.getUserSentiment(); systemGeneratedSmartActions = ranking.getSmartActions() == null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index d2a5864f9f33..535ea624dfc2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -15,9 +15,7 @@ */ package com.android.systemui.statusbar.notification; -import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF; -import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY; import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT; @@ -28,10 +26,7 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; import android.database.ContentObserver; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; @@ -49,25 +44,21 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; import android.util.Log; -import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; -import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.EventLogTags; import com.android.systemui.ForegroundServiceController; -import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationListener; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -79,11 +70,9 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; -import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; -import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; @@ -100,9 +89,12 @@ import java.util.concurrent.TimeUnit; * It also handles tasks such as their inflation and their interaction with other * Notification.*Manager objects. */ -public class NotificationEntryManager implements Dumpable, NotificationInflater.InflationCallback, - ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler, - VisualStabilityManager.Callback, BubbleController.BubbleDismissListener { +public class NotificationEntryManager implements + Dumpable, + NotificationInflater.InflationCallback, + NotificationUpdateHandler, + VisualStabilityManager.Callback, + BubbleController.BubbleDismissListener { private static final String TAG = "NotificationEntryMgr"; protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean ENABLE_HEADS_UP = true; @@ -110,7 +102,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30); - private final NotificationMessagingUtil mMessagingUtil; protected final Context mContext; protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>(); @@ -123,7 +114,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. Dependency.get(DeviceProvisionedController.class); private final VisualStabilityManager mVisualStabilityManager = Dependency.get(VisualStabilityManager.class); - private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); private final ForegroundServiceController mForegroundServiceController = Dependency.get(ForegroundServiceController.class); private final AmbientPulseManager mAmbientPulseManager = @@ -135,6 +125,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. private NotificationMediaManager mMediaManager; private NotificationListener mNotificationListener; private ShadeController mShadeController; + private NotificationRowBinder mNotificationRowBinder; private final Handler mDeferredNotificationViewUpdateHandler; private Runnable mUpdateNotificationViewsCallback; @@ -154,10 +145,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. @VisibleForTesting final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders = new ArrayList<>(); - private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener; private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener; @Nullable private AlertTransferListener mAlertTransferListener; - @Nullable private NotificationClicker mNotificationClicker; private final DeviceProvisionedController.DeviceProvisionedListener mDeviceProvisionedListener = @@ -199,7 +188,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. ServiceManager.getService(Context.STATUS_BAR_SERVICE)); mDreamManager = IDreamManager.Stub.asInterface( ServiceManager.checkService(DreamService.DREAM_SERVICE)); - mMessagingUtil = new NotificationMessagingUtil(context); mBubbleController.setDismissListener(this /* bubbleEventListener */); mNotificationData = new NotificationData(); mDeferredNotificationViewUpdateHandler = new Handler(); @@ -209,10 +197,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mAlertTransferListener = listener; } - public void setNotificationClicker(NotificationClicker clicker) { - mNotificationClicker = clicker; - } - /** * Our dependencies can have cyclic references, so some need to be lazy */ @@ -244,6 +228,13 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. return mShadeController; } + private NotificationRowBinder getRowBinder() { + if (mNotificationRowBinder == null) { + mNotificationRowBinder = Dependency.get(NotificationRowBinder.class); + } + return mNotificationRowBinder; + } + public void setUpWithPresenter(NotificationPresenter presenter, NotificationListContainer listContainer, Callback callback, HeadsUpManager headsUpManager) { @@ -296,7 +287,18 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); mHeadsUpObserver.onChange(true); // set up - mOnAppOpsClickListener = mGutsManager::openGuts; + + getRowBinder().setInterruptionStateProvider(new InterruptionStateProvider() { + @Override + public boolean shouldHeadsUp(NotificationData.Entry entry) { + return NotificationEntryManager.this.shouldHeadsUp(entry); + } + + @Override + public boolean shouldPulse(NotificationData.Entry entry) { + return NotificationEntryManager.this.shouldPulse(entry); + } + }); } public NotificationData getNotificationData() { @@ -312,18 +314,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() { - return mGutsManager::openGuts; - } - - @Override - public void logNotificationExpansion(String key, boolean userAction, boolean expanded) { - mUiOffloadThread.submit(() -> { - try { - mBarService.onNotificationExpansionChanged(key, userAction, expanded); - } catch (RemoteException e) { - // Ignore. - } - }); + return getRowBinder().getNotificationLongClicker(); } @Override @@ -339,64 +330,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. return mNotificationData.shouldSuppressFullScreenIntent(entry); } - private void inflateViews(NotificationData.Entry entry, ViewGroup parent) { - PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext, - entry.notification.getUser().getIdentifier()); - - final StatusBarNotification sbn = entry.notification; - if (entry.rowExists()) { - entry.reset(); - updateNotification(entry, pmUser, sbn, entry.getRow()); - } else { - new RowInflaterTask().inflate(mContext, parent, entry, - row -> { - bindRow(entry, pmUser, sbn, row); - updateNotification(entry, pmUser, sbn, row); - }); - } - } - - private void bindRow(NotificationData.Entry entry, PackageManager pmUser, - StatusBarNotification sbn, ExpandableNotificationRow row) { - row.setExpansionLogger(this, entry.notification.getKey()); - row.setGroupManager(mGroupManager); - row.setHeadsUpManager(mHeadsUpManager); - row.setOnExpandClickListener(mPresenter); - row.setInflationCallback(this); - row.setLongPressListener(getNotificationLongClicker()); - mListContainer.bindRow(row); - getRemoteInputManager().bindRow(row); - - // Get the app name. - // Note that Notification.Builder#bindHeaderAppName has similar logic - // but since this field is used in the guts, it must be accurate. - // Therefore we will only show the application label, or, failing that, the - // package name. No substitutions. - final String pkg = sbn.getPackageName(); - String appname = pkg; - try { - final ApplicationInfo info = pmUser.getApplicationInfo(pkg, - PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_DISABLED_COMPONENTS); - if (info != null) { - appname = String.valueOf(pmUser.getApplicationLabel(info)); - } - } catch (PackageManager.NameNotFoundException e) { - // Do nothing - } - row.setAppName(appname); - row.setOnDismissRunnable(() -> - performRemoveNotification(row.getStatusBarNotification())); - row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - if (ENABLE_REMOTE_INPUT) { - row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); - } - - row.setAppOpsOnClickListener(mOnAppOpsClickListener); - - mCallback.onBindRow(entry, pmUser, sbn, row); - } - public void performRemoveNotification(StatusBarNotification n) { final int rank = mNotificationData.getRank(n.getKey()); final int count = mNotificationData.getActiveNotifications().size(); @@ -687,56 +620,11 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } } - //TODO: This method associates a row with an entry, but eventually needs to not do that - protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser, - StatusBarNotification sbn, ExpandableNotificationRow row) { - boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey()); - boolean isUpdate = mNotificationData.get(entry.key) != null; - boolean wasLowPriority = row.isLowPriority(); - row.setIsLowPriority(isLowPriority); - row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority)); - // bind the click event to the content area - checkNotNull(mNotificationClicker).register(row, sbn); - - // Extract target SDK version. - try { - ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0); - entry.targetSdk = info.targetSdkVersion; - } catch (PackageManager.NameNotFoundException ex) { - Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex); - } - row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD - && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); - entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); - entry.autoRedacted = entry.notification.getNotification().publicVersion == null; - - entry.setRow(row); - row.setOnActivatedListener(mPresenter); - - boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn, - mNotificationData.getImportance(sbn.getKey())); - boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight - && !mPresenter.isPresenterFullyCollapsed(); - row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); - row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp); - row.setEntry(entry); - - if (shouldHeadsUp(entry)) { - row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */); - } - if (shouldPulse(entry)) { - row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */); - } - row.setNeedsRedaction( - Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry)); - row.inflateViews(); - } - - private NotificationData.Entry createNotificationViews( + private NotificationData.Entry createNotificationEntry( StatusBarNotification sbn, NotificationListenerService.Ranking ranking) throws InflationException { if (DEBUG) { - Log.d(TAG, "createNotificationViews(notification=" + sbn + " " + ranking); + Log.d(TAG, "createNotificationEntry(notification=" + sbn + " " + ranking); } NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking); @@ -747,7 +635,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. Dependency.get(LeakDetector.class).trackInstance(entry); entry.createIcons(mContext, sbn); // Construct the expanded view. - inflateViews(entry, mListContainer.getViewParentForNotification(entry)); + getRowBinder().inflateViews(entry, () -> performRemoveNotification(sbn), + mNotificationData.get(entry.key) != null); return entry; } @@ -761,7 +650,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mNotificationData.updateRanking(rankingMap); NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking(); rankingMap.getRanking(key, ranking); - NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking); + NotificationData.Entry shadeEntry = createNotificationEntry(notification, ranking); boolean isHeadsUped = shouldHeadsUp(shadeEntry); if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) { if (shouldSuppressFullScreenIntent(shadeEntry)) { @@ -870,7 +759,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mGroupManager.onEntryUpdated(entry, oldNotification); entry.updateIcons(mContext, notification); - inflateViews(entry, mListContainer.getViewParentForNotification(entry)); + getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification), + mNotificationData.get(entry.key) != null); mForegroundServiceController.updateNotification(notification, mNotificationData.getImportance(key)); @@ -938,26 +828,12 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. // By comparing the old and new UI adjustments, reinflate the view accordingly. for (NotificationData.Entry entry : entries) { - NotificationUiAdjustment newAdjustment = - NotificationUiAdjustment.extractFromNotificationEntry(entry); - - if (NotificationUiAdjustment.needReinflate( - oldAdjustments.get(entry.key), newAdjustment)) { - if (entry.rowExists()) { - entry.reset(); - PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext, - entry.notification.getUser().getIdentifier()); - updateNotification(entry, pmUser, entry.notification, entry.getRow()); - } else { - // Once the RowInflaterTask is done, it will pick up the updated entry, so - // no-op here. - } - } else if (oldImportances.containsKey(entry.key) - && entry.importance != oldImportances.get(entry.key)) { - if (entry.rowExists()) { - entry.getRow().onNotificationRankingUpdated(); - } - } + mNotificationRowBinder.onNotificationRankingUpdated( + entry, + oldImportances.get(entry.key), + oldAdjustments.get(entry.key), + NotificationUiAdjustment.extractFromNotificationEntry(entry), + mNotificationData.get(entry.key) != null); } updateNotifications(); @@ -1202,6 +1078,22 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } /** + * Interface for retrieving heads-up and pulsing state for an entry. + */ + public interface InterruptionStateProvider { + /** + * Whether the provided entry should be marked as heads-up when inflated. + */ + boolean shouldHeadsUp(NotificationData.Entry entry); + + /** + * Whether the provided entry should be marked as pulsing (displayed in ambient) when + * inflated. + */ + boolean shouldPulse(NotificationData.Entry entry); + } + + /** * Callback for NotificationEntryManager. */ public interface Callback { @@ -1229,17 +1121,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. void onNotificationRemoved(String key, StatusBarNotification old); /** - * Called when a new notification and row is created. - * - * @param entry entry for the notification - * @param pmUser package manager for user - * @param sbn notification - * @param row row for the notification - */ - void onBindRow(NotificationData.Entry entry, PackageManager pmUser, - StatusBarNotification sbn, ExpandableNotificationRow row); - - /** * Removes a notification immediately. * * @param statusBarNotification notification that is being removed diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java new file mode 100644 index 000000000000..824bd813d563 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java @@ -0,0 +1,294 @@ +/* + * 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.systemui.statusbar.notification; + +import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; +import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT; +import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.service.notification.StatusBarNotification; +import android.util.Log; +import android.view.ViewGroup; + +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.util.NotificationMessagingUtil; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.UiOffloadThread; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationUiAdjustment; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.row.NotificationInflater; +import com.android.systemui.statusbar.notification.row.RowInflaterTask; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.policy.HeadsUpManager; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** Handles inflating and updating views for notifications. */ +@Singleton +public class NotificationRowBinder { + + private static final String TAG = "NotificationViewManager"; + + private final NotificationGroupManager mGroupManager = + Dependency.get(NotificationGroupManager.class); + private final NotificationGutsManager mGutsManager = + Dependency.get(NotificationGutsManager.class); + private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class); + + private final Context mContext; + private final IStatusBarService mBarService; + private final NotificationMessagingUtil mMessagingUtil; + private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger = + this::logNotificationExpansion; + + private NotificationRemoteInputManager mRemoteInputManager; + private NotificationPresenter mPresenter; + private NotificationListContainer mListContainer; + private HeadsUpManager mHeadsUpManager; + private NotificationInflater.InflationCallback mInflationCallback; + private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener; + private BindRowCallback mBindRowCallback; + private NotificationClicker mNotificationClicker; + private NotificationEntryManager.InterruptionStateProvider mInterruptionStateProvider; + + @Inject + public NotificationRowBinder(Context context) { + mContext = context; + mMessagingUtil = new NotificationMessagingUtil(context); + mBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + } + + private NotificationRemoteInputManager getRemoteInputManager() { + if (mRemoteInputManager == null) { + mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); + } + return mRemoteInputManager; + } + + /** + * Sets up late-bound dependencies for this component. + */ + public void setUpWithPresenter(NotificationPresenter presenter, + NotificationListContainer listContainer, + HeadsUpManager headsUpManager, + NotificationInflater.InflationCallback inflationCallback, + BindRowCallback bindRowCallback) { + mPresenter = presenter; + mListContainer = listContainer; + mHeadsUpManager = headsUpManager; + mInflationCallback = inflationCallback; + mBindRowCallback = bindRowCallback; + mOnAppOpsClickListener = mGutsManager::openGuts; + } + + public void setNotificationClicker(NotificationClicker clicker) { + mNotificationClicker = clicker; + } + + public void setInterruptionStateProvider( + NotificationEntryManager.InterruptionStateProvider interruptionStateProvider) { + mInterruptionStateProvider = interruptionStateProvider; + } + + /** + * Inflates the views for the given entry (possibly asynchronously). + */ + public void inflateViews(NotificationData.Entry entry, Runnable onDismissRunnable, + boolean isUpdate) { + ViewGroup parent = mListContainer.getViewParentForNotification(entry); + PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext, + entry.notification.getUser().getIdentifier()); + + final StatusBarNotification sbn = entry.notification; + if (entry.rowExists()) { + entry.reset(); + updateNotification(entry, pmUser, sbn, entry.getRow(), isUpdate); + } else { + new RowInflaterTask().inflate(mContext, parent, entry, + row -> { + bindRow(entry, pmUser, sbn, row, onDismissRunnable); + updateNotification(entry, pmUser, sbn, row, isUpdate); + }); + } + } + + private void bindRow(NotificationData.Entry entry, PackageManager pmUser, + StatusBarNotification sbn, ExpandableNotificationRow row, + Runnable onDismissRunnable) { + row.setExpansionLogger(mExpansionLogger, entry.notification.getKey()); + row.setGroupManager(mGroupManager); + row.setHeadsUpManager(mHeadsUpManager); + row.setOnExpandClickListener(mPresenter); + row.setInflationCallback(mInflationCallback); + row.setLongPressListener(getNotificationLongClicker()); + mListContainer.bindRow(row); + getRemoteInputManager().bindRow(row); + + // Get the app name. + // Note that Notification.Builder#bindHeaderAppName has similar logic + // but since this field is used in the guts, it must be accurate. + // Therefore we will only show the application label, or, failing that, the + // package name. No substitutions. + final String pkg = sbn.getPackageName(); + String appname = pkg; + try { + final ApplicationInfo info = pmUser.getApplicationInfo(pkg, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS); + if (info != null) { + appname = String.valueOf(pmUser.getApplicationLabel(info)); + } + } catch (PackageManager.NameNotFoundException e) { + // Do nothing + } + row.setAppName(appname); + row.setOnDismissRunnable(onDismissRunnable); + row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + if (ENABLE_REMOTE_INPUT) { + row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); + } + + row.setAppOpsOnClickListener(mOnAppOpsClickListener); + + mBindRowCallback.onBindRow(entry, pmUser, sbn, row); + } + + /** + * Updates the views bound to an entry when the entry's ranking changes, either in-place or by + * reinflating them. + */ + public void onNotificationRankingUpdated( + NotificationData.Entry entry, + @Nullable Integer oldImportance, + NotificationUiAdjustment oldAdjustment, + NotificationUiAdjustment newAdjustment, + boolean isUpdate) { + if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) { + if (entry.rowExists()) { + entry.reset(); + PackageManager pmUser = StatusBar.getPackageManagerForUser( + mContext, + entry.notification.getUser().getIdentifier()); + updateNotification(entry, pmUser, entry.notification, entry.getRow(), isUpdate); + } else { + // Once the RowInflaterTask is done, it will pick up the updated entry, so + // no-op here. + } + } else { + if (oldImportance != null && entry.importance != oldImportance) { + if (entry.rowExists()) { + entry.getRow().onNotificationRankingUpdated(); + } + } + } + } + + //TODO: This method associates a row with an entry, but eventually needs to not do that + private void updateNotification( + NotificationData.Entry entry, + PackageManager pmUser, + StatusBarNotification sbn, + ExpandableNotificationRow row, + boolean isUpdate) { + boolean isLowPriority = entry.ambient; + boolean wasLowPriority = row.isLowPriority(); + row.setIsLowPriority(isLowPriority); + row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority)); + // bind the click event to the content area + checkNotNull(mNotificationClicker).register(row, sbn); + + // Extract target SDK version. + try { + ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0); + entry.targetSdk = info.targetSdkVersion; + } catch (PackageManager.NameNotFoundException ex) { + Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex); + } + row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD + && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); + + // TODO: should updates to the entry be happening somewhere else? + entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP); + entry.autoRedacted = entry.notification.getNotification().publicVersion == null; + + entry.setRow(row); + row.setOnActivatedListener(mPresenter); + + boolean useIncreasedCollapsedHeight = + mMessagingUtil.isImportantMessaging(sbn, entry.importance); + boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight + && !mPresenter.isPresenterFullyCollapsed(); + row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); + row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp); + row.setEntry(entry); + + if (mInterruptionStateProvider.shouldHeadsUp(entry)) { + row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */); + } + if (mInterruptionStateProvider.shouldPulse(entry)) { + row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */); + } + row.setNeedsRedaction( + Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry)); + row.inflateViews(); + } + + ExpandableNotificationRow.LongPressListener getNotificationLongClicker() { + return mGutsManager::openGuts; + } + + private void logNotificationExpansion(String key, boolean userAction, boolean expanded) { + mUiOffloadThread.submit(() -> { + try { + mBarService.onNotificationExpansionChanged(key, userAction, expanded); + } catch (RemoteException e) { + // Ignore. + } + }); + } + + /** Callback for when a row is bound to an entry. */ + public interface BindRowCallback { + /** + * Called when a new notification and row is created. + * + * @param entry entry for the notification + * @param pmUser package manager for user + * @param sbn notification + * @param row row for the notification + */ + void onBindRow(NotificationData.Entry entry, PackageManager pmUser, + StatusBarNotification sbn, ExpandableNotificationRow row); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 96b753679796..5ba59b507fec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -466,6 +466,14 @@ public class KeyguardStatusBarView extends RelativeLayout .onDensityOrFontScaleChanged(); } + @Override + public void onOverlayChanged() { + mCarrierLabel.setTextAppearance( + Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall)); + onThemeChanged(); + mBatteryView.updatePercentView(); + } + private void updateIconsAndTextColors() { @ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 55655d5b6240..2daff2cb18bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -72,7 +72,6 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.LatencyTracker; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.assist.AssistManager; @@ -97,6 +96,8 @@ import java.util.List; import java.util.Locale; import java.util.function.Consumer; +import javax.inject.Inject; + /** * Fragment containing the NavigationBarFragment. Contains logic for what happens * on clicks and view states of the nav bar. @@ -111,11 +112,12 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback /** Allow some time inbetween the long press for back and recents. */ private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; - private final DeviceProvisionedController mDeviceProvisionedController = - Dependency.get(DeviceProvisionedController.class); + private final AccessibilityManagerWrapper mAccessibilityManagerWrapper; + protected final AssistManager mAssistManager; + private final MetricsLogger mMetricsLogger; + private final DeviceProvisionedController mDeviceProvisionedController; protected NavigationBarView mNavigationBarView = null; - protected AssistManager mAssistManager; private int mNavigationBarWindowState = WINDOW_STATE_SHOWING; @@ -124,7 +126,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private AccessibilityManager mAccessibilityManager; private MagnificationContentObserver mMagnificationObserver; private ContentResolver mContentResolver; - private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private int mDisabledFlags1; private int mDisabledFlags2; @@ -193,6 +194,17 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } }; + @Inject + public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper, + DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, + AssistManager assistManager, OverviewProxyService overviewProxyService) { + mAccessibilityManagerWrapper = accessibilityManagerWrapper; + mDeviceProvisionedController = deviceProvisionedController; + mMetricsLogger = metricsLogger; + mAssistManager = assistManager; + mOverviewProxyService = overviewProxyService; + } + // ----- Fragment Lifecycle Callbacks ----- @Override @@ -205,8 +217,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class); mWindowManager = getContext().getSystemService(WindowManager.class); mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class); - Dependency.get(AccessibilityManagerWrapper.class).addCallback( - mAccessibilityListener); mContentResolver = getContext().getContentResolver(); mMagnificationObserver = new MagnificationContentObserver( getContext().getMainThreadHandler()); @@ -218,15 +228,13 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0); mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0); } - mAssistManager = Dependency.get(AssistManager.class); - mOverviewProxyService = Dependency.get(OverviewProxyService.class); + mAccessibilityManagerWrapper.addCallback(mAccessibilityListener); } @Override public void onDestroy() { super.onDestroy(); - Dependency.get(AccessibilityManagerWrapper.class).removeCallback( - mAccessibilityListener); + mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener); mContentResolver.unregisterContentObserver(mMagnificationObserver); } @@ -892,7 +900,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView); if (navigationBarView == null) return null; - final NavigationBarFragment fragment = new NavigationBarFragment(); + final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView) + .create(NavigationBarFragment.class); navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 1e709125c7ba..1b43f8fad9bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -191,6 +191,7 @@ import com.android.systemui.statusbar.notification.NotificationClicker; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -376,6 +377,7 @@ public class StatusBar extends SystemUI implements DemoMode, private NotificationGutsManager mGutsManager; protected NotificationLogger mNotificationLogger; protected NotificationEntryManager mEntryManager; + private NotificationRowBinder mNotificationRowBinder; protected NotificationViewHierarchyManager mViewHierarchyManager; protected ForegroundServiceController mForegroundServiceController; protected AppOpsController mAppOpsController; @@ -620,6 +622,7 @@ public class StatusBar extends SystemUI implements DemoMode, mGutsManager = Dependency.get(NotificationGutsManager.class); mMediaManager = Dependency.get(NotificationMediaManager.class); mEntryManager = Dependency.get(NotificationEntryManager.class); + mNotificationRowBinder = Dependency.get(NotificationRowBinder.class); mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); mForegroundServiceController = Dependency.get(ForegroundServiceController.class); mAppOpsController = Dependency.get(AppOpsController.class); @@ -1014,7 +1017,7 @@ public class StatusBar extends SystemUI implements DemoMode, } protected QS createDefaultQSFragment() { - return new QSFragment(); + return FragmentHostManager.get(mStatusBarWindow).create(QSFragment.class); } private void setUpPresenter() { @@ -1035,10 +1038,10 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationActivityStarter = new StatusBarNotificationActivityStarter( mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); + mNotificationRowBinder.setNotificationClicker(new NotificationClicker( + this, Dependency.get(BubbleController.class), mNotificationActivityStarter)); mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager); - mEntryManager.setNotificationClicker(new NotificationClicker( - this, Dependency.get(BubbleController.class), mNotificationActivityStarter)); } /** @@ -1164,6 +1167,10 @@ public class StatusBar extends SystemUI implements DemoMode, if (mBrightnessMirrorController != null) { mBrightnessMirrorController.onOverlayChanged(); } + // We need the new R.id.keyguard_indication_area before recreating + // mKeyguardIndicationController + mNotificationPanel.onThemeChanged(); + onThemeChanged(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 261f117b58b4..c8c9ebe59677 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -58,6 +58,7 @@ import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -88,6 +89,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, Dependency.get(StatusBarStateController.class); private final NotificationEntryManager mEntryManager = Dependency.get(NotificationEntryManager.class); + private final NotificationRowBinder mNotificationRowBinder = + Dependency.get(NotificationRowBinder.class); private final NotificationMediaManager mMediaManager = Dependency.get(NotificationMediaManager.class); protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class); @@ -168,6 +171,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, Dependency.get(InitController.class).addPostInitTask(() -> { mViewHierarchyManager.setUpWithPresenter(this, notifListContainer); mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager); + mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager, + mEntryManager, this); mLockscreenUserManager.setUpWithPresenter(this); mMediaManager.setUpWithPresenter(this); Dependency.get(NotificationGutsManager.class).setUpWithPresenter(this, diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index b699163c40e8..bb445483c966 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -17,8 +17,10 @@ package com.android.systemui.appops; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -32,7 +34,6 @@ import android.testing.TestableLooper; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.NotificationPresenter; import org.junit.Before; import org.junit.Test; @@ -48,9 +49,12 @@ public class AppOpsControllerTest extends SysuiTestCase { private static final int TEST_UID = 0; private static final int TEST_UID_OTHER = 500000; - @Mock private NotificationPresenter mPresenter; - @Mock private AppOpsManager mAppOpsManager; - @Mock private AppOpsController.Callback mCallback; + @Mock + private AppOpsManager mAppOpsManager; + @Mock + private AppOpsController.Callback mCallback; + @Mock + private AppOpsControllerImpl.H mMockHandler; private AppOpsControllerImpl mController; @@ -77,9 +81,13 @@ public class AppOpsControllerTest extends SysuiTestCase { @Test public void addCallback_includedCode() { - mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); + mController.addCallback( + new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION}, + mCallback); mController.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); + mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, + AppOpsManager.MODE_ALLOWED); verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); } @@ -106,7 +114,7 @@ public class AppOpsControllerTest extends SysuiTestCase { @Test public void addCallback_notSameCode() { mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback); - mController.removeCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); + mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback); mController.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, @@ -128,17 +136,30 @@ public class AppOpsControllerTest extends SysuiTestCase { TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpActiveChanged(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true); - assertEquals(2, mController.getActiveAppOps().size()); + mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, + TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED); + assertEquals(3, mController.getActiveAppOps().size()); } - @Test public void getActiveItemsForUser() { + @Test + public void getActiveItemsForUser() { mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpActiveChanged(AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); - assertEquals(1, + mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, + TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED); + assertEquals(2, mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size()); assertEquals(1, - mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size()); + mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER)).size()); + } + + @Test + public void opNotedScheduledForRemoval() { + mController.setBGHandler(mMockHandler); + mController.onOpNoted(AppOpsManager.OPSTR_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, + AppOpsManager.MODE_ALLOWED); + verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt index 24bcca50d34a..563599b32d60 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.privacy import android.app.ActivityManager import android.app.AppOpsManager import android.content.Intent +import android.content.pm.UserInfo import android.os.Handler import android.os.UserHandle import android.os.UserManager @@ -30,13 +31,16 @@ import com.android.systemui.Dependency import com.android.systemui.SysuiTestCase import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController +import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyList import org.mockito.ArgumentMatchers.eq +import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.doReturn @@ -52,8 +56,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { companion object { val CURRENT_USER_ID = ActivityManager.getCurrentUser() - val OTHER_USER = UserHandle(CURRENT_USER_ID + 1) const val TAG = "PrivacyItemControllerTest" + fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() } @Mock @@ -62,6 +66,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { private lateinit var callback: PrivacyItemController.Callback @Mock private lateinit var userManager: UserManager + @Captor + private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>> private lateinit var testableLooper: TestableLooper private lateinit var privacyItemController: PrivacyItemController @@ -76,8 +82,11 @@ class PrivacyItemControllerTest : SysuiTestCase() { mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper)) mContext.addMockSystemService(UserManager::class.java, userManager) - doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0))) - .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + doReturn(listOf(object : UserInfo() { + init { + id = CURRENT_USER_ID + } + })).`when`(userManager).getProfiles(anyInt()) privacyItemController = PrivacyItemController(mContext, callback) } @@ -100,6 +109,18 @@ class PrivacyItemControllerTest : SysuiTestCase() { } @Test + fun testDistinctItems() { + doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, CURRENT_USER_ID, "", 0), + AppOpItem(AppOpsManager.OP_CAMERA, CURRENT_USER_ID, "", 1))) + .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + + privacyItemController.setListening(true) + testableLooper.processAllMessages() + verify(callback).privacyChanged(capture(argCaptor)) + assertEquals(1, argCaptor.value.size) + } + + @Test fun testRegisterReceiver_allUsers() { val spiedContext = spy(mContext) val itemController = PrivacyItemController(spiedContext, callback) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index bc7d9836d6f8..39afbac15c65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -18,7 +18,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import android.app.Fragment; import android.content.Context; +import android.os.Bundle; import android.os.Looper; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; @@ -35,6 +37,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.statusbar.policy.UserSwitcherController; import org.junit.Before; @@ -52,6 +55,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { public QSFragmentTest() { super(QSFragment.class); + injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); } @Before @@ -70,7 +74,6 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { mDependency.injectTestDependency(Dependency.BG_LOOPER, TestableLooper.get(this).getLooper()); mDependency.injectMockDependency(UserSwitcherController.class); - injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); } @Test @@ -116,4 +119,9 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { assertTrue(qs.isListening()); assertTrue(qs.isExpanded()); } + + @Override + protected Fragment instantiate(Context context, String className, Bundle arguments) { + return new QSFragment(new RemoteInputQuickSettingsDisabler(context)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java index 26fa20de4e86..c3a3e6339daa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java @@ -218,4 +218,12 @@ public class TileQueryHelperTest extends SysuiTestCase { } assertFalse(specs.contains("other")); } + + @Test + public void testQueryTiles_nullSetting() { + Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null); + mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock, + STOCK_TILES); + mTileQueryHelper.queryTiles(mQSTileHost); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 7f0e435eb7da..8fe91cd5e82a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -104,6 +104,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Mock private ExpandableNotificationRow mRow; @Mock private NotificationListContainer mListContainer; @Mock private NotificationEntryManager.Callback mCallback; + @Mock + private NotificationRowBinder.BindRowCallback mBindCallback; @Mock private HeadsUpManager mHeadsUpManager; @Mock private NotificationListenerService.RankingMap mRankingMap; @Mock private RemoteInputController mRemoteInputController; @@ -231,7 +233,11 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mEntryManager = new TestableNotificationEntryManager(mContext, mBarService); Dependency.get(InitController.class).executePostInitTasks(); mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager); - mEntryManager.setNotificationClicker(mock(NotificationClicker.class)); + + NotificationRowBinder notificationRowBinder = Dependency.get(NotificationRowBinder.class); + notificationRowBinder.setUpWithPresenter( + mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback); + notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class)); setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL); } @@ -244,7 +250,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { doAnswer(invocation -> { mCountDownLatch.countDown(); return null; - }).when(mCallback).onBindRow(any(), any(), any(), any()); + }).when(mBindCallback).onBindRow(any(), any(), any(), any()); // Post on main thread, otherwise we will be stuck waiting here for the inflation finished // callback forever, since it won't execute until the tests ends. @@ -261,7 +267,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { // Row inflation: ArgumentCaptor<NotificationData.Entry> entryCaptor = ArgumentCaptor.forClass( NotificationData.Entry.class); - verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any()); + verify(mBindCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any()); NotificationData.Entry entry = entryCaptor.getValue(); verify(mRemoteInputManager).bindRow(entry.getRow()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java index 9e2db913aba5..728723b4094c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java @@ -14,10 +14,13 @@ package com.android.systemui.statusbar.phone; +import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.app.Fragment; import android.content.Context; +import android.os.Bundle; import android.os.Looper; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; @@ -27,13 +30,17 @@ import android.view.Display; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.Dependency; import com.android.systemui.SysuiBaseFragmentTest; +import com.android.systemui.assist.AssistManager; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import org.junit.Before; import org.junit.Test; @@ -44,6 +51,23 @@ import org.junit.runner.RunWith; @SmallTest public class NavigationBarFragmentTest extends SysuiBaseFragmentTest { + private OverviewProxyService mOverviewProxyService = + mDependency.injectMockDependency(OverviewProxyService.class); + private AccessibilityManagerWrapper mAccessibilityWrapper = + new AccessibilityManagerWrapper(mContext) { + Tracker mTracker = mLeakCheck.getTracker("accessibility_manager"); + + @Override + public void addCallback(AccessibilityServicesStateChangeListener listener) { + mTracker.getLeakInfo(listener).addAllocation(new Throwable()); + } + + @Override + public void removeCallback(AccessibilityServicesStateChangeListener listener) { + mTracker.getLeakInfo(listener).clearAllocations(); + } + }; + public NavigationBarFragmentTest() { super(NavigationBarFragment.class); } @@ -54,32 +78,19 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest { @Before public void setup() { - mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper()); mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class)); mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class)); mSysuiContext.putComponent(Recents.class, mock(Recents.class)); mSysuiContext.putComponent(Divider.class, mock(Divider.class)); injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); - mDependency.injectMockDependency(OverviewProxyService.class); WindowManager windowManager = mock(WindowManager.class); Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay(); when(windowManager.getDefaultDisplay()).thenReturn( defaultDisplay); mContext.addMockSystemService(Context.WINDOW_SERVICE, windowManager); - Tracker tracker = mLeakCheck.getTracker("accessibility_manager"); - AccessibilityManagerWrapper wrapper = new AccessibilityManagerWrapper(mContext) { - @Override - public void addCallback(AccessibilityServicesStateChangeListener listener) { - tracker.getLeakInfo(listener).addAllocation(new Throwable()); - } - - @Override - public void removeCallback(AccessibilityServicesStateChangeListener listener) { - tracker.getLeakInfo(listener).clearAllocations(); - } - }; - mDependency.injectTestDependency(AccessibilityManagerWrapper.class, wrapper); + mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper()); + mDependency.injectTestDependency(AccessibilityManagerWrapper.class, mAccessibilityWrapper); } @Test @@ -91,4 +102,15 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest { navigationBarFragment.onHomeLongClick(navigationBarFragment.getView()); } + @Override + protected Fragment instantiate(Context context, String className, Bundle arguments) { + DeviceProvisionedController deviceProvisionedController = + new DeviceProvisionedControllerImpl(context); + assertNotNull(mAccessibilityWrapper); + return new NavigationBarFragment(mAccessibilityWrapper, + deviceProvisionedController, + new MetricsLogger(), + new AssistManager(deviceProvisionedController, mContext), + mOverviewProxyService); + } } diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index fc7265db1be2..681a9941faea 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -38,8 +38,8 @@ import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; +import com.android.internal.infra.AbstractSinglePendingRequestRemoteService; import com.android.internal.os.IResultReceiver; -import com.android.server.infra.AbstractSinglePendingRequestRemoteService; final class RemoteAugmentedAutofillService extends AbstractSinglePendingRequestRemoteService<RemoteAugmentedAutofillService, diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java index 417ea9c84393..e5529aff7c62 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java @@ -39,7 +39,7 @@ import android.service.autofill.SaveRequest; import android.text.format.DateUtils; import android.util.Slog; -import com.android.server.infra.AbstractSinglePendingRequestRemoteService; +import com.android.internal.infra.AbstractSinglePendingRequestRemoteService; final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<RemoteFillService, IAutoFillService> { diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index fd2043769dd1..a9f4e46395f3 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -117,7 +117,7 @@ public class BackupManagerService { * @param userId User id on which the backup operation is being requested. * @param message A message to include in the exception if it is thrown. */ - private void enforceCallingPermissionOnUserId(int userId, String message) { + private void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) { if (Binder.getCallingUserHandle().getIdentifier() != userId) { mContext.enforceCallingOrSelfPermission( Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); @@ -170,9 +170,14 @@ public class BackupManagerService { * @param userId The id of the user to retrieve its instance of {@link * UserBackupManagerService}. * @param caller A {@link String} identifying the caller for logging purposes. + * @throws SecurityException if {@code userId} is different from the calling user id and the + * caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission. */ @Nullable - private UserBackupManagerService getServiceForUser(@UserIdInt int userId, String caller) { + @VisibleForTesting + UserBackupManagerService getServiceForUserIfCallerHasPermission( + @UserIdInt int userId, String caller) { + enforceCallingPermissionOnUserId(userId, caller); UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId); if (userBackupManagerService == null) { Slog.w(TAG, "Called " + caller + " for unknown user: " + userId); @@ -196,9 +201,9 @@ public class BackupManagerService { * backup for their app {@code packageName}. Only used for apps participating in key-value * backup. */ - public void dataChanged(String packageName) { + public void dataChanged(@UserIdInt int userId, String packageName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "dataChanged()"); + getServiceForUserIfCallerHasPermission(userId, "dataChanged()"); if (userBackupManagerService != null) { userBackupManagerService.dataChanged(packageName); @@ -209,9 +214,9 @@ public class BackupManagerService { * Callback: a requested backup agent has been instantiated. This should only be called from the * {@link ActivityManager}. */ - public void agentConnected(String packageName, IBinder agentBinder) { + public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "agentConnected()"); + getServiceForUserIfCallerHasPermission(userId, "agentConnected()"); if (userBackupManagerService != null) { userBackupManagerService.agentConnected(packageName, agentBinder); @@ -222,9 +227,9 @@ public class BackupManagerService { * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be * called from the {@link ActivityManager}. */ - public void agentDisconnected(String packageName) { + public void agentDisconnected(@UserIdInt int userId, String packageName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "agentDisconnected()"); + getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()"); if (userBackupManagerService != null) { userBackupManagerService.agentDisconnected(packageName); @@ -235,9 +240,9 @@ public class BackupManagerService { * Used by a currently-active backup agent to notify the service that it has completed its given * outstanding asynchronous backup/restore operation. */ - public void opComplete(int token, long result) { + public void opComplete(@UserIdInt int userId, int token, long result) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "opComplete()"); + getServiceForUserIfCallerHasPermission(userId, "opComplete()"); if (userBackupManagerService != null) { userBackupManagerService.opComplete(token, result); @@ -249,9 +254,10 @@ public class BackupManagerService { // --------------------------------------------- /** Run an initialize operation for the given transports {@code transportNames}. */ - public void initializeTransports(String[] transportNames, IBackupObserver observer) { + public void initializeTransports( + @UserIdInt int userId, String[] transportNames, IBackupObserver observer) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "initializeTransports()"); + getServiceForUserIfCallerHasPermission(userId, "initializeTransports()"); if (userBackupManagerService != null) { userBackupManagerService.initializeTransports(transportNames, observer); @@ -262,9 +268,9 @@ public class BackupManagerService { * Clear the given package {@code packageName}'s backup data from the transport {@code * transportName}. */ - public void clearBackupData(String transportName, String packageName) { + public void clearBackupData(@UserIdInt int userId, String transportName, String packageName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "clearBackupData()"); + getServiceForUserIfCallerHasPermission(userId, "clearBackupData()"); if (userBackupManagerService != null) { userBackupManagerService.clearBackupData(transportName, packageName); @@ -273,9 +279,9 @@ public class BackupManagerService { /** Return the name of the currently active transport. */ @Nullable - public String getCurrentTransport() { + public String getCurrentTransport(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransport()"); + getServiceForUserIfCallerHasPermission(userId, "getCurrentTransport()"); return userBackupManagerService == null ? null @@ -287,9 +293,9 @@ public class BackupManagerService { * null} if no transport selected or if the transport selected is not registered. */ @Nullable - public ComponentName getCurrentTransportComponent() { + public ComponentName getCurrentTransportComponent(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "getCurrentTransportComponent()"); + getServiceForUserIfCallerHasPermission(userId, "getCurrentTransportComponent()"); return userBackupManagerService == null ? null @@ -298,9 +304,9 @@ public class BackupManagerService { /** Report all known, available backup transports by name. */ @Nullable - public String[] listAllTransports() { + public String[] listAllTransports(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransports()"); + getServiceForUserIfCallerHasPermission(userId, "listAllTransports()"); return userBackupManagerService == null ? null @@ -309,9 +315,9 @@ public class BackupManagerService { /** Report all known, available backup transports by {@link ComponentName}. */ @Nullable - public ComponentName[] listAllTransportComponents() { + public ComponentName[] listAllTransportComponents(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "listAllTransportComponents()"); + getServiceForUserIfCallerHasPermission(userId, "listAllTransportComponents()"); return userBackupManagerService == null ? null @@ -321,12 +327,14 @@ public class BackupManagerService { /** Report all system whitelisted transports. */ @Nullable public String[] getTransportWhitelist() { - UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "getTransportWhitelist()"); - - return userBackupManagerService == null - ? null - : userBackupManagerService.getTransportWhitelist(); + // No permission check, intentionally. + String[] whitelistedTransports = new String[mTransportWhitelist.size()]; + int i = 0; + for (ComponentName component : mTransportWhitelist) { + whitelistedTransports[i] = component.flattenToShortString(); + i++; + } + return whitelistedTransports; } /** @@ -353,6 +361,7 @@ public class BackupManagerService { * {@code transportComponent} or if the caller does NOT have BACKUP permission. */ public void updateTransportAttributes( + @UserIdInt int userId, ComponentName transportComponent, String name, @Nullable Intent configurationIntent, @@ -360,7 +369,7 @@ public class BackupManagerService { @Nullable Intent dataManagementIntent, String dataManagementLabel) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "updateTransportAttributes()"); + getServiceForUserIfCallerHasPermission(userId, "updateTransportAttributes()"); if (userBackupManagerService != null) { userBackupManagerService.updateTransportAttributes( @@ -381,9 +390,9 @@ public class BackupManagerService { */ @Deprecated @Nullable - public String selectBackupTransport(String transportName) { + public String selectBackupTransport(@UserIdInt int userId, String transportName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransport()"); + getServiceForUserIfCallerHasPermission(userId, "selectBackupTransport()"); return userBackupManagerService == null ? null @@ -395,9 +404,11 @@ public class BackupManagerService { * with the result upon completion. */ public void selectBackupTransportAsync( - ComponentName transportComponent, ISelectBackupTransportCallback listener) { + @UserIdInt int userId, + ComponentName transportComponent, + ISelectBackupTransportCallback listener) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "selectBackupTransportAsync()"); + getServiceForUserIfCallerHasPermission(userId, "selectBackupTransportAsync()"); if (userBackupManagerService != null) { userBackupManagerService.selectBackupTransportAsync(transportComponent, listener); @@ -410,9 +421,9 @@ public class BackupManagerService { * returns {@code null}. */ @Nullable - public Intent getConfigurationIntent(String transportName) { + public Intent getConfigurationIntent(@UserIdInt int userId, String transportName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "getConfigurationIntent()"); + getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()"); return userBackupManagerService == null ? null @@ -429,9 +440,9 @@ public class BackupManagerService { * @return The current destination string or null if the transport is not registered. */ @Nullable - public String getDestinationString(String transportName) { + public String getDestinationString(@UserIdInt int userId, String transportName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "getDestinationString()"); + getServiceForUserIfCallerHasPermission(userId, "getDestinationString()"); return userBackupManagerService == null ? null @@ -440,9 +451,9 @@ public class BackupManagerService { /** Supply the manage-data intent for the given transport. */ @Nullable - public Intent getDataManagementIntent(String transportName) { + public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementIntent()"); + getServiceForUserIfCallerHasPermission(userId, "getDataManagementIntent()"); return userBackupManagerService == null ? null @@ -454,9 +465,9 @@ public class BackupManagerService { * transport. */ @Nullable - public String getDataManagementLabel(String transportName) { + public String getDataManagementLabel(@UserIdInt int userId, String transportName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "getDataManagementLabel()"); + getServiceForUserIfCallerHasPermission(userId, "getDataManagementLabel()"); return userBackupManagerService == null ? null @@ -469,9 +480,8 @@ public class BackupManagerService { /** Enable/disable the backup service. This is user-configurable via backup settings. */ public void setBackupEnabled(@UserIdInt int userId, boolean enable) { - enforceCallingPermissionOnUserId(userId, "setBackupEnabled"); UserBackupManagerService userBackupManagerService = - getServiceForUser(userId, "setBackupEnabled()"); + getServiceForUserIfCallerHasPermission(userId, "setBackupEnabled()"); if (userBackupManagerService != null) { userBackupManagerService.setBackupEnabled(enable); @@ -479,32 +489,21 @@ public class BackupManagerService { } /** Enable/disable automatic restore of app data at install time. */ - public void setAutoRestore(boolean autoRestore) { + public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "setAutoRestore()"); + getServiceForUserIfCallerHasPermission(userId, "setAutoRestore()"); if (userBackupManagerService != null) { userBackupManagerService.setAutoRestore(autoRestore); } } - /** Mark the backup service as having been provisioned (device has gone through SUW). */ - public void setBackupProvisioned(boolean provisioned) { - UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "setBackupProvisioned()"); - - if (userBackupManagerService != null) { - userBackupManagerService.setBackupProvisioned(provisioned); - } - } - /** * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}. */ public boolean isBackupEnabled(@UserIdInt int userId) { - enforceCallingPermissionOnUserId(userId, "isBackupEnabled"); UserBackupManagerService userBackupManagerService = - getServiceForUser(userId, "isBackupEnabled()"); + getServiceForUserIfCallerHasPermission(userId, "isBackupEnabled()"); return userBackupManagerService != null && userBackupManagerService.isBackupEnabled(); } @@ -514,9 +513,9 @@ public class BackupManagerService { // --------------------------------------------- /** Checks if the given package {@code packageName} is eligible for backup. */ - public boolean isAppEligibleForBackup(String packageName) { + public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "isAppEligibleForBackup()"); + getServiceForUserIfCallerHasPermission(userId, "isAppEligibleForBackup()"); return userBackupManagerService != null && userBackupManagerService.isAppEligibleForBackup(packageName); @@ -526,9 +525,9 @@ public class BackupManagerService { * Returns from the inputted packages {@code packages}, the ones that are eligible for backup. */ @Nullable - public String[] filterAppsEligibleForBackup(String[] packages) { + public String[] filterAppsEligibleForBackup(@UserIdInt int userId, String[] packages) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "filterAppsEligibleForBackup()"); + getServiceForUserIfCallerHasPermission(userId, "filterAppsEligibleForBackup()"); return userBackupManagerService == null ? null @@ -540,9 +539,8 @@ public class BackupManagerService { * they have pending updates. */ public void backupNow(@UserIdInt int userId) { - enforceCallingPermissionOnUserId(userId, "backupNow"); UserBackupManagerService userBackupManagerService = - getServiceForUser(userId, "backupNow()"); + getServiceForUserIfCallerHasPermission(userId, "backupNow()"); if (userBackupManagerService != null) { userBackupManagerService.backupNow(); @@ -559,9 +557,8 @@ public class BackupManagerService { IBackupObserver observer, IBackupManagerMonitor monitor, int flags) { - enforceCallingPermissionOnUserId(userId, "requestBackup"); UserBackupManagerService userBackupManagerService = - getServiceForUser(userId, "requestBackup()"); + getServiceForUserIfCallerHasPermission(userId, "requestBackup()"); return userBackupManagerService == null ? BackupManager.ERROR_BACKUP_NOT_ALLOWED @@ -570,9 +567,8 @@ public class BackupManagerService { /** Cancel all running backup operations. */ public void cancelBackups(@UserIdInt int userId) { - enforceCallingPermissionOnUserId(userId, "cancelBackups"); UserBackupManagerService userBackupManagerService = - getServiceForUser(userId, "cancelBackups()"); + getServiceForUserIfCallerHasPermission(userId, "cancelBackups()"); if (userBackupManagerService != null) { userBackupManagerService.cancelBackups(); @@ -589,7 +585,7 @@ public class BackupManagerService { */ public boolean beginFullBackup(FullBackupJob scheduledJob) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "beginFullBackup()"); + getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "beginFullBackup()"); return userBackupManagerService != null && userBackupManagerService.beginFullBackup(scheduledJob); @@ -601,7 +597,7 @@ public class BackupManagerService { */ public void endFullBackup() { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "endFullBackup()"); + getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "endFullBackup()"); if (userBackupManagerService != null) { userBackupManagerService.endFullBackup(); @@ -611,9 +607,9 @@ public class BackupManagerService { /** * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'. */ - public void fullTransportBackup(String[] packageNames) { + public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "fullTransportBackup()"); + getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()"); if (userBackupManagerService != null) { userBackupManagerService.fullTransportBackup(packageNames); @@ -628,9 +624,9 @@ public class BackupManagerService { * Used to run a restore pass for an application that is being installed. This should only be * called from the {@link PackageManager}. */ - public void restoreAtInstall(String packageName, int token) { + public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "restoreAtInstall()"); + getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()"); if (userBackupManagerService != null) { userBackupManagerService.restoreAtInstall(packageName, token); @@ -642,9 +638,10 @@ public class BackupManagerService { * {@code transportName}. */ @Nullable - public IRestoreSession beginRestoreSession(String packageName, String transportName) { + public IRestoreSession beginRestoreSession( + @UserIdInt int userId, String packageName, String transportName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "beginRestoreSession()"); + getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()"); return userBackupManagerService == null ? null @@ -655,9 +652,9 @@ public class BackupManagerService { * Get the restore-set token for the best-available restore set for this {@code packageName}: * the active set if possible, else the ancestral one. Returns zero if none available. */ - public long getAvailableRestoreToken(String packageName) { + public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "getAvailableRestoreToken()"); + getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()"); return userBackupManagerService == null ? 0 @@ -671,7 +668,8 @@ public class BackupManagerService { /** Sets the backup password used when running adb backup. */ public boolean setBackupPassword(String currentPassword, String newPassword) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "setBackupPassword()"); + getServiceForUserIfCallerHasPermission( + UserHandle.USER_SYSTEM, "setBackupPassword()"); return userBackupManagerService != null && userBackupManagerService.setBackupPassword(currentPassword, newPassword); @@ -680,7 +678,8 @@ public class BackupManagerService { /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */ public boolean hasBackupPassword() { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "hasBackupPassword()"); + getServiceForUserIfCallerHasPermission( + UserHandle.USER_SYSTEM, "hasBackupPassword()"); return userBackupManagerService != null && userBackupManagerService.hasBackupPassword(); } @@ -703,9 +702,8 @@ public class BackupManagerService { boolean doCompress, boolean doKeyValue, String[] packageNames) { - enforceCallingPermissionOnUserId(userId, "adbBackup"); UserBackupManagerService userBackupManagerService = - getServiceForUser(userId, "adbBackup()"); + getServiceForUserIfCallerHasPermission(userId, "adbBackup()"); if (userBackupManagerService != null) { userBackupManagerService.adbBackup( @@ -728,9 +726,8 @@ public class BackupManagerService { * requires on-screen confirmation by the user. */ public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) { - enforceCallingPermissionOnUserId(userId, "setBackupEnabled"); UserBackupManagerService userBackupManagerService = - getServiceForUser(userId, "adbRestore()"); + getServiceForUserIfCallerHasPermission(userId, "adbRestore()"); if (userBackupManagerService != null) { userBackupManagerService.adbRestore(fd); @@ -742,13 +739,14 @@ public class BackupManagerService { * to require a user-facing disclosure about the operation. */ public void acknowledgeAdbBackupOrRestore( + @UserIdInt int userId, int token, boolean allow, String currentPassword, String encryptionPassword, IFullBackupRestoreObserver observer) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "acknowledgeAdbBackupOrRestore()"); + getServiceForUserIfCallerHasPermission(userId, "acknowledgeAdbBackupOrRestore()"); if (userBackupManagerService != null) { userBackupManagerService.acknowledgeAdbBackupOrRestore( @@ -763,7 +761,7 @@ public class BackupManagerService { /** Prints service state for 'dumpsys backup'. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { UserBackupManagerService userBackupManagerService = - getServiceForUser(UserHandle.USER_SYSTEM, "dump()"); + getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()"); if (userBackupManagerService != null) { userBackupManagerService.dump(fd, pw, args); diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index eb10a04586bf..d40368025c0c 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -287,7 +287,7 @@ public class Trampoline extends IBackupManager.Stub { public void dataChangedForUser(int userId, String packageName) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.dataChanged(packageName); + svc.dataChanged(userId, packageName); } } @@ -301,7 +301,7 @@ public class Trampoline extends IBackupManager.Stub { int userId, String[] transportNames, IBackupObserver observer) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.initializeTransports(transportNames, observer); + svc.initializeTransports(userId, transportNames, observer); } } @@ -310,7 +310,7 @@ public class Trampoline extends IBackupManager.Stub { throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.clearBackupData(transportName, packageName); + svc.clearBackupData(userId, transportName, packageName); } } @@ -325,7 +325,7 @@ public class Trampoline extends IBackupManager.Stub { throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.agentConnected(packageName, agent); + svc.agentConnected(userId, packageName, agent); } } @@ -338,7 +338,7 @@ public class Trampoline extends IBackupManager.Stub { public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.agentDisconnected(packageName); + svc.agentDisconnected(userId, packageName); } } @@ -352,7 +352,7 @@ public class Trampoline extends IBackupManager.Stub { throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.restoreAtInstall(packageName, token); + svc.restoreAtInstall(userId, packageName, token); } } @@ -379,7 +379,7 @@ public class Trampoline extends IBackupManager.Stub { public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.setAutoRestore(doAutoRestore); + svc.setAutoRestore(userId, doAutoRestore); } } @@ -390,10 +390,9 @@ public class Trampoline extends IBackupManager.Stub { @Override public void setBackupProvisioned(boolean isProvisioned) throws RemoteException { - BackupManagerService svc = mService; - if (svc != null) { - svc.setBackupProvisioned(isProvisioned); - } + /* + * This is now a no-op; provisioning is simply the device's own setup state. + */ } @Override @@ -448,7 +447,7 @@ public class Trampoline extends IBackupManager.Stub { throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.fullTransportBackup(packageNames); + svc.fullTransportBackup(userId, packageNames); } } @@ -471,7 +470,7 @@ public class Trampoline extends IBackupManager.Stub { throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.acknowledgeAdbBackupOrRestore(token, allow, + svc.acknowledgeAdbBackupOrRestore(userId, token, allow, curPassword, encryptionPassword, observer); } } @@ -489,7 +488,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public String getCurrentTransportForUser(int userId) throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.getCurrentTransport() : null; + return (svc != null) ? svc.getCurrentTransport(userId) : null; } @Override @@ -505,13 +504,13 @@ public class Trampoline extends IBackupManager.Stub { @Nullable public ComponentName getCurrentTransportComponentForUser(int userId) { BackupManagerService svc = mService; - return (svc != null) ? svc.getCurrentTransportComponent() : null; + return (svc != null) ? svc.getCurrentTransportComponent(userId) : null; } @Override public String[] listAllTransportsForUser(int userId) throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.listAllTransports() : null; + return (svc != null) ? svc.listAllTransports(userId) : null; } @Override @@ -522,7 +521,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.listAllTransportComponents() : null; + return (svc != null) ? svc.listAllTransportComponents(userId) : null; } @Override @@ -543,6 +542,7 @@ public class Trampoline extends IBackupManager.Stub { BackupManagerService svc = mService; if (svc != null) { svc.updateTransportAttributes( + userId, transportComponent, name, configurationIntent, @@ -556,7 +556,7 @@ public class Trampoline extends IBackupManager.Stub { public String selectBackupTransportForUser(int userId, String transport) throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.selectBackupTransport(transport) : null; + return (svc != null) ? svc.selectBackupTransport(userId, transport) : null; } @Override @@ -569,7 +569,7 @@ public class Trampoline extends IBackupManager.Stub { ISelectBackupTransportCallback listener) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.selectBackupTransportAsync(transport, listener); + svc.selectBackupTransportAsync(userId, transport, listener); } else { if (listener != null) { try { @@ -585,7 +585,7 @@ public class Trampoline extends IBackupManager.Stub { public Intent getConfigurationIntentForUser(int userId, String transport) throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.getConfigurationIntent(transport) : null; + return (svc != null) ? svc.getConfigurationIntent(userId, transport) : null; } @Override @@ -597,7 +597,7 @@ public class Trampoline extends IBackupManager.Stub { @Override public String getDestinationStringForUser(int userId, String transport) throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.getDestinationString(transport) : null; + return (svc != null) ? svc.getDestinationString(userId, transport) : null; } @Override @@ -609,7 +609,7 @@ public class Trampoline extends IBackupManager.Stub { public Intent getDataManagementIntentForUser(int userId, String transport) throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.getDataManagementIntent(transport) : null; + return (svc != null) ? svc.getDataManagementIntent(userId, transport) : null; } @Override @@ -622,7 +622,7 @@ public class Trampoline extends IBackupManager.Stub { public String getDataManagementLabelForUser(int userId, String transport) throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.getDataManagementLabel(transport) : null; + return (svc != null) ? svc.getDataManagementLabel(userId, transport) : null; } @Override @@ -635,33 +635,33 @@ public class Trampoline extends IBackupManager.Stub { public IRestoreSession beginRestoreSessionForUser( int userId, String packageName, String transportID) throws RemoteException { BackupManagerService svc = mService; - return (svc != null) ? svc.beginRestoreSession(packageName, transportID) : null; + return (svc != null) ? svc.beginRestoreSession(userId, packageName, transportID) : null; } @Override public void opComplete(int token, long result) throws RemoteException { BackupManagerService svc = mService; if (svc != null) { - svc.opComplete(token, result); + svc.opComplete(binderGetCallingUserId(), token, result); } } @Override public long getAvailableRestoreTokenForUser(int userId, String packageName) { BackupManagerService svc = mService; - return (svc != null) ? svc.getAvailableRestoreToken(packageName) : 0; + return (svc != null) ? svc.getAvailableRestoreToken(userId, packageName) : 0; } @Override public boolean isAppEligibleForBackupForUser(int userId, String packageName) { BackupManagerService svc = mService; - return (svc != null) ? svc.isAppEligibleForBackup(packageName) : false; + return (svc != null) ? svc.isAppEligibleForBackup(userId, packageName) : false; } @Override public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) { BackupManagerService svc = mService; - return (svc != null) ? svc.filterAppsEligibleForBackup(packages) : null; + return (svc != null) ? svc.filterAppsEligibleForBackup(userId, packages) : null; } @Override diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index d35740482059..2e414438c6ff 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -2811,15 +2811,6 @@ public class UserBackupManagerService { } } - /** Mark the backup service as having been provisioned. */ - public void setBackupProvisioned(boolean available) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, - "setBackupProvisioned"); - /* - * This is now a no-op; provisioning is simply the device's own setup state. - */ - } - /** Report whether the backup mechanism is currently enabled. */ public boolean isBackupEnabled() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, @@ -2869,19 +2860,6 @@ public class UserBackupManagerService { return mTransportManager.getRegisteredTransportComponents(); } - /** Report all system whitelisted transports. */ - public String[] getTransportWhitelist() { - // No permission check, intentionally. - Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist(); - String[] whitelistedTransports = new String[whitelistedComponents.size()]; - int i = 0; - for (ComponentName component : whitelistedComponents) { - whitelistedTransports[i] = component.flattenToShortString(); - i++; - } - return whitelistedTransports; - } - /** * Update the attributes of the transport identified by {@code transportComponent}. If the * specified transport has not been bound at least once (for registration), this call will be diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index b9b19435408f..942ee11aa481 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -25,8 +25,8 @@ import android.service.contentcapture.SnapshotData; import android.text.format.DateUtils; import android.view.contentcapture.ContentCaptureContext; +import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.internal.os.IResultReceiver; -import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService; final class RemoteContentCaptureService extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService, diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 1882be26ebf0..a381477b01cb 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -653,7 +653,7 @@ public class BiometricService extends SystemService { } mHandler.post(() -> { - final Pair<Integer, Integer> result = checkAndGetBiometricModality(callingUserId); + final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); final int modality = result.first; final int error = result.second; @@ -950,7 +950,7 @@ public class BiometricService extends SystemService { * {@link BiometricAuthenticator#TYPE_FACE} * and the error containing one of the {@link BiometricConstants} errors. */ - private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) { + private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) { int modality = TYPE_NONE; // No biometric features, send error @@ -979,7 +979,7 @@ public class BiometricService extends SystemService { // order. firstHwAvailable = modality; } - if (authenticator.hasEnrolledTemplates(callingUid)) { + if (authenticator.hasEnrolledTemplates(userId)) { hasTemplatesEnrolled = true; if (isEnabledForApp(modality)) { // TODO(b/110907543): When face settings (and other settings) have both a diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java index 205d40b9e5cd..fd87e3d32b1d 100644 --- a/services/core/java/com/android/server/infra/ServiceNameResolver.java +++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java @@ -18,6 +18,8 @@ package com.android.server.infra; import android.annotation.NonNull; import android.annotation.Nullable; +import com.android.internal.infra.AbstractRemoteService; + import java.io.PrintWriter; /** diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 8fa3c460529c..53ee16b1b585 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -842,25 +842,13 @@ public class NotificationManagerService extends SystemService { // Report to usage stats that notification was made visible if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key); reportSeen(r); - - // If the newly visible notification has smart suggestions - // then log that the user has seen them. - if ((r.getNumSmartRepliesAdded() > 0 || r.getNumSmartActionsAdded() > 0) - && !r.hasSeenSmartReplies()) { - r.setSeenSmartReplies(true); - LogMaker logMaker = r.getLogMaker() - .setCategory(MetricsEvent.SMART_REPLY_VISIBLE) - .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT, - r.getNumSmartRepliesAdded()) - .addTaggedData(MetricsEvent.NOTIFICATION_SMART_ACTION_COUNT, - r.getNumSmartActionsAdded()) - .addTaggedData( - MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED, - r.getSuggestionsGeneratedByAssistant()); - mMetricsLogger.write(logMaker); - } } r.setVisibility(true, nv.rank, nv.count); + // hasBeenVisiblyExpanded must be called after updating the expansion state of + // the NotificationRecord to ensure the expansion state is up-to-date. + if (r.hasBeenVisiblyExpanded()) { + logSmartSuggestionsVisible(r); + } maybeRecordInterruptionLocked(r); nv.recycle(); } @@ -884,6 +872,11 @@ public class NotificationManagerService extends SystemService { NotificationRecord r = mNotificationsByKey.get(key); if (r != null) { r.stats.onExpansionChanged(userAction, expanded); + // hasBeenVisiblyExpanded must be called after updating the expansion state of + // the NotificationRecord to ensure the expansion state is up-to-date. + if (r.hasBeenVisiblyExpanded()) { + logSmartSuggestionsVisible(r); + } final long now = System.currentTimeMillis(); if (userAction) { MetricsLogger.action(r.getItemLogMaker() @@ -961,6 +954,26 @@ public class NotificationManagerService extends SystemService { } }; + @VisibleForTesting + void logSmartSuggestionsVisible(NotificationRecord r) { + // If the newly visible notification has smart suggestions + // then log that the user has seen them. + if ((r.getNumSmartRepliesAdded() > 0 || r.getNumSmartActionsAdded() > 0) + && !r.hasSeenSmartReplies()) { + r.setSeenSmartReplies(true); + LogMaker logMaker = r.getLogMaker() + .setCategory(MetricsEvent.SMART_REPLY_VISIBLE) + .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT, + r.getNumSmartRepliesAdded()) + .addTaggedData(MetricsEvent.NOTIFICATION_SMART_ACTION_COUNT, + r.getNumSmartActionsAdded()) + .addTaggedData( + MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED, + r.getSuggestionsGeneratedByAssistant()); + mMetricsLogger.write(logMaker); + } + } + @GuardedBy("mNotificationLock") private void clearSoundLocked() { mSoundNotificationKey = null; @@ -2279,6 +2292,26 @@ public class NotificationManagerService extends SystemService { } @Override + public boolean areAppOverlaysAllowed(String pkg) { + return areAppOverlaysAllowedForPackage(pkg, Binder.getCallingUid()); + } + + @Override + public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) { + checkCallerIsSystemOrSameApp(pkg); + + return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid); + } + + @Override + public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) { + checkCallerIsSystem(); + + mPreferencesHelper.setAppOverlaysAllowed(pkg, uid, allowed); + handleSavePolicyFile(); + } + + @Override public int getPackageImportance(String pkg) { checkCallerIsSystemOrSameApp(pkg); return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid()); @@ -4409,7 +4442,7 @@ public class NotificationManagerService extends SystemService { notification.flags &= ~Notification.FLAG_CAN_COLORIZE; } - if (ai.targetSdkVersion >= Build.VERSION_CODES.Q) { + if (notification.fullScreenIntent != null && ai.targetSdkVersion >= Build.VERSION_CODES.Q) { int fullscreenIntentPermission = mPackageManagerClient.checkPermission( android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg); if (fullscreenIntentPermission != PERMISSION_GRANTED) { diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 89ec38db99e5..1f8893c56874 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -1166,6 +1166,13 @@ public final class NotificationRecord { mHasSeenSmartReplies = hasSeenSmartReplies; } + /** + * Returns whether this notification has been visible and expanded at the same time. + */ + public boolean hasBeenVisiblyExpanded() { + return stats.hasBeenVisiblyExpanded(); + } + public void setSystemGeneratedSmartActions( ArrayList<Notification.Action> systemGeneratedSmartActions) { mSystemGeneratedSmartActions = systemGeneratedSmartActions; diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index e40dad6e83f1..d630b9ab54d6 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -916,6 +916,13 @@ public class NotificationUsageStats { updateVisiblyExpandedStats(); } + /** + * Returns whether this notification has been visible and expanded at the same. + */ + public boolean hasBeenVisiblyExpanded() { + return posttimeToFirstVisibleExpansionMs >= 0; + } + private void updateVisiblyExpandedStats() { long elapsedNowMs = SystemClock.elapsedRealtime(); if (isExpanded && isVisible) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index eb46d53b5157..7c0e0b0983fb 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -80,6 +80,7 @@ public class PreferencesHelper implements RankingConfig { private static final String ATT_NAME = "name"; private static final String ATT_UID = "uid"; private static final String ATT_ID = "id"; + private static final String ATT_APP_OVERLAY = "overlay"; private static final String ATT_PRIORITY = "priority"; private static final String ATT_VISIBILITY = "visibility"; private static final String ATT_IMPORTANCE = "importance"; @@ -92,6 +93,7 @@ public class PreferencesHelper implements RankingConfig { private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE; private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED; private static final boolean DEFAULT_SHOW_BADGE = true; + private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true; /** * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable * fields. @@ -104,6 +106,7 @@ public class PreferencesHelper implements RankingConfig { @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE}) public @interface LockableAppFields { int USER_LOCKED_IMPORTANCE = 0x00000001; + int USER_LOCKED_APP_OVERLAY = 0x00000002; } // pkg|uid => PackagePreferences @@ -169,7 +172,9 @@ public class PreferencesHelper implements RankingConfig { XmlUtils.readIntAttribute( parser, ATT_VISIBILITY, DEFAULT_VISIBILITY), XmlUtils.readBooleanAttribute( - parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE)); + parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE), + XmlUtils.readBooleanAttribute( + parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY)); r.importance = XmlUtils.readIntAttribute( parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); r.priority = XmlUtils.readIntAttribute( @@ -264,11 +269,12 @@ public class PreferencesHelper implements RankingConfig { private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) { return getOrCreatePackagePreferences(pkg, uid, - DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE); + DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE, + DEFAULT_ALLOW_APP_OVERLAY); } private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance, - int priority, int visibility, boolean showBadge) { + int priority, int visibility, boolean showBadge, boolean allowAppOverlay) { final String key = packagePreferencesKey(pkg, uid); synchronized (mPackagePreferences) { PackagePreferences @@ -282,6 +288,7 @@ public class PreferencesHelper implements RankingConfig { r.priority = priority; r.visibility = visibility; r.showBadge = showBadge; + r.appOverlay = allowAppOverlay; try { createDefaultChannelIfNeeded(r); @@ -382,7 +389,8 @@ public class PreferencesHelper implements RankingConfig { || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS || r.channels.size() > 0 || r.groups.size() > 0 - || r.delegate != null; + || r.delegate != null + || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY; if (hasNonDefaultSettings) { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATT_NAME, r.pkg); @@ -395,6 +403,9 @@ public class PreferencesHelper implements RankingConfig { if (r.visibility != DEFAULT_VISIBILITY) { out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility)); } + if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) { + out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay)); + } out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge)); out.attribute(null, ATT_APP_USER_LOCKED_FIELDS, Integer.toString(r.lockedAppFields)); @@ -439,6 +450,20 @@ public class PreferencesHelper implements RankingConfig { out.endTag(null, TAG_RANKING); } + public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) { + PackagePreferences p = getOrCreatePackagePreferences(pkg, uid); + p.appOverlay = allowed; + p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY; + } + + public boolean areAppOverlaysAllowed(String pkg, int uid) { + return getOrCreatePackagePreferences(pkg, uid).appOverlay; + } + + public int getAppLockedFields(String pkg, int uid) { + return getOrCreatePackagePreferences(pkg, uid).lockedAppFields; + } + /** * Gets importance. */ @@ -512,7 +537,6 @@ public class PreferencesHelper implements RankingConfig { // apps can't update the blocked status or app overlay permission if (fromTargetApp) { group.setBlocked(oldGroup.isBlocked()); - group.setAllowAppOverlay(oldGroup.canOverlayApps()); group.unlockFields(group.getUserLockedFields()); group.lockFields(oldGroup.getUserLockedFields()); } else { @@ -521,9 +545,6 @@ public class PreferencesHelper implements RankingConfig { group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE); updateChannelsBypassingDnd(mContext.getUserId()); } - if (group.canOverlayApps() != oldGroup.canOverlayApps()) { - group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY); - } } } r.groups.put(group.getId(), group); @@ -1581,6 +1602,7 @@ public class PreferencesHelper implements RankingConfig { int priority = DEFAULT_PRIORITY; int visibility = DEFAULT_VISIBILITY; boolean showBadge = DEFAULT_SHOW_BADGE; + boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY; int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS; Delegate delegate = null; diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 02d8c0bcb584..fc21adbdcca3 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -718,12 +718,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D disabledData += " }"; final UiState state = getUiState(displayId); - Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + "net1=" + net1 + Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + ", net1=" + net1 + ", mDisabled1=" + state.mDisabled1 + ", token=" + token + ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData); } final UiState state = getUiState(displayId); - if (state.disableEquals(net1, net2)) { + if (!state.disableEquals(net1, net2)) { state.setDisabled(net1, net2); mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1)); if (mBar != null) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5f00bcc26984..4d8440a899a3 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -42,6 +42,7 @@ import static android.app.WindowConfiguration.activityTypeToString; import static android.content.Intent.ACTION_MAIN; import static android.content.Intent.CATEGORY_HOME; import static android.content.Intent.CATEGORY_LAUNCHER; +import static android.content.Intent.CATEGORY_SECONDARY_HOME; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; @@ -1178,7 +1179,8 @@ final class ActivityRecord extends ConfigurationContainer { private boolean isHomeIntent(Intent intent) { return ACTION_MAIN.equals(intent.getAction()) - && intent.hasCategory(CATEGORY_HOME) + && (intent.hasCategory(CATEGORY_HOME) + || intent.hasCategory(CATEGORY_SECONDARY_HOME)) && intent.getCategories().size() == 1 && intent.getData() == null && intent.getType() == null; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 986115726efb..f662d0cc7f23 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5475,6 +5475,31 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return intent; } + /** + * Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home + * activities. + * + * @param preferredPackage Specify a preferred package name, otherwise use secondary home + * component defined in config_secondaryHomeComponent. + * @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} + */ + Intent getSecondaryHomeIntent(String preferredPackage) { + final Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null); + if (preferredPackage == null) { + // Using the component stored in config if no package name. + final String secondaryHomeComponent = mContext.getResources().getString( + com.android.internal.R.string.config_secondaryHomeComponent); + intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent)); + } else { + intent.setPackage(preferredPackage); + } + intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); + if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { + intent.addCategory(Intent.CATEGORY_SECONDARY_HOME); + } + return intent; + } + ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) { if (info == null) return null; ApplicationInfo newInfo = new ApplicationInfo(info); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 8d49bf374baf..d8b2b5200f0c 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1837,23 +1837,23 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return false; } } + } - if (transferStartingWindow(transferFrom)) { - return true; - } - - // There is no existing starting window, and we don't want to create a splash screen, so - // that's it! - if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) { - return false; - } + if (transferStartingWindow(transferFrom)) { + return true; + } - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData"); - startingData = new SplashScreenStartingData(mWmService, pkg, - theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, - getMergedOverrideConfiguration()); - scheduleAddStartingWindow(); + // There is no existing starting window, and we don't want to create a splash screen, so + // that's it! + if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) { + return false; } + + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData"); + startingData = new SplashScreenStartingData(mWmService, pkg, + theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, + getMergedOverrideConfiguration()); + scheduleAddStartingWindow(); return true; } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index ec2d6737ee51..cb9cbd6465fb 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -134,10 +134,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, mWindowManager.deferSurfaceLayout(); try { - final int userId = mService.getCurrentUserId(); - // Kick off the assist data request in the background before showing the target activity - requestAssistData(recentsComponent, recentsUid, assistDataReceiver, userId); + requestAssistData(recentsComponent, recentsUid, assistDataReceiver); if (hasExistingActivity) { // Move the recents activity into place for the animation if it is not top most @@ -164,7 +162,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, .setCallingUid(recentsUid) .setCallingPackage(recentsComponent.getPackageName()) .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle())) - .setMayWait(userId) + .setMayWait(mService.getCurrentUserId()) .execute(); // Move the recents activity into place for the animation @@ -221,7 +219,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, * Requests assist data for the top visible activities. */ private void requestAssistData(ComponentName recentsComponent, int recentsUid, - @Deprecated IAssistDataReceiver assistDataReceiver, int userId) { + @Deprecated IAssistDataReceiver assistDataReceiver) { final AppOpsManager appOpsManager = (AppOpsManager) mService.mContext.getSystemService(Context.APP_OPS_SERVICE); final List<IBinder> topActivities = @@ -237,8 +235,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks, final ContentCaptureManagerInternal imService = LocalServices.getService(ContentCaptureManagerInternal.class); final IBinder activityToken = topActivities.get(activityIndex); - if (imService == null - || !imService.sendActivityAssistData(userId, activityToken, data)) { + final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken); + if (r != null && (imService == null + || !imService.sendActivityAssistData(r.mUserId, activityToken, data))) { // Otherwise, use the provided assist data receiver super.onAssistDataReceivedLocked(data, activityIndex, activityCount); } @@ -263,7 +262,10 @@ class RecentsAnimation implements RecentsAnimationCallbacks, int activityCount) { // Try to notify the intelligence service final IBinder activityToken = topActivities.get(activityIndex); - imService.sendActivityAssistData(userId, activityToken, data); + final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken); + if (r != null) { + imService.sendActivityAssistData(r.mUserId, activityToken, data); + } } }; } diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index d0144fdf670a..8ec97c5117f1 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -90,17 +90,18 @@ import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.power.V1_0.PowerHint; -import android.os.Build; import android.os.FactoryTest; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; +import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.IntArray; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -110,6 +111,7 @@ import android.view.Display; import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ResolverActivity; import com.android.server.LocalServices; import com.android.server.am.ActivityManagerService; import com.android.server.am.AppTimeTracker; @@ -346,35 +348,53 @@ class RootActivityContainer extends ConfigurationContainer } /** - * This starts home activity on displays that can have system decorations and only if the - * home activity can have multiple instances. + * This starts home activity on displays that can have system decorations based on displayId - + * Default display always use primary home component. + * For Secondary displays, the home activity must have category SECONDARY_HOME and then resolves + * according to the priorities listed below. + * - If default home is not set, always use the secondary home defined in the config. + * - Use currently selected primary home activity. + * - Use the activity in the same package as currently selected primary home activity. + * If there are multiple activities matched, use first one. + * - Use the secondary home defined in the config. */ boolean startHomeOnDisplay(int userId, String reason, int displayId) { - final Intent homeIntent = mService.getHomeIntent(); - final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent); + Intent homeIntent; + ActivityInfo aInfo; + if (displayId == DEFAULT_DISPLAY) { + homeIntent = mService.getHomeIntent(); + aInfo = resolveHomeActivity(userId, homeIntent); + } else { + Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, displayId); + aInfo = info.first; + homeIntent = info.second; + } if (aInfo == null) { return false; } - if (!canStartHomeOnDisplay(aInfo, displayId, - false /* allowInstrumenting */)) { + if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) { return false; } + // Updates the home component of the intent. + homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); + homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK); // Update the reason for ANR debugging to verify if the user activity is the one that // actually launched. final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId( - aInfo.applicationInfo.uid); + aInfo.applicationInfo.uid) + ":" + displayId; mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, displayId); return true; } /** - * This resolves the home activity info and updates the home component of the given intent. + * This resolves the home activity info. * @return the home activity info if any. */ - private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) { + @VisibleForTesting + ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) { final int flags = ActivityManagerService.STOCK_PM_FLAGS; final ComponentName comp = homeIntent.getComponent(); ActivityInfo aInfo = null; @@ -400,13 +420,82 @@ class RootActivityContainer extends ConfigurationContainer return null; } - homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); aInfo = new ActivityInfo(aInfo); aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId); - homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK); return aInfo; } + @VisibleForTesting + Pair<ActivityInfo, Intent> resolveSecondaryHomeActivity(int userId, int displayId) { + if (displayId == DEFAULT_DISPLAY) { + throw new IllegalArgumentException( + "resolveSecondaryHomeActivity: Should not be DEFAULT_DISPLAY"); + } + // Resolve activities in the same package as currently selected primary home activity. + Intent homeIntent = mService.getHomeIntent(); + ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent); + if (aInfo != null) { + if (ResolverActivity.class.getName().equals(aInfo.name)) { + // Always fallback to secondary home component if default home is not set. + aInfo = null; + } else { + // Look for secondary home activities in the currently selected default home + // package. + homeIntent = mService.getSecondaryHomeIntent(aInfo.applicationInfo.packageName); + final List<ResolveInfo> resolutions = resolveActivities(userId, homeIntent); + final int size = resolutions.size(); + final String targetName = aInfo.name; + aInfo = null; + for (int i = 0; i < size; i++) { + ResolveInfo resolveInfo = resolutions.get(i); + // We need to traverse all resolutions to check if the currently selected + // default home activity is present. + if (resolveInfo.activityInfo.name.equals(targetName)) { + aInfo = resolveInfo.activityInfo; + break; + } + } + if (aInfo == null && size > 0) { + // First one is the best. + aInfo = resolutions.get(0).activityInfo; + } + } + } + + if (aInfo != null) { + if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) { + aInfo = null; + } + } + + // Fallback to secondary home component. + if (aInfo == null) { + homeIntent = mService.getSecondaryHomeIntent(null); + aInfo = resolveHomeActivity(userId, homeIntent); + } + return Pair.create(aInfo, homeIntent); + } + + /** + * Retrieve all activities that match the given intent. + * The list should already ordered from best to worst matched. + * {@link android.content.pm.PackageManager#queryIntentActivities} + */ + @VisibleForTesting + List<ResolveInfo> resolveActivities(int userId, Intent homeIntent) { + List<ResolveInfo> resolutions; + try { + final String resolvedType = + homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver()); + resolutions = AppGlobals.getPackageManager().queryIntentActivities(homeIntent, + resolvedType, ActivityManagerService.STOCK_PM_FLAGS, userId).getList(); + + } catch (RemoteException e) { + resolutions = new ArrayList<>(); + } + return resolutions; + } + boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) { if (!mService.isBooting() && !mService.isBooted()) { // Not ready yet! @@ -457,6 +546,14 @@ class RootActivityContainer extends ConfigurationContainer return true; } + final boolean deviceProvisioned = Settings.Global.getInt( + mService.mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 0) != 0; + if (displayId != DEFAULT_DISPLAY && displayId != INVALID_DISPLAY && !deviceProvisioned) { + // Can't launch home on secondary display before device is provisioned. + return false; + } + final ActivityDisplay display = getActivityDisplay(displayId); if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) { // Can't launch home on display that doesn't support system decorations. @@ -464,13 +561,9 @@ class RootActivityContainer extends ConfigurationContainer } final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK - && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE - && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q; + && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE; if (!supportMultipleInstance) { - // Can't launch home on other displays if it requested to be single instance. Also we - // don't allow home applications that target before Q to have multiple home activity - // instances because they may not be expected to have multiple home scenario and - // haven't explicitly request for single instance. + // Can't launch home on secondary displays if it requested to be single instance. return false; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cf03d613634f..d1dfd7d14437 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -2116,30 +2116,33 @@ public final class SystemServer { private void startContentCaptureService(@NonNull Context context) { - // First check if it was explicitly enabled by Settings - boolean explicitlySupported = false; + // Check if it was explicitly enabled by Settings final String settings = Settings.Global.getString(context.getContentResolver(), Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED); - if (settings != null) { - explicitlySupported = Boolean.parseBoolean(settings); - if (explicitlySupported) { - Slog.d(TAG, "ContentCaptureService explicitly enabled by Settings"); - } else { - Slog.d(TAG, "ContentCaptureService explicitly disabled by Settings"); - return; - } + if (settings == null) { + // Better be safe than sorry... + Slog.d(TAG, "ContentCaptureService disabled because its not set by OEM"); + return; } - - // Then check if OEM overlaid the resource that defines the service. - if (!explicitlySupported) { - final String serviceName = context - .getString(com.android.internal.R.string.config_defaultContentCaptureService); - if (TextUtils.isEmpty(serviceName)) { - Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid"); + switch (settings) { + case Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_ALWAYS: + // Should be used only during development + Slog.d(TAG, "ContentCaptureService explicitly enabled by Settings"); + break; + case Settings.Global.CONTENT_CAPTURE_SERVICE_EXPLICITLY_ENABLED_DEFAULT: + // Default case: check if OEM overlaid the resource that defines the service. + final String serviceName = context.getString( + com.android.internal.R.string.config_defaultContentCaptureService); + if (TextUtils.isEmpty(serviceName)) { + Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid"); + return; + } + break; + default: + // Kill switch for OEMs + Slog.d(TAG, "ContentCaptureService disabled because its set to: " + settings); return; - } } - traceBeginAndSlog("StartContentCaptureService"); mSystemServiceManager.startService(CONTENT_CAPTURE_MANAGER_SERVICE_CLASS); traceEnd(); diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java index 83f66c5258b2..b253e0ae0a40 100644 --- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java @@ -23,6 +23,7 @@ import static com.android.server.backup.testing.TransportData.backupTransport; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -86,9 +87,7 @@ public class BackupManagerServiceTest { mContext = application; mShadowContext = shadowOf(application); - // TODO(b/120212806): Hardcoding system user for now since most methods in BMS don't yet - // take an user parameter (and instead hardcode the system user). - mUserOneId = UserHandle.USER_SYSTEM; + mUserOneId = UserHandle.USER_SYSTEM + 1; mUserTwoId = mUserOneId + 1; } @@ -176,9 +175,52 @@ public class BackupManagerServiceTest { assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService); } - // TODO(b/120212806): When BMS methods take in a user parameter, modify unknown user tests to - // check that that we don't call the method on another registered user. Currently these tests - // have no registered users since we hardcode the system user in BMS. + /** + * Test that the backup services throws a {@link SecurityException} if the caller does not have + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); + + expectThrows( + SecurityException.class, + () -> + backupManagerService.getServiceForUserIfCallerHasPermission( + mUserOneId, "test")); + } + + /** + * Test that the backup services does not throw a {@link SecurityException} if the caller has + * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. + */ + @Test + public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true); + + assertEquals( + mUserOneService, + backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test")); + } + + /** + * Test that the backup services does not throw a {@link SecurityException} if the caller does + * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id. + */ + @Test + public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() { + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); + + assertEquals( + mUserOneService, + backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test")); + } // --------------------------------------------- // Backup agent tests @@ -189,8 +231,9 @@ public class BackupManagerServiceTest { public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.dataChanged(TEST_PACKAGE); + backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE); verify(mUserOneService).dataChanged(TEST_PACKAGE); } @@ -198,9 +241,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.dataChanged(TEST_PACKAGE); + backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE); verify(mUserOneService, never()).dataChanged(TEST_PACKAGE); } @@ -210,9 +255,10 @@ public class BackupManagerServiceTest { public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); IBinder agentBinder = mock(IBinder.class); - backupManagerService.agentConnected(TEST_PACKAGE, agentBinder); + backupManagerService.agentConnected(mUserOneId, TEST_PACKAGE, agentBinder); verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder); } @@ -220,10 +266,12 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); IBinder agentBinder = mock(IBinder.class); - backupManagerService.agentConnected(TEST_PACKAGE, agentBinder); + backupManagerService.agentConnected(mUserTwoId, TEST_PACKAGE, agentBinder); verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder); } @@ -233,8 +281,9 @@ public class BackupManagerServiceTest { public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.agentDisconnected(TEST_PACKAGE); + backupManagerService.agentDisconnected(mUserOneId, TEST_PACKAGE); verify(mUserOneService).agentDisconnected(TEST_PACKAGE); } @@ -242,9 +291,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.agentDisconnected(TEST_PACKAGE); + backupManagerService.agentDisconnected(mUserTwoId, TEST_PACKAGE); verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE); } @@ -254,8 +305,9 @@ public class BackupManagerServiceTest { public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.opComplete(/* token */ 0, /* result */ 0L); + backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L); verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L); } @@ -263,9 +315,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.opComplete(/* token */ 0, /* result */ 0L); + backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L); verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L); } @@ -279,9 +333,10 @@ public class BackupManagerServiceTest { public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); String[] transports = {TEST_TRANSPORT}; - backupManagerService.initializeTransports(transports, /* observer */ null); + backupManagerService.initializeTransports(mUserOneId, transports, /* observer */ null); verify(mUserOneService).initializeTransports(transports, /* observer */ null); } @@ -289,10 +344,12 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); String[] transports = {TEST_TRANSPORT}; - backupManagerService.initializeTransports(transports, /* observer */ null); + backupManagerService.initializeTransports(mUserTwoId, transports, /* observer */ null); verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null); } @@ -302,8 +359,9 @@ public class BackupManagerServiceTest { public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); + backupManagerService.clearBackupData(mUserOneId, TEST_TRANSPORT, TEST_PACKAGE); verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); } @@ -311,9 +369,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); + backupManagerService.clearBackupData(mUserTwoId, TEST_TRANSPORT, TEST_PACKAGE); verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE); } @@ -323,8 +383,9 @@ public class BackupManagerServiceTest { public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.getCurrentTransport(); + backupManagerService.getCurrentTransport(mUserOneId); verify(mUserOneService).getCurrentTransport(); } @@ -332,9 +393,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.getCurrentTransport(); + backupManagerService.getCurrentTransport(mUserTwoId); verify(mUserOneService, never()).getCurrentTransport(); } @@ -345,8 +408,9 @@ public class BackupManagerServiceTest { throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.getCurrentTransportComponent(); + backupManagerService.getCurrentTransportComponent(mUserOneId); verify(mUserOneService).getCurrentTransportComponent(); } @@ -355,9 +419,11 @@ public class BackupManagerServiceTest { @Test public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.getCurrentTransportComponent(); + backupManagerService.getCurrentTransportComponent(mUserTwoId); verify(mUserOneService, never()).getCurrentTransportComponent(); } @@ -367,8 +433,9 @@ public class BackupManagerServiceTest { public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.listAllTransports(); + backupManagerService.listAllTransports(mUserOneId); verify(mUserOneService).listAllTransports(); } @@ -376,9 +443,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.listAllTransports(); + backupManagerService.listAllTransports(mUserTwoId); verify(mUserOneService, never()).listAllTransports(); } @@ -389,8 +458,9 @@ public class BackupManagerServiceTest { throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.listAllTransportComponents(); + backupManagerService.listAllTransportComponents(mUserOneId); verify(mUserOneService).listAllTransportComponents(); } @@ -399,32 +469,13 @@ public class BackupManagerServiceTest { @Test public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); - - backupManagerService.listAllTransportComponents(); - - verify(mUserOneService, never()).listAllTransportComponents(); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testGetTransportWhitelist_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.getTransportWhitelist(); - - verify(mUserOneService).getTransportWhitelist(); - } - - /** Test that the backup service does not route methods for non-registered users. */ - @Test - public void testGetTransportWhitelist_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); - - backupManagerService.getTransportWhitelist(); + backupManagerService.listAllTransportComponents(mUserTwoId); - verify(mUserOneService, never()).getTransportWhitelist(); + verify(mUserOneService, never()).listAllTransportComponents(); } /** Test that the backup service routes methods correctly to the user that requests it. */ @@ -433,11 +484,13 @@ public class BackupManagerServiceTest { throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); TransportData transport = backupTransport(); Intent configurationIntent = new Intent(); Intent dataManagementIntent = new Intent(); backupManagerService.updateTransportAttributes( + mUserOneId, transport.getTransportComponent(), transport.transportName, configurationIntent, @@ -459,12 +512,15 @@ public class BackupManagerServiceTest { @Test public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); TransportData transport = backupTransport(); Intent configurationIntent = new Intent(); Intent dataManagementIntent = new Intent(); backupManagerService.updateTransportAttributes( + mUserTwoId, transport.getTransportComponent(), transport.transportName, configurationIntent, @@ -487,8 +543,9 @@ public class BackupManagerServiceTest { public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.selectBackupTransport(TEST_TRANSPORT); + backupManagerService.selectBackupTransport(mUserOneId, TEST_TRANSPORT); verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT); } @@ -496,9 +553,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.selectBackupTransport(TEST_TRANSPORT); + backupManagerService.selectBackupTransport(mUserTwoId, TEST_TRANSPORT); verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT); } @@ -508,11 +567,12 @@ public class BackupManagerServiceTest { public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); TransportData transport = backupTransport(); ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); backupManagerService.selectBackupTransportAsync( - transport.getTransportComponent(), callback); + mUserOneId, transport.getTransportComponent(), callback); verify(mUserOneService) .selectBackupTransportAsync(transport.getTransportComponent(), callback); @@ -522,12 +582,14 @@ public class BackupManagerServiceTest { @Test public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); TransportData transport = backupTransport(); ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class); backupManagerService.selectBackupTransportAsync( - transport.getTransportComponent(), callback); + mUserTwoId, transport.getTransportComponent(), callback); verify(mUserOneService, never()) .selectBackupTransportAsync(transport.getTransportComponent(), callback); @@ -538,8 +600,9 @@ public class BackupManagerServiceTest { public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.getConfigurationIntent(TEST_TRANSPORT); + backupManagerService.getConfigurationIntent(mUserOneId, TEST_TRANSPORT); verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT); } @@ -547,9 +610,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.getConfigurationIntent(TEST_TRANSPORT); + backupManagerService.getConfigurationIntent(mUserTwoId, TEST_TRANSPORT); verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT); } @@ -559,8 +624,9 @@ public class BackupManagerServiceTest { public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.getDestinationString(TEST_TRANSPORT); + backupManagerService.getDestinationString(mUserOneId, TEST_TRANSPORT); verify(mUserOneService).getDestinationString(TEST_TRANSPORT); } @@ -568,9 +634,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.getDestinationString(TEST_TRANSPORT); + backupManagerService.getDestinationString(mUserTwoId, TEST_TRANSPORT); verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT); } @@ -580,8 +648,9 @@ public class BackupManagerServiceTest { public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.getDataManagementIntent(TEST_TRANSPORT); + backupManagerService.getDataManagementIntent(mUserOneId, TEST_TRANSPORT); verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT); } @@ -589,9 +658,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.getDataManagementIntent(TEST_TRANSPORT); + backupManagerService.getDataManagementIntent(mUserTwoId, TEST_TRANSPORT); verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT); } @@ -601,8 +672,9 @@ public class BackupManagerServiceTest { public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.getDataManagementLabel(TEST_TRANSPORT); + backupManagerService.getDataManagementLabel(mUserOneId, TEST_TRANSPORT); verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT); } @@ -610,9 +682,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.getDataManagementLabel(TEST_TRANSPORT); + backupManagerService.getDataManagementLabel(mUserTwoId, TEST_TRANSPORT); verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT); } @@ -620,7 +694,6 @@ public class BackupManagerServiceTest { // --------------------------------------------- // Settings tests // --------------------------------------------- - /** * Test that the backup services throws a {@link SecurityException} if the caller does not have * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. @@ -681,8 +754,9 @@ public class BackupManagerServiceTest { public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.setAutoRestore(true); + backupManagerService.setAutoRestore(mUserOneId, true); verify(mUserOneService).setAutoRestore(true); } @@ -690,62 +764,13 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); - - backupManagerService.setAutoRestore(true); - - verify(mUserOneService, never()).setAutoRestore(true); - } - - /** Test that the backup service routes methods correctly to the user that requests it. */ - @Test - public void testSetBackupProvisioned_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.setBackupProvisioned(true); - - verify(mUserOneService).setBackupProvisioned(true); - } - - /** Test that the backup service does not route methods for non-registered users. */ - @Test - public void testSetBackupProvisioned_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); - - backupManagerService.setBackupProvisioned(true); - - verify(mUserOneService, never()).setBackupProvisioned(true); - } - - /** - * Test that the backup services throws a {@link SecurityException} if the caller does not have - * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. - */ - @Test - public void testIsBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - - expectThrows( - SecurityException.class, () -> backupManagerService.isBackupEnabled(mUserTwoId)); - } - - /** - * Test that the backup service does not throw a {@link SecurityException} if the caller has - * INTERACT_ACROSS_USERS_FULL permission and passes a different user id. - */ - @Test - public void testIsBackupEnabled_withPermission_propagatesForNonCallingUser() { - BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); - backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService); - setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); - - backupManagerService.isBackupEnabled(mUserTwoId); + backupManagerService.setAutoRestore(mUserTwoId, true); - verify(mUserTwoService).isBackupEnabled(); + verify(mUserOneService, never()).setAutoRestore(true); } /** Test that the backup service routes methods correctly to the user that requests it. */ @@ -781,8 +806,9 @@ public class BackupManagerServiceTest { public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.isAppEligibleForBackup(TEST_PACKAGE); + backupManagerService.isAppEligibleForBackup(mUserOneId, TEST_PACKAGE); verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE); } @@ -790,9 +816,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.isAppEligibleForBackup(TEST_PACKAGE); + backupManagerService.isAppEligibleForBackup(mUserTwoId, TEST_PACKAGE); verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE); } @@ -803,9 +831,10 @@ public class BackupManagerServiceTest { throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); String[] packages = {TEST_PACKAGE}; - backupManagerService.filterAppsEligibleForBackup(packages); + backupManagerService.filterAppsEligibleForBackup(mUserOneId, packages); verify(mUserOneService).filterAppsEligibleForBackup(packages); } @@ -814,10 +843,12 @@ public class BackupManagerServiceTest { @Test public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); String[] packages = {TEST_PACKAGE}; - backupManagerService.filterAppsEligibleForBackup(packages); + backupManagerService.filterAppsEligibleForBackup(mUserTwoId, packages); verify(mUserOneService, never()).filterAppsEligibleForBackup(packages); } @@ -1001,7 +1032,7 @@ public class BackupManagerServiceTest { @Test public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); FullBackupJob job = new FullBackupJob(); backupManagerService.beginFullBackup(job); @@ -1024,7 +1055,7 @@ public class BackupManagerServiceTest { @Test public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); backupManagerService.endFullBackup(); @@ -1046,9 +1077,10 @@ public class BackupManagerServiceTest { public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); String[] packages = {TEST_PACKAGE}; - backupManagerService.fullTransportBackup(packages); + backupManagerService.fullTransportBackup(mUserOneId, packages); verify(mUserOneService).fullTransportBackup(packages); } @@ -1056,10 +1088,12 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); String[] packages = {TEST_PACKAGE}; - backupManagerService.fullTransportBackup(packages); + backupManagerService.fullTransportBackup(mUserTwoId, packages); verify(mUserOneService, never()).fullTransportBackup(packages); } @@ -1073,8 +1107,9 @@ public class BackupManagerServiceTest { public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0); + backupManagerService.restoreAtInstall(mUserOneId, TEST_PACKAGE, /* token */ 0); verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0); } @@ -1082,9 +1117,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0); + backupManagerService.restoreAtInstall(mUserTwoId, TEST_PACKAGE, /* token */ 0); verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0); } @@ -1094,8 +1131,9 @@ public class BackupManagerServiceTest { public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); + backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT); verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); } @@ -1103,9 +1141,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); + backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT); verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); } @@ -1116,8 +1156,9 @@ public class BackupManagerServiceTest { throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.getAvailableRestoreToken(TEST_PACKAGE); + backupManagerService.getAvailableRestoreToken(mUserOneId, TEST_PACKAGE); verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE); } @@ -1125,9 +1166,11 @@ public class BackupManagerServiceTest { /** Test that the backup service does not route methods for non-registered users. */ @Test public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.getAvailableRestoreToken(TEST_PACKAGE); + backupManagerService.getAvailableRestoreToken(mUserTwoId, TEST_PACKAGE); verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE); } @@ -1140,7 +1183,7 @@ public class BackupManagerServiceTest { @Test public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); backupManagerService.setBackupPassword("currentPassword", "newPassword"); @@ -1161,7 +1204,7 @@ public class BackupManagerServiceTest { @Test public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); backupManagerService.hasBackupPassword(); @@ -1377,10 +1420,16 @@ public class BackupManagerServiceTest { throws Exception { BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class); backupManagerService.acknowledgeAdbBackupOrRestore( - /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer); + mUserOneId, + /* token */ 0, + /* allow */ true, + "currentPassword", + "encryptionPassword", + observer); verify(mUserOneService) .acknowledgeAdbBackupOrRestore( @@ -1395,11 +1444,18 @@ public class BackupManagerServiceTest { @Test public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall() throws Exception { - BackupManagerService backupManagerService = createService(); + BackupManagerService backupManagerService = + createServiceAndRegisterUser(mUserOneId, mUserOneService); + setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class); backupManagerService.acknowledgeAdbBackupOrRestore( - /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer); + mUserTwoId, + /* token */ 0, + /* allow */ true, + "currentPassword", + "encryptionPassword", + observer); verify(mUserOneService, never()) .acknowledgeAdbBackupOrRestore( @@ -1418,7 +1474,7 @@ public class BackupManagerServiceTest { @Test public void testDump_onRegisteredUser_callsMethodForUser() throws Exception { BackupManagerService backupManagerService = - createServiceAndRegisterUser(mUserOneId, mUserOneService); + createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService); File testFile = new File(mContext.getFilesDir(), "test"); testFile.createNewFile(); FileDescriptor fileDescriptor = new FileDescriptor(); diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java index 5615dff6c607..0851cf3bc4c0 100644 --- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java @@ -307,14 +307,17 @@ public class TrampolineTest { mTrampoline.dataChangedForUser(mUserId, PACKAGE_NAME); - verify(mBackupManagerServiceMock).dataChanged(PACKAGE_NAME); + verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME); } @Test public void dataChanged_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.dataChanged(PACKAGE_NAME); - verify(mBackupManagerServiceMock).dataChanged(PACKAGE_NAME); + + verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME); } @Test @@ -329,14 +332,17 @@ public class TrampolineTest { mTrampoline.clearBackupDataForUser(mUserId, TRANSPORT_NAME, PACKAGE_NAME); - verify(mBackupManagerServiceMock).clearBackupData(TRANSPORT_NAME, PACKAGE_NAME); + verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME); } @Test public void clearBackupData_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.clearBackupData(TRANSPORT_NAME, PACKAGE_NAME); - verify(mBackupManagerServiceMock).clearBackupData(TRANSPORT_NAME, PACKAGE_NAME); + + verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME); } @Test @@ -351,14 +357,17 @@ public class TrampolineTest { mTrampoline.agentConnectedForUser(mUserId, PACKAGE_NAME, mAgentMock); - verify(mBackupManagerServiceMock).agentConnected(PACKAGE_NAME, mAgentMock); + verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock); } @Test public void agentConnected_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock); - verify(mBackupManagerServiceMock).agentConnected(PACKAGE_NAME, mAgentMock); + + verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock); } @Test @@ -373,14 +382,17 @@ public class TrampolineTest { mTrampoline.agentDisconnectedForUser(mUserId, PACKAGE_NAME); - verify(mBackupManagerServiceMock).agentDisconnected(PACKAGE_NAME); + verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME); } @Test public void agentDisconnected_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.agentDisconnected(PACKAGE_NAME); - verify(mBackupManagerServiceMock).agentDisconnected(PACKAGE_NAME); + + verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME); } @Test @@ -395,14 +407,17 @@ public class TrampolineTest { mTrampoline.restoreAtInstallForUser(mUserId, PACKAGE_NAME, 123); - verify(mBackupManagerServiceMock).restoreAtInstall(PACKAGE_NAME, 123); + verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123); } @Test public void restoreAtInstall_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.restoreAtInstall(PACKAGE_NAME, 123); - verify(mBackupManagerServiceMock).restoreAtInstall(PACKAGE_NAME, 123); + + verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123); } @Test @@ -442,14 +457,17 @@ public class TrampolineTest { mTrampoline.setAutoRestoreForUser(mUserId, true); - verify(mBackupManagerServiceMock).setAutoRestore(true); + verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true); } @Test public void setAutoRestore_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.setAutoRestore(true); - verify(mBackupManagerServiceMock).setAutoRestore(true); + + verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true); } @Test @@ -462,7 +480,7 @@ public class TrampolineTest { public void setBackupProvisioned_forwarded() throws RemoteException { mTrampoline.initializeService(UserHandle.USER_SYSTEM); mTrampoline.setBackupProvisioned(true); - verify(mBackupManagerServiceMock).setBackupProvisioned(true); + verifyNoMoreInteractions(mBackupManagerServiceMock); } @Test @@ -568,8 +586,10 @@ public class TrampolineTest { @Test public void fullTransportBackupForUser_forwarded() throws RemoteException { mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES); - verify(mBackupManagerServiceMock).fullTransportBackup(PACKAGE_NAMES); + + verify(mBackupManagerServiceMock).fullTransportBackup(mUserId, PACKAGE_NAMES); } @Test @@ -605,17 +625,32 @@ public class TrampolineTest { ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock); - verify(mBackupManagerServiceMock).acknowledgeAdbBackupOrRestore(123, true, CURRENT_PASSWORD, - ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock); + verify(mBackupManagerServiceMock) + .acknowledgeAdbBackupOrRestore( + mUserId, + 123, + true, + CURRENT_PASSWORD, + ENCRYPTION_PASSWORD, + mFullBackupRestoreObserverMock); } @Test public void acknowledgeFullBackupOrRestore_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock); - verify(mBackupManagerServiceMock).acknowledgeAdbBackupOrRestore(123, true, CURRENT_PASSWORD, - ENCRYPTION_PASSWORD, mFullBackupRestoreObserverMock); + + verify(mBackupManagerServiceMock) + .acknowledgeAdbBackupOrRestore( + mUserId, + 123, + true, + CURRENT_PASSWORD, + ENCRYPTION_PASSWORD, + mFullBackupRestoreObserverMock); } @Test @@ -626,22 +661,21 @@ public class TrampolineTest { @Test public void getCurrentTransportForUser_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.getCurrentTransport()).thenReturn(TRANSPORT_NAME); + when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransportForUser(mUserId)); - - verify(mBackupManagerServiceMock).getCurrentTransport(); + verify(mBackupManagerServiceMock).getCurrentTransport(mUserId); } @Test public void getCurrentTransport_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.getCurrentTransport()).thenReturn(TRANSPORT_NAME); - + TrampolineTestable.sCallingUserId = mUserId; + when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransport()); - verify(mBackupManagerServiceMock).getCurrentTransport(); + verify(mBackupManagerServiceMock).getCurrentTransport(mUserId); } @Test @@ -652,21 +686,22 @@ public class TrampolineTest { @Test public void listAllTransportsForUser_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.listAllTransports()).thenReturn(TRANSPORTS); + when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals(TRANSPORTS, mTrampoline.listAllTransportsForUser(mUserId)); - verify(mBackupManagerServiceMock).listAllTransports(); + verify(mBackupManagerServiceMock).listAllTransports(mUserId); } @Test public void listAllTransports_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.listAllTransports()).thenReturn(TRANSPORTS); - + TrampolineTestable.sCallingUserId = mUserId; + when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS); mTrampoline.initializeService(UserHandle.USER_SYSTEM); + assertEquals(TRANSPORTS, mTrampoline.listAllTransports()); - verify(mBackupManagerServiceMock).listAllTransports(); + verify(mBackupManagerServiceMock).listAllTransports(mUserId); } @Test @@ -678,12 +713,12 @@ public class TrampolineTest { @Test public void listAllTransportComponentsForUser_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.listAllTransportComponents()).thenReturn( + when(mBackupManagerServiceMock.listAllTransportComponents(mUserId)).thenReturn( TRANSPORT_COMPONENTS); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponentsForUser(mUserId)); - verify(mBackupManagerServiceMock).listAllTransportComponents(); + verify(mBackupManagerServiceMock).listAllTransportComponents(mUserId); } @Test @@ -730,8 +765,15 @@ public class TrampolineTest { null, "Data Management"); - verify(mBackupManagerServiceMock).updateTransportAttributes(TRANSPORT_COMPONENT_NAME, - TRANSPORT_NAME, null, "Transport Destination", null, "Data Management"); + verify(mBackupManagerServiceMock) + .updateTransportAttributes( + mUserId, + TRANSPORT_COMPONENT_NAME, + TRANSPORT_NAME, + null, + "Transport Destination", + null, + "Data Management"); } @Test @@ -746,14 +788,17 @@ public class TrampolineTest { mTrampoline.selectBackupTransportForUser(mUserId, TRANSPORT_NAME); - verify(mBackupManagerServiceMock).selectBackupTransport(TRANSPORT_NAME); + verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME); } @Test public void selectBackupTransport_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.selectBackupTransport(TRANSPORT_NAME); - verify(mBackupManagerServiceMock).selectBackupTransport(TRANSPORT_NAME); + + verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME); } @Test @@ -829,8 +874,8 @@ public class TrampolineTest { mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null); - verify(mBackupManagerServiceMock).selectBackupTransportAsync(TRANSPORT_COMPONENT_NAME, - null); + verify(mBackupManagerServiceMock) + .selectBackupTransportAsync(mUserId, TRANSPORT_COMPONENT_NAME, null); } @Test @@ -842,25 +887,26 @@ public class TrampolineTest { @Test public void getConfigurationIntentForUser_forwarded() throws RemoteException { Intent configurationIntentStub = new Intent(); - when(mBackupManagerServiceMock.getConfigurationIntent(TRANSPORT_NAME)).thenReturn( + when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn( configurationIntentStub); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals( configurationIntentStub, mTrampoline.getConfigurationIntentForUser(mUserId, TRANSPORT_NAME)); - verify(mBackupManagerServiceMock).getConfigurationIntent(TRANSPORT_NAME); + verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME); } @Test public void getConfigurationIntent_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; Intent configurationIntentStub = new Intent(); - when(mBackupManagerServiceMock.getConfigurationIntent(TRANSPORT_NAME)).thenReturn( + when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn( configurationIntentStub); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + assertEquals(configurationIntentStub, mTrampoline.getConfigurationIntent(TRANSPORT_NAME)); - verify(mBackupManagerServiceMock).getConfigurationIntent(TRANSPORT_NAME); + verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME); } @Test @@ -871,24 +917,25 @@ public class TrampolineTest { @Test public void getDestinationStringForUser_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.getDestinationString(TRANSPORT_NAME)).thenReturn( + when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn( DESTINATION_STRING); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals( DESTINATION_STRING, mTrampoline.getDestinationStringForUser(mUserId, TRANSPORT_NAME)); - verify(mBackupManagerServiceMock).getDestinationString(TRANSPORT_NAME); + verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME); } @Test public void getDestinationString_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.getDestinationString(TRANSPORT_NAME)).thenReturn( + TrampolineTestable.sCallingUserId = mUserId; + when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn( DESTINATION_STRING); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals(DESTINATION_STRING, mTrampoline.getDestinationString(TRANSPORT_NAME)); - verify(mBackupManagerServiceMock).getDestinationString(TRANSPORT_NAME); + verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME); } @Test @@ -900,25 +947,26 @@ public class TrampolineTest { @Test public void getDataManagementIntentForUser_forwarded() throws RemoteException { Intent dataManagementIntent = new Intent(); - when(mBackupManagerServiceMock.getDataManagementIntent(TRANSPORT_NAME)).thenReturn( + when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn( dataManagementIntent); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals( dataManagementIntent, mTrampoline.getDataManagementIntentForUser(mUserId, TRANSPORT_NAME)); - verify(mBackupManagerServiceMock).getDataManagementIntent(TRANSPORT_NAME); + verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME); } @Test public void getDataManagementIntent_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; Intent dataManagementIntent = new Intent(); - when(mBackupManagerServiceMock.getDataManagementIntent(TRANSPORT_NAME)).thenReturn( + when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn( dataManagementIntent); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + assertEquals(dataManagementIntent, mTrampoline.getDataManagementIntent(TRANSPORT_NAME)); - verify(mBackupManagerServiceMock).getDataManagementIntent(TRANSPORT_NAME); + verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME); } @Test @@ -929,24 +977,25 @@ public class TrampolineTest { @Test public void getDataManagementLabelForUser_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.getDataManagementLabel(TRANSPORT_NAME)).thenReturn( + when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn( DATA_MANAGEMENT_LABEL); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals( DATA_MANAGEMENT_LABEL, mTrampoline.getDataManagementLabelForUser(mUserId, TRANSPORT_NAME)); - verify(mBackupManagerServiceMock).getDataManagementLabel(TRANSPORT_NAME); + verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME); } @Test public void getDataManagementLabel_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.getDataManagementLabel(TRANSPORT_NAME)).thenReturn( + TrampolineTestable.sCallingUserId = mUserId; + when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn( DATA_MANAGEMENT_LABEL); - mTrampoline.initializeService(UserHandle.USER_SYSTEM); + assertEquals(DATA_MANAGEMENT_LABEL, mTrampoline.getDataManagementLabel(TRANSPORT_NAME)); - verify(mBackupManagerServiceMock).getDataManagementLabel(TRANSPORT_NAME); + verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME); } @Test @@ -961,7 +1010,8 @@ public class TrampolineTest { mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME); - verify(mBackupManagerServiceMock).beginRestoreSession(PACKAGE_NAME, TRANSPORT_NAME); + verify(mBackupManagerServiceMock) + .beginRestoreSession(mUserId, PACKAGE_NAME, TRANSPORT_NAME); } @Test @@ -972,9 +1022,12 @@ public class TrampolineTest { @Test public void opComplete_forwarded() throws RemoteException { + TrampolineTestable.sCallingUserId = mUserId; mTrampoline.initializeService(UserHandle.USER_SYSTEM); + mTrampoline.opComplete(1, 2); - verify(mBackupManagerServiceMock).opComplete(1, 2); + + verify(mBackupManagerServiceMock).opComplete(mUserId, 1, 2); } @Test @@ -986,11 +1039,12 @@ public class TrampolineTest { @Test public void getAvailableRestoreTokenForUser_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.getAvailableRestoreToken(PACKAGE_NAME)).thenReturn(123L); + when(mBackupManagerServiceMock.getAvailableRestoreToken(mUserId, PACKAGE_NAME)) + .thenReturn(123L); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertEquals(123, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME)); - verify(mBackupManagerServiceMock).getAvailableRestoreToken(PACKAGE_NAME); + verify(mBackupManagerServiceMock).getAvailableRestoreToken(mUserId, PACKAGE_NAME); } @Test @@ -1002,11 +1056,12 @@ public class TrampolineTest { @Test public void isAppEligibleForBackupForUser_forwarded() throws RemoteException { - when(mBackupManagerServiceMock.isAppEligibleForBackup(PACKAGE_NAME)).thenReturn(true); + when(mBackupManagerServiceMock.isAppEligibleForBackup(mUserId, PACKAGE_NAME)) + .thenReturn(true); mTrampoline.initializeService(UserHandle.USER_SYSTEM); assertTrue(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME)); - verify(mBackupManagerServiceMock).isAppEligibleForBackup(PACKAGE_NAME); + verify(mBackupManagerServiceMock).isAppEligibleForBackup(mUserId, PACKAGE_NAME); } @Test 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 527a1eef7b0f..2aed35f32d6c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -208,6 +208,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private static class TestableNotificationManagerService extends NotificationManagerService { int countSystemChecks = 0; boolean isSystemUid = true; + int countLogSmartSuggestionsVisible = 0; public TestableNotificationManagerService(Context context) { super(context); @@ -244,6 +245,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { protected void handleSavePolicyFile() { return; } + + @Override + void logSmartSuggestionsVisible(NotificationRecord r) { + super.logSmartSuggestionsVisible(r); + countLogSmartSuggestionsVisible++; + } + + } private class TestableToastCallback extends ITransientNotification.Stub { @@ -3507,6 +3516,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testAppOverlay() throws Exception { + mBinderService.setAppOverlaysAllowed(PKG, mUid, false); + assertFalse(mBinderService.areAppOverlaysAllowedForPackage(PKG, mUid)); + } + + @Test public void testIsCallerInstantApp_primaryUser() throws Exception { ApplicationInfo info = new ApplicationInfo(); info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT; @@ -3777,4 +3792,43 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mAssistants).notifyAssistantActionClicked( eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant)); } + + @Test + public void testLogSmartSuggestionsVisible_triggerOnExpandAndVisible() { + NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); + mService.addNotification(r); + + mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true); + NotificationVisibility[] notificationVisibility = new NotificationVisibility[] { + NotificationVisibility.obtain(r.getKey(), 0, 0, true) + }; + mService.mNotificationDelegate.onNotificationVisibilityChanged(notificationVisibility, + new NotificationVisibility[0]); + + assertEquals(1, mService.countLogSmartSuggestionsVisible); + } + + @Test + public void testLogSmartSuggestionsVisible_noTriggerOnExpand() { + NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); + mService.addNotification(r); + + mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true); + + assertEquals(0, mService.countLogSmartSuggestionsVisible); + } + + @Test + public void testLogSmartSuggestionsVisible_noTriggerOnVisible() { + NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); + mService.addNotification(r); + + NotificationVisibility[] notificationVisibility = new NotificationVisibility[] { + NotificationVisibility.obtain(r.getKey(), 0, 0, true) + }; + mService.mNotificationDelegate.onNotificationVisibilityChanged(notificationVisibility, + new NotificationVisibility[0]); + + assertEquals(0, mService.countLogSmartSuggestionsVisible); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index b0279357d372..0b7348194b26 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -1584,39 +1584,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testUpdateGroup_fromSystem_appOverlay() { - NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); - mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true); - - // from system, allowed - NotificationChannelGroup update = ncg.clone(); - update.setAllowAppOverlay(false); - - mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, update, false); - NotificationChannelGroup updated = - mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1); - assertFalse(updated.canOverlayApps()); - assertEquals(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY, - updated.getUserLockedFields()); - } - - @Test - public void testUpdateGroup_fromApp_appOverlay() { - NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); - mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true); - - // from app, not allowed - NotificationChannelGroup update = new NotificationChannelGroup("group1", "name1"); - update.setAllowAppOverlay(false); - - mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true); - NotificationChannelGroup updated = - mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1); - assertTrue(updated.canOverlayApps()); - assertEquals(0, updated.getUserLockedFields()); - } - - @Test public void testCannotCreateChannel_badGroup() { NotificationChannel channel1 = new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); @@ -2192,4 +2159,32 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.toggleNotificationDelegate(PKG_O, UID_O, true); assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O)); } + + @Test + public void testAllowAppOverlay_defaults() throws Exception { + assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); + + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); + loadStreamXml(baos, false); + + assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); + assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); + } + + @Test + public void testAllowAppOverlay_xml() throws Exception { + mHelper.setAppOverlaysAllowed(PKG_O, UID_O, false); + assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); + assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY, + mHelper.getAppLockedFields(PKG_O, UID_O)); + + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); + loadStreamXml(baos, false); + + assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); + assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY, + mHelper.getAppLockedFields(PKG_O, UID_O)); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index b955e56c80a8..49f134fdeac0 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -76,7 +76,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { verify(mAm, times(1)).setExactAndAllowWhileIdle( anyInt(), captor.capture(), any(PendingIntent.class)); long actualSnoozedUntilDuration = captor.getValue() - SystemClock.elapsedRealtime(); - assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 25); + assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 250); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey())); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 9b18388b5305..58302d6b8b75 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -23,8 +23,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; -import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.view.Display.DEFAULT_DISPLAY; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; @@ -36,7 +36,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.ActivityDisplay.POSITION_TOP; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; -import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -47,18 +47,27 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.refEq; import android.app.ActivityOptions; +import android.content.ComponentName; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; import android.graphics.Rect; -import android.os.Build; import android.platform.test.annotations.Presubmit; +import android.util.Pair; + import androidx.test.filters.MediumTest; + +import com.android.internal.app.ResolverActivity; + import org.junit.Before; import org.junit.Test; import java.util.ArrayList; +import java.util.List; /** * Tests for the {@link ActivityStackSupervisor} class. @@ -385,31 +394,10 @@ public class RootActivityContainerTests extends ActivityTestsBase { } /** - * Tests home activities that targeted sdk before Q cannot start on secondary display. - */ - @Test - public void testStartHomeTargetSdkBeforeQ() throws Exception { - final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); - mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); - doReturn(true).when(secondDisplay).supportsSystemDecorations(); - - final ActivityInfo info = new ActivityInfo(); - info.launchMode = LAUNCH_MULTIPLE; - info.applicationInfo = new ApplicationInfo(); - info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; - assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId, - false /* allowInstrumenting */)); - - info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P; - assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId, - false /* allowInstrumenting */)); - } - - /** * Tests that home activities can be started on the displays that supports system decorations. */ - @Test - public void testStartHomeOnAllDisplays() { + // TODO (b/118206886): Will add it back once launcher's patch is merged into master. + private void testStartHomeOnAllDisplays() { // Create secondary displays. final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); @@ -477,4 +465,142 @@ public class RootActivityContainerTests extends ActivityTestsBase { assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, true /* allowInstrumenting*/)); } + + /** + * Tests that secondary home should be selected if default home not set. + */ + @Test + public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() { + final Intent defaultHomeIntent = mService.getHomeIntent(); + final ActivityInfo aInfoDefault = new ActivityInfo(); + aInfoDefault.name = ResolverActivity.class.getName(); + doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), + refEq(defaultHomeIntent)); + + final String secondaryHomeComponent = mService.mContext.getResources().getString( + com.android.internal.R.string.config_secondaryHomeComponent); + final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent); + final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); + final ActivityInfo aInfoSecondary = new ActivityInfo(); + aInfoSecondary.name = comp.getClassName(); + doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(), + refEq(secondaryHomeIntent)); + + // Should fallback to secondary home if default home not set. + final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer + .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); + + assertEquals(comp.getClassName(), resolvedInfo.first.name); + } + + /** + * Tests that secondary home should be selected if default home not support secondary displays + * or there is no matched activity in the same package as selected default home. + */ + @Test + public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() { + final Intent defaultHomeIntent = mService.getHomeIntent(); + final ActivityInfo aInfoDefault = new ActivityInfo(); + aInfoDefault.name = "fakeHomeActivity"; + aInfoDefault.applicationInfo = new ApplicationInfo(); + aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; + doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), + refEq(defaultHomeIntent)); + + final List<ResolveInfo> resolutions = new ArrayList<>(); + doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any()); + + final String secondaryHomeComponent = mService.mContext.getResources().getString( + com.android.internal.R.string.config_secondaryHomeComponent); + final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent); + final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); + final ActivityInfo aInfoSecondary = new ActivityInfo(); + aInfoSecondary.name = comp.getClassName(); + doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(), + refEq(secondaryHomeIntent)); + + // Should fallback to secondary home if selected default home not support secondary displays + // or there is no matched activity in the same package as selected default home. + final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer + .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); + + assertEquals(comp.getClassName(), resolvedInfo.first.name); + } + + /** + * Tests that default home activity should be selected if it already support secondary displays. + */ + @Test + public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() { + final Intent homeIntent = mService.getHomeIntent(); + final ActivityInfo aInfoDefault = new ActivityInfo(); + aInfoDefault.name = "fakeHomeActivity"; + aInfoDefault.applicationInfo = new ApplicationInfo(); + aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; + doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), + refEq(homeIntent)); + + final List<ResolveInfo> resolutions = new ArrayList<>(); + final ResolveInfo infoFake1 = new ResolveInfo(); + infoFake1.activityInfo = new ActivityInfo(); + infoFake1.activityInfo.name = "fakeActivity1"; + infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); + infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; + final ResolveInfo infoFake2 = new ResolveInfo(); + infoFake2.activityInfo = aInfoDefault; + resolutions.add(infoFake1); + resolutions.add(infoFake2); + doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any()); + + doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( + any(), anyInt(), anyBoolean()); + + // Use default home activity if it support secondary displays. + final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer + .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); + + assertEquals(aInfoDefault.applicationInfo.packageName, + resolvedInfo.first.applicationInfo.packageName); + assertEquals(aInfoDefault.name, resolvedInfo.first.name); + } + + /** + * Tests that the first one that matches should be selected if there are multiple activities. + */ + @Test + public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { + final Intent homeIntent = mService.getHomeIntent(); + final ActivityInfo aInfoDefault = new ActivityInfo(); + aInfoDefault.name = "fakeHomeActivity"; + aInfoDefault.applicationInfo = new ApplicationInfo(); + aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; + doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), + refEq(homeIntent)); + + final List<ResolveInfo> resolutions = new ArrayList<>(); + final ResolveInfo infoFake1 = new ResolveInfo(); + infoFake1.activityInfo = new ActivityInfo(); + infoFake1.activityInfo.name = "fakeActivity1"; + infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); + infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; + final ResolveInfo infoFake2 = new ResolveInfo(); + infoFake2.activityInfo = new ActivityInfo(); + infoFake2.activityInfo.name = "fakeActivity2"; + infoFake2.activityInfo.applicationInfo = new ApplicationInfo(); + infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2"; + resolutions.add(infoFake1); + resolutions.add(infoFake2); + doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any()); + + doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( + any(), anyInt(), anyBoolean()); + + // Use the first one of matched activities in the same package as selected default home. + final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer + .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); + + assertEquals(infoFake1.activityInfo.applicationInfo.packageName, + resolvedInfo.first.applicationInfo.packageName); + assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java index 3991e06d6f96..c343fe7d0675 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertFalse; @@ -66,6 +67,10 @@ public class TaskPositioningControllerTests extends WindowTestsBase { synchronized (mWm.mGlobalLock) { mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow); } + + spyOn(mDisplayContent); + InputMonitor inputMonitor = mock(InputMonitor.class); + when(mDisplayContent.getInputMonitor()).thenReturn(inputMonitor); } @Test diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl b/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl new file mode 100644 index 000000000000..e2fa7e4032c1 --- /dev/null +++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.aidl @@ -0,0 +1,22 @@ +/* + * 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.telecom; + +/** + * {@hide} + */ +parcelable PhoneAccountSuggestion;
\ No newline at end of file diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java index 4e6a178c8170..b401bcf0f876 100644 --- a/telecomm/java/android/telecom/PhoneAccountSuggestion.java +++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java @@ -24,6 +24,7 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; public final class PhoneAccountSuggestion implements Parcelable { @@ -132,4 +133,19 @@ public final class PhoneAccountSuggestion implements Parcelable { dest.writeInt(mReason); dest.writeByte((byte) (mShouldAutoSelect ? 1 : 0)); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PhoneAccountSuggestion that = (PhoneAccountSuggestion) o; + return mReason == that.mReason + && mShouldAutoSelect == that.mShouldAutoSelect + && Objects.equals(mHandle, that.mHandle); + } + + @Override + public int hashCode() { + return Objects.hash(mHandle, mReason, mShouldAutoSelect); + } } diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestionService.java b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java new file mode 100644 index 000000000000..ba3822cb9951 --- /dev/null +++ b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecom; + +import android.annotation.NonNull; +import android.annotation.SdkConstant; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; + +import com.android.internal.telecom.IPhoneAccountSuggestionCallback; +import com.android.internal.telecom.IPhoneAccountSuggestionService; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Base class for service that allows system apps to suggest phone accounts for outgoing calls. + * + * Phone account suggestions allow OEMs to intelligently select phone accounts based on knowledge + * about the user's past behavior, carrier billing patterns, or other factors unknown to the AOSP + * Telecom system. + * OEMs who wish to provide a phone account suggestion service on their device should implement this + * service in an app that resides in the /system/priv-app/ directory on their device. For security + * reasons, the service's entry {@code AndroidManifest.xml} file must declare the + * {@link android.Manifest.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE} permission: + * <pre> + * {@code + * <service android:name="your.package.YourServiceName" + * android:permission="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE"> + * <intent-filter> + * <action android:name="android.telecom.PhoneAccountSuggestionService"/> + * </intent-filter> + * </service> + * } + * </pre> + * Only one system app on each device may implement this service. If multiple system apps implement + * this service, none of them will be queried for suggestions. + * @hide + */ +@SystemApi +@TestApi +public class PhoneAccountSuggestionService extends Service { + /** + * The {@link Intent} that must be declared in the {@code intent-filter} element of the + * service's manifest entry. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService"; + + private IPhoneAccountSuggestionService mInterface = new IPhoneAccountSuggestionService.Stub() { + @Override + public void onAccountSuggestionRequest(IPhoneAccountSuggestionCallback callback, + String number) { + mCallbackMap.put(number, callback); + PhoneAccountSuggestionService.this.onAccountSuggestionRequest(number); + } + }; + + private final Map<String, IPhoneAccountSuggestionCallback> mCallbackMap = + new HashMap<>(); + + @Override + public IBinder onBind(Intent intent) { + return mInterface.asBinder(); + } + + /** + * The system calls this method during the outgoing call flow if it needs account suggestions. + * + * The implementer of this service must override this method to implement its account suggestion + * logic. After preparing the suggestions, the implementation of the service must call + * {@link #suggestPhoneAccounts(String, List)} to deliver the suggestions back to the system. + * + * Note that the system will suspend the outgoing call process after it calls this method until + * this service calls {@link #suggestPhoneAccounts}. + * + * @param number The phone number to provide suggestions for. + */ + public void onAccountSuggestionRequest(@NonNull String number) {} + + /** + * The implementation of this service calls this method to deliver suggestions to the system. + * + * The implementation of this service must call this method after receiving a call to + * {@link #onAccountSuggestionRequest(String)}. If no suggestions are available, pass an empty + * list as the {@code suggestions} argument. + * + * @param number The phone number to provide suggestions for. + * @param suggestions The list of suggestions. + */ + public final void suggestPhoneAccounts(@NonNull String number, + @NonNull List<PhoneAccountSuggestion> suggestions) { + IPhoneAccountSuggestionCallback callback = mCallbackMap.remove(number); + if (callback == null) { + Log.w(this, "No suggestions requested for the number %s", Log.pii(number)); + return; + } + try { + callback.suggestPhoneAccounts(number, suggestions); + } catch (RemoteException e) { + Log.w(this, "Remote exception calling suggestPhoneAccounts"); + } + } +} diff --git a/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl new file mode 100644 index 000000000000..cb142417451c --- /dev/null +++ b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.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 com.android.internal.telecom; + +import android.telecom.PhoneAccountSuggestion; +/** + * Internal remote callback interface for a phone acct suggestion service. + * @hide + */ +oneway interface IPhoneAccountSuggestionCallback{ + void suggestPhoneAccounts(in String number, in List<PhoneAccountSuggestion> suggestions); +} diff --git a/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl new file mode 100644 index 000000000000..0ffab93d9f1b --- /dev/null +++ b/telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl @@ -0,0 +1,28 @@ +/* + * 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.internal.telecom; + +import com.android.internal.telecom.IPhoneAccountSuggestionCallback; + +/** + * Internal remote interface for a phone acct suggestion service. + * @hide + */ +oneway interface IPhoneAccountSuggestionService { + void onAccountSuggestionRequest(in IPhoneAccountSuggestionCallback callback, + in String number); +} diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 50204e70f63d..954a7098f6be 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -292,6 +292,8 @@ interface ITelecomService { void setTestDefaultCallRedirectionApp(String packageName); + void setTestPhoneAcctSuggestionComponent(String flattenedComponentName); + void setTestDefaultCallScreeningApp(String packageName); void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index fd14916ecac9..f87472d9ec38 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2400,6 +2400,34 @@ public class CarrierConfigManager { public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string"; + /** + * Controls RSRP threshold at which AlternativeNetworkService will decide whether + * the opportunistic network is good enough for internet data. + */ + public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT = + "opportunistic_network_entry_threshold_rsrp_int"; + + /** + * Controls RSSNR threshold at which AlternativeNetworkService will decide whether + * the opportunistic network is good enough for internet data. + */ + public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = + "opportunistic_network_entry_threshold_rssnr_int"; + + /** + * Controls RSRP threshold below which AlternativeNetworkService will decide whether + * the opportunistic network available is not good enough for internet data. + */ + public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = + "opportunistic_network_exit_threshold_rsrp_int"; + + /** + * Controls RSSNR threshold below which AlternativeNetworkService will decide whether + * the opportunistic network available is not good enough for internet data. + */ + public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = + "opportunistic_network_exit_threshold_rssnr_int"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -2761,6 +2789,14 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */); sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING, "connected_mmwave:None,connected:5G,not_restricted:None,restricted:None"); + /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */ + sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108); + /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */ + sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -118); + /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_GOOD */ + sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 45); + /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_MODERATE */ + sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 10); } /** diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index c6f7d0e458db..c53b37d8ae6a 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -15,151 +15,383 @@ */ package android.telephony; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.content.Context; import android.os.PersistableBundle; +import com.android.internal.util.ArrayUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** - * Returned as the reason for a connection failure as defined - * by RIL_DataCallFailCause in ril.h and some local errors. + * Returned as the reason for a data connection failure as defined by modem and some local errors. * @hide */ -public enum DataFailCause { - NONE(0), +public final class DataFailCause { + /** There is no failure */ + public static final int NONE = 0; // This series of errors as specified by the standards // specified in ril.h - OPERATOR_BARRED(0x08), /* no retry */ - NAS_SIGNALLING(0x0E), - LLC_SNDCP(0x19), - INSUFFICIENT_RESOURCES(0x1A), - MISSING_UNKNOWN_APN(0x1B), /* no retry */ - UNKNOWN_PDP_ADDRESS_TYPE(0x1C), /* no retry */ - USER_AUTHENTICATION(0x1D), /* no retry */ - ACTIVATION_REJECT_GGSN(0x1E), /* no retry */ - ACTIVATION_REJECT_UNSPECIFIED(0x1F), - SERVICE_OPTION_NOT_SUPPORTED(0x20), /* no retry */ - SERVICE_OPTION_NOT_SUBSCRIBED(0x21), /* no retry */ - SERVICE_OPTION_OUT_OF_ORDER(0x22), - NSAPI_IN_USE(0x23), /* no retry */ - REGULAR_DEACTIVATION(0x24), /* possibly restart radio, based on config */ - QOS_NOT_ACCEPTED(0x25), - NETWORK_FAILURE(0x26), - UMTS_REACTIVATION_REQ(0x27), - FEATURE_NOT_SUPP(0x28), - TFT_SEMANTIC_ERROR(0x29), - TFT_SYTAX_ERROR(0x2A), - UNKNOWN_PDP_CONTEXT(0x2B), - FILTER_SEMANTIC_ERROR(0x2C), - FILTER_SYTAX_ERROR(0x2D), - PDP_WITHOUT_ACTIVE_TFT(0x2E), - ONLY_IPV4_ALLOWED(0x32), /* no retry */ - ONLY_IPV6_ALLOWED(0x33), /* no retry */ - ONLY_SINGLE_BEARER_ALLOWED(0x34), - ESM_INFO_NOT_RECEIVED(0x35), - PDN_CONN_DOES_NOT_EXIST(0x36), - MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED(0x37), - MAX_ACTIVE_PDP_CONTEXT_REACHED(0x41), - UNSUPPORTED_APN_IN_CURRENT_PLMN(0x42), - INVALID_TRANSACTION_ID(0x51), - MESSAGE_INCORRECT_SEMANTIC(0x5F), - INVALID_MANDATORY_INFO(0x60), - MESSAGE_TYPE_UNSUPPORTED(0x61), - MSG_TYPE_NONCOMPATIBLE_STATE(0x62), - UNKNOWN_INFO_ELEMENT(0x63), - CONDITIONAL_IE_ERROR(0x64), - MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE(0x65), - PROTOCOL_ERRORS(0x6F), /* no retry */ - APN_TYPE_CONFLICT(0x70), - INVALID_PCSCF_ADDR(0x71), - INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN(0x72), - EMM_ACCESS_BARRED(0x73), - EMERGENCY_IFACE_ONLY(0x74), - IFACE_MISMATCH(0x75), - COMPANION_IFACE_IN_USE(0x76), - IP_ADDRESS_MISMATCH(0x77), - IFACE_AND_POL_FAMILY_MISMATCH(0x78), - EMM_ACCESS_BARRED_INFINITE_RETRY(0x79), - AUTH_FAILURE_ON_EMERGENCY_CALL(0x7A), + /** Operator determined barring. */ + public static final int OPERATOR_BARRED = 0x08; + /** NAS signalling. */ + public static final int NAS_SIGNALLING = 0x0E; + /** Logical Link Control (LLC) Sub Network Dependent Convergence Protocol (SNDCP). */ + public static final int LLC_SNDCP = 0x19; + /** Insufficient resources. */ + public static final int INSUFFICIENT_RESOURCES = 0x1A; + /** Missing or unknown APN. */ + public static final int MISSING_UNKNOWN_APN = 0x1B; /* no retry */ + /** Unknown Packet Data Protocol (PDP) address type. */ + public static final int UNKNOWN_PDP_ADDRESS_TYPE = 0x1C; /* no retry */ + /** User authentication. */ + public static final int USER_AUTHENTICATION = 0x1D; /* no retry */ + /** Activation rejected by Gateway GPRS Support Node (GGSN), Serving Gateway or PDN Gateway. */ + public static final int ACTIVATION_REJECT_GGSN = 0x1E; /* no retry */ + /** Activation rejected, unspecified. */ + public static final int ACTIVATION_REJECT_UNSPECIFIED = 0x1F; + /** Service option not supported. */ + public static final int SERVICE_OPTION_NOT_SUPPORTED = 0x20; /* no retry */ + /** Requested service option not subscribed. */ + public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 0x21; /* no retry */ + /** Service option temporarily out of order. */ + public static final int SERVICE_OPTION_OUT_OF_ORDER = 0x22; + /** The Network Service Access Point Identifier (NSAPI) is in use. */ + public static final int NSAPI_IN_USE = 0x23; /* no retry */ + /* possibly restart radio, based on config */ + /** Regular deactivation. */ + public static final int REGULAR_DEACTIVATION = 0x24; + /** Quality of service (QoS) is not accepted. */ + public static final int QOS_NOT_ACCEPTED = 0x25; + /** Network Failure. */ + public static final int NETWORK_FAILURE = 0x26; + /** Universal Mobile Telecommunications System (UMTS) reactivation request. */ + public static final int UMTS_REACTIVATION_REQ = 0x27; + /** Feature not supported. */ + public static final int FEATURE_NOT_SUPP = 0x28; + /** Semantic error in the Traffic flow templates (TFT) operation. */ + public static final int TFT_SEMANTIC_ERROR = 0x29; + /** Syntactical error in the Traffic flow templates (TFT) operation. */ + public static final int TFT_SYTAX_ERROR = 0x2A; + /** Unknown Packet Data Protocol (PDP) context. */ + public static final int UNKNOWN_PDP_CONTEXT = 0x2B; + /** Semantic errors in packet filter. */ + public static final int FILTER_SEMANTIC_ERROR = 0x2C; + /** Syntactical errors in packet filter(s). */ + public static final int FILTER_SYTAX_ERROR = 0x2D; + /** Packet Data Protocol (PDP) without active traffic flow template (TFT). */ + public static final int PDP_WITHOUT_ACTIVE_TFT = 0x2E; + /** Packet Data Protocol (PDP) type IPv4 only allowed. */ + public static final int ONLY_IPV4_ALLOWED = 0x32; /* no retry */ + /** Packet Data Protocol (PDP) type IPv6 only allowed. */ + public static final int ONLY_IPV6_ALLOWED = 0x33; /* no retry */ + /** Single address bearers only allowed. */ + public static final int ONLY_SINGLE_BEARER_ALLOWED = 0x34; + /** EPS Session Management (ESM) information is not received. */ + public static final int ESM_INFO_NOT_RECEIVED = 0x35; + /** PDN connection does not exist. */ + public static final int PDN_CONN_DOES_NOT_EXIST = 0x36; + /** Multiple connections to a same PDN is not allowed. */ + public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37; + /** Packet Data Protocol (PDP) */ + public static final int MAX_ACTIVE_PDP_CONTEXT_REACHED = 0x41; + /** Unsupported APN in current public land mobile network (PLMN). */ + public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 0x42; + /** Invalid transaction id. */ + public static final int INVALID_TRANSACTION_ID = 0x51; + /** Incorrect message semantic. */ + public static final int MESSAGE_INCORRECT_SEMANTIC = 0x5F; + /** Invalid mandatory information. */ + public static final int INVALID_MANDATORY_INFO = 0x60; + /** Unsupported message type. */ + public static final int MESSAGE_TYPE_UNSUPPORTED = 0x61; + /** Message type uncompatible. */ + public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 0x62; + /** Unknown info element. */ + public static final int UNKNOWN_INFO_ELEMENT = 0x63; + /** Conditional Information Element (IE) error. */ + public static final int CONDITIONAL_IE_ERROR = 0x64; + /** Message and protocol state uncompatible. */ + public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 0x65; + /** Protocol errors. */ + public static final int PROTOCOL_ERRORS = 0x6F; /* no retry */ + /** APN type conflict. */ + public static final int APN_TYPE_CONFLICT = 0x70; + /** Invalid Proxy-Call Session Control Function (P-CSCF) address. */ + public static final int INVALID_PCSCF_ADDR = 0x71; + /** Internal data call preempt by high priority APN. */ + public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 0x72; + /** EPS (Evolved Packet System) Mobility Management (EMM) access barred. */ + public static final int EMM_ACCESS_BARRED = 0x73; + /** Emergency interface only. */ + public static final int EMERGENCY_IFACE_ONLY = 0x74; + /** Interface mismatch. */ + public static final int IFACE_MISMATCH = 0x75; + /** Companion interface in use. */ + public static final int COMPANION_IFACE_IN_USE = 0x76; + /** IP address mismatch. */ + public static final int IP_ADDRESS_MISMATCH = 0x77; + public static final int IFACE_AND_POL_FAMILY_MISMATCH = 0x78; + /** EPS (Evolved Packet System) Mobility Management (EMM) access barred infinity retry. **/ + public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 0x79; + /** Authentication failure on emergency call. */ + public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 0x7A; // OEM sepecific error codes. To be used by OEMs when they don't // want to reveal error code which would be replaced by ERROR_UNSPECIFIED - OEM_DCFAILCAUSE_1(0x1001), - OEM_DCFAILCAUSE_2(0x1002), - OEM_DCFAILCAUSE_3(0x1003), - OEM_DCFAILCAUSE_4(0x1004), - OEM_DCFAILCAUSE_5(0x1005), - OEM_DCFAILCAUSE_6(0x1006), - OEM_DCFAILCAUSE_7(0x1007), - OEM_DCFAILCAUSE_8(0x1008), - OEM_DCFAILCAUSE_9(0x1009), - OEM_DCFAILCAUSE_10(0x100A), - OEM_DCFAILCAUSE_11(0x100B), - OEM_DCFAILCAUSE_12(0x100C), - OEM_DCFAILCAUSE_13(0x100D), - OEM_DCFAILCAUSE_14(0x100E), - OEM_DCFAILCAUSE_15(0x100F), + public static final int OEM_DCFAILCAUSE_1 = 0x1001; + public static final int OEM_DCFAILCAUSE_2 = 0x1002; + public static final int OEM_DCFAILCAUSE_3 = 0x1003; + public static final int OEM_DCFAILCAUSE_4 = 0x1004; + public static final int OEM_DCFAILCAUSE_5 = 0x1005; + public static final int OEM_DCFAILCAUSE_6 = 0x1006; + public static final int OEM_DCFAILCAUSE_7 = 0x1007; + public static final int OEM_DCFAILCAUSE_8 = 0x1008; + public static final int OEM_DCFAILCAUSE_9 = 0x1009; + public static final int OEM_DCFAILCAUSE_10 = 0x100A; + public static final int OEM_DCFAILCAUSE_11 = 0x100B; + public static final int OEM_DCFAILCAUSE_12 = 0x100C; + public static final int OEM_DCFAILCAUSE_13 = 0x100D; + public static final int OEM_DCFAILCAUSE_14 = 0x100E; + public static final int OEM_DCFAILCAUSE_15 = 0x100F; // Local errors generated by Vendor RIL // specified in ril.h - REGISTRATION_FAIL(-1), - GPRS_REGISTRATION_FAIL(-2), - SIGNAL_LOST(-3), /* no retry */ - PREF_RADIO_TECH_CHANGED(-4), - RADIO_POWER_OFF(-5), /* no retry */ - TETHERED_CALL_ACTIVE(-6), /* no retry */ - ERROR_UNSPECIFIED(0xFFFF), + public static final int REGISTRATION_FAIL = -1; + public static final int GPRS_REGISTRATION_FAIL = -2; + public static final int SIGNAL_LOST = -3; /* no retry */ + public static final int PREF_RADIO_TECH_CHANGED = -4; + public static final int RADIO_POWER_OFF = -5; /* no retry */ + public static final int TETHERED_CALL_ACTIVE = -6; /* no retry */ + public static final int ERROR_UNSPECIFIED = 0xFFFF; // Errors generated by the Framework // specified here - UNKNOWN(0x10000), - RADIO_NOT_AVAILABLE(0x10001), /* no retry */ - UNACCEPTABLE_NETWORK_PARAMETER(0x10002), /* no retry */ - CONNECTION_TO_DATACONNECTIONAC_BROKEN(0x10003), - LOST_CONNECTION(0x10004), - RESET_BY_FRAMEWORK(0x10005); + public static final int UNKNOWN = 0x10000; + public static final int RADIO_NOT_AVAILABLE = 0x10001; /* no retry */ + public static final int UNACCEPTABLE_NETWORK_PARAMETER = 0x10002; /* no retry */ + public static final int CONNECTION_TO_DATACONNECTIONAC_BROKEN = 0x10003; + public static final int LOST_CONNECTION = 0x10004; + /** Data was reset by framework. */ + public static final int RESET_BY_FRAMEWORK = 0x10005; + + /** @hide */ + @IntDef(value = { + NONE, + OPERATOR_BARRED, + NAS_SIGNALLING, + LLC_SNDCP, + INSUFFICIENT_RESOURCES, + MISSING_UNKNOWN_APN, + UNKNOWN_PDP_ADDRESS_TYPE, + USER_AUTHENTICATION, + ACTIVATION_REJECT_GGSN, + ACTIVATION_REJECT_UNSPECIFIED, + SERVICE_OPTION_NOT_SUPPORTED, + SERVICE_OPTION_NOT_SUBSCRIBED, + SERVICE_OPTION_OUT_OF_ORDER, + NSAPI_IN_USE, + REGULAR_DEACTIVATION, + QOS_NOT_ACCEPTED, + NETWORK_FAILURE, + UMTS_REACTIVATION_REQ, + FEATURE_NOT_SUPP, + TFT_SEMANTIC_ERROR, + TFT_SYTAX_ERROR, + UNKNOWN_PDP_CONTEXT, + FILTER_SEMANTIC_ERROR, + FILTER_SYTAX_ERROR, + PDP_WITHOUT_ACTIVE_TFT, + ONLY_IPV4_ALLOWED, + ONLY_IPV6_ALLOWED, + ONLY_SINGLE_BEARER_ALLOWED, + ESM_INFO_NOT_RECEIVED, + PDN_CONN_DOES_NOT_EXIST, + MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED, + MAX_ACTIVE_PDP_CONTEXT_REACHED, + UNSUPPORTED_APN_IN_CURRENT_PLMN, + INVALID_TRANSACTION_ID, + MESSAGE_INCORRECT_SEMANTIC, + INVALID_MANDATORY_INFO, + MESSAGE_TYPE_UNSUPPORTED, + MSG_TYPE_NONCOMPATIBLE_STATE, + UNKNOWN_INFO_ELEMENT, + CONDITIONAL_IE_ERROR, + MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE, + PROTOCOL_ERRORS, /* no retry */ + APN_TYPE_CONFLICT, + INVALID_PCSCF_ADDR, + INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN, + EMM_ACCESS_BARRED, + EMERGENCY_IFACE_ONLY, + IFACE_MISMATCH, + COMPANION_IFACE_IN_USE, + IP_ADDRESS_MISMATCH, + IFACE_AND_POL_FAMILY_MISMATCH, + EMM_ACCESS_BARRED_INFINITE_RETRY, + AUTH_FAILURE_ON_EMERGENCY_CALL, + OEM_DCFAILCAUSE_1, + OEM_DCFAILCAUSE_2, + OEM_DCFAILCAUSE_3, + OEM_DCFAILCAUSE_4, + OEM_DCFAILCAUSE_5, + OEM_DCFAILCAUSE_6, + OEM_DCFAILCAUSE_7, + OEM_DCFAILCAUSE_8, + OEM_DCFAILCAUSE_9, + OEM_DCFAILCAUSE_10, + OEM_DCFAILCAUSE_11, + OEM_DCFAILCAUSE_12, + OEM_DCFAILCAUSE_13, + OEM_DCFAILCAUSE_14, + OEM_DCFAILCAUSE_15, + REGISTRATION_FAIL, + GPRS_REGISTRATION_FAIL, + SIGNAL_LOST, + PREF_RADIO_TECH_CHANGED, + RADIO_POWER_OFF, + TETHERED_CALL_ACTIVE, + ERROR_UNSPECIFIED, + UNKNOWN, + RADIO_NOT_AVAILABLE, + UNACCEPTABLE_NETWORK_PARAMETER, + CONNECTION_TO_DATACONNECTIONAC_BROKEN, + LOST_CONNECTION, + RESET_BY_FRAMEWORK + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FailCause{} - private final int mErrorCode; - private static final HashMap<Integer, DataFailCause> sErrorCodeToFailCauseMap; + private static final Map<Integer, String> sFailCauseMap; static { - sErrorCodeToFailCauseMap = new HashMap<Integer, DataFailCause>(); - for (DataFailCause fc : values()) { - sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc); - } + sFailCauseMap = new HashMap<>(); + sFailCauseMap.put(NONE, "NONE"); + sFailCauseMap.put(OPERATOR_BARRED, "OPERATOR_BARRED"); + sFailCauseMap.put(NAS_SIGNALLING, "NAS_SIGNALLING"); + sFailCauseMap.put(LLC_SNDCP, "LLC_SNDCP"); + sFailCauseMap.put(INSUFFICIENT_RESOURCES, "INSUFFICIENT_RESOURCES"); + sFailCauseMap.put(MISSING_UNKNOWN_APN, "MISSING_UNKNOWN_APN"); + sFailCauseMap.put(UNKNOWN_PDP_ADDRESS_TYPE, "UNKNOWN_PDP_ADDRESS_TYPE"); + sFailCauseMap.put(USER_AUTHENTICATION, "USER_AUTHENTICATION"); + sFailCauseMap.put(ACTIVATION_REJECT_GGSN, "ACTIVATION_REJECT_GGSN"); + sFailCauseMap.put(ACTIVATION_REJECT_UNSPECIFIED, + "ACTIVATION_REJECT_UNSPECIFIED"); + sFailCauseMap.put(SERVICE_OPTION_NOT_SUPPORTED, + "SERVICE_OPTION_NOT_SUPPORTED"); + sFailCauseMap.put(SERVICE_OPTION_NOT_SUBSCRIBED, + "SERVICE_OPTION_NOT_SUBSCRIBED"); + sFailCauseMap.put(SERVICE_OPTION_OUT_OF_ORDER, "SERVICE_OPTION_OUT_OF_ORDER"); + sFailCauseMap.put(NSAPI_IN_USE, "NSAPI_IN_USE"); + sFailCauseMap.put(REGULAR_DEACTIVATION, "REGULAR_DEACTIVATION"); + sFailCauseMap.put(QOS_NOT_ACCEPTED, "QOS_NOT_ACCEPTED"); + sFailCauseMap.put(NETWORK_FAILURE, "NETWORK_FAILURE"); + sFailCauseMap.put(UMTS_REACTIVATION_REQ, "UMTS_REACTIVATION_REQ"); + sFailCauseMap.put(FEATURE_NOT_SUPP, "FEATURE_NOT_SUPP"); + sFailCauseMap.put(TFT_SEMANTIC_ERROR, "TFT_SEMANTIC_ERROR"); + sFailCauseMap.put(TFT_SYTAX_ERROR, "TFT_SYTAX_ERROR"); + sFailCauseMap.put(UNKNOWN_PDP_CONTEXT, "UNKNOWN_PDP_CONTEXT"); + sFailCauseMap.put(FILTER_SEMANTIC_ERROR, "FILTER_SEMANTIC_ERROR"); + sFailCauseMap.put(FILTER_SYTAX_ERROR, "FILTER_SYTAX_ERROR"); + sFailCauseMap.put(PDP_WITHOUT_ACTIVE_TFT, "PDP_WITHOUT_ACTIVE_TFT"); + sFailCauseMap.put(ONLY_IPV4_ALLOWED, "ONLY_IPV4_ALLOWED"); + sFailCauseMap.put(ONLY_IPV6_ALLOWED, "ONLY_IPV6_ALLOWED"); + sFailCauseMap.put(ONLY_SINGLE_BEARER_ALLOWED, "ONLY_SINGLE_BEARER_ALLOWED"); + sFailCauseMap.put(ESM_INFO_NOT_RECEIVED, "ESM_INFO_NOT_RECEIVED"); + sFailCauseMap.put(PDN_CONN_DOES_NOT_EXIST, "PDN_CONN_DOES_NOT_EXIST"); + sFailCauseMap.put(MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED, + "MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED"); + sFailCauseMap.put(MAX_ACTIVE_PDP_CONTEXT_REACHED, + "MAX_ACTIVE_PDP_CONTEXT_REACHED"); + sFailCauseMap.put(UNSUPPORTED_APN_IN_CURRENT_PLMN, + "UNSUPPORTED_APN_IN_CURRENT_PLMN"); + sFailCauseMap.put(INVALID_TRANSACTION_ID, "INVALID_TRANSACTION_ID"); + sFailCauseMap.put(MESSAGE_INCORRECT_SEMANTIC, "MESSAGE_INCORRECT_SEMANTIC"); + sFailCauseMap.put(INVALID_MANDATORY_INFO, "INVALID_MANDATORY_INFO"); + sFailCauseMap.put(MESSAGE_TYPE_UNSUPPORTED, "MESSAGE_TYPE_UNSUPPORTED"); + sFailCauseMap.put(MSG_TYPE_NONCOMPATIBLE_STATE, "MSG_TYPE_NONCOMPATIBLE_STATE"); + sFailCauseMap.put(UNKNOWN_INFO_ELEMENT, "UNKNOWN_INFO_ELEMENT"); + sFailCauseMap.put(CONDITIONAL_IE_ERROR, "CONDITIONAL_IE_ERROR"); + sFailCauseMap.put(MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE, + "MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE"); + sFailCauseMap.put(PROTOCOL_ERRORS, "PROTOCOL_ERRORS"); + sFailCauseMap.put(APN_TYPE_CONFLICT, "APN_TYPE_CONFLICT"); + sFailCauseMap.put(INVALID_PCSCF_ADDR, "INVALID_PCSCF_ADDR"); + sFailCauseMap.put(INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN, + "INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN"); + sFailCauseMap.put(EMM_ACCESS_BARRED, "EMM_ACCESS_BARRED"); + sFailCauseMap.put(EMERGENCY_IFACE_ONLY, "EMERGENCY_IFACE_ONLY"); + sFailCauseMap.put(IFACE_MISMATCH, "IFACE_MISMATCH"); + sFailCauseMap.put(COMPANION_IFACE_IN_USE, "COMPANION_IFACE_IN_USE"); + sFailCauseMap.put(IP_ADDRESS_MISMATCH, "IP_ADDRESS_MISMATCH"); + sFailCauseMap.put(IFACE_AND_POL_FAMILY_MISMATCH, + "IFACE_AND_POL_FAMILY_MISMATCH"); + sFailCauseMap.put(EMM_ACCESS_BARRED_INFINITE_RETRY, + "EMM_ACCESS_BARRED_INFINITE_RETRY"); + sFailCauseMap.put(AUTH_FAILURE_ON_EMERGENCY_CALL, + "AUTH_FAILURE_ON_EMERGENCY_CALL"); + sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1"); + sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2"); + sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3"); + sFailCauseMap.put(OEM_DCFAILCAUSE_4, "OEM_DCFAILCAUSE_4"); + sFailCauseMap.put(OEM_DCFAILCAUSE_5, "OEM_DCFAILCAUSE_5"); + sFailCauseMap.put(OEM_DCFAILCAUSE_6, "OEM_DCFAILCAUSE_6"); + sFailCauseMap.put(OEM_DCFAILCAUSE_7, "OEM_DCFAILCAUSE_7"); + sFailCauseMap.put(OEM_DCFAILCAUSE_8, "OEM_DCFAILCAUSE_8"); + sFailCauseMap.put(OEM_DCFAILCAUSE_9, "OEM_DCFAILCAUSE_9"); + sFailCauseMap.put(OEM_DCFAILCAUSE_10, "OEM_DCFAILCAUSE_10"); + sFailCauseMap.put(OEM_DCFAILCAUSE_11, "OEM_DCFAILCAUSE_11"); + sFailCauseMap.put(OEM_DCFAILCAUSE_12, "OEM_DCFAILCAUSE_12"); + sFailCauseMap.put(OEM_DCFAILCAUSE_13, "OEM_DCFAILCAUSE_13"); + sFailCauseMap.put(OEM_DCFAILCAUSE_14, "OEM_DCFAILCAUSE_14"); + sFailCauseMap.put(OEM_DCFAILCAUSE_15, "OEM_DCFAILCAUSE_15"); + sFailCauseMap.put(REGISTRATION_FAIL, "REGISTRATION_FAIL"); + sFailCauseMap.put(GPRS_REGISTRATION_FAIL, "GPRS_REGISTRATION_FAIL"); + sFailCauseMap.put(SIGNAL_LOST, "SIGNAL_LOST"); + sFailCauseMap.put(PREF_RADIO_TECH_CHANGED, "PREF_RADIO_TECH_CHANGED"); + sFailCauseMap.put(RADIO_POWER_OFF, "RADIO_POWER_OFF"); + sFailCauseMap.put(TETHERED_CALL_ACTIVE, "TETHERED_CALL_ACTIVE"); + sFailCauseMap.put(ERROR_UNSPECIFIED, "ERROR_UNSPECIFIED"); + sFailCauseMap.put(UNKNOWN, "UNKNOWN"); + sFailCauseMap.put(RADIO_NOT_AVAILABLE, "RADIO_NOT_AVAILABLE"); + sFailCauseMap.put(UNACCEPTABLE_NETWORK_PARAMETER, + "UNACCEPTABLE_NETWORK_PARAMETER"); + sFailCauseMap.put(CONNECTION_TO_DATACONNECTIONAC_BROKEN, + "CONNECTION_TO_DATACONNECTIONAC_BROKEN"); + sFailCauseMap.put(LOST_CONNECTION, "LOST_CONNECTION"); + sFailCauseMap.put(RESET_BY_FRAMEWORK, "RESET_BY_FRAMEWORK"); } /** * Map of subId -> set of data call setup permanent failure for the carrier. */ - private static final HashMap<Integer, HashSet<DataFailCause>> sPermanentFailureCache = + private static final HashMap<Integer, Set<Integer>> sPermanentFailureCache = new HashMap<>(); - DataFailCause(int errorCode) { - mErrorCode = errorCode; - } - - public int getErrorCode() { - return mErrorCode; - } - /** * Returns whether or not the fail cause is a failure that requires a modem restart * * @param context device context + * @param cause data disconnect cause * @param subId subscription index * @return true if the fail cause code needs platform to trigger a modem restart. */ - public boolean isRadioRestartFailure(Context context, int subId) { + public static boolean isRadioRestartFailure(@NonNull Context context, @FailCause int cause, + int subId) { CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); if (configManager != null) { PersistableBundle b = configManager.getConfigForSubId(subId); if (b != null) { - if (this == REGULAR_DEACTIVATION + if (cause == REGULAR_DEACTIVATION && b.getBoolean(CarrierConfigManager .KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL)) { // This is for backward compatibility support. We need to continue support this @@ -170,7 +402,7 @@ public enum DataFailCause { int[] causeCodes = b.getIntArray(CarrierConfigManager .KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY); if (causeCodes != null) { - return Arrays.stream(causeCodes).anyMatch(i -> i == getErrorCode()); + return Arrays.stream(causeCodes).anyMatch(i -> i == cause); } } } @@ -178,11 +410,11 @@ public enum DataFailCause { return false; } - public boolean isPermanentFailure(Context context, int subId) { - + public static boolean isPermanentFailure(@NonNull Context context, @FailCause int failCause, + int subId) { synchronized (sPermanentFailureCache) { - HashSet<DataFailCause> permanentFailureSet = sPermanentFailureCache.get(subId); + Set<Integer> permanentFailureSet = sPermanentFailureCache.get(subId); // In case of cache miss, we need to look up the settings from carrier config. if (permanentFailureSet == null) { @@ -194,11 +426,12 @@ public enum DataFailCause { if (b != null) { String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager. KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS); - if (permanentFailureStrings != null) { permanentFailureSet = new HashSet<>(); - for (String failure : permanentFailureStrings) { - permanentFailureSet.add(DataFailCause.valueOf(failure)); + for (Map.Entry<Integer, String> e : sFailCauseMap.entrySet()) { + if (ArrayUtils.contains(permanentFailureStrings, e.getValue())) { + permanentFailureSet.add(e.getKey()); + } } } } @@ -207,7 +440,7 @@ public enum DataFailCause { // If we are not able to find the configuration from carrier config, use the default // ones. if (permanentFailureSet == null) { - permanentFailureSet = new HashSet<DataFailCause>() { + permanentFailureSet = new HashSet<Integer>() { { add(OPERATOR_BARRED); add(MISSING_UNKNOWN_APN); @@ -232,28 +465,39 @@ public enum DataFailCause { sPermanentFailureCache.put(subId, permanentFailureSet); } - return permanentFailureSet.contains(this); + return permanentFailureSet.contains(failCause); } } - public boolean isEventLoggable() { - return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) || - (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) || - (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) || - (this == SERVICE_OPTION_NOT_SUBSCRIBED) || - (this == SERVICE_OPTION_NOT_SUPPORTED) || - (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) || - (this == ONLY_IPV4_ALLOWED) || (this == ONLY_IPV6_ALLOWED) || - (this == PROTOCOL_ERRORS) || (this == SIGNAL_LOST) || - (this == RADIO_POWER_OFF) || (this == TETHERED_CALL_ACTIVE) || - (this == UNACCEPTABLE_NETWORK_PARAMETER); + public static boolean isEventLoggable(@FailCause int dataFailCause) { + return (dataFailCause == OPERATOR_BARRED) || (dataFailCause == INSUFFICIENT_RESOURCES) + || (dataFailCause == UNKNOWN_PDP_ADDRESS_TYPE) + || (dataFailCause == USER_AUTHENTICATION) + || (dataFailCause == ACTIVATION_REJECT_GGSN) + || (dataFailCause == ACTIVATION_REJECT_UNSPECIFIED) + || (dataFailCause == SERVICE_OPTION_NOT_SUBSCRIBED) + || (dataFailCause == SERVICE_OPTION_NOT_SUPPORTED) + || (dataFailCause == SERVICE_OPTION_OUT_OF_ORDER) + || (dataFailCause == NSAPI_IN_USE) + || (dataFailCause == ONLY_IPV4_ALLOWED) + || (dataFailCause == ONLY_IPV6_ALLOWED) + || (dataFailCause == PROTOCOL_ERRORS) + || (dataFailCause == SIGNAL_LOST) + || (dataFailCause == RADIO_POWER_OFF) + || (dataFailCause == TETHERED_CALL_ACTIVE) + || (dataFailCause == UNACCEPTABLE_NETWORK_PARAMETER); + } + + public static String toString(@FailCause int dataFailCause) { + int cause = getFailCause(dataFailCause); + return (cause == UNKNOWN) ? "UNKNOWN(" + dataFailCause + ")" : sFailCauseMap.get(cause); } - public static DataFailCause fromInt(int errorCode) { - DataFailCause fc = sErrorCodeToFailCauseMap.get(errorCode); - if (fc == null) { - fc = UNKNOWN; + public static int getFailCause(@FailCause int failCause) { + if (sFailCauseMap.containsKey(failCause)) { + return failCause; + } else { + return UNKNOWN; } - return fc; } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 29d32e946b2c..f241d45bf1e2 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -6382,8 +6382,9 @@ public class TelephonyManager { public @PrefNetworkMode int getPreferredNetworkType(int subId) { try { ITelephony telephony = getITelephony(); - if (telephony != null) + if (telephony != null) { return telephony.getPreferredNetworkType(subId); + } } catch (RemoteException ex) { Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex); } catch (NullPointerException ex) { @@ -6393,6 +6394,37 @@ public class TelephonyManager { } /** + * Get the preferred network type bitmap. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return a 32-bit bitmap. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + public @NetworkTypeBitMask int getPreferredNetworkTypeBitmap() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return RadioAccessFamily.getRafFromNetworkType( + telephony.getPreferredNetworkType(getSubId())); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getPreferredNetworkTypeBitmap RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getPreferredNetworkTypeBitmap NPE", ex); + } + return 0; + } + + /** * Sets the network selection mode to automatic. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the @@ -6607,6 +6639,37 @@ public class TelephonyManager { } /** + * Set the preferred network type bitmap. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param networkTypeBitmap a 32-bit bitmap. + * @return true on success; false on any failure. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + public boolean setPreferredNetworkTypeBitmap(@NetworkTypeBitMask int networkTypeBitmap) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.setPreferredNetworkType( + getSubId(), RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmap)); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "setPreferredNetworkType NPE", ex); + } + return false; + } + + /** * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA. * * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). diff --git a/tests/testables/src/android/testing/BaseFragmentTest.java b/tests/testables/src/android/testing/BaseFragmentTest.java index 5fa065a9135a..d18c126a96c1 100644 --- a/tests/testables/src/android/testing/BaseFragmentTest.java +++ b/tests/testables/src/android/testing/BaseFragmentTest.java @@ -21,7 +21,9 @@ import android.app.Fragment; import android.app.FragmentController; import android.app.FragmentHostCallback; import android.app.FragmentManagerNonConfig; +import android.content.Context; import android.graphics.PixelFormat; +import android.os.Bundle; import android.os.Handler; import android.os.Parcelable; import android.support.test.InstrumentationRegistry; @@ -75,7 +77,7 @@ public abstract class BaseFragmentTest { TestableLooper.get(this).runWithLooper(() -> { mHandler = new Handler(); - mFragment = mCls.newInstance(); + mFragment = instantiate(mContext, mCls.getName(), null); mFragments = FragmentController.createController(new HostCallbacks()); mFragments.attachHost(null); mFragments.getFragmentManager().beginTransaction() @@ -187,6 +189,13 @@ public abstract class BaseFragmentTest { TestableLooper.get(this).processAllMessages(); } + /** + * Method available for override to replace fragment instantiation. + */ + protected Fragment instantiate(Context context, String className, @Nullable Bundle arguments) { + return Fragment.instantiate(context, className, arguments); + } + private View findViewById(int id) { return mView.findViewById(id); } @@ -206,6 +215,11 @@ public abstract class BaseFragmentTest { } @Override + public Fragment instantiate(Context context, String className, Bundle arguments) { + return BaseFragmentTest.this.instantiate(context, className, arguments); + } + + @Override public boolean onShouldSaveFragmentState(Fragment fragment) { return true; // True for now. } |