diff options
179 files changed, 3197 insertions, 4412 deletions
diff --git a/api/current.txt b/api/current.txt index 6042b2fa4c32..3beb491c5412 100755 --- a/api/current.txt +++ b/api/current.txt @@ -42787,6 +42787,7 @@ package android.telephony { method public static int getDefaultSubscriptionId(); method public static int getDefaultVoiceSubscriptionId(); method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions(int); + method public static int getSlotIndex(int); method public static int[] getSubscriptionIds(int); method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int); method public boolean isNetworkRoaming(int); @@ -42804,7 +42805,9 @@ package android.telephony { field public static final int DATA_ROAMING_DISABLE = 0; // 0x0 field public static final int DATA_ROAMING_ENABLE = 1; // 0x1 field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX"; + field public static final int INVALID_SIM_SLOT_INDEX = -2; // 0xfffffffe field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff + field public static final int SIM_NOT_INSERTED = -3; // 0xfffffffd } public static class SubscriptionManager.OnOpportunisticSubscriptionsChangedListener { diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index e14f2eb41816..d334f96d47fb 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -198,8 +198,7 @@ int main(int argc, char** argv) sp<GraphicBuffer> outBuffer; status_t result = ScreenshotClient::capture(display, Rect(), 0 /* reqWidth */, - 0 /* reqHeight */, INT32_MIN, INT32_MAX, /* all layers */ false, captureOrientation, - &outBuffer); + 0 /* reqHeight */, false, captureOrientation, &outBuffer); if (result != NO_ERROR) { close(fd); return 1; diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 5e87ef658dd5..27c5a1701c0d 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -67,7 +67,9 @@ statsd_common_src := \ src/subscriber/SubscriberReporter.cpp \ src/HashableDimensionKey.cpp \ src/guardrail/StatsdStats.cpp \ - src/socket/StatsSocketListener.cpp + src/socket/StatsSocketListener.cpp \ + src/shell/ShellSubscriber.cpp \ + src/shell/shell_config.proto # TODO(b/110563449): Once statsd is using a blueprint file, migrate to the proper filegroups. statsd_common_src += \ diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 8e02f9cb7628..f4c70bee2806 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -257,12 +257,18 @@ size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const { return it->second->byteSize(); } -void StatsLogProcessor::dumpStates(FILE* out, bool verbose) { +void StatsLogProcessor::dumpStates(int out, bool verbose) { std::lock_guard<std::mutex> lock(mMetricsMutex); - fprintf(out, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size()); + FILE* fout = fdopen(out, "w"); + if (fout == NULL) { + return; + } + fprintf(fout, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size()); for (auto metricsManager : mMetricsManagers) { - metricsManager.second->dumpStates(out, verbose); + metricsManager.second->dumpStates(fout, verbose); } + + fclose(fout); } /* diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index df80b8e6e052..4091d951ba06 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -83,7 +83,7 @@ public: return mUidMap; } - void dumpStates(FILE* out, bool verbose); + void dumpStates(int outFd, bool verbose); void informPullAlarmFired(const int64_t timestampNs); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 9a7934576abd..2ef1169851ff 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -34,13 +34,14 @@ #include <dirent.h> #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h> #include <private/android_filesystem_config.h> -#include <utils/Looper.h> -#include <utils/String16.h> #include <statslog.h> #include <stdio.h> #include <stdlib.h> #include <sys/system_properties.h> #include <unistd.h> +#include <utils/Looper.h> +#include <utils/String16.h> +#include <chrono> using namespace android; @@ -214,30 +215,8 @@ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* rep sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(data.readStrongBinder()); - FILE* fin = fdopen(in, "r"); - FILE* fout = fdopen(out, "w"); - FILE* ferr = fdopen(err, "w"); - - if (fin == NULL || fout == NULL || ferr == NULL) { - resultReceiver->send(NO_MEMORY); - } else { - err = command(fin, fout, ferr, args); - resultReceiver->send(err); - } - - if (fin != NULL) { - fflush(fin); - fclose(fin); - } - if (fout != NULL) { - fflush(fout); - fclose(fout); - } - if (fout != NULL) { - fflush(ferr); - fclose(ferr); - } - + err = command(in, out, err, args, resultReceiver); + resultReceiver->send(err); return NO_ERROR; } default: { return BnStatsManager::onTransact(code, data, reply, flags); } @@ -251,10 +230,6 @@ status_t StatsService::dump(int fd, const Vector<String16>& args) { if (!checkCallingPermission(String16(kPermissionDump))) { return PERMISSION_DENIED; } - FILE* out = fdopen(fd, "w"); - if (out == NULL) { - return NO_MEMORY; // the fd is already open - } bool verbose = false; bool proto = false; @@ -265,21 +240,20 @@ status_t StatsService::dump(int fd, const Vector<String16>& args) { proto = true; } - dump_impl(out, verbose, proto); + dump_impl(fd, verbose, proto); - fclose(out); return NO_ERROR; } /** * Write debugging data about statsd in text or proto format. */ -void StatsService::dump_impl(FILE* out, bool verbose, bool proto) { +void StatsService::dump_impl(int out, bool verbose, bool proto) { if (proto) { vector<uint8_t> data; StatsdStats::getInstance().dumpStats(&data, false); // does not reset statsdStats. for (size_t i = 0; i < data.size(); i ++) { - fprintf(out, "%c", data[i]); + dprintf(out, "%c", data[i]); } } else { StatsdStats::getInstance().dumpStats(out); @@ -290,7 +264,8 @@ void StatsService::dump_impl(FILE* out, bool verbose, bool proto) { /** * Implementation of the adb shell cmd stats command. */ -status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) { +status_t StatsService::command(int in, int out, int err, Vector<String8>& args, + sp<IResultReceiver> resultReceiver) { uid_t uid = IPCThreadState::self()->getCallingUid(); if (uid != AID_ROOT && uid != AID_SHELL) { return PERMISSION_DENIED; @@ -342,97 +317,106 @@ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& if (!args[0].compare(String8("print-logs"))) { return cmd_print_logs(out, args); } + if (!args[0].compare(String8("data-subscribe"))) { + if (mShellSubscriber == nullptr) { + mShellSubscriber = new ShellSubscriber(mUidMap); + } + mShellSubscriber->startNewSubscription(in, out, resultReceiver); + return NO_ERROR; + } } print_cmd_help(out); return NO_ERROR; } -void StatsService::print_cmd_help(FILE* out) { - fprintf(out, +void StatsService::print_cmd_help(int out) { + dprintf(out, "usage: adb shell cmd stats print-stats-log [tag_required] " "[timestamp_nsec_optional]\n"); - fprintf(out, "\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats meminfo\n"); - fprintf(out, "\n"); - fprintf(out, " Prints the malloc debug information. You need to run the following first: \n"); - fprintf(out, " # adb shell stop\n"); - fprintf(out, " # adb shell setprop libc.debug.malloc.program statsd \n"); - fprintf(out, " # adb shell setprop libc.debug.malloc.options backtrace \n"); - fprintf(out, " # adb shell start\n"); - fprintf(out, "\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats print-uid-map [PKG]\n"); - fprintf(out, "\n"); - fprintf(out, " Prints the UID, app name, version mapping.\n"); - fprintf(out, " PKG Optional package name to print the uids of the package\n"); - fprintf(out, "\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats pull-source [int] \n"); - fprintf(out, "\n"); - fprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n"); - fprintf(out, "\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats write-to-disk \n"); - fprintf(out, "\n"); - fprintf(out, " Flushes all data on memory to disk.\n"); - fprintf(out, "\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n"); - fprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n"); - fprintf(out, " UID The uid to use. It is only possible to pass a UID\n"); - fprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); - fprintf(out, " uid is used.\n"); - fprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n"); - fprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n"); - fprintf(out, "\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n"); - fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n"); - fprintf(out, "\n"); - fprintf(out, " Adds, updates or removes a configuration. The proto should be in\n"); - fprintf(out, " wire-encoded protobuf format and passed via stdin. If no UID and name is\n"); - fprintf(out, " provided, then all configs will be removed from memory and disk.\n"); - fprintf(out, "\n"); - fprintf(out, " UID The uid to use. It is only possible to pass the UID\n"); - fprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); - fprintf(out, " uid is used.\n"); - fprintf(out, " NAME The per-uid name to use\n"); - fprintf(out, "\n"); - fprintf(out, "\n *Note: If both UID and NAME are omitted then all configs will\n"); - fprintf(out, "\n be removed from memory and disk!\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats dump-report [UID] NAME [--include_current_bucket] [--proto]\n"); - fprintf(out, " Dump all metric data for a configuration.\n"); - fprintf(out, " UID The uid of the configuration. It is only possible to pass\n"); - fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); - fprintf(out, " calling uid is used.\n"); - fprintf(out, " NAME The name of the configuration\n"); - fprintf(out, " --proto Print proto binary.\n"); - fprintf(out, "\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n"); - fprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\n"); - fprintf(out, " UID The uid of the configuration. It is only possible to pass\n"); - fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); - fprintf(out, " calling uid is used.\n"); - fprintf(out, " NAME The name of the configuration\n"); - fprintf(out, "\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats print-stats\n"); - fprintf(out, " Prints some basic stats.\n"); - fprintf(out, " --proto Print proto binary instead of string format.\n"); - fprintf(out, "\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats clear-puller-cache\n"); - fprintf(out, " Clear cached puller data.\n"); - fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats print-logs\n"); - fprintf(out, " Only works on eng build\n"); -} - -status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) { + dprintf(out, "\n"); + dprintf(out, "\n"); + dprintf(out, "usage: adb shell cmd stats meminfo\n"); + dprintf(out, "\n"); + dprintf(out, " Prints the malloc debug information. You need to run the following first: \n"); + dprintf(out, " # adb shell stop\n"); + dprintf(out, " # adb shell setprop libc.debug.malloc.program statsd \n"); + dprintf(out, " # adb shell setprop libc.debug.malloc.options backtrace \n"); + dprintf(out, " # adb shell start\n"); + dprintf(out, "\n"); + dprintf(out, "\n"); + dprintf(out, "usage: adb shell cmd stats print-uid-map [PKG]\n"); + dprintf(out, "\n"); + dprintf(out, " Prints the UID, app name, version mapping.\n"); + dprintf(out, " PKG Optional package name to print the uids of the package\n"); + dprintf(out, "\n"); + dprintf(out, "\n"); + dprintf(out, "usage: adb shell cmd stats pull-source [int] \n"); + dprintf(out, "\n"); + dprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n"); + dprintf(out, "\n"); + dprintf(out, "\n"); + dprintf(out, "usage: adb shell cmd stats write-to-disk \n"); + dprintf(out, "\n"); + dprintf(out, " Flushes all data on memory to disk.\n"); + dprintf(out, "\n"); + dprintf(out, "\n"); + dprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n"); + dprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n"); + dprintf(out, " UID The uid to use. It is only possible to pass a UID\n"); + dprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); + dprintf(out, " uid is used.\n"); + dprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n"); + dprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n"); + dprintf(out, "\n"); + dprintf(out, "\n"); + dprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n"); + dprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n"); + dprintf(out, "\n"); + dprintf(out, " Adds, updates or removes a configuration. The proto should be in\n"); + dprintf(out, " wire-encoded protobuf format and passed via stdin. If no UID and name is\n"); + dprintf(out, " provided, then all configs will be removed from memory and disk.\n"); + dprintf(out, "\n"); + dprintf(out, " UID The uid to use. It is only possible to pass the UID\n"); + dprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); + dprintf(out, " uid is used.\n"); + dprintf(out, " NAME The per-uid name to use\n"); + dprintf(out, "\n"); + dprintf(out, "\n *Note: If both UID and NAME are omitted then all configs will\n"); + dprintf(out, "\n be removed from memory and disk!\n"); + dprintf(out, "\n"); + dprintf(out, + "usage: adb shell cmd stats dump-report [UID] NAME [--include_current_bucket] " + "[--proto]\n"); + dprintf(out, " Dump all metric data for a configuration.\n"); + dprintf(out, " UID The uid of the configuration. It is only possible to pass\n"); + dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); + dprintf(out, " calling uid is used.\n"); + dprintf(out, " NAME The name of the configuration\n"); + dprintf(out, " --proto Print proto binary.\n"); + dprintf(out, "\n"); + dprintf(out, "\n"); + dprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n"); + dprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\n"); + dprintf(out, " UID The uid of the configuration. It is only possible to pass\n"); + dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); + dprintf(out, " calling uid is used.\n"); + dprintf(out, " NAME The name of the configuration\n"); + dprintf(out, "\n"); + dprintf(out, "\n"); + dprintf(out, "usage: adb shell cmd stats print-stats\n"); + dprintf(out, " Prints some basic stats.\n"); + dprintf(out, " --proto Print proto binary instead of string format.\n"); + dprintf(out, "\n"); + dprintf(out, "\n"); + dprintf(out, "usage: adb shell cmd stats clear-puller-cache\n"); + dprintf(out, " Clear cached puller data.\n"); + dprintf(out, "\n"); + dprintf(out, "usage: adb shell cmd stats print-logs\n"); + dprintf(out, " Only works on eng build\n"); +} + +status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) { string name; bool good = false; int uid; @@ -456,9 +440,9 @@ status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) { } } } else { - fprintf(out, + dprintf(out, "The metrics can only be dumped for other UIDs on eng or userdebug " - "builds.\n"); + "builds.\n"); } } if (!good) { @@ -481,7 +465,7 @@ status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) { return NO_ERROR; } -status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args) { +status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& args) { const int argCount = args.size(); if (argCount >= 2) { if (args[1] == "update" || args[1] == "remove") { @@ -508,7 +492,7 @@ status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8 } } } else { - fprintf(err, + dprintf(err, "The config can only be set for other UIDs on eng or userdebug " "builds.\n"); } @@ -526,21 +510,21 @@ status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8 char* endp; int64_t configID = strtoll(name.c_str(), &endp, 10); if (endp == name.c_str() || *endp != '\0') { - fprintf(err, "Error parsing config ID.\n"); + dprintf(err, "Error parsing config ID.\n"); return UNKNOWN_ERROR; } // Read stream into buffer. string buffer; - if (!android::base::ReadFdToString(fileno(in), &buffer)) { - fprintf(err, "Error reading stream for StatsConfig.\n"); + if (!android::base::ReadFdToString(in, &buffer)) { + dprintf(err, "Error reading stream for StatsConfig.\n"); return UNKNOWN_ERROR; } // Parse buffer. StatsdConfig config; if (!config.ParseFromString(buffer)) { - fprintf(err, "Error parsing proto stream for StatsConfig.\n"); + dprintf(err, "Error parsing proto stream for StatsConfig.\n"); return UNKNOWN_ERROR; } @@ -562,7 +546,7 @@ status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8 return UNKNOWN_ERROR; } -status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String8>& args) { +status_t StatsService::cmd_dump_report(int out, int err, const Vector<String8>& args) { if (mProcessor != nullptr) { int argCount = args.size(); bool good = false; @@ -597,7 +581,7 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String } } } else { - fprintf(out, + dprintf(out, "The metrics can only be dumped for other UIDs on eng or userdebug " "builds.\n"); } @@ -608,11 +592,11 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String includeCurrentBucket, ADB_DUMP, &data); if (proto) { for (size_t i = 0; i < data.size(); i ++) { - fprintf(out, "%c", data[i]); + dprintf(out, "%c", data[i]); } } else { - fprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str()); - fprintf(out, "See the StatsLogReport in logcat...\n"); + dprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str()); + dprintf(out, "See the StatsLogReport in logcat...\n"); } return android::OK; } else { @@ -621,12 +605,12 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String return UNKNOWN_ERROR; } } else { - fprintf(out, "Log processor does not exist...\n"); + dprintf(out, "Log processor does not exist...\n"); return UNKNOWN_ERROR; } } -status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) { +status_t StatsService::cmd_print_stats(int out, const Vector<String8>& args) { int argCount = args.size(); bool proto = false; if (!std::strcmp("--proto", args[argCount-1].c_str())) { @@ -638,13 +622,13 @@ status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) { vector<uint8_t> data; statsdStats.dumpStats(&data, false); // does not reset statsdStats. for (size_t i = 0; i < data.size(); i ++) { - fprintf(out, "%c", data[i]); + dprintf(out, "%c", data[i]); } } else { vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys(); for (const ConfigKey& key : configs) { - fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(), + dprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(), mProcessor->GetMetricsSize(key)); } statsdStats.dumpStats(out); @@ -652,29 +636,29 @@ status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) { return NO_ERROR; } -status_t StatsService::cmd_print_uid_map(FILE* out, const Vector<String8>& args) { +status_t StatsService::cmd_print_uid_map(int out, const Vector<String8>& args) { if (args.size() > 1) { string pkg; pkg.assign(args[1].c_str(), args[1].size()); auto uids = mUidMap->getAppUid(pkg); - fprintf(out, "%s -> [ ", pkg.c_str()); + dprintf(out, "%s -> [ ", pkg.c_str()); for (const auto& uid : uids) { - fprintf(out, "%d ", uid); + dprintf(out, "%d ", uid); } - fprintf(out, "]\n"); + dprintf(out, "]\n"); } else { mUidMap->printUidMap(out); } return NO_ERROR; } -status_t StatsService::cmd_write_data_to_disk(FILE* out) { - fprintf(out, "Writing data to disk\n"); +status_t StatsService::cmd_write_data_to_disk(int out) { + dprintf(out, "Writing data to disk\n"); mProcessor->WriteDataToDisk(ADB_DUMP); return NO_ERROR; } -status_t StatsService::cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args) { +status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& args) { bool good = false; int32_t uid; int32_t label; @@ -695,13 +679,13 @@ status_t StatsService::cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& state = atoi(args[3].c_str()); good = true; } else { - fprintf(out, + dprintf(out, "Selecting a UID for writing AppBreadcrumb can only be done for other UIDs " - "on eng or userdebug builds.\n"); + "on eng or userdebug builds.\n"); } } if (good) { - fprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state); + dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state); android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED, uid, label, state); } else { print_cmd_help(out); @@ -710,46 +694,46 @@ status_t StatsService::cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& return NO_ERROR; } -status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) { +status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) { int s = atoi(args[1].c_str()); vector<shared_ptr<LogEvent> > stats; if (mPullerManager->Pull(s, getElapsedRealtimeNs(), &stats)) { for (const auto& it : stats) { - fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str()); + dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str()); } - fprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size()); + dprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size()); return NO_ERROR; } return UNKNOWN_ERROR; } -status_t StatsService::cmd_remove_all_configs(FILE* out) { - fprintf(out, "Removing all configs...\n"); +status_t StatsService::cmd_remove_all_configs(int out) { + dprintf(out, "Removing all configs...\n"); VLOG("StatsService::cmd_remove_all_configs was called"); mConfigManager->RemoveAllConfigs(); StorageManager::deleteAllFiles(STATS_SERVICE_DIR); return NO_ERROR; } -status_t StatsService::cmd_dump_memory_info(FILE* out) { - fprintf(out, "meminfo not available.\n"); +status_t StatsService::cmd_dump_memory_info(int out) { + dprintf(out, "meminfo not available.\n"); return NO_ERROR; } -status_t StatsService::cmd_clear_puller_cache(FILE* out) { +status_t StatsService::cmd_clear_puller_cache(int out) { IPCThreadState* ipc = IPCThreadState::self(); VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid()); if (checkCallingPermission(String16(kPermissionDump))) { int cleared = mPullerManager->ForceClearPullerCache(); - fprintf(out, "Puller removed %d cached data!\n", cleared); + dprintf(out, "Puller removed %d cached data!\n", cleared); return NO_ERROR; } else { return PERMISSION_DENIED; } } -status_t StatsService::cmd_print_logs(FILE* out, const Vector<String8>& args) { +status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) { IPCThreadState* ipc = IPCThreadState::self(); VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid()); @@ -885,6 +869,9 @@ void StatsService::Startup() { void StatsService::OnLogEvent(LogEvent* event) { mProcessor->OnLogEvent(event); + if (mShellSubscriber != nullptr) { + mShellSubscriber->onLogEvent(*event); + } } Status StatsService::getData(int64_t key, const String16& packageName, vector<uint8_t>* output) { diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 613f509e915a..06189278abce 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -24,6 +24,7 @@ #include "external/StatsPullerManager.h" #include "logd/LogListener.h" #include "packages/UidMap.h" +#include "shell/ShellSubscriber.h" #include "statscompanion_util.h" #include <android/os/BnStatsManager.h> @@ -54,7 +55,8 @@ public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); virtual status_t dump(int fd, const Vector<String16>& args); - virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args); + virtual status_t command(int inFd, int outFd, int err, Vector<String8>& args, + sp<IResultReceiver> resultReceiver); virtual Status systemRunning(); virtual Status statsCompanionReady(); @@ -162,73 +164,73 @@ private: /** * Text or proto output of dumpsys. */ - void dump_impl(FILE* out, bool verbose, bool proto); + void dump_impl(int outFd, bool verbose, bool proto); /** * Print usage information for the commands */ - void print_cmd_help(FILE* out); + void print_cmd_help(int out); /** * Trigger a broadcast. */ - status_t cmd_trigger_broadcast(FILE* out, Vector<String8>& args); + status_t cmd_trigger_broadcast(int outFd, Vector<String8>& args); /** * Handle the config sub-command. */ - status_t cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args); + status_t cmd_config(int inFd, int outFd, int err, Vector<String8>& args); /** * Prints some basic stats to std out. */ - status_t cmd_print_stats(FILE* out, const Vector<String8>& args); + status_t cmd_print_stats(int outFd, const Vector<String8>& args); /** * Print the event log. */ - status_t cmd_dump_report(FILE* out, FILE* err, const Vector<String8>& args); + status_t cmd_dump_report(int outFd, int err, const Vector<String8>& args); /** * Print the mapping of uids to package names. */ - status_t cmd_print_uid_map(FILE* out, const Vector<String8>& args); + status_t cmd_print_uid_map(int outFd, const Vector<String8>& args); /** * Flush the data to disk. */ - status_t cmd_write_data_to_disk(FILE* out); + status_t cmd_write_data_to_disk(int outFd); /** * Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling * StatsLog.write(APP_BREADCRUMB_REPORTED). */ - status_t cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args); + status_t cmd_log_app_breadcrumb(int outFd, const Vector<String8>& args); /** * Print contents of a pulled metrics source. */ - status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args); + status_t cmd_print_pulled_metrics(int outFd, const Vector<String8>& args); /** * Removes all configs stored on disk and on memory. */ - status_t cmd_remove_all_configs(FILE* out); + status_t cmd_remove_all_configs(int outFd); /* * Dump memory usage by statsd. */ - status_t cmd_dump_memory_info(FILE* out); + status_t cmd_dump_memory_info(int outFd); /* * Clear all puller cached data */ - status_t cmd_clear_puller_cache(FILE* out); + status_t cmd_clear_puller_cache(int outFd); /** * Print all stats logs received to logcat. */ - status_t cmd_print_logs(FILE* out, const Vector<String8>& args); + status_t cmd_print_logs(int outFd, const Vector<String8>& args); /** * Adds a configuration after checking permissions and obtaining UID from binder call. @@ -275,6 +277,8 @@ private: */ bool mEngBuild; + sp<ShellSubscriber> mShellSubscriber; + FRIEND_TEST(StatsServiceTest, TestAddConfig_simple); FRIEND_TEST(StatsServiceTest, TestAddConfig_empty); FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index b8f19ee0d558..c6c10ec7e3d9 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -2339,7 +2339,7 @@ message BinderCallsExceptions { * Next tag: 11 */ message LooperStats { - // Currently not collected and always set to 0. + // The uid that made a call to the System Server and caused the message to be enqueued. optional int32 uid = 1 [(is_uid) = true]; // Fully qualified class name of the handler target class. diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 33f3917f05ad..bf0bfec9c08d 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -400,36 +400,35 @@ string buildTimeString(int64_t timeSec) { return string(timeBuffer); } -void StatsdStats::dumpStats(FILE* out) const { +void StatsdStats::dumpStats(int out) const { lock_guard<std::mutex> lock(mLock); time_t t = mStartTimeSec; struct tm* tm = localtime(&t); char timeBuffer[80]; strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm); - fprintf(out, "Stats collection start second: %s\n", timeBuffer); - fprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size()); + dprintf(out, "Stats collection start second: %s\n", timeBuffer); + dprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size()); for (const auto& configStats : mIceBox) { - fprintf(out, + dprintf(out, "Config {%d_%lld}: creation=%d, deletion=%d, reset=%d, #metric=%d, #condition=%d, " "#matcher=%d, #alert=%d, valid=%d\n", configStats->uid, (long long)configStats->id, configStats->creation_time_sec, configStats->deletion_time_sec, configStats->reset_time_sec, - configStats->metric_count, - configStats->condition_count, configStats->matcher_count, configStats->alert_count, - configStats->is_valid); + configStats->metric_count, configStats->condition_count, configStats->matcher_count, + configStats->alert_count, configStats->is_valid); for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { - fprintf(out, "\tbroadcast time: %d\n", broadcastTime); + dprintf(out, "\tbroadcast time: %d\n", broadcastTime); } for (const auto& dataDropTime : configStats->data_drop_time_sec) { - fprintf(out, "\tdata drop time: %d\n", dataDropTime); + dprintf(out, "\tdata drop time: %d\n", dataDropTime); } } - fprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size()); + dprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size()); for (auto& pair : mConfigStats) { auto& configStats = pair.second; - fprintf(out, + dprintf(out, "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " "#matcher=%d, #alert=%d, valid=%d\n", configStats->uid, (long long)configStats->id, configStats->creation_time_sec, @@ -437,81 +436,81 @@ void StatsdStats::dumpStats(FILE* out) const { configStats->condition_count, configStats->matcher_count, configStats->alert_count, configStats->is_valid); for (const auto& annotation : configStats->annotations) { - fprintf(out, "\tannotation: %lld, %d\n", (long long)annotation.first, + dprintf(out, "\tannotation: %lld, %d\n", (long long)annotation.first, annotation.second); } for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { - fprintf(out, "\tbroadcast time: %s(%lld)\n", - buildTimeString(broadcastTime).c_str(), (long long)broadcastTime); + dprintf(out, "\tbroadcast time: %s(%lld)\n", buildTimeString(broadcastTime).c_str(), + (long long)broadcastTime); } for (const auto& dataDropTime : configStats->data_drop_time_sec) { - fprintf(out, "\tdata drop time: %s(%lld)\n", - buildTimeString(dataDropTime).c_str(), (long long)dataDropTime); + dprintf(out, "\tdata drop time: %s(%lld)\n", buildTimeString(dataDropTime).c_str(), + (long long)dataDropTime); } for (const auto& dump : configStats->dump_report_stats) { - fprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n", + dprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n", buildTimeString(dump.first).c_str(), (long long)dump.first, (long long)dump.second); } for (const auto& stats : pair.second->matcher_stats) { - fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second); + dprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second); } for (const auto& stats : pair.second->condition_stats) { - fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first, + dprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first, stats.second); } for (const auto& stats : pair.second->condition_stats) { - fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first, + dprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first, stats.second); } for (const auto& stats : pair.second->alert_stats) { - fprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second); + dprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second); } } - fprintf(out, "********Disk Usage stats***********\n"); + dprintf(out, "********Disk Usage stats***********\n"); StorageManager::printStats(out); - fprintf(out, "********Pushed Atom stats***********\n"); + dprintf(out, "********Pushed Atom stats***********\n"); const size_t atomCounts = mPushedAtomStats.size(); for (size_t i = 2; i < atomCounts; i++) { if (mPushedAtomStats[i] > 0) { - fprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]); + dprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]); } } - fprintf(out, "********Pulled Atom stats***********\n"); + dprintf(out, "********Pulled Atom stats***********\n"); for (const auto& pair : mPulledAtomStats) { - fprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull, + dprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec); } if (mAnomalyAlarmRegisteredStats > 0) { - fprintf(out, "********AnomalyAlarmStats stats***********\n"); - fprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats); + dprintf(out, "********AnomalyAlarmStats stats***********\n"); + dprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats); } if (mPeriodicAlarmRegisteredStats > 0) { - fprintf(out, "********SubscriberAlarmStats stats***********\n"); - fprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats); + dprintf(out, "********SubscriberAlarmStats stats***********\n"); + dprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats); } - fprintf(out, "UID map stats: bytes=%d, changes=%d, deleted=%d, changes lost=%d\n", + dprintf(out, "UID map stats: bytes=%d, changes=%d, deleted=%d, changes lost=%d\n", mUidMapStats.bytes_used, mUidMapStats.changes, mUidMapStats.deleted_apps, mUidMapStats.dropped_changes); for (const auto& restart : mSystemServerRestartSec) { - fprintf(out, "System server restarts at %s(%lld)\n", - buildTimeString(restart).c_str(), (long long)restart); + dprintf(out, "System server restarts at %s(%lld)\n", buildTimeString(restart).c_str(), + (long long)restart); } for (const auto& loss : mLogLossStats) { - fprintf(out, "Log loss: %lld (wall clock sec) - %d (count)\n", (long long)loss.first, + dprintf(out, "Log loss: %lld (wall clock sec) - %d (count)\n", (long long)loss.first, loss.second); } } diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index b5156dadade6..a8188c89928a 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -156,7 +156,7 @@ public: * Report a config has been removed. */ void noteConfigRemoved(const ConfigKey& key); - /** + /** * Report a config has been reset when ttl expires. */ void noteConfigReset(const ConfigKey& key); @@ -202,7 +202,6 @@ public: */ void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size); - /** * Report the max size of output tuple of dimension in condition across dimensions in what. * @@ -272,8 +271,8 @@ public: void notePullFromCache(int pullAtomId); /* - * Records when system server restarts. - */ + * Records when system server restarts. + */ void noteSystemServerRestart(int32_t timeSec); /** @@ -296,9 +295,9 @@ public: void dumpStats(std::vector<uint8_t>* buffer, bool reset); /** - * Output statsd stats in human readable format to [out] file. + * Output statsd stats in human readable format to [out] file descriptor. */ - void dumpStats(FILE* out) const; + void dumpStats(int outFd) const; typedef struct { long totalPull; diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index be94725991af..4325f0fd77bd 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -386,12 +386,12 @@ void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, StatsdStats::getInstance().setUidMapChanges(mChanges.size()); } -void UidMap::printUidMap(FILE* out) const { +void UidMap::printUidMap(int out) const { lock_guard<mutex> lock(mMutex); for (const auto& kv : mMap) { if (!kv.second.deleted) { - fprintf(out, "%s, v%" PRId64 " (%i)\n", kv.first.second.c_str(), kv.second.versionCode, + dprintf(out, "%s, v%" PRId64 " (%i)\n", kv.first.second.c_str(), kv.second.versionCode, kv.first.first); } } diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index 91f203084388..4598369f1222 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -103,7 +103,7 @@ public: // Helper for debugging contents of this uid map. Can be triggered with: // adb shell cmd stats print-uid-map - void printUidMap(FILE* out) const; + void printUidMap(int outFd) const; // Commands for indicating to the map that a producer should be notified if an app is updated. // This allows the metric producer to distinguish when the same uid or app represents a diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp new file mode 100644 index 000000000000..3cd49d722fea --- /dev/null +++ b/cmds/statsd/src/shell/ShellSubscriber.cpp @@ -0,0 +1,140 @@ +/* + * 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. + */ +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include "ShellSubscriber.h" + +#include "matchers/matcher_util.h" + +#include <android-base/file.h> + +using android::util::ProtoOutputStream; + +namespace android { +namespace os { +namespace statsd { + +void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver) { + VLOG("start new shell subscription"); + { + std::lock_guard<std::mutex> lock(mMutex); + if (mResultReceiver != nullptr) { + VLOG("Only one shell subscriber is allowed."); + return; + } + mInput = in; + mOutput = out; + mResultReceiver = resultReceiver; + IInterface::asBinder(mResultReceiver)->linkToDeath(this); + } + + // Spawn another thread to read the config updates from the input file descriptor + std::thread reader([in, this] { readConfig(in); }); + reader.detach(); + + std::unique_lock<std::mutex> lk(mMutex); + + mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; }); + if (reader.joinable()) { + reader.join(); + } +} + +void ShellSubscriber::updateConfig(const ShellSubscription& config) { + std::lock_guard<std::mutex> lock(mMutex); + mPushedMatchers.clear(); + for (const auto& pushed : config.pushed()) { + mPushedMatchers.push_back(pushed); + VLOG("adding matcher for atom %d", pushed.atom_id()); + } +} + +void ShellSubscriber::readConfig(int in) { + if (in <= 0) { + return; + } + + while (1) { + size_t bufferSize = 0; + int result = 0; + if ((result = read(in, &bufferSize, sizeof(bufferSize))) == 0) { + VLOG("Done reading"); + break; + } else if (result < 0 || result != sizeof(bufferSize)) { + ALOGE("Error reading config size"); + break; + } + + vector<uint8_t> buffer(bufferSize); + if ((result = read(in, buffer.data(), bufferSize)) > 0 && ((size_t)result) == bufferSize) { + ShellSubscription config; + if (config.ParseFromArray(buffer.data(), bufferSize)) { + updateConfig(config); + } else { + ALOGE("error parsing the config"); + break; + } + } else { + VLOG("Error reading the config, returned: %d, expecting %zu", result, bufferSize); + break; + } + } +} + +void ShellSubscriber::cleanUpLocked() { + // The file descriptors will be closed by binder. + mInput = 0; + mOutput = 0; + mResultReceiver = nullptr; + mPushedMatchers.clear(); + VLOG("done clean up"); +} + +void ShellSubscriber::onLogEvent(const LogEvent& event) { + std::lock_guard<std::mutex> lock(mMutex); + + if (mOutput <= 0) { + return; + } + + for (const auto& matcher : mPushedMatchers) { + if (matchesSimple(*mUidMap, matcher, event)) { + // First write the payload size. + size_t bufferSize = mProto.size(); + write(mOutput, &bufferSize, sizeof(bufferSize)); + + // Then write the payload. + event.ToProto(mProto); + mProto.flush(mOutput); + mProto.clear(); + break; + } + } +} + +void ShellSubscriber::binderDied(const wp<IBinder>& who) { + { + VLOG("Shell exits"); + std::lock_guard<std::mutex> lock(mMutex); + cleanUpLocked(); + } + mShellDied.notify_all(); +} + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h new file mode 100644 index 000000000000..0ace35fab850 --- /dev/null +++ b/cmds/statsd/src/shell/ShellSubscriber.h @@ -0,0 +1,100 @@ +/* + * 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. + */ + +#pragma once + +#include "logd/LogEvent.h" + +#include <android/util/ProtoOutputStream.h> +#include <binder/IResultReceiver.h> +#include <condition_variable> +#include <mutex> +#include <string> +#include <thread> +#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "packages/UidMap.h" + +namespace android { +namespace os { +namespace statsd { + +/** + * Handles atoms subscription via shell cmd. + * + * A shell subscription lasts *until shell exits*. Unlike config based clients, a shell client + * communicates with statsd via file descriptors. They can subscribe pushed and pulled atoms. + * The atoms are sent back to the client in real time, as opposed to + * keeping the data in memory. Shell clients do not subscribe aggregated metrics, as they are + * responsible for doing the aggregation after receiving the atom events. + * + * Shell client pass ShellSubscription in the proto binary format. Client can update the + * subscription by sending a new subscription. The new subscription would replace the old one. + * Input data stream format is: + * + * |size_t|subscription proto|size_t|subscription proto|.... + * + * statsd sends the events back in Atom proto binary format. Each Atom message is preceded + * with sizeof(size_t) bytes indicating the size of the proto message payload. + * + * The stream would be in the following format: + * |size_t|atom1 proto|size_t|atom2 proto|.... + * + * Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread + * until it exits. + */ +class ShellSubscriber : public virtual IBinder::DeathRecipient { +public: + ShellSubscriber(sp<UidMap> uidMap) : mUidMap(uidMap){}; + + /** + * Start a new subscription. + */ + void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver); + + void binderDied(const wp<IBinder>& who); + + void onLogEvent(const LogEvent& event); + +private: + void readConfig(int in); + + void updateConfig(const ShellSubscription& config); + + void cleanUpLocked(); + + sp<UidMap> mUidMap; + + // bool mWritten = false; + + android::util::ProtoOutputStream mProto; + + mutable std::mutex mMutex; + + std::condition_variable mShellDied; // semaphore for waiting until shell exits. + + int mInput; // The input file descriptor + + int mOutput; // The output file descriptor + + sp<IResultReceiver> mResultReceiver; + + std::vector<SimpleAtomMatcher> mPushedMatchers; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto new file mode 100644 index 000000000000..516693d4e7f7 --- /dev/null +++ b/cmds/statsd/src/shell/shell_config.proto @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.os.statsd; + +option java_package = "com.android.os"; +option java_outer_classname = "ShellConfig"; + +import "frameworks/base/cmds/statsd/src/statsd_config.proto"; + +message PulledAtomSubscription { + optional int32 atom_id = 1; + + /* gap between two pulls in milliseconds */ + optional int32 freq_millis = 2; +} + +message ShellSubscription { + repeated SimpleAtomMatcher pushed = 1; + repeated PulledAtomSubscription pulled = 2; +}
\ No newline at end of file diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 3ebc8a492d68..2f19a02ecafe 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -392,13 +392,13 @@ void StorageManager::trimToFit(const char* path) { } } -void StorageManager::printStats(FILE* out) { - printDirStats(out, STATS_SERVICE_DIR); - printDirStats(out, STATS_DATA_DIR); +void StorageManager::printStats(int outFd) { + printDirStats(outFd, STATS_SERVICE_DIR); + printDirStats(outFd, STATS_DATA_DIR); } -void StorageManager::printDirStats(FILE* out, const char* path) { - fprintf(out, "Printing stats of %s\n", path); +void StorageManager::printDirStats(int outFd, const char* path) { + dprintf(outFd, "Printing stats of %s\n", path); unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir); if (dir == NULL) { VLOG("Path %s does not exist", path); @@ -418,25 +418,22 @@ void StorageManager::printDirStats(FILE* out, const char* path) { int64_t timestamp = result[0]; int64_t uid = result[1]; int64_t configID = result[2]; - fprintf(out, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld", - fileCount + 1, - (long long)timestamp, - (int)uid, - (long long)configID); + dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld", fileCount + 1, + (long long)timestamp, (int)uid, (long long)configID); string file_name = getFilePath(path, timestamp, uid, configID); ifstream file(file_name.c_str(), ifstream::in | ifstream::binary); if (file.is_open()) { file.seekg(0, ios::end); int fileSize = file.tellg(); file.close(); - fprintf(out, ", File Size: %d bytes", fileSize); + dprintf(outFd, ", File Size: %d bytes", fileSize); totalFileSize += fileSize; } - fprintf(out, "\n"); + dprintf(outFd, "\n"); fileCount++; } - fprintf(out, "\tTotal number of files: %d, Total size of files: %d bytes.\n", - fileCount, totalFileSize); + dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount, + totalFileSize); } } // namespace statsd diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index 4840f3c4fa34..8fbc89e4f657 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -100,13 +100,13 @@ public: /** * Prints disk usage statistics related to statsd. */ - static void printStats(FILE* out); + static void printStats(int out); private: /** * Prints disk usage statistics about a directory related to statsd. */ - static void printDirStats(FILE* out, const char* path); + static void printDirStats(int out, const char* path); }; } // namespace statsd diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index e86a49d93cf9..055a91eaa1cc 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -776,7 +776,7 @@ public class ActivityManager { /** @hide */ public int getPackageScreenCompatMode(String packageName) { try { - return getService().getPackageScreenCompatMode(packageName); + return getTaskService().getPackageScreenCompatMode(packageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -785,7 +785,7 @@ public class ActivityManager { /** @hide */ public void setPackageScreenCompatMode(String packageName, int mode) { try { - getService().setPackageScreenCompatMode(packageName, mode); + getTaskService().setPackageScreenCompatMode(packageName, mode); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -794,7 +794,7 @@ public class ActivityManager { /** @hide */ public boolean getPackageAskScreenCompat(String packageName) { try { - return getService().getPackageAskScreenCompat(packageName); + return getTaskService().getPackageAskScreenCompat(packageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -803,7 +803,7 @@ public class ActivityManager { /** @hide */ public void setPackageAskScreenCompat(String packageName, boolean ask) { try { - getService().setPackageAskScreenCompat(packageName, ask); + getTaskService().setPackageAskScreenCompat(packageName, ask); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 87366db56976..be1f2dbc8e4f 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -18,6 +18,7 @@ package android.app; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ComponentName; import android.content.IIntentSender; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -183,12 +184,6 @@ public abstract class ActivityManagerInternal { /** Trims memory usage in the system by removing/stopping unused application processes. */ public abstract void trimApplications(); - /** Returns the screen compatibility mode for the given application. */ - public abstract int getPackageScreenCompatMode(ApplicationInfo ai); - - /** Sets the screen compatibility mode for the given application. */ - public abstract void setPackageScreenCompatMode(ApplicationInfo ai, int mode); - /** Closes all system dialogs. */ public abstract void closeSystemDialogs(String reason); @@ -201,6 +196,11 @@ public abstract class ActivityManagerInternal { public abstract boolean hasRunningActivity(int uid, @Nullable String packageName); public abstract void updateOomAdj(); + public abstract void updateCpuStats(); + public abstract void updateUsageStats( + ComponentName activity, int uid, int userId, boolean resumed); + public abstract void updateForegroundTimeIfOnBattery( + String packageName, int uid, long cpuTimeDiff); public abstract void sendForegroundProfileChanged(int userId); /** @@ -226,4 +226,10 @@ public abstract class ActivityManagerInternal { /** Gets the task id for a given activity. */ public abstract int getTaskIdForActivity(@NonNull IBinder token, boolean onlyRoot); + + public abstract void setBooting(boolean booting); + public abstract boolean isBooting(); + public abstract void setBooted(boolean booted); + public abstract boolean isBooted(); + public abstract void finishBooting(); } diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java index 37a05f0e1d3a..9d82ffa838ca 100644 --- a/core/java/android/app/DatePickerDialog.java +++ b/core/java/android/app/DatePickerDialog.java @@ -239,7 +239,7 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, * @param year the selected year * @param month the selected month (0-11 for compatibility with * {@link Calendar#MONTH}) - * @param dayOfMonth th selected day of the month (1-31, depending on + * @param dayOfMonth the selected day of the month (1-31, depending on * month) */ void onDateSet(DatePicker view, int year, int month, int dayOfMonth); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 2490caef1b2f..c0c8b799ec3b 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -248,10 +248,7 @@ interface IActivityManager { boolean runGc, in String path, in ParcelFileDescriptor fd, in RemoteCallback finishCallback); boolean isUserRunning(int userid, int flags); - int getPackageScreenCompatMode(in String packageName); void setPackageScreenCompatMode(in String packageName, int mode); - boolean getPackageAskScreenCompat(in String packageName); - void setPackageAskScreenCompat(in String packageName, boolean ask); boolean switchUser(int userid); boolean removeTask(int taskId); void registerProcessObserver(in IProcessObserver observer); diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 46664c61fbb9..abd1cca06d26 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -422,4 +422,9 @@ interface IActivityTaskManager { void resumeAppSwitches(); void setActivityController(in IActivityController watcher, boolean imAMonkey); void setVoiceKeepAwake(in IVoiceInteractionSession session, boolean keepAwake); + + int getPackageScreenCompatMode(in String packageName); + void setPackageScreenCompatMode(in String packageName, int mode); + boolean getPackageAskScreenCompat(in String packageName); + void setPackageAskScreenCompat(in String packageName, boolean ask); } diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 4f172a4251d4..e532ecec95bc 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -25,7 +25,6 @@ import android.content.res.Configuration; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; -import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -214,7 +213,7 @@ public class UiModeManager { } /** - * Sets the night mode. + * Sets the system-wide night mode. * <p> * The mode can be one of: * <ul> @@ -231,6 +230,12 @@ public class UiModeManager { * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car} * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a * device. Starting in API 23, changes to night mode are always effective. + * <p> + * Changes to night mode take effect globally and will result in a configuration change + * (and potentially an Activity lifecycle event) being applied to all running apps. + * Developers interested in an app-local implementation of night mode should consider using + * {@link androidx.appcompat.app.AppCompatDelegate#setDefaultNightMode(int)} to manage the + * -night qualifier locally. * * @param mode the night mode to set * @see #getNightMode() diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index bd7a2ddb5af8..fc67c10e7e5e 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3450,15 +3450,14 @@ public class DevicePolicyManager { * @param flags Bit mask of additional options: currently supported flags are * {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}. * @param reason a string that contains the reason for wiping data, which can be - * presented to the user. + * presented to the user. If the string is null or empty, user won't be notified. * @throws SecurityException if the calling application does not own an active administrator * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} * @throws IllegalArgumentException if the input reason string is null or empty. */ - public void wipeData(int flags, @NonNull CharSequence reason) { + public void wipeData(int flags, CharSequence reason) { throwIfParentInstance("wipeData"); - Preconditions.checkNotNull(reason, "CharSequence is null"); - wipeDataInternal(flags, reason.toString()); + wipeDataInternal(flags, reason != null ? reason.toString() : null); } /** @@ -3469,7 +3468,7 @@ public class DevicePolicyManager { * @see #wipeData(int, CharSequence) * @hide */ - private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) { + private void wipeDataInternal(int flags, String wipeReasonForUser) { if (mService != null) { try { mService.wipeDataWithReason(flags, wipeReasonForUser); diff --git a/core/java/android/content/res/package.html b/core/java/android/content/res/package.html index 3d0bac18d7b7..3970b16a57e1 100644 --- a/core/java/android/content/res/package.html +++ b/core/java/android/content/res/package.html @@ -1,7 +1,7 @@ <HTML> <BODY> <p>Contains classes for accessing application resources, -such as raw asset files, colors, drawables, media or other other files +such as raw asset files, colors, drawables, media, or other files in the package, plus important device configuration details (orientation, input types, etc.) that affect how the application may behave.</p> @@ -9,4 +9,4 @@ in the package, plus important device configuration details href="{@docRoot}guide/topics/resources/index.html">Application Resources</a> guide.</p> {@more} </BODY> -</HTML>
\ No newline at end of file +</HTML> diff --git a/core/java/android/hardware/camera2/utils/CloseableLock.java b/core/java/android/hardware/camera2/utils/CloseableLock.java index 9ac89c825430..3b8beaccdcd9 100644 --- a/core/java/android/hardware/camera2/utils/CloseableLock.java +++ b/core/java/android/hardware/camera2/utils/CloseableLock.java @@ -290,7 +290,7 @@ public class CloseableLock implements AutoCloseable { /** * Release a single lock that was acquired. * - * <p>Any other other that is blocked and trying to acquire a lock will get a chance + * <p>Any other thread that is blocked and trying to acquire a lock will get a chance * to acquire the lock.</p> * * @throws IllegalStateException if no locks were acquired, or if the lock was already closed diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index b9d900730b69..f947b5ef9f5b 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -773,6 +773,7 @@ public class Binder implements IBinder { if (tracingEnabled) { Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code); } + ThreadLocalWorkSourceUid.set(Binder.getCallingUid()); res = onTransact(code, data, reply, flags); } catch (RemoteException|RuntimeException e) { if (observer != null) { @@ -793,6 +794,7 @@ public class Binder implements IBinder { } res = true; } finally { + ThreadLocalWorkSourceUid.clear(); if (tracingEnabled) { Trace.traceEnd(Trace.TRACE_TAG_ALWAYS); } diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index e03af9db1975..f3a9a504a765 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -739,6 +739,8 @@ public class Handler { private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; + msg.workSourceUid = ThreadLocalWorkSourceUid.get(); + if (mAsynchronous) { msg.setAsynchronous(true); } diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index ded3a1983fb2..5b8ababb8ca4 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -205,6 +205,7 @@ public final class Looper { token = observer.messageDispatchStarting(); } try { + ThreadLocalWorkSourceUid.set(msg.workSourceUid); msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); @@ -216,6 +217,7 @@ public final class Looper { } throw exception; } finally { + ThreadLocalWorkSourceUid.clear(); if (traceTag != 0) { Trace.traceEnd(traceTag); } diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index 455d8edd4138..cd3f301ebf08 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -16,7 +16,6 @@ package android.os; -import android.os.MessageProto; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -74,11 +73,25 @@ public final class Message implements Parcelable { public Messenger replyTo; /** + * Indicates that the uid is not set; + * + * @hide Only for use within the system server. + */ + public static final int UID_NONE = -1; + + /** * Optional field indicating the uid that sent the message. This is * only valid for messages posted by a {@link Messenger}; otherwise, * it will be -1. */ - public int sendingUid = -1; + public int sendingUid = UID_NONE; + + /** + * Optional field indicating the uid that caused this message to be enqueued. + * + * @hide Only for use within the system server. + */ + public int workSourceUid = UID_NONE; /** If set message is in use. * This flag is set when the message is enqueued and remains set while it @@ -151,6 +164,7 @@ public final class Message implements Parcelable { m.obj = orig.obj; m.replyTo = orig.replyTo; m.sendingUid = orig.sendingUid; + m.workSourceUid = orig.workSourceUid; if (orig.data != null) { m.data = new Bundle(orig.data); } @@ -301,7 +315,8 @@ public final class Message implements Parcelable { arg2 = 0; obj = null; replyTo = null; - sendingUid = -1; + sendingUid = UID_NONE; + workSourceUid = UID_NONE; when = 0; target = null; callback = null; @@ -329,6 +344,7 @@ public final class Message implements Parcelable { this.obj = o.obj; this.replyTo = o.replyTo; this.sendingUid = o.sendingUid; + this.workSourceUid = o.workSourceUid; if (o.data != null) { this.data = (Bundle) o.data.clone(); @@ -612,6 +628,7 @@ public final class Message implements Parcelable { dest.writeBundle(data); Messenger.writeMessengerOrNullToParcel(replyTo, dest); dest.writeInt(sendingUid); + dest.writeInt(workSourceUid); } private void readFromParcel(Parcel source) { @@ -625,5 +642,6 @@ public final class Message implements Parcelable { data = source.readBundle(); replyTo = Messenger.readMessengerOrNullFromParcel(source); sendingUid = source.readInt(); + workSourceUid = source.readInt(); } } diff --git a/core/java/android/os/ThreadLocalWorkSourceUid.java b/core/java/android/os/ThreadLocalWorkSourceUid.java new file mode 100644 index 000000000000..df1d275f358d --- /dev/null +++ b/core/java/android/os/ThreadLocalWorkSourceUid.java @@ -0,0 +1,44 @@ +/* + * 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.os; + +/** + * @hide Only for use within system server. + */ +public final class ThreadLocalWorkSourceUid { + public static final int UID_NONE = Message.UID_NONE; + private static final ThreadLocal<Integer> sWorkSourceUid = + ThreadLocal.withInitial(() -> UID_NONE); + + /** Returns the original work source uid. */ + public static int get() { + return sWorkSourceUid.get(); + } + + /** Sets the original work source uid. */ + public static void set(int uid) { + sWorkSourceUid.set(uid); + } + + /** Clears the stored work source uid. */ + public static void clear() { + sWorkSourceUid.set(UID_NONE); + } + + private ThreadLocalWorkSourceUid() { + } +} diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index e819c963ae45..5012d77568bf 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -146,6 +146,7 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_CONDITION_ID = "conditionId"; private static final String RULE_ATT_CREATION_TIME = "creationTime"; private static final String RULE_ATT_ENABLER = "enabler"; + private static final String RULE_ATT_MODIFIED = "modified"; @UnsupportedAppUsage public boolean allowAlarms = DEFAULT_ALLOW_ALARMS; @@ -633,6 +634,7 @@ public class ZenModeConfig implements Parcelable { Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name); rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; } + rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false); return rt; } @@ -656,6 +658,7 @@ public class ZenModeConfig implements Parcelable { if (rule.condition != null) { writeConditionXml(rule.condition, out); } + out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified)); } public static Condition readConditionXml(XmlPullParser parser) { @@ -1456,6 +1459,7 @@ public class ZenModeConfig implements Parcelable { public long creationTime; // required for automatic public String enabler; // package name, only used for manual rules. public ZenPolicy zenPolicy; + public boolean modified; // rule has been modified from initial creation public ZenRule() { } @@ -1477,6 +1481,7 @@ public class ZenModeConfig implements Parcelable { enabler = source.readString(); } zenPolicy = source.readParcelable(null); + modified = source.readInt() == 1; } @Override @@ -1512,6 +1517,7 @@ public class ZenModeConfig implements Parcelable { dest.writeInt(0); } dest.writeParcelable(zenPolicy, 0); + dest.writeInt(modified ? 1 : 0); } @Override @@ -1528,6 +1534,7 @@ public class ZenModeConfig implements Parcelable { .append(",creationTime=").append(creationTime) .append(",enabler=").append(enabler) .append(",zenPolicy=").append(zenPolicy) + .append(",modified=").append(modified) .append(']').toString(); } @@ -1554,6 +1561,7 @@ public class ZenModeConfig implements Parcelable { if (zenPolicy != null) { zenPolicy.writeToProto(proto, ZenRuleProto.ZEN_POLICY); } + proto.write(ZenRuleProto.MODIFIED, modified); proto.end(token); } @@ -1606,6 +1614,9 @@ public class ZenModeConfig implements Parcelable { if (!Objects.equals(zenPolicy, to.zenPolicy)) { d.addLine(item, "zenPolicy", zenPolicy, to.zenPolicy); } + if (modified != to.modified) { + d.addLine(item, "modified", modified, to.modified); + } } @Override @@ -1622,13 +1633,14 @@ public class ZenModeConfig implements Parcelable { && Objects.equals(other.component, component) && Objects.equals(other.id, id) && Objects.equals(other.enabler, enabler) - && Objects.equals(other.zenPolicy, zenPolicy); + && Objects.equals(other.zenPolicy, zenPolicy) + && other.modified == modified; } @Override public int hashCode() { return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, - component, id, enabler, zenPolicy); + component, id, enabler, zenPolicy, modified); } public boolean isAutomaticActive() { diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index 533d72590f0a..1203541756e8 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -410,11 +410,11 @@ public class ApkSignatureSchemeV2Verifier { NoSuchAlgorithmException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { SignatureInfo signatureInfo = findSignature(apk); - return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo); + return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo); } } - static byte[] generateFsverityRootHash(String apkPath) + static byte[] generateApkVerityRootHash(String apkPath) throws IOException, SignatureNotFoundException, DigestException, NoSuchAlgorithmException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { @@ -423,7 +423,7 @@ public class ApkSignatureSchemeV2Verifier { if (vSigner.verityRootHash == null) { return null; } - return ApkVerityBuilder.generateFsverityRootHash( + return ApkVerityBuilder.generateApkVerityRootHash( apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo); } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index 758cd2b877f2..939522dcd57f 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -534,11 +534,11 @@ public class ApkSignatureSchemeV3Verifier { NoSuchAlgorithmException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { SignatureInfo signatureInfo = findSignature(apk); - return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo); + return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo); } } - static byte[] generateFsverityRootHash(String apkPath) + static byte[] generateApkVerityRootHash(String apkPath) throws NoSuchAlgorithmException, DigestException, IOException, SignatureNotFoundException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { @@ -547,7 +547,7 @@ public class ApkSignatureSchemeV3Verifier { if (vSigner.verityRootHash == null) { return null; } - return ApkVerityBuilder.generateFsverityRootHash( + return ApkVerityBuilder.generateApkVerityRootHash( apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo); } } diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index de9f55b09200..a299b11c2b25 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -432,16 +432,16 @@ public class ApkSignatureVerifier { * * @return FSverity root hash */ - public static byte[] generateFsverityRootHash(String apkPath) + public static byte[] generateApkVerityRootHash(String apkPath) throws NoSuchAlgorithmException, DigestException, IOException { // first try v3 try { - return ApkSignatureSchemeV3Verifier.generateFsverityRootHash(apkPath); + return ApkSignatureSchemeV3Verifier.generateApkVerityRootHash(apkPath); } catch (SignatureNotFoundException e) { // try older version } try { - return ApkSignatureSchemeV2Verifier.generateFsverityRootHash(apkPath); + return ApkSignatureSchemeV2Verifier.generateApkVerityRootHash(apkPath); } catch (SignatureNotFoundException e) { return null; } diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index e247c87fdb4c..081033ae84e9 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -332,7 +332,7 @@ final class ApkSigningBlockUtils { try { byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest, apk.length(), signatureInfo); - ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk, + ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerityTree(apk, signatureInfo, new ByteBufferFactory() { @Override public ByteBuffer create(int capacity) { @@ -348,26 +348,6 @@ final class ApkSigningBlockUtils { } /** - * Generates the fsverity header and hash tree to be used by kernel for the given apk. This - * method does not check whether the root hash exists in the Signing Block or not. - * - * <p>The output is stored in the {@link ByteBuffer} created by the given {@link - * ByteBufferFactory}. - * - * @return the root hash of the generated hash tree. - */ - public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory, - SignatureInfo signatureInfo) - throws IOException, SignatureNotFoundException, SecurityException, DigestException, - NoSuchAlgorithmException { - try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { - ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk, - signatureInfo, bufferFactory); - return result.rootHash; - } - } - - /** * Returns the ZIP End of Central Directory (EoCD) and its offset in the file. * * @throws IOException if an I/O error occurs while reading the file. diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java index 2dd0117d583d..553511d73670 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/ApkVerityBuilder.java @@ -16,6 +16,9 @@ package android.util.apk; +import android.annotation.NonNull; +import android.annotation.Nullable; + import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; @@ -26,8 +29,10 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; /** - * ApkVerityBuilder builds the APK verity tree and the verity header, which will be used by the - * kernel to verity the APK content on access. + * ApkVerityBuilder builds the APK verity tree and the verity header. The generated tree format can + * be stored on disk for apk-verity setup and used by kernel. Note that since the current + * implementation is different from the upstream, we call this implementation apk-verity instead of + * fs-verity. * * <p>Unlike a regular Merkle tree, APK verity tree does not cover the content fully. Due to * the existing APK format, it has to skip APK Signing Block and also has some special treatment for @@ -47,26 +52,28 @@ abstract class ApkVerityBuilder { private static final byte[] DEFAULT_SALT = new byte[8]; static class ApkVerityResult { - public final ByteBuffer fsverityData; + public final ByteBuffer verityData; + public final int merkleTreeSize; public final byte[] rootHash; - ApkVerityResult(ByteBuffer fsverityData, byte[] rootHash) { - this.fsverityData = fsverityData; + ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) { + this.verityData = verityData; + this.merkleTreeSize = merkleTreeSize; this.rootHash = rootHash; } } /** - * Generates fsverity metadata and the Merkle tree into the {@link ByteBuffer} created by the - * {@link ByteBufferFactory}. The bytes layout in the buffer will be used by the kernel and is - * ready to be appended to the target file to set up fsverity. For fsverity to work, this data - * must be placed at the next page boundary, and the caller must add additional padding in that - * case. + * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link + * ByteBuffer} created by the {@link ByteBufferFactory}. The Merkle tree is suitable to be used + * as the on-disk format for apk-verity. * - * @return ApkVerityResult containing the fsverity data and the root hash of the Merkle tree. + * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the + * front, the tree size, and the calculated root hash. */ - static ApkVerityResult generateApkVerity(RandomAccessFile apk, - SignatureInfo signatureInfo, ByteBufferFactory bufferFactory) + @NonNull + static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk, + @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory) throws IOException, SecurityException, NoSuchAlgorithmException, DigestException { long signingBlockSize = signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset; @@ -76,86 +83,69 @@ abstract class ApkVerityBuilder { ByteBuffer output = bufferFactory.create( merkleTreeSize - + CHUNK_SIZE_BYTES); // maximum size of fsverity metadata + + CHUNK_SIZE_BYTES); // maximum size of apk-verity metadata output.order(ByteOrder.LITTLE_ENDIAN); ByteBuffer tree = slice(output, 0, merkleTreeSize); - ByteBuffer header = slice(output, merkleTreeSize, - merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES); - ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES, - merkleTreeSize + CHUNK_SIZE_BYTES); - byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES]; - ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes); - apkDigest.order(ByteOrder.LITTLE_ENDIAN); - - // NB: Buffer limit is set inside once finished. - calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions); - - // Put the reverse offset to fs-verity header at the end. - output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit()); - output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit() - + 4); // size of this integer right before EOF - output.flip(); - - return new ApkVerityResult(output, apkDigestBytes); + byte[] apkRootHash = generateApkVerityTreeInternal(apk, signatureInfo, DEFAULT_SALT, + levelOffset, tree); + return new ApkVerityResult(output, merkleTreeSize, apkRootHash); + } + + static void generateApkVerityFooter(@NonNull RandomAccessFile apk, + @NonNull SignatureInfo signatureInfo, @NonNull ByteBuffer footerOutput) + throws IOException { + footerOutput.order(ByteOrder.LITTLE_ENDIAN); + generateApkVerityHeader(footerOutput, apk.length(), DEFAULT_SALT); + long signingBlockSize = + signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset; + generateApkVerityExtensions(footerOutput, signatureInfo.apkSigningBlockOffset, + signingBlockSize, signatureInfo.eocdOffset); } /** - * Calculates the fsverity root hash for integrity measurement. This needs to be consistent to - * what kernel returns. + * Calculates the apk-verity root hash for integrity measurement. This needs to be consistent + * to what kernel returns. */ - static byte[] generateFsverityRootHash(RandomAccessFile apk, ByteBuffer apkDigest, - SignatureInfo signatureInfo) + @NonNull + static byte[] generateApkVerityRootHash(@NonNull RandomAccessFile apk, + @NonNull ByteBuffer apkDigest, @NonNull SignatureInfo signatureInfo) throws NoSuchAlgorithmException, DigestException, IOException { - ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES) - .order(ByteOrder.LITTLE_ENDIAN); - ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES); - ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, - CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES); + assertSigningBlockAlignedAndHasFullPages(signatureInfo); - calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions); + ByteBuffer footer = ByteBuffer.allocate(CHUNK_SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN); + generateApkVerityFooter(apk, signatureInfo, footer); + footer.flip(); MessageDigest md = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM); - md.update(header); - md.update(extensions); + md.update(footer); md.update(apkDigest); return md.digest(); } /** - * Internal method to generate various parts of FSVerity constructs, including the header, - * extensions, Merkle tree, and the tree's root hash. The output buffer is flipped to the - * generated data size and is readey for consuming. + * Generates the apk-verity header and hash tree to be used by kernel for the given apk. This + * method does not check whether the root hash exists in the Signing Block or not. + * + * <p>The output is stored in the {@link ByteBuffer} created by the given {@link + * ByteBufferFactory}. + * + * @return the root hash of the generated hash tree. */ - private static void calculateFsveritySignatureInternal( - RandomAccessFile apk, SignatureInfo signatureInfo, ByteBuffer treeOutput, - ByteBuffer rootHashOutput, ByteBuffer headerOutput, ByteBuffer extensionsOutput) - throws IOException, NoSuchAlgorithmException, DigestException { - assertSigningBlockAlignedAndHasFullPages(signatureInfo); - long signingBlockSize = - signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset; - long dataSize = apk.length() - signingBlockSize; - int[] levelOffset = calculateVerityLevelOffset(dataSize); - - if (treeOutput != null) { - byte[] apkRootHash = generateApkVerityTree(apk, signatureInfo, DEFAULT_SALT, - levelOffset, treeOutput); - if (rootHashOutput != null) { - rootHashOutput.put(apkRootHash); - rootHashOutput.flip(); - } - } - - if (headerOutput != null) { - headerOutput.order(ByteOrder.LITTLE_ENDIAN); - generateFsverityHeader(headerOutput, apk.length(), levelOffset.length - 1, - DEFAULT_SALT); - } - - if (extensionsOutput != null) { - extensionsOutput.order(ByteOrder.LITTLE_ENDIAN); - generateFsverityExtensions(extensionsOutput, signatureInfo.apkSigningBlockOffset, - signingBlockSize, signatureInfo.eocdOffset); + @NonNull + static byte[] generateApkVerity(@NonNull String apkPath, + @NonNull ByteBufferFactory bufferFactory, @NonNull SignatureInfo signatureInfo) + throws IOException, SignatureNotFoundException, SecurityException, DigestException, + NoSuchAlgorithmException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + ApkVerityResult result = generateApkVerityTree(apk, signatureInfo, bufferFactory); + ByteBuffer footer = slice(result.verityData, result.merkleTreeSize, + result.verityData.limit()); + generateApkVerityFooter(apk, signatureInfo, footer); + // Put the reverse offset to apk-verity header at the end. + footer.putInt(footer.position() + 4); + result.verityData.limit(result.merkleTreeSize + footer.position()); + return result.rootHash; } } @@ -297,9 +287,13 @@ abstract class ApkVerityBuilder { digester.fillUpLastOutputChunk(); } - private static byte[] generateApkVerityTree(RandomAccessFile apk, SignatureInfo signatureInfo, - byte[] salt, int[] levelOffset, ByteBuffer output) + @NonNull + private static byte[] generateApkVerityTreeInternal(@NonNull RandomAccessFile apk, + @Nullable SignatureInfo signatureInfo, @NonNull byte[] salt, + @NonNull int[] levelOffset, @NonNull ByteBuffer output) throws IOException, NoSuchAlgorithmException, DigestException { + assertSigningBlockAlignedAndHasFullPages(signatureInfo); + // 1. Digest the apk to generate the leaf level hashes. generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output, levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1])); @@ -324,7 +318,7 @@ abstract class ApkVerityBuilder { return rootHash; } - private static ByteBuffer generateFsverityHeader(ByteBuffer buffer, long fileSize, int depth, + private static ByteBuffer generateApkVerityHeader(ByteBuffer buffer, long fileSize, byte[] salt) { if (salt.length != 8) { throw new IllegalArgumentException("salt is not 8 bytes long"); @@ -351,13 +345,12 @@ abstract class ApkVerityBuilder { buffer.put(salt); // salt (8 bytes) skip(buffer, 22); // reserved - buffer.flip(); return buffer; } - private static ByteBuffer generateFsverityExtensions(ByteBuffer buffer, long signingBlockOffset, - long signingBlockSize, long eocdOffset) { - // Snapshot of the FSVerity structs (subject to change once upstreamed). + private static ByteBuffer generateApkVerityExtensions(ByteBuffer buffer, + long signingBlockOffset, long signingBlockSize, long eocdOffset) { + // Snapshot of the experimental fs-verity structs (different from upstream). // // struct fsverity_extension_elide { // __le64 offset; @@ -409,7 +402,6 @@ abstract class ApkVerityBuilder { skip(buffer, kPadding); // padding } - buffer.flip(); return buffer; } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index bfe1e95dbaed..3bab87aea644 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -17,8 +17,6 @@ package android.view; import com.android.internal.os.IResultReceiver; -import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethodClient; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IShortcutService; @@ -71,8 +69,7 @@ interface IWindowManager boolean stopViewServer(); // Transaction #2 boolean isViewServerRunning(); // Transaction #3 - IWindowSession openSession(in IWindowSessionCallback callback, in IInputMethodClient client, - in IInputContext inputContext); + IWindowSession openSession(in IWindowSessionCallback callback); void getInitialDisplaySize(int displayId, out Point size); void getBaseDisplaySize(int displayId, out Point size); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 79eafa80454a..7271a9e4616f 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -70,15 +70,8 @@ public class SurfaceControl implements Parcelable { private static native void nativeDestroy(long nativeObject); private static native void nativeDisconnect(long nativeObject); - private static native Bitmap nativeScreenshot(IBinder displayToken, - Rect sourceCrop, int width, int height, int minLayer, int maxLayer, - boolean allLayers, boolean useIdentityTransform, int rotation); - private static native GraphicBuffer nativeScreenshotToBuffer(IBinder displayToken, - Rect sourceCrop, int width, int height, int minLayer, int maxLayer, - boolean allLayers, boolean useIdentityTransform, int rotation); - private static native void nativeScreenshot(IBinder displayToken, Surface consumer, - Rect sourceCrop, int width, int height, int minLayer, int maxLayer, - boolean allLayers, boolean useIdentityTransform); + private static native GraphicBuffer nativeScreenshot(IBinder displayToken, + Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation); private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken, Rect sourceCrop, float frameScale); @@ -1189,52 +1182,39 @@ public class SurfaceControl implements Parcelable { } /** - * Copy the current screen contents into the provided {@link Surface} - * - * @param display The display to take the screenshot of. - * @param consumer The {@link Surface} to take the screenshot into. - * @param width The desired width of the returned bitmap; the raw - * screen will be scaled down to this size. - * @param height The desired height of the returned bitmap; the raw - * screen will be scaled down to this size. - * @param minLayer The lowest (bottom-most Z order) surface layer to - * include in the screenshot. - * @param maxLayer The highest (top-most Z order) surface layer to - * include in the screenshot. - * @param useIdentityTransform Replace whatever transformation (rotation, - * scaling, translation) the surface layers are currently using with the - * identity transformation while taking the screenshot. + * @see SurfaceControl#screenshot(IBinder, Surface, Rect, int, int, boolean, int) */ - public static void screenshot(IBinder display, Surface consumer, - int width, int height, int minLayer, int maxLayer, - boolean useIdentityTransform) { - screenshot(display, consumer, new Rect(), width, height, minLayer, maxLayer, - false, useIdentityTransform); + public static void screenshot(IBinder display, Surface consumer) { + screenshot(display, consumer, new Rect(), 0, 0, false, 0); } /** * Copy the current screen contents into the provided {@link Surface} * - * @param display The display to take the screenshot of. * @param consumer The {@link Surface} to take the screenshot into. - * @param width The desired width of the returned bitmap; the raw - * screen will be scaled down to this size. - * @param height The desired height of the returned bitmap; the raw - * screen will be scaled down to this size. + * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int) */ - public static void screenshot(IBinder display, Surface consumer, - int width, int height) { - screenshot(display, consumer, new Rect(), width, height, 0, 0, true, false); + public static void screenshot(IBinder display, Surface consumer, Rect sourceCrop, int width, + int height, boolean useIdentityTransform, int rotation) { + if (consumer == null) { + throw new IllegalArgumentException("consumer must not be null"); + } + + final GraphicBuffer buffer = screenshotToBuffer(display, sourceCrop, width, height, + useIdentityTransform, rotation); + try { + consumer.attachAndQueueBuffer(buffer); + } catch (RuntimeException e) { + Log.w(TAG, "Failed to take screenshot - " + e.getMessage()); + } } /** - * Copy the current screen contents into the provided {@link Surface} - * - * @param display The display to take the screenshot of. - * @param consumer The {@link Surface} to take the screenshot into. + * @see SurfaceControl#screenshot(Rect, int, int, boolean, int)} */ - public static void screenshot(IBinder display, Surface consumer) { - screenshot(display, consumer, new Rect(), 0, 0, 0, 0, true, false); + @UnsupportedAppUsage + public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) { + return screenshot(sourceCrop, width, height, false, rotation); } /** @@ -1242,79 +1222,16 @@ public class SurfaceControl implements Parcelable { * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap into * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)} * - * CAVEAT: Versions of screenshot that return a {@link Bitmap} can - * be extremely slow; avoid use unless absolutely necessary; prefer - * the versions that use a {@link Surface} instead, such as - * {@link SurfaceControl#screenshot(IBinder, Surface)}. + * CAVEAT: Versions of screenshot that return a {@link Bitmap} can be extremely slow; avoid use + * unless absolutely necessary; prefer the versions that use a {@link Surface} such as + * {@link SurfaceControl#screenshot(IBinder, Surface)} or {@link GraphicBuffer} such as + * {@link SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}. * - * @param sourceCrop The portion of the screen to capture into the Bitmap; - * caller may pass in 'new Rect()' if no cropping is desired. - * @param width The desired width of the returned bitmap; the raw - * screen will be scaled down to this size. - * @param height The desired height of the returned bitmap; the raw - * screen will be scaled down to this size. - * @param minLayer The lowest (bottom-most Z order) surface layer to - * include in the screenshot. - * @param maxLayer The highest (top-most Z order) surface layer to - * include in the screenshot. - * @param useIdentityTransform Replace whatever transformation (rotation, - * scaling, translation) the surface layers are currently using with the - * identity transformation while taking the screenshot. - * @param rotation Apply a custom clockwise rotation to the screenshot, i.e. - * Surface.ROTATION_0,90,180,270. Surfaceflinger will always take - * screenshots in its native portrait orientation by default, so this is - * useful for returning screenshots that are independent of device - * orientation. - * @return Returns a hardware Bitmap containing the screen contents, or null - * if an error occurs. Make sure to call Bitmap.recycle() as soon as - * possible, once its content is not needed anymore. + * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)} */ @UnsupportedAppUsage public static Bitmap screenshot(Rect sourceCrop, int width, int height, - int minLayer, int maxLayer, boolean useIdentityTransform, - int rotation) { - // TODO: should take the display as a parameter - IBinder displayToken = SurfaceControl.getBuiltInDisplay( - SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); - return nativeScreenshot(displayToken, sourceCrop, width, height, - minLayer, maxLayer, false, useIdentityTransform, rotation); - } - - /** - * Like {@link SurfaceControl#screenshot(Rect, int, int, int, int, boolean, int)} - * but returns a GraphicBuffer. - */ - public static GraphicBuffer screenshotToBuffer(Rect sourceCrop, int width, int height, - int minLayer, int maxLayer, boolean useIdentityTransform, - int rotation) { - IBinder displayToken = SurfaceControl.getBuiltInDisplay( - SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); - return nativeScreenshotToBuffer(displayToken, sourceCrop, width, height, - minLayer, maxLayer, false, useIdentityTransform, rotation); - } - - /** - * Like {@link SurfaceControl#screenshot(Rect, int, int, int, int, boolean, int)} but - * includes all Surfaces in the screenshot. This will also update the orientation so it - * sends the correct coordinates to SF based on the rotation value. - * - * @param sourceCrop The portion of the screen to capture into the Bitmap; - * caller may pass in 'new Rect()' if no cropping is desired. - * @param width The desired width of the returned bitmap; the raw - * screen will be scaled down to this size. - * @param height The desired height of the returned bitmap; the raw - * screen will be scaled down to this size. - * @param rotation Apply a custom clockwise rotation to the screenshot, i.e. - * Surface.ROTATION_0,90,180,270. Surfaceflinger will always take - * screenshots in its native portrait orientation by default, so this is - * useful for returning screenshots that are independent of device - * orientation. - * @return Returns a Bitmap containing the screen contents, or null - * if an error occurs. Make sure to call Bitmap.recycle() as soon as - * possible, once its content is not needed anymore. - */ - @UnsupportedAppUsage - public static Bitmap screenshot(Rect sourceCrop, int width, int height, int rotation) { + boolean useIdentityTransform, int rotation) { // TODO: should take the display as a parameter IBinder displayToken = SurfaceControl.getBuiltInDisplay( SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); @@ -1323,22 +1240,45 @@ public class SurfaceControl implements Parcelable { } SurfaceControl.rotateCropForSF(sourceCrop, rotation); - return nativeScreenshot(displayToken, sourceCrop, width, height, 0, 0, true, - false, rotation); + final GraphicBuffer buffer = screenshotToBuffer(displayToken, sourceCrop, width, height, + useIdentityTransform, rotation); + + if (buffer == null) { + Log.w(TAG, "Failed to take screenshot"); + return null; + } + return Bitmap.createHardwareBitmap(buffer); } - @UnsupportedAppUsage - private static void screenshot(IBinder display, Surface consumer, Rect sourceCrop, - int width, int height, int minLayer, int maxLayer, boolean allLayers, - boolean useIdentityTransform) { + /** + * Captures all the surfaces in a display and returns a {@link GraphicBuffer} with the content. + * + * @param display The display to take the screenshot of. + * @param sourceCrop The portion of the screen to capture into the Bitmap; caller may + * pass in 'new Rect()' if no cropping is desired. + * @param width The desired width of the returned bitmap; the raw screen will be + * scaled down to this size; caller may pass in 0 if no scaling is + * desired. + * @param height The desired height of the returned bitmap; the raw screen will + * be scaled down to this size; caller may pass in 0 if no scaling + * is desired. + * @param useIdentityTransform Replace whatever transformation (rotation, scaling, translation) + * the surface layers are currently using with the identity + * transformation while taking the screenshot. + * @param rotation Apply a custom clockwise rotation to the screenshot, i.e. + * Surface.ROTATION_0,90,180,270. SurfaceFlinger will always take + * screenshots in its native portrait orientation by default, so + * this is useful for returning screenshots that are independent of + * device orientation. + * @return Returns a GraphicBuffer that contains the captured content. + */ + public static GraphicBuffer screenshotToBuffer(IBinder display, Rect sourceCrop, int width, + int height, boolean useIdentityTransform, int rotation) { if (display == null) { throw new IllegalArgumentException("displayToken must not be null"); } - if (consumer == null) { - throw new IllegalArgumentException("consumer must not be null"); - } - nativeScreenshot(display, consumer, sourceCrop, width, height, - minLayer, maxLayer, allLayers, useIdentityTransform); + + return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation); } private static void rotateCropForSF(Rect crop, int rot) { diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index e20acf1a0008..92d145c3bdeb 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -196,7 +196,11 @@ public final class WindowManagerGlobal { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { - InputMethodManager imm = InputMethodManager.getInstance(); + if (InputMethodManager.ENABLE_LEGACY_EAGER_INITIALIZATION) { + // Emulate the legacy behavior. The global instance of InputMethodManager + // was instantiated here. + InputMethodManager.getInstance(); + } IWindowManager windowManager = getWindowManagerService(); sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { @@ -204,8 +208,7 @@ public final class WindowManagerGlobal { public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } - }, - imm.getClient(), imm.getInputContext()); + }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java index f89743c149b1..2d1249df4fb5 100644 --- a/core/java/android/view/animation/DecelerateInterpolator.java +++ b/core/java/android/view/animation/DecelerateInterpolator.java @@ -41,8 +41,8 @@ public class DecelerateInterpolator extends BaseInterpolator implements NativeIn * Constructor * * @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces - * an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the - * ease-out effect (i.e., it starts even faster and ends evens slower) + * an upside-down y=x^2 parabola. Increasing factor above 1.0f exaggerates the + * ease-out effect (i.e., it starts even faster and ends evens slower). */ public DecelerateInterpolator(float factor) { mFactor = factor; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index be50dd1f1448..9ede57f96177 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -225,6 +225,44 @@ public final class InputMethodManager { static final String PENDING_EVENT_COUNTER = "aq:imm"; + /** + * {@code true} if we want to instantiate {@link InputMethodManager} eagerly in + * {@link android.view.WindowManagerGlobal#getWindowSession()}, which is often called in an + * early stage of process startup, which is how Android has worked. + * + * <p>We still have this settings because we know there are possible compatibility concerns if + * we stop doing so. Here are scenarios we know and there could be more scenarios we are not + * aware of right know.</p> + * + * <ul> + * <li>Apps that directly access {@link #sInstance} via reflection, which is currently + * allowed because of {@link UnsupportedAppUsage} annotation. Currently + * {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that + * {@link #sInstance} is not {@code null} when such an app is accessing it, but removing + * that code from {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal + * untested code paths in their apps, which probably happen in an early startup time of that + * app.</li> + * <li>Apps that directly access {@link #peekInstance()} via reflection, which is currently + * allowed because of {@link UnsupportedAppUsage} annotation. Currently + * {@link android.view.WindowManagerGlobal#getWindowSession()} is likely to guarantee that + * {@link #peekInstance()} returns non-{@code null} object when such an app is calling + * {@link #peekInstance()}, but removing that code from + * {@link android.view.WindowManagerGlobal#getWindowSession()} can reveal untested code + * paths in their apps, which probably happen in an early startup time of that app. The good + * news is that unlike {@link #sInstance}'s case we can at least work around this scenario + * by changing the semantics of {@link #peekInstance()}, which is currently defined as + * "retrieve the global {@link InputMethodManager} instance, if it exists" to something that + * always returns non-{@code null} {@link InputMethodManager}. However, introducing such an + * workaround can also trigger different compatibility issues if {@link #peekInstance()} was + * called before {@link android.view.WindowManagerGlobal#getWindowSession()} and it expected + * {@link #peekInstance()} to return {@code null} as written in the JavaDoc.</li> + * </ul> + * + * <p>TODO(Bug 116157766): Check if we can set {@code false} here then remove this settings.</p> + * @hide + */ + public static final boolean ENABLE_LEGACY_EAGER_INITIALIZATION = true; + @UnsupportedAppUsage static InputMethodManager sInstance; @@ -651,8 +689,10 @@ public final class InputMethodManager { synchronized (InputMethodManager.class) { if (sInstance == null) { try { - sInstance = new InputMethodManager(Looper.getMainLooper()); - } catch (ServiceNotFoundException e) { + final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper()); + imm.mService.addClient(imm.mClient, imm.mIInputContext); + sInstance = imm; + } catch (ServiceNotFoundException | RemoteException e) { throw new IllegalStateException(e); } } diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java index 05c0304e042c..30f465cb42c6 100644 --- a/core/java/android/webkit/TracingController.java +++ b/core/java/android/webkit/TracingController.java @@ -36,7 +36,7 @@ import java.util.concurrent.Executor; * <pre class="prettyprint"> * TracingController tracingController = TracingController.getInstance(); * tracingController.start(new TracingConfig.Builder() - * .addCategories(CATEGORIES_WEB_DEVELOPER).build()); + * .addCategories(TracingConfig.CATEGORIES_WEB_DEVELOPER).build()); * ... * tracingController.stop(new FileOutputStream("trace.json"), * Executors.newSingleThreadExecutor()); diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java index 0650d0af7caf..e4724ff45b9e 100644 --- a/core/java/com/android/internal/os/LooperStats.java +++ b/core/java/com/android/internal/os/LooperStats.java @@ -17,6 +17,7 @@ package com.android.internal.os; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -36,7 +37,7 @@ import java.util.concurrent.ThreadLocalRandom; * @hide Only for use within the system server. */ public class LooperStats implements Looper.Observer { - private static final int TOKEN_POOL_SIZE = 50; + private static final int SESSION_POOL_SIZE = 50; @GuardedBy("mLock") private final SparseArray<Entry> mEntries = new SparseArray<>(512); @@ -78,17 +79,19 @@ public class LooperStats implements Looper.Observer { } DispatchSession session = (DispatchSession) token; - Entry entry = getOrCreateEntry(msg); - synchronized (entry) { - entry.messageCount++; - if (session != DispatchSession.NOT_SAMPLED) { - entry.recordedMessageCount++; - long latency = getElapsedRealtimeMicro() - session.startTimeMicro; - long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro; - entry.totalLatencyMicro += latency; - entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency); - entry.cpuUsageMicro += cpuUsage; - entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage); + Entry entry = findEntry(msg, /* allowCreateNew= */session != DispatchSession.NOT_SAMPLED); + if (entry != null) { + synchronized (entry) { + entry.messageCount++; + if (session != DispatchSession.NOT_SAMPLED) { + entry.recordedMessageCount++; + long latency = getElapsedRealtimeMicro() - session.startTimeMicro; + long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro; + entry.totalLatencyMicro += latency; + entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency); + entry.cpuUsageMicro += cpuUsage; + entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage); + } } } @@ -102,7 +105,7 @@ public class LooperStats implements Looper.Observer { } DispatchSession session = (DispatchSession) token; - Entry entry = getOrCreateEntry(msg); + Entry entry = findEntry(msg, /* allowCreateNew= */true); synchronized (entry) { entry.exceptionCount++; } @@ -159,24 +162,28 @@ public class LooperStats implements Looper.Observer { mSamplingInterval = samplingInterval; } - @NonNull - private Entry getOrCreateEntry(Message msg) { + @Nullable + private Entry findEntry(Message msg, boolean allowCreateNew) { final boolean isInteractive = mDeviceState.isScreenInteractive(); final int id = Entry.idFor(msg, isInteractive); Entry entry; synchronized (mLock) { entry = mEntries.get(id); if (entry == null) { - if (mEntries.size() >= mEntriesSizeCap) { - // If over the size cap, track totals under a single entry. + if (!allowCreateNew) { + return null; + } else if (mEntries.size() >= mEntriesSizeCap) { + // If over the size cap track totals under OVERFLOW entry. return mOverflowEntry; + } else { + entry = new Entry(msg, isInteractive); + mEntries.put(id, entry); } - entry = new Entry(msg, isInteractive); - mEntries.put(id, entry); } } - if (entry.handler.getClass() != msg.getTarget().getClass() + if (entry.workSourceUid != msg.workSourceUid + || entry.handler.getClass() != msg.getTarget().getClass() || entry.handler.getLooper().getThread() != msg.getTarget().getLooper().getThread() || entry.isInteractive != isInteractive) { // If a hash collision happened, track totals under a single entry. @@ -186,7 +193,7 @@ public class LooperStats implements Looper.Observer { } private void recycleSession(DispatchSession session) { - if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < TOKEN_POOL_SIZE) { + if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < SESSION_POOL_SIZE) { mSessionPool.add(session); } } @@ -210,6 +217,7 @@ public class LooperStats implements Looper.Observer { } private static class Entry { + public final int workSourceUid; public final Handler handler; public final String messageName; public final boolean isInteractive; @@ -222,12 +230,14 @@ public class LooperStats implements Looper.Observer { public long maxCpuUsageMicro; Entry(Message msg, boolean isInteractive) { + this.workSourceUid = msg.workSourceUid; this.handler = msg.getTarget(); this.messageName = handler.getMessageName(msg); this.isInteractive = isInteractive; } Entry(String specialEntryName) { + this.workSourceUid = Message.UID_NONE; this.messageName = specialEntryName; this.handler = null; this.isInteractive = false; @@ -245,6 +255,7 @@ public class LooperStats implements Looper.Observer { static int idFor(Message msg, boolean isInteractive) { int result = 7; + result = 31 * result + msg.workSourceUid; result = 31 * result + msg.getTarget().getLooper().getThread().hashCode(); result = 31 * result + msg.getTarget().getClass().hashCode(); result = 31 * result + (isInteractive ? 1231 : 1237); @@ -258,6 +269,7 @@ public class LooperStats implements Looper.Observer { /** Aggregated data of Looper message dispatching in the in the current process. */ public static class ExportedEntry { + public final int workSourceUid; public final String handlerClassName; public final String threadName; public final String messageName; @@ -271,6 +283,7 @@ public class LooperStats implements Looper.Observer { public final long maxCpuUsageMicros; ExportedEntry(Entry entry) { + this.workSourceUid = entry.workSourceUid; if (entry.handler != null) { this.handlerClassName = entry.handler.getClass().getName(); this.threadName = entry.handler.getLooper().getThread().getName(); diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 543f4a532307..5f1243f37542 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -31,6 +31,8 @@ import com.android.internal.view.IInputMethodClient; * applications. */ interface IInputMethodManager { + void addClient(in IInputMethodClient client, in IInputContext inputContext); + // TODO: Use ParceledListSlice instead List<InputMethodInfo> getInputMethodList(); List<InputMethodInfo> getVrInputMethodList(); diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 0a252716b69e..0c9a85a92422 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -15,7 +15,6 @@ #include "SkPM4f.h" #include "SkPM4fPriv.h" #include "GraphicsJNI.h" -#include "SkDither.h" #include "SkUnPreMultiply.h" #include "SkStream.h" diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 743b9f6e471d..b70177ffab9a 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -157,23 +157,17 @@ static Rect rectFromObj(JNIEnv* env, jobject rectObj) { return Rect(left, top, right, bottom); } -static jobject nativeScreenshotToBuffer(JNIEnv* env, jclass clazz, +static jobject nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj, jobject sourceCropObj, jint width, jint height, - jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform, - int rotation) { + bool useIdentityTransform, int rotation) { sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); if (displayToken == NULL) { return NULL; } Rect sourceCrop = rectFromObj(env, sourceCropObj); - if (allLayers) { - minLayer = INT32_MIN; - maxLayer = INT32_MAX; - } sp<GraphicBuffer> buffer; - status_t res = ScreenshotClient::capture(displayToken, - sourceCrop, width, height, minLayer, maxLayer, useIdentityTransform, - rotation, &buffer); + status_t res = ScreenshotClient::capture(displayToken, sourceCrop, width, height, + useIdentityTransform, rotation, &buffer); if (res != NO_ERROR) { return NULL; } @@ -187,100 +181,6 @@ static jobject nativeScreenshotToBuffer(JNIEnv* env, jclass clazz, (jlong)buffer.get()); } -static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, - jobject displayTokenObj, jobject sourceCropObj, jint width, jint height, - jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform, - int rotation) { - sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); - if (displayToken == NULL) { - return NULL; - } - - Rect sourceCrop = rectFromObj(env, sourceCropObj); - - std::unique_ptr<ScreenshotClient> screenshot(new ScreenshotClient()); - status_t res; - if (allLayers) { - minLayer = INT32_MIN; - maxLayer = INT32_MAX; - } - - sp<GraphicBuffer> buffer; - res = ScreenshotClient::capture(displayToken, sourceCrop, width, height, - minLayer, maxLayer, useIdentityTransform, static_cast<uint32_t>(rotation), &buffer); - if (res != NO_ERROR) { - return NULL; - } - - SkColorType colorType; - SkAlphaType alphaType; - - PixelFormat format = buffer->getPixelFormat(); - switch (format) { - case PIXEL_FORMAT_RGBX_8888: { - colorType = kRGBA_8888_SkColorType; - alphaType = kOpaque_SkAlphaType; - break; - } - case PIXEL_FORMAT_RGBA_8888: { - colorType = kRGBA_8888_SkColorType; - alphaType = kPremul_SkAlphaType; - break; - } - case PIXEL_FORMAT_RGBA_FP16: { - colorType = kRGBA_F16_SkColorType; - alphaType = kPremul_SkAlphaType; - break; - } - case PIXEL_FORMAT_RGB_565: { - colorType = kRGB_565_SkColorType; - alphaType = kOpaque_SkAlphaType; - break; - } - default: { - return NULL; - } - } - - SkImageInfo info = SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(), - colorType, alphaType, - SkColorSpace::MakeSRGB()); - - auto bitmap = sk_sp<Bitmap>(new Bitmap(buffer.get(), info)); - return bitmap::createBitmap(env, bitmap.release(), - android::bitmap::kBitmapCreateFlag_Premultiplied, NULL); -} - -static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj, - jobject surfaceObj, jobject sourceCropObj, jint width, jint height, - jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) { - sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); - if (displayToken == NULL) { - return; - } - - sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj); - if (consumer == NULL) { - return; - } - - Rect sourceCrop; - if (sourceCropObj != NULL) { - sourceCrop = rectFromObj(env, sourceCropObj); - } - - if (allLayers) { - minLayer = INT32_MIN; - maxLayer = INT32_MAX; - } - - sp<GraphicBuffer> buffer; - ScreenshotClient::capture(displayToken, sourceCrop, width, height, minLayer, maxLayer, - useIdentityTransform, 0, &buffer); - - Surface::attachAndQueueBuffer(consumer.get(), buffer); -} - static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandleToken, jobject sourceCropObj, jfloat frameScale) { @@ -919,10 +819,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeDestroy }, {"nativeDisconnect", "(J)V", (void*)nativeDisconnect }, - {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/Bitmap;", - (void*)nativeScreenshotBitmap }, - {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V", - (void*)nativeScreenshot }, {"nativeCreateTransaction", "()J", (void*)nativeCreateTransaction }, {"nativeApplyTransaction", "(JZ)V", @@ -1013,9 +909,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeDestroyInTransaction }, {"nativeGetHandle", "(J)Landroid/os/IBinder;", (void*)nativeGetHandle }, - {"nativeScreenshotToBuffer", - "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZI)Landroid/graphics/GraphicBuffer;", - (void*)nativeScreenshotToBuffer }, + {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZI)Landroid/graphics/GraphicBuffer;", + (void*)nativeScreenshot }, {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;", (void*)nativeCaptureLayers }, }; diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto index 8836c2e4241f..c08d7cafd9cc 100644 --- a/core/proto/android/service/notification.proto +++ b/core/proto/android/service/notification.proto @@ -201,6 +201,9 @@ message ZenRuleProto { optional ConditionProto condition = 9; optional android.content.ComponentNameProto component = 10; optional ZenPolicyProto zenPolicy = 11; + + // Indicates whether this ZenRule has been modified after its initial creation + optional bool modified = 12 [ (android.privacy).dest = DEST_AUTOMATIC ]; } // A dump from com.android.server.notification.ZenModeHelper. diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml index cb4e5c46b73e..6cf6a8273f5f 100644 --- a/core/res/res/xml/default_zen_mode_config.xml +++ b/core/res/res/xml/default_zen_mode_config.xml @@ -18,13 +18,17 @@ --> <!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. --> -<zen version="7"> +<zen version="8"> <allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="false" reminders="false" events="false" repeatCallers="true" /> - + <automatic ruleId="EVENTS_DEFAULT_RULE" enabled="false" snoozing="false" name="Event" zen="1" + component="android/com.android.server.notification.EventConditionProvider" + conditionId="condition://android/event?userId=-10000&calendar=&reply=1"/> + <automatic ruleId="EVERY_NIGHT_DEFAULT_RULE" enabled="false" snoozing="false" name="Sleeping" + zen="1" component="android/com.android.server.notification.ScheduleConditionProvider" + conditionId="condition://android/schedule?days=1.2.3.4.5.6.7&start=22.0&end=7.0&exitAtAlarm=true"/> <!-- all visual effects that exist as of P --> <disallow visualEffects="511" /> - <!-- whether there are notification channels that can bypass dnd --> <state areChannelsBypassingDnd="false" /> </zen> diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index 565a3ecd0411..0c8dd9d6ed59 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; +import android.os.Message; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -74,14 +75,17 @@ public final class LooperStatsTest { public void testSingleMessageDispatched() { TestableLooperStats looperStats = new TestableLooperStats(1, 100); + Message message = mHandlerFirst.obtainMessage(1000); + message.workSourceUid = 1000; Object token = looperStats.messageDispatchStarting(); looperStats.tickRealtime(100); looperStats.tickThreadTime(10); - looperStats.messageDispatched(token, mHandlerFirst.obtainMessage(1000)); + looperStats.messageDispatched(token, message); List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); assertThat(entries).hasSize(1); LooperStats.ExportedEntry entry = entries.get(0); + assertThat(entry.workSourceUid).isEqualTo(1000); assertThat(entry.threadName).isEqualTo("TestThread1"); assertThat(entry.handlerClassName).isEqualTo( "com.android.internal.os.LooperStatsTest$TestHandlerFirst"); @@ -100,15 +104,17 @@ public final class LooperStatsTest { public void testThrewException() { TestableLooperStats looperStats = new TestableLooperStats(1, 100); + Message message = mHandlerFirst.obtainMessage(7); + message.workSourceUid = 123; Object token = looperStats.messageDispatchStarting(); looperStats.tickRealtime(100); looperStats.tickThreadTime(10); - looperStats.dispatchingThrewException(token, mHandlerFirst.obtainMessage(7), - new ArithmeticException()); + looperStats.dispatchingThrewException(token, message, new ArithmeticException()); List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); assertThat(entries).hasSize(1); LooperStats.ExportedEntry entry = entries.get(0); + assertThat(entry.workSourceUid).isEqualTo(123); assertThat(entry.threadName).isEqualTo("TestThread1"); assertThat(entry.handlerClassName).isEqualTo( "com.android.internal.os.LooperStatsTest$TestHandlerFirst"); @@ -146,31 +152,39 @@ public final class LooperStatsTest { looperStats.messageDispatched(token3, mHandlerSecond.obtainMessage().setCallback(() -> { })); - // Contributes to entry1. + // Will not be sampled so does not contribute to any entries. Object token4 = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(10); + looperStats.tickThreadTime(10); + looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(0)); + + // Contributes to entry1. + Object token5 = looperStats.messageDispatchStarting(); looperStats.tickRealtime(100); looperStats.tickThreadTime(100); - looperStats.messageDispatched(token4, mHandlerAnonymous.obtainMessage(1)); + looperStats.messageDispatched(token5, mHandlerAnonymous.obtainMessage(1)); List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); assertThat(entries).hasSize(3); entries.sort(Comparator.comparing(e -> e.handlerClassName)); - // Captures data for token4 call. + // Captures data for token5 call. LooperStats.ExportedEntry entry1 = entries.get(0); + assertThat(entry1.workSourceUid).isEqualTo(-1); assertThat(entry1.threadName).isEqualTo("TestThread1"); assertThat(entry1.handlerClassName).isEqualTo("com.android.internal.os.LooperStatsTest$1"); assertThat(entry1.messageName).isEqualTo("0x1" /* 1 in hex */); assertThat(entry1.messageCount).isEqualTo(1); - assertThat(entry1.recordedMessageCount).isEqualTo(0); + assertThat(entry1.recordedMessageCount).isEqualTo(1); assertThat(entry1.exceptionCount).isEqualTo(0); - assertThat(entry1.totalLatencyMicros).isEqualTo(0); - assertThat(entry1.maxLatencyMicros).isEqualTo(0); - assertThat(entry1.cpuUsageMicros).isEqualTo(0); - assertThat(entry1.maxCpuUsageMicros).isEqualTo(0); + assertThat(entry1.totalLatencyMicros).isEqualTo(100); + assertThat(entry1.maxLatencyMicros).isEqualTo(100); + assertThat(entry1.cpuUsageMicros).isEqualTo(100); + assertThat(entry1.maxCpuUsageMicros).isEqualTo(100); // Captures data for token1 and token2 calls. LooperStats.ExportedEntry entry2 = entries.get(1); + assertThat(entry2.workSourceUid).isEqualTo(-1); assertThat(entry2.threadName).isEqualTo("TestThread1"); assertThat(entry2.handlerClassName).isEqualTo( "com.android.internal.os.LooperStatsTest$TestHandlerFirst"); @@ -185,6 +199,7 @@ public final class LooperStatsTest { // Captures data for token3 call. LooperStats.ExportedEntry entry3 = entries.get(2); + assertThat(entry3.workSourceUid).isEqualTo(-1); assertThat(entry3.threadName).isEqualTo("TestThread2"); assertThat(entry3.handlerClassName).isEqualTo( "com.android.internal.os.LooperStatsTest$TestHandlerSecond"); @@ -265,7 +280,7 @@ public final class LooperStatsTest { @Test public void testMessagesOverSizeCap() { - TestableLooperStats looperStats = new TestableLooperStats(2, 1 /* sizeCap */); + TestableLooperStats looperStats = new TestableLooperStats(1, 1 /* sizeCap */); Object token1 = looperStats.messageDispatchStarting(); looperStats.tickRealtime(100); @@ -296,12 +311,12 @@ public final class LooperStatsTest { assertThat(entry1.handlerClassName).isEqualTo(""); assertThat(entry1.messageName).isEqualTo("OVERFLOW"); assertThat(entry1.messageCount).isEqualTo(3); - assertThat(entry1.recordedMessageCount).isEqualTo(1); + assertThat(entry1.recordedMessageCount).isEqualTo(3); assertThat(entry1.exceptionCount).isEqualTo(0); - assertThat(entry1.totalLatencyMicros).isEqualTo(10); - assertThat(entry1.maxLatencyMicros).isEqualTo(10); - assertThat(entry1.cpuUsageMicros).isEqualTo(10); - assertThat(entry1.maxCpuUsageMicros).isEqualTo(10); + assertThat(entry1.totalLatencyMicros).isEqualTo(70); + assertThat(entry1.maxLatencyMicros).isEqualTo(50); + assertThat(entry1.cpuUsageMicros).isEqualTo(40); + assertThat(entry1.maxCpuUsageMicros).isEqualTo(20); LooperStats.ExportedEntry entry2 = entries.get(1); assertThat(entry2.threadName).isEqualTo("TestThread1"); diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp index adcdc18ab418..a03b31723808 100644 --- a/libs/hwui/CanvasTransform.cpp +++ b/libs/hwui/CanvasTransform.cpp @@ -28,6 +28,7 @@ #include <cmath> #include <log/log.h> +#include <SkHighContrastFilter.h> namespace android::uirenderer { @@ -113,4 +114,20 @@ bool transformPaint(ColorTransform transform, SkPaint* paint) { return true; } +bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette) { + bool shouldInvert = false; + if (palette == BitmapPalette::Light && transform == ColorTransform::Dark) { + shouldInvert = true; + } + if (palette == BitmapPalette::Dark && transform == ColorTransform::Light) { + shouldInvert = true; + } + if (shouldInvert) { + SkHighContrastConfig config; + config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness; + paint->setColorFilter(SkHighContrastFilter::Make(config)->makeComposed(paint->refColorFilter())); + } + return shouldInvert; +} + }; // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/CanvasTransform.h b/libs/hwui/CanvasTransform.h index 32d9a0594879..e723d645e05e 100644 --- a/libs/hwui/CanvasTransform.h +++ b/libs/hwui/CanvasTransform.h @@ -16,6 +16,8 @@ #pragma once +#include "hwui/Bitmap.h" + #include <SkCanvas.h> #include <SkPaintFilterCanvas.h> @@ -26,6 +28,7 @@ namespace android::uirenderer { enum class UsageHint { Unknown = 0, Background = 1, + Foreground = 2, }; enum class ColorTransform { @@ -37,4 +40,6 @@ enum class ColorTransform { // True if the paint was modified, false otherwise bool transformPaint(ColorTransform transform, SkPaint* paint); +bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette); + } // namespace android::uirenderer;
\ No newline at end of file diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 3eaff03db369..5f54c025c819 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -278,8 +278,8 @@ struct DrawPicture final : Op { struct DrawImage final : Op { static const auto kType = Type::DrawImage; - DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint) - : image(std::move(image)), x(x), y(y) { + DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint, BitmapPalette palette) + : image(std::move(image)), x(x), y(y), palette(palette) { if (paint) { this->paint = *paint; } @@ -287,6 +287,7 @@ struct DrawImage final : Op { sk_sp<const SkImage> image; SkScalar x, y; SkPaint paint; + BitmapPalette palette; void draw(SkCanvas* c, const SkMatrix&) const { c->drawImage(image.get(), x, y, &paint); } }; struct DrawImageNine final : Op { @@ -309,8 +310,8 @@ struct DrawImageNine final : Op { struct DrawImageRect final : Op { static const auto kType = Type::DrawImageRect; DrawImageRect(sk_sp<const SkImage>&& image, const SkRect* src, const SkRect& dst, - const SkPaint* paint, SkCanvas::SrcRectConstraint constraint) - : image(std::move(image)), dst(dst), constraint(constraint) { + const SkPaint* paint, SkCanvas::SrcRectConstraint constraint, BitmapPalette palette) + : image(std::move(image)), dst(dst), constraint(constraint), palette(palette) { this->src = src ? *src : SkRect::MakeIWH(this->image->width(), this->image->height()); if (paint) { this->paint = *paint; @@ -320,6 +321,7 @@ struct DrawImageRect final : Op { SkRect src, dst; SkPaint paint; SkCanvas::SrcRectConstraint constraint; + BitmapPalette palette; void draw(SkCanvas* c, const SkMatrix&) const { c->drawImageRect(image.get(), src, dst, &paint, constraint); } @@ -609,8 +611,8 @@ void DisplayListData::drawPicture(const SkPicture* picture, const SkMatrix* matr this->push<DrawPicture>(0, picture, matrix, paint); } void DisplayListData::drawImage(sk_sp<const SkImage> image, SkScalar x, SkScalar y, - const SkPaint* paint) { - this->push<DrawImage>(0, std::move(image), x, y, paint); + const SkPaint* paint, BitmapPalette palette) { + this->push<DrawImage>(0, std::move(image), x, y, paint, palette); } void DisplayListData::drawImageNine(sk_sp<const SkImage> image, const SkIRect& center, const SkRect& dst, const SkPaint* paint) { @@ -618,8 +620,8 @@ void DisplayListData::drawImageNine(sk_sp<const SkImage> image, const SkIRect& c } void DisplayListData::drawImageRect(sk_sp<const SkImage> image, const SkRect* src, const SkRect& dst, const SkPaint* paint, - SkCanvas::SrcRectConstraint constraint) { - this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint); + SkCanvas::SrcRectConstraint constraint, BitmapPalette palette) { + this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint, palette); } void DisplayListData::drawImageLattice(sk_sp<const SkImage> image, const SkCanvas::Lattice& lattice, const SkRect& dst, const SkPaint* paint) { @@ -638,28 +640,33 @@ void DisplayListData::drawText(const void* text, size_t bytes, SkScalar x, SkSca const SkPaint& paint) { void* pod = this->push<DrawText>(bytes, bytes, x, y, paint); copy_v(pod, (const char*)text, bytes); + mHasText = true; } void DisplayListData::drawPosText(const void* text, size_t bytes, const SkPoint pos[], const SkPaint& paint) { int n = paint.countText(text, bytes); void* pod = this->push<DrawPosText>(n * sizeof(SkPoint) + bytes, bytes, paint, n); copy_v(pod, pos, n, (const char*)text, bytes); + mHasText = true; } void DisplayListData::drawPosTextH(const void* text, size_t bytes, const SkScalar xs[], SkScalar y, const SkPaint& paint) { int n = paint.countText(text, bytes); void* pod = this->push<DrawPosTextH>(n * sizeof(SkScalar) + bytes, bytes, y, paint, n); copy_v(pod, xs, n, (const char*)text, bytes); + mHasText = true; } void DisplayListData::drawTextRSXform(const void* text, size_t bytes, const SkRSXform xforms[], const SkRect* cull, const SkPaint& paint) { int n = paint.countText(text, bytes); void* pod = this->push<DrawTextRSXform>(bytes + n * sizeof(SkRSXform), bytes, n, cull, paint); copy_v(pod, xforms, n, (const char*)text, bytes); + mHasText = true; } void DisplayListData::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { this->push<DrawTextBlob>(0, blob, x, y, paint); + mHasText = true; } void DisplayListData::drawPatch(const SkPoint points[12], const SkColor colors[4], @@ -733,20 +740,35 @@ void DisplayListData::reset() { } template <class T> -using has_paint_t = decltype(std::declval<T>().paint); +using has_paint_helper = decltype(std::declval<T>().paint); + +template <class T> +constexpr bool has_paint = std::experimental::is_detected_v<has_paint_helper, T>; + +template <class T> +using has_palette_helper = decltype(std::declval<T>().palette); + +template <class T> +constexpr bool has_palette = std::experimental::is_detected_v<has_palette_helper, T>; template <class T> constexpr color_transform_fn colorTransformForOp() { - if - constexpr(std::experimental::is_detected_v<has_paint_t, T>) { - return [](const void* op, ColorTransform transform) { - // TODO: We should be const. Or not. Or just use a different map - // Unclear, but this is the quick fix - transformPaint(transform, - const_cast<SkPaint*>(&(reinterpret_cast<const T*>(op)->paint))); - }; - } - else { + if constexpr(has_paint<T> && has_palette<T>) { + // It's a bitmap + return [](const void* opRaw, ColorTransform transform) { + // TODO: We should be const. Or not. Or just use a different map + // Unclear, but this is the quick fix + const T* op = reinterpret_cast<const T*>(opRaw); + transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette); + }; + } else if constexpr(has_paint<T>) { + return [](const void* opRaw, ColorTransform transform) { + // TODO: We should be const. Or not. Or just use a different map + // Unclear, but this is the quick fix + const T* op = reinterpret_cast<const T*>(opRaw); + transformPaint(transform, const_cast<SkPaint*>(&(op->paint))); + }; + } else { return nullptr; } } @@ -875,7 +897,7 @@ void RecordingCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScala void RecordingCanvas::onDrawBitmap(const SkBitmap& bm, SkScalar x, SkScalar y, const SkPaint* paint) { - fDL->drawImage(SkImage::MakeFromBitmap(bm), x, y, paint); + fDL->drawImage(SkImage::MakeFromBitmap(bm), x, y, paint, BitmapPalette::Unknown); } void RecordingCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center, const SkRect& dst, const SkPaint* paint) { @@ -883,16 +905,26 @@ void RecordingCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center } void RecordingCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { - fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint); + fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint, BitmapPalette::Unknown); } void RecordingCanvas::onDrawBitmapLattice(const SkBitmap& bm, const SkCanvas::Lattice& lattice, const SkRect& dst, const SkPaint* paint) { fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint); } +void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y, + const SkPaint* paint, BitmapPalette palette) { + fDL->drawImage(image, x, y, paint, palette); +} + +void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette) { + fDL->drawImageRect(image, &src, dst, paint, constraint, palette); +} + void RecordingCanvas::onDrawImage(const SkImage* img, SkScalar x, SkScalar y, const SkPaint* paint) { - fDL->drawImage(sk_ref_sp(img), x, y, paint); + fDL->drawImage(sk_ref_sp(img), x, y, paint, BitmapPalette::Unknown); } void RecordingCanvas::onDrawImageNine(const SkImage* img, const SkIRect& center, const SkRect& dst, const SkPaint* paint) { @@ -900,7 +932,7 @@ void RecordingCanvas::onDrawImageNine(const SkImage* img, const SkIRect& center, } void RecordingCanvas::onDrawImageRect(const SkImage* img, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { - fDL->drawImageRect(sk_ref_sp(img), src, dst, paint, constraint); + fDL->drawImageRect(sk_ref_sp(img), src, dst, paint, constraint, BitmapPalette::Unknown); } void RecordingCanvas::onDrawImageLattice(const SkImage* img, const SkCanvas::Lattice& lattice, const SkRect& dst, const SkPaint* paint) { diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 32ce1d333448..eecf51ce0996 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -17,6 +17,7 @@ #pragma once #include "CanvasTransform.h" +#include "hwui/Bitmap.h" #include "hwui/Canvas.h" #include "utils/Macros.h" #include "utils/TypeLogic.h" @@ -53,6 +54,7 @@ class RecordingCanvas; class DisplayListData final { public: + DisplayListData() : mHasText(false) {} ~DisplayListData(); void draw(SkCanvas* canvas) const; @@ -62,6 +64,8 @@ public: void applyColorTransform(ColorTransform transform); + bool hasText() const { return mHasText; } + private: friend class RecordingCanvas; @@ -101,10 +105,10 @@ private: void drawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*, const SkPaint&); void drawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&); - void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkPaint*); + void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkPaint*, BitmapPalette palette); void drawImageNine(sk_sp<const SkImage>, const SkIRect&, const SkRect&, const SkPaint*); void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkPaint*, - SkCanvas::SrcRectConstraint); + SkCanvas::SrcRectConstraint, BitmapPalette palette); void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&, const SkRect&, const SkPaint*); @@ -126,6 +130,8 @@ private: SkAutoTMalloc<uint8_t> fBytes; size_t fUsed = 0; size_t fReserved = 0; + + bool mHasText : 1; }; class RecordingCanvas final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> { @@ -178,6 +184,12 @@ public: void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*, SrcRectConstraint) override; + void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top, + const SkPaint* paint, BitmapPalette pallete); + + void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette); + void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override; void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override; void onDrawImageNine(const SkImage*, const SkIRect&, const SkRect&, const SkPaint*) override; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 896b4340f1b8..06dbb2410a12 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -272,8 +272,12 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) { mStagingDisplayList = nullptr; if (mDisplayList) { mDisplayList->syncContents(); + if (CC_UNLIKELY(Properties::forceDarkMode)) { auto usage = usageHint(); + if (mDisplayList->hasText()) { + usage = UsageHint::Foreground; + } if (usage == UsageHint::Unknown) { if (mDisplayList->mChildNodes.size() > 1) { usage = UsageHint::Background; diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 83b0c2278151..211dd2db5cf8 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -107,7 +107,7 @@ public: bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); } bool hasProjectionReceiver() const { - return mDisplayList && mDisplayList->projectionReceiveIndex >= 0; + return mDisplayList && mDisplayList->containsProjectionReceiver(); } const char* getName() const { return mName.string(); } diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index f1c10d1570a4..9c284531674f 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -328,15 +328,6 @@ sk_sp<SkImage> Bitmap::makeImage(sk_sp<SkColorFilter>* outputColorFilter) { if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) { *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace()); } - - // TODO: Move this to the canvas (or other?) layer where we have the target lightness - // mode and can selectively do the right thing. - // if (palette() != BitmapPalette::Unknown && uirenderer::Properties::forceDarkMode) { - // SkHighContrastConfig config; - // config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness; - // *outputColorFilter = - // SkHighContrastFilter::Make(config)->makeComposed(*outputColorFilter); - // } return image; } diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index 78b64b2f01eb..38905138e332 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -132,7 +132,6 @@ void SkiaDisplayList::reset() { mChildFunctors.clear(); mChildNodes.clear(); - projectionReceiveIndex = -1; allocator.~LinearAllocator(); new (&allocator) LinearAllocator(); } diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 4c7853931029..ac7bb7b0950c 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -49,9 +49,6 @@ namespace skiapipeline { */ class SkiaDisplayList { public: - // index of DisplayListOp restore, after which projected descendants should be drawn - int projectionReceiveIndex = -1; - size_t getUsedSize() { return allocator.usedSize(); } ~SkiaDisplayList() { @@ -96,6 +93,8 @@ public: */ bool hasVectorDrawables() const { return !mVectorDrawables.empty(); } + bool hasText() const { return mDisplayList.hasText(); } + /** * Attempts to reset and reuse this DisplayList. * diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 3c281e78ddd5..3042006e5684 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -113,8 +113,6 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { // use staging property, since recording on UI thread if (renderNode->stagingProperties().isProjectionReceiver()) { mDisplayList->mProjectionReceiver = &renderNodeDrawable; - // set projectionReceiveIndex so that RenderNode.hasProjectionReceiver returns true - mDisplayList->projectionReceiveIndex = mDisplayList->mChildNodes.size() - 1; } } @@ -196,7 +194,7 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint, void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { sk_sp<SkColorFilter> colorFilter; sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); - mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter))); + mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)), bitmap.palette()); // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means // it is not safe to store a raw SkImage pointer, because the image object will be destroyed // when this function ends. @@ -211,7 +209,7 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con sk_sp<SkColorFilter> colorFilter; sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); - mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter))); + mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)), bitmap.palette()); if (!bitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); } @@ -226,7 +224,7 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop sk_sp<SkColorFilter> colorFilter; sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)), - SkCanvas::kFast_SrcRectConstraint); + SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() && !dstRect.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); diff --git a/location/java/android/location/Address.java b/location/java/android/location/Address.java index 335ad5ebcde5..83f05c263f85 100644 --- a/location/java/android/location/Address.java +++ b/location/java/android/location/Address.java @@ -363,7 +363,7 @@ public class Address implements Parcelable { * or null if it is unknown. * * @throws IllegalStateException if this Address has not been assigned - * a latitude. + * a phone number. */ public String getPhone() { return mPhone; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 9050b4b0a15b..0dbc037f2298 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -81,7 +81,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private static final long DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS = 20 * DateUtils.MINUTE_IN_MILLIS; /** Maximum age of scan results to hold onto while actively scanning. **/ - private static final long MAX_SCAN_RESULT_AGE_MILLIS = 25000; + private static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000; private static final String TAG = "WifiTracker"; private static final boolean DBG() { @@ -134,8 +134,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro /** * Tracks whether fresh scan results have been received since scanning start. * - * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks - * so that we do not update the UI with stale data / clear out existing UI elements prematurely. + * <p>If this variable is false, we will not invoke callbacks so that we do not + * update the UI with stale data / clear out existing UI elements prematurely. */ private boolean mStaleScanResults = true; @@ -327,7 +327,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * <p>Intended to only be invoked within {@link #onStart()}. */ @MainThread - private void forceUpdate() { + @VisibleForTesting + void forceUpdate() { mLastInfo = mWifiManager.getConnectionInfo(); mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); @@ -443,10 +444,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mScanResultCache.put(newResult.BSSID, newResult); } - // Don't evict old results if no new scan results - if (!mStaleScanResults) { - evictOldScans(); - } + // Evict old results in all conditions + evictOldScans(); ArrayMap<String, List<ScanResult>> scanResultsByApKey = new ArrayMap<>(); for (ScanResult result : mScanResultCache.values()) { diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index 517db78d5dd1..1860b31081e1 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; @@ -104,6 +103,10 @@ public class WifiTrackerTest { private static final byte SCORE_2 = 15; private static final int BADGE_2 = AccessPoint.Speed.FAST; + private static final String SSID_3 = "ssid3"; + private static final String BSSID_3 = "CC:00:00:00:00:00"; + private static final int RSSI_3 = -40; + // TODO(b/65594609): Convert mutable Data objects to instance variables / builder pattern private static final int NETWORK_ID_1 = 123; private static final int CONNECTED_RSSI = -50; @@ -255,6 +258,19 @@ public class WifiTrackerTest { SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */); } + private static ScanResult buildStaleScanResult() { + return new ScanResult( + WifiSsid.createFromAsciiEncoded(SSID_3), + BSSID_3, + 0, // hessid + 0, //anqpDomainId + null, // osuProviders + "", // capabilities + RSSI_3, + 0, // frequency + 0 /* microsecond timestamp */); + } + private WifiTracker createTrackerWithImmediateBroadcastsAndInjectInitialScanResults( Intent ... intents) throws InterruptedException { @@ -896,4 +912,18 @@ public class WifiTrackerTest { assertThat(aps.get(0).isReachable()).isTrue(); assertThat(aps.get(1).isReachable()).isTrue(); } + + @Test + public void onStart_updateScanResults_evictOldScanResult() { + when(mockWifiManager.getScanResults()).thenReturn( + Arrays.asList(buildScanResult1(), buildScanResult2(), buildStaleScanResult())); + WifiTracker tracker = createMockedWifiTracker(); + + tracker.forceUpdate(); + + // Only has scanResult1 and scanResult2 + assertThat(tracker.getAccessPoints()).hasSize(2); + assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1); + assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2); + } } diff --git a/packages/SystemUI/res/layout/qs_paged_page.xml b/packages/SystemUI/res/layout/qs_paged_page.xml index 07f0c83e7b2e..a8960d9b9437 100644 --- a/packages/SystemUI/res/layout/qs_paged_page.xml +++ b/packages/SystemUI/res/layout/qs_paged_page.xml @@ -19,7 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tile_page" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:paddingStart="@dimen/notification_side_paddings" android:paddingEnd="@dimen/notification_side_paddings" android:clipChildren="false" diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml index e96a09baa9db..11a01871b782 100644 --- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml +++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml @@ -19,6 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_weight="1" android:clipChildren="false" android:clipToPadding="false" android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom"> diff --git a/packages/SystemUI/res/values-h320dp/config.xml b/packages/SystemUI/res/values-h320dp/config.xml deleted file mode 100644 index a9c19db0f46f..000000000000 --- a/packages/SystemUI/res/values-h320dp/config.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (c) 2017, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ ---> - -<resources> - <!-- The number of rows in the QuickSettings --> - <integer name="quick_settings_num_rows">2</integer> -</resources> diff --git a/packages/SystemUI/res/values-h600dp/config.xml b/packages/SystemUI/res/values-h600dp/config.xml deleted file mode 100644 index 8616e3e62779..000000000000 --- a/packages/SystemUI/res/values-h600dp/config.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (c) 2017, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -*/ ---> - -<resources> - <!-- The number of rows in the QuickSettings --> - <integer name="quick_settings_num_rows">3</integer> -</resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 11bd392b7d52..24dcd3e3b70d 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -107,7 +107,7 @@ <integer name="quick_settings_num_columns">3</integer> <!-- The number of rows in the QuickSettings --> - <integer name="quick_settings_num_rows">1</integer> + <integer name="quick_settings_max_rows">3</integer> <!-- The number of columns that the top level tiles span in the QuickSettings --> <integer name="quick_settings_user_time_settings_tile_span">1</integer> @@ -146,19 +146,29 @@ <!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? --> <bool name="config_hideLtePlus">false</bool> - <!-- milliseconds before the heads up notification auto-dismisses. --> + <!-- The number of milliseconds before the heads up notification auto-dismisses. --> <integer name="heads_up_notification_decay">5000</integer> - <!-- milliseconds after a heads up notification is pushed back + <!-- The number of milliseconds after a heads up notification is pushed back before the app can interrupt again. --> <integer name="heads_up_default_snooze_length_ms">60000</integer> <!-- Minimum display time for a heads up notification, in milliseconds. --> <integer name="heads_up_notification_minimum_time">2000</integer> - <!-- milliseconds before the heads up notification accepts touches. --> + <!-- The number of milliseconds before the heads up notification accepts touches. --> <integer name="touch_acceptance_delay">700</integer> + <!-- The number of milliseconds before the ambient notification auto-dismisses. This will + override the default pulse length. --> + <integer name="ambient_notification_decay">6000</integer> + + <!-- Minimum display time for a heads up notification, in milliseconds. --> + <integer name="ambient_notification_minimum_time">2000</integer> + + <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) --> + <integer name="ambient_notification_extension_time">6000</integer> + <!-- The duration in seconds to wait before the dismiss buttons are shown. --> <integer name="recents_task_bar_dismiss_delay_seconds">1000</integer> diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 04d72cec402f..258b6f61d4c0 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.systemui.Dependency.DependencyProvider; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.qs.QSTileHost; +import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; @@ -137,6 +138,7 @@ public class SystemUIFactory { providers.put(NotificationGroupManager.class, NotificationGroupManager::new); providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context)); providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context)); + providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context)); providers.put(NotificationBlockingHelperManager.class, () -> new NotificationBlockingHelperManager(context)); providers.put(NotificationRemoteInputManager.class, diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java index d6a1cf082c0d..5739c997d8bf 100644 --- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java @@ -16,7 +16,6 @@ package com.android.systemui.car; import android.content.Context; -import android.service.notification.StatusBarNotification; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.NotificationData; @@ -41,7 +40,7 @@ public class CarNotificationEntryManager extends NotificationEntryManager { } @Override - public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) { + public boolean shouldHeadsUp(NotificationData.Entry entry) { // Because space is usually constrained in the auto use-case, there should not be a // pinned notification when the shade has been expanded. Ensure this by not pinning any // notification if the shade is already opened. @@ -49,6 +48,6 @@ public class CarNotificationEntryManager extends NotificationEntryManager { return false; } - return super.shouldPeek(entry, sbn); + return super.shouldHeadsUp(entry); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index 6a29299872a4..bb059809be2b 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -48,7 +48,15 @@ public interface DozeHost { void onIgnoreTouchWhilePulsing(boolean ignore); interface Callback { - default void onNotificationHeadsUp() {} + /** + * Called when a high priority notification is added. + */ + default void onNotificationAlerted() {} + + /** + * Called when battery state or power save mode changes. + * @param active whether power save is active or not + */ default void onPowerSaveChanged(boolean active) {} } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 1589969d19c1..31548b93ea60 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -409,7 +409,7 @@ public class DozeTriggers implements DozeMachine.Part { private DozeHost.Callback mHostCallback = new DozeHost.Callback() { @Override - public void onNotificationHeadsUp() { + public void onNotificationAlerted() { onNotification(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 1655c01f5e8e..757c821f37e3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -20,6 +20,9 @@ import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; import static android.view.Display.INVALID_DISPLAY; import static com.android.internal.telephony.IccCardConstants.State.ABSENT; +import static com.android.internal.telephony.IccCardConstants.State.PIN_REQUIRED; +import static com.android.internal.telephony.IccCardConstants.State.PUK_REQUIRED; +import static com.android.internal.telephony.IccCardConstants.State.READY; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; @@ -30,7 +33,6 @@ import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.StatusBarManager; -import android.hardware.biometrics.BiometricSourceType; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -38,6 +40,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; +import android.hardware.biometrics.BiometricSourceType; import android.media.AudioManager; import android.media.SoundPool; import android.os.Bundle; @@ -58,6 +61,7 @@ import android.telephony.TelephonyManager; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import android.view.ViewGroup; import android.view.WindowManagerPolicyConstants; import android.view.animation.Animation; @@ -70,21 +74,21 @@ import com.android.internal.policy.IKeyguardDrawnCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardStateCallback; import com.android.internal.telephony.IccCardConstants; +import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardConstants; import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardSecurityView; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.internal.util.LatencyTracker; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.Dependency; import com.android.systemui.SystemUI; import com.android.systemui.SystemUIFactory; import com.android.systemui.UiOffloadThread; import com.android.systemui.classifier.FalsingManager; -import com.android.systemui.statusbar.phone.NotificationPanelView; import com.android.systemui.statusbar.phone.BiometricUnlockController; +import com.android.systemui.statusbar.phone.NotificationPanelView; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -274,6 +278,12 @@ public class KeyguardViewMediator extends SystemUI { private KeyguardUpdateMonitor mUpdateMonitor; + /** + * Last SIM state reported by the telephony system. + * Index is the slotId - in case of multiple SIM cards. + */ + private final SparseArray<IccCardConstants.State> mLastSimStates = new SparseArray<>(); + private boolean mDeviceInteractive; private boolean mGoingToSleep; @@ -450,6 +460,14 @@ public class KeyguardViewMediator extends SystemUI { } } + boolean simWasLocked; + synchronized (KeyguardViewMediator.this) { + IccCardConstants.State lastState = mLastSimStates.get(slotId); + simWasLocked = (lastState == PIN_REQUIRED || lastState == PUK_REQUIRED) + && simState == READY; + mLastSimStates.append(slotId, simState); + } + switch (simState) { case NOT_READY: case ABSENT: @@ -503,6 +521,9 @@ public class KeyguardViewMediator extends SystemUI { case READY: synchronized (KeyguardViewMediator.this) { if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing); + if (mShowing && simWasLocked) { + resetStateLocked(); + } mLockWhenSimRemoved = true; } break; diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 123fca71deaf..fcd479ce6627 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -6,14 +6,12 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; + import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; @@ -41,14 +39,12 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { return t * t * t + 1.0f; }; - private final ArrayList<TileRecord> mTiles = new ArrayList<>(); private final ArrayList<TilePage> mPages = new ArrayList<>(); private PageIndicator mPageIndicator; private float mPageIndicatorPosition; - private int mNumPages; private PageListener mPageListener; private boolean mListening; @@ -56,6 +52,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { private AnimatorSet mBounceAnimatorSet; private float mLastExpansion; + private boolean mDistributeTiles = false; public PagedTileLayout(Context context, AttributeSet attrs) { super(context, attrs); @@ -122,7 +119,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { public void setPageIndicator(PageIndicator indicator) { mPageIndicator = indicator; - mPageIndicator.setNumPages(mNumPages); + mPageIndicator.setNumPages(mPages.size()); mPageIndicator.setLocation(mPageIndicatorPosition); } @@ -136,13 +133,15 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override public void addTile(TileRecord tile) { mTiles.add(tile); - postDistributeTiles(); + mDistributeTiles = true; + requestLayout(); } @Override public void removeTile(TileRecord tile) { if (mTiles.remove(tile)) { - postDistributeTiles(); + mDistributeTiles = true; + requestLayout(); } } @@ -175,44 +174,50 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { mPageListener = listener; } - private void postDistributeTiles() { - removeCallbacks(mDistribute); - post(mDistribute); - } - private void distributeTiles() { + emptyAndInflateOrRemovePages(); + + final int tileCount = mPages.get(0).maxTiles(); if (DEBUG) Log.d(TAG, "Distributing tiles"); - final int NP = mPages.size(); - for (int i = 0; i < NP; i++) { - mPages.get(i).removeAllViews(); - } int index = 0; final int NT = mTiles.size(); for (int i = 0; i < NT; i++) { TileRecord tile = mTiles.get(i); - if (mPages.get(index).isFull()) { - if (++index == mPages.size()) { - if (DEBUG) Log.d(TAG, "Adding page for " - + tile.tile.getClass().getSimpleName()); - mPages.add((TilePage) LayoutInflater.from(getContext()) - .inflate(R.layout.qs_paged_page, this, false)); - } + if (mPages.get(index).mRecords.size() == tileCount) index++; + if (DEBUG) { + Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to " + + index); } - if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to " - + index); mPages.get(index).addTile(tile); } - if (mNumPages != index + 1) { - mNumPages = index + 1; - while (mPages.size() > mNumPages) { - mPages.remove(mPages.size() - 1); - } - if (DEBUG) Log.d(TAG, "Size: " + mNumPages); - mPageIndicator.setNumPages(mNumPages); - setAdapter(mAdapter); - mAdapter.notifyDataSetChanged(); - setCurrentItem(0, false); + } + + private void emptyAndInflateOrRemovePages() { + final int nTiles = mTiles.size(); + int numPages = nTiles / mPages.get(0).maxTiles(); + // Add one more not full page if needed + numPages += (nTiles % mPages.get(0).maxTiles() == 0 ? 0 : 1); + + final int NP = mPages.size(); + for (int i = 0; i < NP; i++) { + mPages.get(i).removeAllViews(); } + if (NP == numPages) { + return; + } + while (mPages.size() < numPages) { + if (DEBUG) Log.d(TAG, "Adding page"); + mPages.add((TilePage) LayoutInflater.from(getContext()) + .inflate(R.layout.qs_paged_page, this, false)); + } + while (mPages.size() > numPages) { + if (DEBUG) Log.d(TAG, "Removing page"); + mPages.remove(mPages.size() - 1); + } + mPageIndicator.setNumPages(mPages.size()); + setAdapter(mAdapter); + mAdapter.notifyDataSetChanged(); + setCurrentItem(0, false); } @Override @@ -222,20 +227,39 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { setPadding(0, 0, 0, getContext().getResources().getDimensionPixelSize( R.dimen.qs_paged_tile_layout_padding_bottom)); - boolean changed = false; for (int i = 0; i < mPages.size(); i++) { changed |= mPages.get(i).updateResources(); } if (changed) { - distributeTiles(); + mDistributeTiles = true; + requestLayout(); } return changed; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + final int nTiles = mTiles.size(); + if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { + + // Only change the pages if the number of rows or columns (from updateResources) has + // changed or the tiles have changed + if (mPages.get(0).updateMaxRows(heightMeasureSpec, nTiles) || mDistributeTiles) { + mDistributeTiles = false; + distributeTiles(); + } + + final int nRows = mPages.get(0).mRows; + for (int i = 0; i < mPages.size(); i++) { + TilePage t = mPages.get(i); + t.mRows = nRows; + } + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + // The ViewPager likes to eat all of the space, instead force it to wrap to the max height // of the pages. int maxHeight = 0; @@ -249,13 +273,6 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { setMeasuredDimension(getMeasuredWidth(), maxHeight + getPaddingBottom()); } - private final Runnable mDistribute = new Runnable() { - @Override - public void run() { - distributeTiles(); - } - }; - public int getColumnCount() { if (mPages.size() == 0) return 0; return mPages.get(0).mColumns; @@ -346,33 +363,17 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { }; public static class TilePage extends TileLayout { - private int mMaxRows = 3; + public TilePage(Context context, AttributeSet attrs) { super(context, attrs); - updateResources(); } - @Override - public boolean updateResources() { - final int rows = getRows(); - boolean changed = rows != mMaxRows; - if (changed) { - mMaxRows = rows; - requestLayout(); - } - return super.updateResources() || changed; - } - - private int getRows() { - return Math.max(1, getResources().getInteger(R.integer.quick_settings_num_rows)); - } - - public void setMaxRows(int maxRows) { - mMaxRows = maxRows; + public boolean isFull() { + return mRecords.size() >= mColumns * mRows; } - public boolean isFull() { - return mRecords.size() >= mColumns * mMaxRows; + public int maxTiles() { + return mColumns * mRows; } } @@ -401,7 +402,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override public int getCount() { - return mNumPages; + return mPages.size(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index feff5d4a3cc1..1451e71e2df5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.widget.FrameLayout; @@ -91,15 +92,24 @@ public class QSContainerImpl extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // Since we control our own bottom, be whatever size we want. - // Otherwise the QSPanel ends up with 0 height when the window is only the - // size of the status bar. - mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED)); + // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the + // bottom and footer are inside the screen. + Configuration config = getResources().getConfiguration(); + boolean navBelow = config.smallestScreenWidthDp >= 600 + || config.orientation != Configuration.ORIENTATION_LANDSCAPE; + MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanel.getLayoutParams(); + + // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to + // subtract its height. We do not care if the collapsed notifications fit in the screen. + int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin + - getPaddingBottom(); + if (navBelow) { + maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height); + } + mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST)); int width = mQSPanel.getMeasuredWidth(); - LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams(); int height = layoutParams.topMargin + layoutParams.bottomMargin - + mQSPanel.getMeasuredHeight(); + + mQSPanel.getMeasuredHeight() + getPaddingBottom(); super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index c67165ea95a1..01ff72e6d152 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -24,10 +24,12 @@ public class TileLayout extends ViewGroup implements QSTileLayout { protected int mCellMarginHorizontal; protected int mCellMarginVertical; protected int mSidePadding; + protected int mRows = 1; protected final ArrayList<TileRecord> mRecords = new ArrayList<>(); private int mCellMarginTop; private boolean mListening; + protected int mMaxAllowedRows = 3; public TileLayout(Context context) { this(context, null); @@ -86,6 +88,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical); mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top); mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side); + mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows)); if (mColumns != columns) { mColumns = columns; requestLayout(); @@ -96,10 +99,16 @@ public class TileLayout extends ViewGroup implements QSTileLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED + // it will show all its tiles. In this case, the tiles have to be entered before the + // container is measured. Any change in the tiles, should trigger a remeasure. final int numTiles = mRecords.size(); final int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingStart() - getPaddingEnd(); - final int numRows = (numTiles + mColumns - 1) / mColumns; + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + if (heightMode == MeasureSpec.UNSPECIFIED) { + mRows = (numTiles + mColumns - 1) / mColumns; + } mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns; // Measure each QS tile. @@ -112,13 +121,35 @@ public class TileLayout extends ViewGroup implements QSTileLayout { // Only include the top margin in our measurement if we have more than 1 row to show. // Otherwise, don't add the extra margin buffer at top. - int height = (mCellHeight + mCellMarginVertical) * numRows + - (numRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0); + int height = (mCellHeight + mCellMarginVertical) * mRows + + (mRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0); if (height < 0) height = 0; setMeasuredDimension(width, height); } + /** + * Determines the maximum number of rows that can be shown based on height. Clips at a minimum + * of 1 and a maximum of mMaxAllowedRows. + * + * @param heightMeasureSpec Available height. + * @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows. + */ + public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) { + final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop; + final int previousRows = mRows; + mRows = availableHeight / (mCellHeight + mCellMarginVertical); + if (mRows >= mMaxAllowedRows) { + mRows = mMaxAllowedRows; + } else if (mRows <= 1) { + mRows = 1; + } + if (mRows > (tilesCount + mColumns - 1) / mColumns) { + mRows = (tilesCount + mColumns - 1) / mColumns; + } + return previousRows != mRows; + } + @Override public boolean hasOverlappingRendering() { return false; @@ -135,7 +166,8 @@ public class TileLayout extends ViewGroup implements QSTileLayout { int column = 0; // Layout each QS tile. - for (int i = 0; i < numRecords; i++, column++) { + final int tilesToLayout = Math.min(numRecords, mRows * mColumns); + for (int i = 0; i < tilesToLayout; i++, column++) { // If we reached the last column available to layout a tile, wrap back to the next row. if (column == mColumns) { column = 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java index 45165180e93a..b6e88d6dfc11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java @@ -165,7 +165,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim * Whether or not the given notification is alerting and managed by this manager. * @return true if the notification is alerting */ - public boolean contains(@NonNull String key) { + public boolean isAlerting(@NonNull String key) { return mAlertEntries.containsKey(key); } @@ -294,7 +294,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim removeAutoRemovalCallbacks(); if (!isSticky()) { - long finishTime = mPostTime + mAutoDismissNotificationDecay; + long finishTime = calculateFinishTime(); long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime); mHandler.postDelayed(mRemoveAlertRunnable, removeDelay); } @@ -357,6 +357,14 @@ public abstract class AlertingNotificationManager implements NotificationLifetim protected long calculatePostTime() { return mClock.currentTimeMillis(); } + + /** + * Calculate when the notification should auto-dismiss itself. + * @return the finish time + */ + protected long calculateFinishTime() { + return mPostTime + mAutoDismissNotificationDecay; + } } protected final static class Clock { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java new file mode 100644 index 000000000000..2c384d0f4d80 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java @@ -0,0 +1,149 @@ +/* + * 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; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.res.Resources; +import android.util.ArraySet; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.R; +import com.android.systemui.statusbar.notification.NotificationData; + +/** + * Manager which handles high priority notifications that should "pulse" in when the device is + * dozing and/or in AOD. The pulse uses the notification's ambient view and pops in briefly + * before automatically dismissing the alert. + */ +public final class AmbientPulseManager extends AlertingNotificationManager { + + protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>(); + @VisibleForTesting + protected long mExtensionTime; + + public AmbientPulseManager(@NonNull final Context context) { + Resources resources = context.getResources(); + mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay); + mMinimumDisplayTime = resources.getInteger(R.integer.ambient_notification_minimum_time); + mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time); + } + + /** + * Adds an OnAmbientChangedListener to observe events. + */ + public void addListener(@NonNull OnAmbientChangedListener listener) { + mListeners.add(listener); + } + + /** + * Removes the OnAmbientChangedListener from the observer list. + */ + public void removeListener(@NonNull OnAmbientChangedListener listener) { + mListeners.remove(listener); + } + + /** + * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts + * longer. + */ + public void extendPulse() { + AmbientEntry topEntry = getTopEntry(); + if (topEntry == null) { + return; + } + topEntry.extendPulse(); + } + + @Override + protected void onAlertEntryAdded(AlertEntry alertEntry) { + NotificationData.Entry entry = alertEntry.mEntry; + entry.row.setAmbientPulsing(true); + for (OnAmbientChangedListener listener : mListeners) { + listener.onAmbientStateChanged(entry, true); + } + } + + @Override + protected void onAlertEntryRemoved(AlertEntry alertEntry) { + NotificationData.Entry entry = alertEntry.mEntry; + entry.row.setAmbientPulsing(false); + for (OnAmbientChangedListener listener : mListeners) { + listener.onAmbientStateChanged(entry, false); + } + } + + @Override + protected AlertEntry createAlertEntry() { + return new AmbientEntry(); + } + + /** + * Get the top pulsing entry. This should be the currently showing one if there are multiple. + * @return the currently showing entry + */ + private AmbientEntry getTopEntry() { + if (mAlertEntries.isEmpty()) { + return null; + } + AlertEntry topEntry = null; + for (AlertEntry entry : mAlertEntries.values()) { + if (topEntry == null || entry.compareTo(topEntry) < 0) { + topEntry = entry; + } + } + return (AmbientEntry) topEntry; + } + + /** + * Observer interface for any changes in the ambient entries. + */ + public interface OnAmbientChangedListener { + /** + * Called when an entry starts or stops pulsing. + * @param entry the entry that changed + * @param isPulsing true if the entry is now pulsing, false otherwise + */ + void onAmbientStateChanged(NotificationData.Entry entry, boolean isPulsing); + } + + private final class AmbientEntry extends AlertEntry { + private boolean extended; + + /** + * Extend the lifetime of the alertEntry so that it auto-removes later. Can only be + * extended once. + */ + private void extendPulse() { + if (!extended) { + extended = true; + updateEntry(false); + } + } + + @Override + public void reset() { + super.reset(); + extended = false; + } + + @Override + protected long calculateFinishTime() { + return super.calculateFinishTime() + (extended ? mExtensionTime : 0); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index d479838cdb2f..f69ad43ed79c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -321,7 +321,7 @@ public class NotificationViewHierarchyManager { && !row.isLowPriority())); } - entry.row.setShowAmbient(mPresenter.isDozing()); + entry.row.setOnAmbient(mPresenter.isDozing()); int userId = entry.notification.getUserId(); boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup( entry.notification) && !entry.row.isRemoved(); 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 804e8429e171..d097c8e706ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java @@ -440,6 +440,8 @@ public class NotificationData { } else if (isHeadsUp) { // Provide consistent ranking with headsUpManager return mHeadsUpManager.compare(a, b); + } else if (a.row.isAmbientPulsing() != b.row.isAmbientPulsing()) { + return a.row.isAmbientPulsing() ? -1 : 1; } else if (aMedia != bMedia) { // Upsort current media notification. return aMedia ? -1 : 1; 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 ac01fa32c902..935eaac78362 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -58,6 +58,8 @@ import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.statusbar.NotificationLifetimeExtender; +import com.android.systemui.statusbar.AlertingNotificationManager; +import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; @@ -91,7 +93,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. ExpandableNotificationRow.ExpansionLogger, NotificationUpdateHandler, VisualStabilityManager.Callback { private static final String TAG = "NotificationEntryMgr"; - protected static final boolean DEBUG = false; + protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); protected static final boolean ENABLE_HEADS_UP = true; protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up"; @@ -121,6 +123,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. Dependency.get(ForegroundServiceController.class); protected final NotificationListener mNotificationListener = Dependency.get(NotificationListener.class); + protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class); protected IStatusBarService mBarService; protected NotificationPresenter mPresenter; @@ -264,6 +267,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } mNotificationLifetimeExtenders.add(mHeadsUpManager); + mNotificationLifetimeExtenders.add(mAmbientPulseManager); mNotificationLifetimeExtenders.add(mGutsManager); mNotificationLifetimeExtenders.addAll(mRemoteInputManager.getLifetimeExtenders()); @@ -381,7 +385,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. final int userId = n.getUserId(); try { int dismissalSurface = NotificationStats.DISMISSAL_SHADE; - if (isHeadsUp(n.getKey())) { + if (mHeadsUpManager.isAlerting(n.getKey())) { dismissalSurface = NotificationStats.DISMISSAL_PEEK; } else if (mListContainer.hasPulsingNotifications()) { dismissalSurface = NotificationStats.DISMISSAL_AOD; @@ -432,12 +436,14 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } private void addEntry(NotificationData.Entry shadeEntry) { - boolean isHeadsUped = shouldPeek(shadeEntry); - if (isHeadsUped) { + if (shouldHeadsUp(shadeEntry)) { mHeadsUpManager.showNotification(shadeEntry); // Mark as seen immediately setNotificationShown(shadeEntry.notification); } + if (shouldPulse(shadeEntry)) { + mAmbientPulseManager.showNotification(shadeEntry); + } addNotificationViews(shadeEntry); mCallback.onNotificationAdded(shadeEntry); } @@ -465,7 +471,11 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. private void removeNotificationInternal(String key, @Nullable NotificationListenerService.RankingMap ranking, boolean forceRemove) { abortExistingInflation(key); - if (mHeadsUpManager.contains(key)) { + + // Attempt to remove notifications from their alert managers (heads up, ambient pulse). + // Though the remove itself may fail, it lets the manager know to remove as soon as + // possible. + if (mHeadsUpManager.isAlerting(key)) { // A cancel() in response to a remote input shouldn't be delayed, as it makes the // sending look longer than it takes. // Also we should not defer the removal if reordering isn't allowed since otherwise @@ -473,10 +483,11 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key) && !FORCE_REMOTE_INPUT_HISTORY || !mVisualStabilityManager.isReorderingAllowed(); - - // Attempt to remove notification. mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime); } + if (mAmbientPulseManager.isAlerting(key)) { + mAmbientPulseManager.removeNotification(key, false /* ignoreEarliestRemovalTime */); + } NotificationData.Entry entry = mNotificationData.get(key); @@ -651,13 +662,15 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. private void addNotificationInternal(StatusBarNotification notification, NotificationListenerService.RankingMap rankingMap) throws InflationException { String key = notification.getKey(); - if (DEBUG) Log.d(TAG, "addNotification key=" + key); + if (DEBUG) { + Log.d(TAG, "addNotification key=" + key); + } mNotificationData.updateRanking(rankingMap); NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking(); rankingMap.getRanking(key, ranking); NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking); - boolean isHeadsUped = shouldPeek(shadeEntry); + boolean isHeadsUped = shouldHeadsUp(shadeEntry); if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) { if (shouldSuppressFullScreenIntent(shadeEntry)) { if (DEBUG) { @@ -750,7 +763,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. extender.setShouldManageLifetime(entry, false /* shouldManage */); } - Notification n = notification.getNotification(); mNotificationData.updateRanking(ranking); final StatusBarNotification oldNotification = entry.notification; @@ -763,10 +775,12 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mForegroundServiceController.updateNotification(notification, mNotificationData.getImportance(key)); - boolean shouldPeek = shouldPeek(entry, notification); - boolean alertAgain = alertAgain(entry, n); - - updateHeadsUp(key, entry, shouldPeek, alertAgain); + boolean alertAgain = alertAgain(entry, entry.notification.getNotification()); + if (mPresenter.isDozing()) { + updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager); + } else { + updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager); + } updateNotifications(); if (!notification.isClearable()) { @@ -851,66 +865,147 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } } - protected boolean shouldPeek(NotificationData.Entry entry) { - return shouldPeek(entry, entry.notification); - } + /** + * Whether the notification should peek in from the top and alert the user. + * + * @param entry the entry to check + * @return true if the entry should heads up, false otherwise + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) + public boolean shouldHeadsUp(NotificationData.Entry entry) { + StatusBarNotification sbn = entry.notification; - public boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn) { - if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) { - if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode"); + if (mPresenter.isDozing()) { + if (DEBUG) { + Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey()); + } return false; } - if (mNotificationData.shouldFilterOut(entry)) { - if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey()); + if (!canAlertCommon(entry)) { + if (DEBUG) { + Log.d(TAG, "No heads up: notification shouldn't alert: " + sbn.getKey()); + } + return false; + } + + if (!mUseHeadsUp || mPresenter.isDeviceInVrMode()) { + if (DEBUG) { + Log.d(TAG, "No heads up: no huns or vr mode"); + } return false; } boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming(); - if (!inUse && !mPresenter.isDozing()) { + if (!inUse) { if (DEBUG) { - Log.d(TAG, "No peeking: not in use: " + sbn.getKey()); + Log.d(TAG, "No heads up: not in use: " + sbn.getKey()); } return false; } - if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(entry)) { - if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey()); + if (mNotificationData.shouldSuppressPeek(entry)) { + if (DEBUG) { + Log.d(TAG, "No heads up: suppressed by DND: " + sbn.getKey()); + } return false; } - // Peeking triggers an ambient display pulse, so disable peek is ambient is active - if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(entry)) { - if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey()); + if (isSnoozedPackage(sbn)) { + if (DEBUG) { + Log.d(TAG, "No heads up: snoozed package: " + sbn.getKey()); + } return false; } if (entry.hasJustLaunchedFullScreenIntent()) { - if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey()); + if (DEBUG) { + Log.d(TAG, "No heads up: recent fullscreen: " + sbn.getKey()); + } return false; } - if (isSnoozedPackage(sbn)) { - if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey()); + if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) { + if (DEBUG) { + Log.d(TAG, "No heads up: unimportant notification: " + sbn.getKey()); + } return false; } - // Allow peeking for DEFAULT notifications only if we're on Ambient Display. - int importanceLevel = mPresenter.isDozing() ? NotificationManager.IMPORTANCE_DEFAULT - : NotificationManager.IMPORTANCE_HIGH; - if (mNotificationData.getImportance(sbn.getKey()) < importanceLevel) { - if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey()); + if (!mCallback.canHeadsUp(entry, sbn)) { return false; } - // Don't peek notifications that are suppressed due to group alert behavior - if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) { - if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior"); + return true; + } + + /** + * Whether or not the notification should "pulse" on the user's display when the phone is + * dozing. This displays the ambient view of the notification. + * + * @param entry the entry to check + * @return true if the entry should ambient pulse, false otherwise + */ + protected boolean shouldPulse(NotificationData.Entry entry) { + StatusBarNotification sbn = entry.notification; + + if (!mPresenter.isDozing()) { + if (DEBUG) { + Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey()); + } + return false; + } + + if (!canAlertCommon(entry)) { + if (DEBUG) { + Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey()); + } + return false; + } + + if (mNotificationData.shouldSuppressAmbient(entry)) { + if (DEBUG) { + Log.d(TAG, "No pulsing: ambient effect suppressed: " + sbn.getKey()); + } + return false; + } + + if (mNotificationData.getImportance(sbn.getKey()) + < NotificationManager.IMPORTANCE_DEFAULT) { + if (DEBUG) { + Log.d(TAG, "No pulsing: not important enough: " + sbn.getKey()); + } return false; } - if (!mCallback.shouldPeek(entry, sbn)) { + return true; + } + + /** + * Common checks between heads up alerting and ambient pulse alerting. See + * {@link NotificationEntryManager#shouldHeadsUp(NotificationData.Entry)} and + * {@link NotificationEntryManager#shouldPulse(NotificationData.Entry)}. Notifications that + * fail any of these checks should not alert at all. + * + * @param entry the entry to check + * @return true if these checks pass, false if the notification should not alert + */ + protected boolean canAlertCommon(NotificationData.Entry entry) { + StatusBarNotification sbn = entry.notification; + + if (mNotificationData.shouldFilterOut(entry)) { + if (DEBUG) { + Log.d(TAG, "No alerting: filtered notification: " + sbn.getKey()); + } + return false; + } + + // Don't alert notifications that are suppressed due to group alert behavior + if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) { + if (DEBUG) { + Log.d(TAG, "No alerting: suppressed due to group alert behavior"); + } return false; } @@ -933,26 +1028,31 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. return mHeadsUpManager.isSnoozed(sbn.getPackageName()); } - protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldPeek, - boolean alertAgain) { - final boolean wasHeadsUp = isHeadsUp(key); - if (wasHeadsUp) { - if (!shouldPeek) { + /** + * Update the entry's alert state and call the appropriate {@link AlertingNotificationManager} + * method. + * @param entry entry to update + * @param shouldAlert whether or not it should be alerting + * @param alertAgain whether or not an alert should actually come in as if it were new + * @param alertManager the alerting notification manager that manages the alert state + */ + private void updateAlertState(NotificationData.Entry entry, boolean shouldAlert, + boolean alertAgain, AlertingNotificationManager alertManager) { + final boolean wasAlerting = alertManager.isAlerting(entry.key); + if (wasAlerting) { + if (!shouldAlert) { // We don't want this to be interrupting anymore, lets remove it - mHeadsUpManager.removeNotification(key, false /* ignoreEarliestRemovalTime */); + alertManager.removeNotification(entry.key, + false /* ignoreEarliestRemovalTime */); } else { - mHeadsUpManager.updateNotification(entry.key, alertAgain); + alertManager.updateNotification(entry.key, alertAgain); } - } else if (shouldPeek && alertAgain) { - // This notification was updated to be a heads-up, show it! - mHeadsUpManager.showNotification(entry); + } else if (shouldAlert && alertAgain) { + // This notification was updated to be alerting, show it! + alertManager.showNotification(entry); } } - protected boolean isHeadsUp(String key) { - return mHeadsUpManager.contains(key); - } - /** * Callback for NotificationEntryManager. */ @@ -1008,12 +1108,12 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. void onPerformRemoveNotification(StatusBarNotification statusBarNotification); /** - * Returns true if NotificationEntryManager should peek this notification. + * Returns true if NotificationEntryManager can heads up this notification. * - * @param entry entry of the notification that might be peeked - * @param sbn notification that might be peeked - * @return true if the notification should be peeked + * @param entry entry of the notification that might be heads upped + * @param sbn notification that might be heads upped + * @return true if the notification can be heads upped */ - boolean shouldPeek(NotificationData.Entry entry, StatusBarNotification sbn); + boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 216ed68e4fbe..019e88b11407 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -174,6 +174,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView */ private boolean mOnKeyguard; + /** + * Whether or not the row is currently on the doze screen. + */ + private boolean mOnAmbient; + private Animator mTranslateAnim; private ArrayList<View> mTranslateableViews; private NotificationContentView mPublicLayout; @@ -186,7 +191,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationData.Entry mEntry; private StatusBarNotification mStatusBarNotification; private String mAppName; + + /** + * Whether or not the notification is using the heads up view and should peek from the top. + */ private boolean mIsHeadsUp; + + /** + * Whether or not the notification is using the ambient display view and is pulsing. This + * occurs when a high priority notification alerts while the phone is dozing or is on AOD. + */ + private boolean mIsAmbientPulsing; + private boolean mLastChronometerRunning = true; private ViewStub mChildrenContainerStub; private NotificationGroupManager mGroupManager; @@ -289,7 +305,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private float mContentTransformationAmount; private boolean mIconsVisible = true; private boolean mAboveShelf; - private boolean mShowAmbient; private boolean mIsLastChild; private Runnable mOnDismissRunnable; private boolean mIsLowPriority; @@ -606,6 +621,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } + public boolean isAmbientPulsing() { + return mIsAmbientPulsing; + } + + public void setAmbientPulsing(boolean isAmbientPulsing) { + mIsAmbientPulsing = isAmbientPulsing; + } + public void setGroupManager(NotificationGroupManager groupManager) { mGroupManager = groupManager; mPrivateLayout.setGroupManager(groupManager); @@ -1854,7 +1877,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public void setDark(boolean dark, boolean fade, long delay) { super.setDark(dark, fade, delay); mDark = dark; - if (!mIsHeadsUp) { + if (!mIsAmbientPulsing) { // Only fade the showing view of the pulsing notification. fade = false; } @@ -2155,7 +2178,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mPrivateLayout.getMinHeight(); } else if (mSensitive && mHideSensitiveForIntrinsicHeight) { return getMinHeight(); - } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) { + } else if (mIsSummaryWithChildren && (!mOnKeyguard || mOnAmbient)) { return mChildrenContainer.getIntrinsicHeight(); } else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) { if (isPinned() || mHeadsupDisappearRunning) { @@ -2173,7 +2196,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } private boolean isHeadsUpAllowed() { - return !mOnKeyguard && !mShowAmbient; + return !mOnKeyguard && !mOnAmbient; } @Override @@ -2818,11 +2841,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView || mExpandAnimationRunning || mChildIsExpanding); } - public void setShowAmbient(boolean showAmbient) { - if (showAmbient != mShowAmbient) { - mShowAmbient = showAmbient; + public void setOnAmbient(boolean onAmbient) { + if (onAmbient != mOnAmbient) { + mOnAmbient = onAmbient; if (mChildrenContainer != null) { - mChildrenContainer.notifyShowAmbientChanged(); + mChildrenContainer.notifyDozingStateChanged(); } notifyHeightChanged(false /* needsAnimation */); } @@ -2891,8 +2914,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return getCurrentBottomRoundness() == 0.0f && getCurrentTopRoundness() == 0.0f; } - public boolean isShowingAmbient() { - return mShowAmbient; + public boolean isOnAmbient() { + return mOnAmbient; } public void setAboveShelf(boolean aboveShelf) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 0110610ca5aa..4963a0c6ae01 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -725,7 +725,7 @@ public class NotificationContentView extends FrameLayout { } public int getMaxHeight() { - if (mContainingNotification.isShowingAmbient()) { + if (mContainingNotification.isOnAmbient()) { return getShowingAmbientView().getHeight(); } else if (mExpandedChild != null) { return getViewHeight(VISIBLE_TYPE_EXPANDED) @@ -752,7 +752,7 @@ public class NotificationContentView extends FrameLayout { } public int getMinHeight(boolean likeGroupExpanded) { - if (mContainingNotification.isShowingAmbient()) { + if (mContainingNotification.isOnAmbient()) { return getShowingAmbientView().getHeight(); } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) { return getViewHeight(VISIBLE_TYPE_CONTRACTED); @@ -1039,7 +1039,7 @@ public class NotificationContentView extends FrameLayout { * @return one of the static enum types in this view, calculated form the current state */ public int calculateVisibleType() { - if (mContainingNotification.isShowingAmbient()) { + if (mContainingNotification.isOnAmbient()) { if (mIsChildInGroup && mAmbientSingleLineChild != null) { return VISIBLE_TYPE_AMBIENT_SINGLELINE; } else if (mAmbientChild != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 15eaaaca2842..8969acaac532 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -20,14 +20,15 @@ import android.annotation.Nullable; import android.content.Context; import android.view.View; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.NotificationShelf; -import com.android.systemui.statusbar.policy.HeadsUpManager; import java.util.ArrayList; @@ -44,7 +45,7 @@ public class AmbientState { private int mSpeedBumpIndex = -1; private boolean mDark; private boolean mHideSensitive; - private HeadsUpManager mHeadsUpManager; + private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class); private float mStackTranslation; private int mLayoutHeight; private int mTopPadding; @@ -207,10 +208,6 @@ public class AmbientState { mSpeedBumpIndex = shelfIndex; } - public void setHeadsUpManager(HeadsUpManager headsUpManager) { - mHeadsUpManager = headsUpManager; - } - public float getStackTranslation() { return mStackTranslation; } @@ -334,10 +331,10 @@ public class AmbientState { } public boolean isPulsing(NotificationData.Entry entry) { - if (!mPulsing || mHeadsUpManager == null) { + if (!mPulsing || mAmbientPulseManager == null) { return false; } - return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry)); + return mAmbientPulseManager.isAlerting(entry.key); } public boolean isPanelTracking() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 3d44e3ce6a10..da089b307850 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -213,7 +213,7 @@ public class NotificationChildrenContainer extends ViewGroup { // calculated correctly as they are used to calculate how many we can fit on the screen. boolean isOverflow = i == overflowIndex; child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null && - !mContainingNotification.isShowingAmbient() + !mContainingNotification.isOnAmbient() ? mOverflowNumber.getMeasuredWidth() : 0); child.measure(widthMeasureSpec, newHeightSpec); // layout the divider @@ -406,7 +406,7 @@ public class NotificationChildrenContainer extends ViewGroup { if (childCount > maxAllowedVisibleChildren) { int number = childCount - maxAllowedVisibleChildren; mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number); - if (mContainingNotification.isShowingAmbient()) { + if (mContainingNotification.isOnAmbient()) { ExpandableNotificationRow overflowView = mChildren.get(0); HybridNotificationView ambientSingleLineView = overflowView == null ? null : overflowView.getAmbientSingleLineView(); @@ -522,7 +522,7 @@ public class NotificationChildrenContainer extends ViewGroup { if (mUserLocked) { expandFactor = getGroupExpandFraction(); } - boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isShowingAmbient(); + boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isOnAmbient(); for (int i = 0; i < childCount; i++) { if (visibleChildren >= maxAllowedVisibleChildren) { break; @@ -641,7 +641,7 @@ public class NotificationChildrenContainer extends ViewGroup { getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1); mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView)); - if (mContainingNotification.isShowingAmbient()) { + if (mContainingNotification.isOnAmbient()) { mGroupOverFlowState.alpha = 0.0f; } else if (!mChildrenExpanded) { HybridNotificationView alignView = overflowView.getSingleLineView(); @@ -710,7 +710,7 @@ public class NotificationChildrenContainer extends ViewGroup { @VisibleForTesting int getMaxAllowedVisibleChildren(boolean likeCollapsed) { - if (mContainingNotification.isShowingAmbient()) { + if (mContainingNotification.isOnAmbient()) { return NUMBER_OF_CHILDREN_WHEN_AMBIENT; } if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked()) @@ -900,7 +900,7 @@ public class NotificationChildrenContainer extends ViewGroup { return mCurrentHeader; } - public void notifyShowAmbientChanged() { + public void notifyDozingStateChanged() { updateHeaderVisibility(false); updateGroupOverflow(); } @@ -970,7 +970,7 @@ public class NotificationChildrenContainer extends ViewGroup { private ViewGroup calculateDesiredHeader() { ViewGroup desiredHeader; - if (mContainingNotification.isShowingAmbient()) { + if (mContainingNotification.isOnAmbient()) { desiredHeader = mNotificationHeaderAmbient; } else if (showingAsLowPriority()) { desiredHeader = mNotificationHeaderLowPriority; @@ -1126,7 +1126,7 @@ public class NotificationChildrenContainer extends ViewGroup { } public int getMinHeight() { - return getMinHeight(mContainingNotification.isShowingAmbient() + return getMinHeight(mContainingNotification.isOnAmbient() ? NUMBER_OF_CHILDREN_WHEN_AMBIENT : NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index da98565f9324..958a162f1c7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -4828,7 +4828,6 @@ public class NotificationStackScrollLayout extends ViewGroup @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { mHeadsUpManager = headsUpManager; - mAmbientState.setHeadsUpManager(headsUpManager); mHeadsUpManager.addListener(mRoundnessManager); mHeadsUpManager.setAnimationStateHandler(this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index 0d3ba77d5079..25db4f20a450 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -63,9 +63,14 @@ public class DozeScrimController { if (!mDozing) { return; } - mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); - mHandler.postDelayed(mPulseOutExtended, - mDozeParameters.getPulseVisibleDurationExtended()); + // All pulses except notifications should time out on their own. Pulses due to + // notifications should instead be managed externally based off the notification's + // lifetime. + if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION) { + mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); + mHandler.postDelayed(mPulseOutExtended, + mDozeParameters.getPulseVisibleDurationExtended()); + } mFullyPulsing = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 4a059895ffe4..cfc327116303 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -177,7 +177,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, mReleaseOnExpandFinish = false; } else { for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) { - if (contains(entry.key)) { + if (isAlerting(entry.key)) { // Maybe the heads-up was removed already removeAlertEntry(entry.key); } @@ -345,7 +345,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, public void onReorderingAllowed() { mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false); for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) { - if (contains(entry.key)) { + if (isAlerting(entry.key)) { // Maybe the heads-up was removed already removeAlertEntry(entry.key); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index 6b6566cc1d68..c08366a95f08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import android.annotation.NonNull; import android.app.Notification; import android.os.SystemClock; import android.service.notification.StatusBarNotification; @@ -23,6 +24,9 @@ import androidx.annotation.Nullable; import android.util.Log; import com.android.systemui.Dependency; +import com.android.systemui.statusbar.AlertingNotificationManager; +import com.android.systemui.statusbar.AmbientPulseManager; +import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListener; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -43,15 +47,18 @@ import java.util.Objects; /** * A class to handle notifications and their corresponding groups. */ -public class NotificationGroupManager implements OnHeadsUpChangedListener { +public class NotificationGroupManager implements OnHeadsUpChangedListener, + OnAmbientChangedListener { private static final String TAG = "NotificationGroupManager"; - private static final long HEADS_UP_TRANSFER_TIMEOUT = 300; + private static final long ALERT_TRANSFER_TIMEOUT = 300; private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>(); private OnGroupChangeListener mListener; private int mBarState = -1; private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>(); private HeadsUpManager mHeadsUpManager; + private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class); + private boolean mIsDozing; private boolean mIsUpdatingUnchangedGroup; private HashMap<String, NotificationData.Entry> mPendingNotifications; @@ -162,40 +169,58 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { mListener.onGroupCreatedFromChildren(group); } } - cleanUpHeadsUpStatesOnAdd(group, false /* addIsPending */); + cleanUpAlertStatesOnAdd(group, false /* addIsPending */); } public void onPendingEntryAdded(NotificationData.Entry shadeEntry) { String groupKey = getGroupKey(shadeEntry.notification); NotificationGroup group = mGroupMap.get(groupKey); if (group != null) { - cleanUpHeadsUpStatesOnAdd(group, true /* addIsPending */); + cleanUpAlertStatesOnAdd(group, true /* addIsPending */); } } /** - * Clean up the heads up states when a new child was added. + * Set whether or not the device is dozing. This allows the group manager to reset some + * specific alert state logic based off when the state changes. + * @param isDozing if the device is dozing. + */ + public void setDozing(boolean isDozing) { + if (mIsDozing != isDozing) { + for (NotificationGroup group : mGroupMap.values()) { + group.lastAlertTransfer = 0; + group.alertSummaryOnNextAddition = false; + } + } + mIsDozing = isDozing; + } + + /** + * Clean up the alert states when a new child was added. * @param group The group where a view was added or will be added. * @param addIsPending True if is the addition still pending or false has it already been added. */ - private void cleanUpHeadsUpStatesOnAdd(NotificationGroup group, boolean addIsPending) { - if (!addIsPending && group.hunSummaryOnNextAddition) { - if (!mHeadsUpManager.contains(group.summary.key)) { - mHeadsUpManager.showNotification(group.summary); + private void cleanUpAlertStatesOnAdd(NotificationGroup group, boolean addIsPending) { + + AlertingNotificationManager alertManager = + mIsDozing ? mAmbientPulseManager : mHeadsUpManager; + if (!addIsPending && group.alertSummaryOnNextAddition) { + if (!alertManager.isAlerting(group.summary.key)) { + alertManager.showNotification(group.summary); } - group.hunSummaryOnNextAddition = false; + group.alertSummaryOnNextAddition = false; } // Because notification groups are not delivered as a whole unit, it may happen that a // group child gets added quite a bit after the summary got posted. Our guidance is, that // apps should always post the group summary as well and we'll hide it for them if the child - // is the only child in a group. Because of this, we also have to transfer heads up to the - // child, otherwise the invisible summary would be heads-upped. + // is the only child in a group. Because of this, we also have to transfer alert to the + // child, otherwise the invisible summary would be alerted. // This transfer to the child is not always correct in case the app has just posted another // child in addition to the existing one, but it hasn't arrived in systemUI yet. In such - // a scenario we would transfer the heads up to the old child and the wrong notification - // would be heads-upped. In oder to avoid this, we'll recover from this issue and hun the + // a scenario we would transfer the alert to the old child and the wrong notification + // would be alerted. In order to avoid this, we'll recover from this issue and alert the // summary again instead of the old child if it's within a certain timeout. - if (SystemClock.elapsedRealtime() - group.lastHeadsUpTransfer < HEADS_UP_TRANSFER_TIMEOUT) { + if (SystemClock.elapsedRealtime() - group.lastAlertTransfer < ALERT_TRANSFER_TIMEOUT) { if (!onlySummaryAlerts(group.summary)) { return; } @@ -215,26 +240,24 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { int size = children.size(); for (int i = 0; i < size; i++) { NotificationData.Entry entry = children.get(i); - if (onlySummaryAlerts(entry) && entry.row.isHeadsUp()) { + if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) { releasedChild = true; - mHeadsUpManager.removeNotification( - entry.key, true /* releaseImmediately */); + alertManager.removeNotification(entry.key, true /* releaseImmediately */); } } if (isolatedChild != null && onlySummaryAlerts(isolatedChild) - && isolatedChild.row.isHeadsUp()) { + && alertManager.isAlerting(isolatedChild.key)) { releasedChild = true; - mHeadsUpManager.removeNotification( - isolatedChild.key, true /* releaseImmediately */); + alertManager.removeNotification(isolatedChild.key, true /* releaseImmediately */); } - if (releasedChild && !mHeadsUpManager.contains(group.summary.key)) { + if (releasedChild && !alertManager.isAlerting(group.summary.key)) { boolean notifyImmediately = (numChildren - numPendingChildren) > 1; if (notifyImmediately) { - mHeadsUpManager.showNotification(group.summary); + alertManager.showNotification(group.summary); } else { - group.hunSummaryOnNextAddition = true; + group.alertSummaryOnNextAddition = true; } - group.lastHeadsUpTransfer = 0; + group.lastAlertTransfer = 0; } } } @@ -264,8 +287,8 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { } private void onEntryBecomingChild(NotificationData.Entry entry) { - if (entry.row.isHeadsUp()) { - onHeadsUpStateChanged(entry, true); + if (shouldIsolate(entry)) { + isolateNotification(entry); } } @@ -281,7 +304,11 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { && hasIsolatedChildren(group))); if (prevSuppressed != group.suppressed) { if (group.suppressed) { - handleSuppressedSummaryHeadsUpped(group.summary); + if (mHeadsUpManager.isAlerting(group.summary.key)) { + handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager); + } else if (mAmbientPulseManager.isAlerting(group.summary.key)) { + handleSuppressedSummaryAlerted(group.summary, mAmbientPulseManager); + } } if (!mIsUpdatingUnchangedGroup && mListener != null) { mListener.onGroupsChanged(); @@ -495,54 +522,56 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { } @Override + public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) { + onAlertStateChanged(entry, isAmbient, mAmbientPulseManager); + } + + @Override public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { + onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager); + } + + private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting, + AlertingNotificationManager alertManager) { final StatusBarNotification sbn = entry.notification; - if (entry.row.isHeadsUp()) { - if (shouldIsolate(sbn)) { - // We will be isolated now, so lets update the groups - onEntryRemovedInternal(entry, entry.notification); - - mIsolatedEntries.put(sbn.getKey(), sbn); - - onEntryAdded(entry); - // We also need to update the suppression of the old group, because this call comes - // even before the groupManager knows about the notification at all. - // When the notification gets added afterwards it is already isolated and therefore - // it doesn't lead to an update. - updateSuppression(mGroupMap.get(entry.notification.getGroupKey())); - mListener.onGroupsChanged(); - } else { - handleSuppressedSummaryHeadsUpped(entry); + if (isAlerting) { + if (shouldIsolate(entry)) { + isolateNotification(entry); + } else if (sbn.getNotification().isGroupSummary() + && isGroupSuppressed(sbn.getGroupKey())){ + handleSuppressedSummaryAlerted(entry, alertManager); } } else { - if (mIsolatedEntries.containsKey(sbn.getKey())) { - // not isolated anymore, we need to update the groups - onEntryRemovedInternal(entry, entry.notification); - mIsolatedEntries.remove(sbn.getKey()); - onEntryAdded(entry); - mListener.onGroupsChanged(); - } + stopIsolatingNotification(entry); } } - private void handleSuppressedSummaryHeadsUpped(NotificationData.Entry entry) { - StatusBarNotification sbn = entry.notification; + /** + * Handles the scenario where a summary that has been suppressed is alerted. A suppressed + * summary should for all intents and purposes be invisible to the user and as a result should + * not alert. When this is the case, it is our responsibility to pass the alert to the + * appropriate child which will be the representative notification alerting for the group. + * @param summary the summary that is suppressed and alerting + * @param alertManager the alert manager that manages the alerting summary + */ + private void handleSuppressedSummaryAlerted(@NonNull NotificationData.Entry summary, + @NonNull AlertingNotificationManager alertManager) { + StatusBarNotification sbn = summary.notification; if (!isGroupSuppressed(sbn.getGroupKey()) || !sbn.getNotification().isGroupSummary() - || !entry.row.isHeadsUp()) { + || !alertManager.isAlerting(sbn.getKey())) { return; } - // The parent of a suppressed group got huned, lets hun the child! + // The parent of a suppressed group got alerted, lets alert the child! NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey()); - if (pendingInflationsWillAddChildren(notificationGroup)) { - // New children will actually be added to this group, let's not transfer the heads - // up - return; - } - if (notificationGroup != null) { + if (pendingInflationsWillAddChildren(notificationGroup)) { + // New children will actually be added to this group, let's not transfer the alert. + return; + } + Iterator<NotificationData.Entry> iterator = notificationGroup.children.values().iterator(); NotificationData.Entry child = iterator.hasNext() ? iterator.next() : null; @@ -551,20 +580,35 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { } if (child != null) { if (child.row.keepInParent() || child.row.isRemoved() || child.row.isDismissed()) { - // the notification is actually already removed, no need to do heads-up on it. + // the notification is actually already removed, no need to do alert on it. return; } - if (mHeadsUpManager.contains(child.key)) { - mHeadsUpManager.updateNotification(child.key, true /* alert */); - } else { - if (onlySummaryAlerts(entry)) { - notificationGroup.lastHeadsUpTransfer = SystemClock.elapsedRealtime(); - } - mHeadsUpManager.showNotification(child); - } + transferAlertStateToChild(summary, child, alertManager); } } - mHeadsUpManager.removeNotification(entry.key, true /* releaseImmediately */); + } + + /** + * Transfers the alert state from a given summary notification to the specified child. The + * result is the child will now alert while the summary does not. + * + * @param summary the currently alerting summary notification + * @param child the child that should receive the alert + * @param alertManager the manager for the alert + */ + private void transferAlertStateToChild(@NonNull NotificationData.Entry summary, + @NonNull NotificationData.Entry child, + @NonNull AlertingNotificationManager alertManager) { + NotificationGroup notificationGroup = mGroupMap.get(summary.notification.getGroupKey()); + if (alertManager.isAlerting(child.key)) { + alertManager.updateNotification(child.key, true /* alert */); + } else { + if (onlySummaryAlerts(summary)) { + notificationGroup.lastAlertTransfer = SystemClock.elapsedRealtime(); + } + alertManager.showNotification(child); + } + alertManager.removeNotification(summary.key, true /* releaseImmediately */); } private boolean onlySummaryAlerts(NotificationData.Entry entry) { @@ -596,13 +640,69 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { return false; } - private boolean shouldIsolate(StatusBarNotification sbn) { + /** + * Whether a notification that is normally part of a group should be temporarily isolated from + * the group and put in their own group visually. This generally happens when the notification + * is alerting. + * + * @param entry the notification to check + * @return true if the entry should be isolated + */ + + private boolean shouldIsolate(NotificationData.Entry entry) { + StatusBarNotification sbn = entry.notification; NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey()); - return (sbn.isGroup() && !sbn.getNotification().isGroupSummary()) - && (sbn.getNotification().fullScreenIntent != null - || notificationGroup == null - || !notificationGroup.expanded - || isGroupNotFullyVisible(notificationGroup)); + if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) { + return false; + } + if (!mIsDozing && !mHeadsUpManager.isAlerting(entry.key)) { + return false; + } + if (mIsDozing && !mAmbientPulseManager.isAlerting(entry.key)) { + return false; + } + return (sbn.getNotification().fullScreenIntent != null + || notificationGroup == null + || !notificationGroup.expanded + || isGroupNotFullyVisible(notificationGroup)); + } + + /** + * Isolate a notification from its group so that it visually shows as its own group. + * + * @param entry the notification to isolate + */ + private void isolateNotification(NotificationData.Entry entry) { + StatusBarNotification sbn = entry.notification; + + // We will be isolated now, so lets update the groups + onEntryRemovedInternal(entry, entry.notification); + + mIsolatedEntries.put(sbn.getKey(), sbn); + + onEntryAdded(entry); + // We also need to update the suppression of the old group, because this call comes + // even before the groupManager knows about the notification at all. + // When the notification gets added afterwards it is already isolated and therefore + // it doesn't lead to an update. + updateSuppression(mGroupMap.get(entry.notification.getGroupKey())); + mListener.onGroupsChanged(); + } + + /** + * Stop isolating a notification and re-group it with its original logical group. + * + * @param entry the notification to un-isolate + */ + private void stopIsolatingNotification(NotificationData.Entry entry) { + StatusBarNotification sbn = entry.notification; + if (mIsolatedEntries.containsKey(sbn.getKey())) { + // not isolated anymore, we need to update the groups + onEntryRemovedInternal(entry, entry.notification); + mIsolatedEntries.remove(sbn.getKey()); + onEntryAdded(entry); + mListener.onGroupsChanged(); + } } private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) { @@ -641,11 +741,11 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { */ public boolean suppressed; /** - * The time when the last heads transfer from group to child happened, while the summary - * has the flags to heads up on its own. + * The time when the last alert transfer from group to child happened, while the summary + * has the flags to alert up on its own. */ - public long lastHeadsUpTransfer; - public boolean hunSummaryOnNextAddition; + public long lastAlertTransfer; + public boolean alertSummaryOnNextAddition; @Override public String toString() { 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 759e8ea90761..e1d86387cdfd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -187,6 +187,7 @@ import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.stackdivider.WindowManagerProxy; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.AppOpsListener; import com.android.systemui.statusbar.BackDropView; @@ -254,7 +255,7 @@ public class StatusBar extends SystemUI implements DemoMode, ActivityStarter, OnUnlockMethodChangedListener, OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback, ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter, - StatusBarStateController.StateListener { + StatusBarStateController.StateListener, AmbientPulseManager.OnAmbientChangedListener { public static final boolean MULTIUSER_DEBUG = false; public static final boolean ENABLE_CHILD_NOTIFICATIONS @@ -865,6 +866,8 @@ public class StatusBar extends SystemUI implements DemoMode, mHeadsUpManager.addListener(mNotificationPanel); mHeadsUpManager.addListener(mGroupManager); mHeadsUpManager.addListener(mVisualStabilityManager); + mAmbientPulseManager.addListener(this); + mAmbientPulseManager.addListener(mGroupManager); mNotificationPanel.setHeadsUpManager(mHeadsUpManager); mGroupManager.setHeadsUpManager(mHeadsUpManager); putComponent(HeadsUpManager.class, mHeadsUpManager); @@ -1233,7 +1236,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onPerformRemoveNotification(StatusBarNotification n) { if (mNotificationPanel.hasPulsingNotifications() && - !mHeadsUpManager.hasNotifications()) { + !mAmbientPulseManager.hasNotifications()) { // We were showing a pulse for a notification, but no notifications are pulsing anymore. // Finish the pulse. mDozeScrimController.pulseOutNow(); @@ -1697,8 +1700,12 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public boolean shouldPeek(Entry entry, StatusBarNotification sbn) { - if (mIsOccluded && !isDozing()) { + public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) { + if (isDozing()) { + return false; + } + + if (mIsOccluded) { boolean devicePublic = mLockscreenUserManager. isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); boolean userPublic = devicePublic @@ -1711,17 +1718,14 @@ public class StatusBar extends SystemUI implements DemoMode, if (!panelsEnabled()) { if (DEBUG) { - Log.d(TAG, "No peeking: disabled panel : " + sbn.getKey()); + Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey()); } return false; } if (sbn.getNotification().fullScreenIntent != null) { if (mAccessibilityManager.isTouchExplorationEnabled()) { - if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey()); - return false; - } else if (isDozing()) { - // We never want heads up when we are dozing. + if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey()); return false; } else { // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent @@ -1797,9 +1801,16 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) { mEntryManager.updateNotificationRanking(null /* rankingMap */); + } - if (isHeadsUp) { - mDozeServiceHost.fireNotificationHeadsUp(); + @Override + public void onAmbientStateChanged(Entry entry, boolean isAmbient) { + mEntryManager.updateNotificationRanking(null); + if (isAmbient) { + mDozeServiceHost.fireNotificationPulse(); + } else if (!mAmbientPulseManager.hasNotifications()) { + // There are no longer any notifications to show. We should end the pulse now. + mDozeScrimController.pulseOutNow(); } } @@ -3603,6 +3614,7 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardIndicationController.setDozing(mDozing); mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation); mNotificationLogger.setDozing(mDozing); + mGroupManager.setDozing(mDozing); updateQsExpansionEnabled(); Trace.endSection(); } @@ -4149,6 +4161,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onStartedWakingUp() { mDeviceInteractive = true; + mAmbientPulseManager.releaseAllImmediately(); mVisualStabilityManager.setScreenOn(true); mNotificationPanel.setTouchAndAnimationDisabled(false); mDozeServiceHost.stopDozing(); @@ -4181,11 +4194,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void onScreenTurnedOff() { mFalsingManager.onScreenOff(); mScrimController.onScreenTurnedOff(); - // If we pulse in from AOD, we turn the screen off first. However, updatingIsKeyguard - // in that case destroys the HeadsUpManager state, so don't do it in that case. - if (!isPulsing()) { - updateIsKeyguard(); - } + updateIsKeyguard(); } }; @@ -4424,9 +4433,9 @@ public class StatusBar extends SystemUI implements DemoMode, } } - public void fireNotificationHeadsUp() { + public void fireNotificationPulse() { for (Callback callback : mCallbacks) { - callback.onNotificationHeadsUp(); + callback.onNotificationAlerted(); } } @@ -4462,7 +4471,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onPulseStarted() { callback.onPulseStarted(); - if (mHeadsUpManager.hasNotifications()) { + if (mAmbientPulseManager.hasNotifications()) { // Only pulse the stack scroller if there's actually something to show. // Otherwise just show the always-on screen. setPulsing(true); @@ -4543,7 +4552,11 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void extendPulse() { - mDozeScrimController.extendPulse(); + if (mDozeScrimController.isPulsing() && mAmbientPulseManager.hasNotifications()) { + mAmbientPulseManager.extendPulse(); + } else { + mDozeScrimController.extendPulse(); + } } @Override @@ -4629,6 +4642,8 @@ public class StatusBar extends SystemUI implements DemoMode, // for heads up notifications protected HeadsUpManagerPhone mHeadsUpManager; + protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class); + private AboveShelfObserver mAboveShelfObserver; // handling reordering @@ -4734,7 +4749,7 @@ public class StatusBar extends SystemUI implements DemoMode, final boolean wasOccluded = mIsOccluded; dismissKeyguardThenExecute(() -> { // TODO: Some of this code may be able to move to NotificationEntryManager. - if (mHeadsUpManager != null && mHeadsUpManager.contains(notificationKey)) { + if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) { // Release the HUN notification to the shade. if (isPresenterFullyCollapsed()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index e8389af85f22..3db1456446a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -460,6 +460,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb boolean staying = mStatusBar.hideKeyguard(); if (!staying) { mStatusBarWindowController.setKeyguardFadingAway(true); + // hide() will happen asynchronously and might arrive after the scrims + // were already hidden, this means that the transition callback won't + // be triggered anymore and StatusBarWindowController will be forever in + // the fadingAway state. + mStatusBar.updateScrimController(); wakeAndUnlockDejank(); } else { mStatusBar.finishKeyguardFadingAway(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 52b813f5388b..7dd0d0fbef47 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -586,7 +586,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene final InputConnection inputConnection = super.onCreateInputConnection(outAttrs); if (mShowImeOnInputConnection && inputConnection != null) { - final InputMethodManager imm = InputMethodManager.getInstance(); + final InputMethodManager imm = + getContext().getSystemService(InputMethodManager.class); if (imm != null) { // onCreateInputConnection is called by InputMethodManager in the middle of // setting up the connection to the IME; wait with requesting the IME until that diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index c2da7f5280b5..5c8336c8dee1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -88,14 +88,14 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); - mHost.callback.onNotificationHeadsUp(); + mHost.callback.onNotificationAlerted(); mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */ verify(mMachine, never()).requestState(any()); verify(mMachine, never()).requestPulse(anyInt()); - mHost.callback.onNotificationHeadsUp(); + mHost.callback.onNotificationAlerted(); mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java index f21ce2780d40..8b41516044f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java @@ -56,19 +56,19 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; private static final int TEST_UID = 0; - private static final int TEST_MINIMUM_DISPLAY_TIME = 200; - private static final int TEST_AUTO_DISMISS_TIME = 500; + protected static final int TEST_MINIMUM_DISPLAY_TIME = 200; + protected static final int TEST_AUTO_DISMISS_TIME = 500; // Number of notifications to use in tests requiring multiple notifications private static final int TEST_NUM_NOTIFICATIONS = 4; - private static final int TEST_TIMEOUT_TIME = 10000; - private final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true; + protected static final int TEST_TIMEOUT_TIME = 10000; + protected final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true; private AlertingNotificationManager mAlertingNotificationManager; protected NotificationData.Entry mEntry; protected Handler mTestHandler; private StatusBarNotification mSbn; - private boolean mTimedOut = false; + protected boolean mTimedOut = false; @Mock protected ExpandableNotificationRow mRow; @@ -122,7 +122,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { public void testShowNotification_addsEntry() { mAlertingNotificationManager.showNotification(mEntry); - assertTrue(mAlertingNotificationManager.contains(mEntry.key)); + assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key)); assertTrue(mAlertingNotificationManager.hasNotifications()); assertEquals(mEntry, mAlertingNotificationManager.getEntry(mEntry.key)); } @@ -136,7 +136,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { TestableLooper.get(this).processMessages(1); assertFalse("Test timed out", mTimedOut); - assertFalse(mAlertingNotificationManager.contains(mEntry.key)); + assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key)); } @Test @@ -146,7 +146,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { // Try to remove but defer, since the notification has not been shown long enough. mAlertingNotificationManager.removeNotification(mEntry.key, false /* releaseImmediately */); - assertTrue(mAlertingNotificationManager.contains(mEntry.key)); + assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key)); } @Test @@ -156,7 +156,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { // Remove forcibly with releaseImmediately = true. mAlertingNotificationManager.removeNotification(mEntry.key, true /* releaseImmediately */); - assertFalse(mAlertingNotificationManager.contains(mEntry.key)); + assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key)); } @Test @@ -174,10 +174,18 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { } @Test - public void testShouldExtendLifetime_notShownLongEnough() { + public void testCanRemoveImmediately_notShownLongEnough() { mAlertingNotificationManager.showNotification(mEntry); - // The entry has just been added so the lifetime should be extended + // The entry has just been added so we should not remove immediately. + assertFalse(mAlertingNotificationManager.canRemoveImmediately(mEntry.key)); + } + + @Test + public void testShouldExtendLifetime() { + mAlertingNotificationManager.showNotification(mEntry); + + // While the entry is alerting, it should not be removable. assertTrue(mAlertingNotificationManager.shouldExtendLifetime(mEntry)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java new file mode 100644 index 000000000000..f0344e6936e7 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AmbientPulseManagerTest.java @@ -0,0 +1,78 @@ +/* + * 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; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class AmbientPulseManagerTest extends AlertingNotificationManagerTest { + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + private static final int TEST_EXTENSION_TIME = 500; + private AmbientPulseManager mAmbientPulseManager; + private boolean mLivesPastNormalTime; + + protected AlertingNotificationManager createAlertingNotificationManager() { + return mAmbientPulseManager; + } + + @Before + public void setUp() { + mAmbientPulseManager = new AmbientPulseManager(mContext); + mAmbientPulseManager.mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME; + mAmbientPulseManager.mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; + mAmbientPulseManager.mExtensionTime = TEST_EXTENSION_TIME; + super.setUp(); + mAmbientPulseManager.mHandler = mTestHandler; + } + + @Test + public void testExtendPulse() { + mAmbientPulseManager.showNotification(mEntry); + Runnable pastNormalTimeRunnable = + () -> mLivesPastNormalTime = mAmbientPulseManager.isAlerting(mEntry.key); + mTestHandler.postDelayed(pastNormalTimeRunnable, + mAmbientPulseManager.mAutoDismissNotificationDecay + + mAmbientPulseManager.mExtensionTime / 2); + mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_TIMEOUT_TIME); + + mAmbientPulseManager.extendPulse(); + + // Wait for normal time runnable and extended remove runnable and process them on arrival. + TestableLooper.get(this).processMessages(2); + + assertFalse("Test timed out", mTimedOut); + assertTrue("Pulse was not extended", mLivesPastNormalTime); + assertFalse(mAmbientPulseManager.isAlerting(mEntry.key)); + } +} + diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index b2170fa3cb95..edf29ac1c4f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -31,6 +31,7 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.widget.RemoteViews; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.row.NotificationInflaterTest; @@ -63,6 +64,7 @@ public class NotificationTestHelper { mContext = context; mInstrumentation = InstrumentationRegistry.getInstrumentation(); mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null); + mGroupManager.setHeadsUpManager(mHeadsUpManager); } public ExpandableNotificationRow createRow() throws Exception { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java index 087aa5953d90..272845396e27 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.notification.stack; import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.view.NotificationHeaderView; @@ -51,7 +50,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { @Test public void testGetMaxAllowedVisibleChildren_ambient() { - mGroup.setShowAmbient(true); + mGroup.setOnAmbient(true); Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(), NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_AMBIENT); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index a81d17f12de1..10707957521e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -82,28 +82,26 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { // Remove should succeed because the notification is swiped out mHeadsUpManager.removeNotification(mEntry.key, false /* releaseImmediately */); - assertFalse(mHeadsUpManager.contains(mEntry.key)); + assertFalse(mHeadsUpManager.isAlerting(mEntry.key)); } @Test - public void testShouldExtendLifetime_swipedOut() { + public void testCanRemoveImmediately_swipedOut() { mHeadsUpManager.showNotification(mEntry); mHeadsUpManager.addSwipedOutNotification(mEntry.key); - // Notification is swiped so its lifetime should not be extended even if it hasn't been - // shown long enough - assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry)); + // Notification is swiped so it can be immediately removed. + assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key)); } @Test - public void testShouldExtendLifetime_notTopEntry() { + public void testCanRemoveImmediately_notTopEntry() { NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1)); laterEntry.row = mRow; mHeadsUpManager.showNotification(mEntry); mHeadsUpManager.showNotification(laterEntry); - // Notification is "behind" a higher priority notification so we have no reason to keep - // its lifetime extended - assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry)); + // Notification is "behind" a higher priority notification so we can remove it immediately. + assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java index 6a3c8a850831..464f74b3aa36 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java @@ -36,6 +36,7 @@ import android.testing.TestableLooper; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener; @@ -60,15 +61,23 @@ public class NotificationGroupManagerTest extends SysuiTestCase { private static final String TEST_CHANNEL_ID = "test_channel"; private static final String TEST_GROUP_ID = "test_group"; private static final String TEST_PACKAGE_NAME = "test_pkg"; - private NotificationGroupManager mGroupManager = new NotificationGroupManager(); + private NotificationGroupManager mGroupManager; private int mId = 0; @Mock HeadsUpManager mHeadsUpManager; + @Mock AmbientPulseManager mAmbientPulseManager; @Before public void setup() { - mGroupManager.setHeadsUpManager(mHeadsUpManager); - mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class)); + mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager); + + initializeGroupManager(); + } + + private void initializeGroupManager() { + mGroupManager = new NotificationGroupManager(); + mGroupManager.setHeadsUpManager(mHeadsUpManager); + mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class)); } @Test @@ -141,8 +150,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase { mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); mGroupManager.onEntryAdded(createChildNotification()); - when(childEntry.row.isHeadsUp()).thenReturn(true); - when(mHeadsUpManager.contains(childEntry.key)).thenReturn(true); + when(mHeadsUpManager.isAlerting(childEntry.key)).thenReturn(true); mGroupManager.onHeadsUpStateChanged(childEntry, true); @@ -154,17 +162,35 @@ public class NotificationGroupManagerTest extends SysuiTestCase { } @Test + public void testAmbientPulseEntryIsIsolated() { + mGroupManager.setDozing(true); + NotificationData.Entry childEntry = createChildNotification(); + NotificationData.Entry summaryEntry = createSummaryNotification(); + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + mGroupManager.onEntryAdded(createChildNotification()); + when(mAmbientPulseManager.isAlerting(childEntry.key)).thenReturn(true); + + mGroupManager.onAmbientStateChanged(childEntry, true); + + // Child entries that are heads upped should be considered separate groups visually even if + // they are the same group logically + assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification)); + assertEquals(summaryEntry.row, + mGroupManager.getLogicalGroupSummary(childEntry.notification)); + } + + @Test public void testSuppressedSummaryHeadsUpTransfersToChild() { NotificationData.Entry summaryEntry = createSummaryNotification(); - when(summaryEntry.row.isHeadsUp()).thenReturn(true); - when(mHeadsUpManager.contains(summaryEntry.key)).thenReturn(true); + when(mHeadsUpManager.isAlerting(summaryEntry.key)).thenReturn(true); NotificationData.Entry childEntry = createChildNotification(); - // Summary will be suppressed because there is only one child + // Summary will be suppressed because there is only one child. mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); - // A suppressed summary should transfer its heads up state to the child + // A suppressed summary should transfer its heads up state to the child. verify(mHeadsUpManager, never()).showNotification(summaryEntry); verify(mHeadsUpManager).showNotification(childEntry); } @@ -175,24 +201,64 @@ public class NotificationGroupManagerTest extends SysuiTestCase { mGroupManager.setHeadsUpManager(mHeadsUpManager); NotificationData.Entry summaryEntry = createSummaryNotification(Notification.GROUP_ALERT_SUMMARY); - when(summaryEntry.row.isHeadsUp()).thenReturn(true); NotificationData.Entry childEntry = createChildNotification(Notification.GROUP_ALERT_SUMMARY); NotificationData.Entry childEntry2 = createChildNotification(Notification.GROUP_ALERT_SUMMARY); + mHeadsUpManager.showNotification(summaryEntry); // Trigger a transfer of heads up state from summary to child. mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); - when(summaryEntry.row.isHeadsUp()).thenReturn(false); - when(childEntry.row.isHeadsUp()).thenReturn(true); // Add second child notification so that summary is no longer suppressed. mGroupManager.onEntryAdded(childEntry2); // The heads up state should transfer back to the summary as there is now more than one // child and the summary should no longer be suppressed. - assertTrue(mHeadsUpManager.contains(summaryEntry.key)); - assertFalse(mHeadsUpManager.contains(childEntry.key)); + assertTrue(mHeadsUpManager.isAlerting(summaryEntry.key)); + assertFalse(mHeadsUpManager.isAlerting(childEntry.key)); + } + + @Test + public void testSuppressedSummaryAmbientPulseTransfersToChild() { + mGroupManager.setDozing(true); + NotificationData.Entry summaryEntry = createSummaryNotification(); + when(mAmbientPulseManager.isAlerting(summaryEntry.key)).thenReturn(true); + NotificationData.Entry childEntry = createChildNotification(); + + // Summary will be suppressed because there is only one child. + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + + // A suppressed summary should transfer its ambient state to the child. + verify(mAmbientPulseManager, never()).showNotification(summaryEntry); + verify(mAmbientPulseManager).showNotification(childEntry); + } + + @Test + public void testSuppressedSummaryAmbientPulseTransfersToChildButBackAgain() { + mGroupManager.setDozing(true); + mAmbientPulseManager = new AmbientPulseManager(mContext); + mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager); + initializeGroupManager(); + NotificationData.Entry summaryEntry = + createSummaryNotification(Notification.GROUP_ALERT_SUMMARY); + NotificationData.Entry childEntry = + createChildNotification(Notification.GROUP_ALERT_SUMMARY); + NotificationData.Entry childEntry2 = + createChildNotification(Notification.GROUP_ALERT_SUMMARY); + mAmbientPulseManager.showNotification(summaryEntry); + // Trigger a transfer of ambient state from summary to child. + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + + // Add second child notification so that summary is no longer suppressed. + mGroupManager.onEntryAdded(childEntry2); + + // The ambient state should transfer back to the summary as there is now more than one + // child and the summary should no longer be suppressed. + assertTrue(mAmbientPulseManager.isAlerting(summaryEntry.key)); + assertFalse(mAmbientPulseManager.isAlerting(childEntry.key)); } private NotificationData.Entry createSummaryNotification() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index cbba251c8895..5006b0b29b0c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -358,7 +358,7 @@ public class StatusBarTest extends SysuiTestCase { } @Test - public void testShouldPeek_nonSuppressedGroupSummary() { + public void testShouldHeadsUp_nonSuppressedGroupSummary() { when(mPowerManager.isScreenOn()).thenReturn(true); when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false); @@ -375,11 +375,11 @@ public class StatusBarTest extends SysuiTestCase { UserHandle.of(0), null, 0); NotificationData.Entry entry = new NotificationData.Entry(sbn); - assertTrue(mEntryManager.shouldPeek(entry, sbn)); + assertTrue(mEntryManager.shouldHeadsUp(entry)); } @Test - public void testShouldPeek_suppressedGroupSummary() { + public void testShouldHeadsUp_suppressedGroupSummary() { when(mPowerManager.isScreenOn()).thenReturn(true); when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationData.shouldSuppressStatusBar(any())).thenReturn(false); @@ -396,11 +396,11 @@ public class StatusBarTest extends SysuiTestCase { UserHandle.of(0), null, 0); NotificationData.Entry entry = new NotificationData.Entry(sbn); - assertFalse(mEntryManager.shouldPeek(entry, sbn)); + assertFalse(mEntryManager.shouldHeadsUp(entry)); } @Test - public void testShouldPeek_suppressedPeek() { + public void testShouldHeadsUp_suppressedHeadsUp() { when(mPowerManager.isScreenOn()).thenReturn(true); when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationData.shouldFilterOut(any())).thenReturn(false); @@ -414,11 +414,11 @@ public class StatusBarTest extends SysuiTestCase { UserHandle.of(0), null, 0); NotificationData.Entry entry = new NotificationData.Entry(sbn); - assertFalse(mEntryManager.shouldPeek(entry, sbn)); + assertFalse(mEntryManager.shouldHeadsUp(entry)); } @Test - public void testShouldPeek_noSuppressedPeek() { + public void testShouldHeadsUp_noSuppressedHeadsUp() { when(mPowerManager.isScreenOn()).thenReturn(true); when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false); when(mNotificationData.shouldFilterOut(any())).thenReturn(false); @@ -432,31 +432,31 @@ public class StatusBarTest extends SysuiTestCase { UserHandle.of(0), null, 0); NotificationData.Entry entry = new NotificationData.Entry(sbn); - assertTrue(mEntryManager.shouldPeek(entry, sbn)); + assertTrue(mEntryManager.shouldHeadsUp(entry)); } @Test - public void testPeek_disabledStatusBar() { + public void testHeadsUp_disabledStatusBar() { Notification n = new Notification.Builder(getContext(), "a").build(); StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, UserHandle.of(0), null, 0); NotificationData.Entry entry = new NotificationData.Entry(sbn); mStatusBar.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); - assertFalse("The panel shouldn't allow peek while disabled", - mStatusBar.shouldPeek(entry, sbn)); + assertFalse("The panel shouldn't allow heads up while disabled", + mStatusBar.canHeadsUp(entry, sbn)); } @Test - public void testPeek_disabledNotificationShade() { + public void testHeadsUp_disabledNotificationShade() { Notification n = new Notification.Builder(getContext(), "a").build(); StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, UserHandle.of(0), null, 0); NotificationData.Entry entry = new NotificationData.Entry(sbn); mStatusBar.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); - assertFalse("The panel shouldn't allow peek while notitifcation shade disabled", - mStatusBar.shouldPeek(entry, sbn)); + assertFalse("The panel shouldn't allow heads up while notification shade disabled", + mStatusBar.canHeadsUp(entry, sbn)); } @Test @@ -472,7 +472,7 @@ public class StatusBarTest extends SysuiTestCase { } @Test - public void testPanelOpenForPeek() { + public void testPanelOpenForHeadsUp() { when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList); when(mNotificationList.size()).thenReturn(5); diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java index ee01d8633ae7..4f0e17055769 100644 --- a/services/core/java/com/android/server/LooperStatsService.java +++ b/services/core/java/com/android/server/LooperStatsService.java @@ -93,10 +93,12 @@ public class LooperStatsService extends Binder { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; List<LooperStats.ExportedEntry> entries = mStats.getEntries(); entries.sort(Comparator - .comparing((LooperStats.ExportedEntry entry) -> entry.threadName) + .comparing((LooperStats.ExportedEntry entry) -> entry.workSourceUid) + .thenComparing(entry -> entry.threadName) .thenComparing(entry -> entry.handlerClassName) .thenComparing(entry -> entry.messageName)); String header = String.join(",", Arrays.asList( + "work_source_uid", "thread_name", "handler_class", "message_name", @@ -110,11 +112,11 @@ public class LooperStatsService extends Binder { "exception_count")); pw.println(header); for (LooperStats.ExportedEntry entry : entries) { - pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.threadName, - entry.handlerClassName, entry.messageName, entry.isInteractive, - entry.messageCount, entry.recordedMessageCount, entry.totalLatencyMicros, - entry.maxLatencyMicros, entry.cpuUsageMicros, entry.maxCpuUsageMicros, - entry.exceptionCount); + pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.workSourceUid, + entry.threadName, entry.handlerClassName, entry.messageName, + entry.isInteractive, entry.messageCount, entry.recordedMessageCount, + entry.totalLatencyMicros, entry.maxLatencyMicros, entry.cpuUsageMicros, + entry.maxCpuUsageMicros, entry.exceptionCount); } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index e5aa3f41857c..510d333355a3 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2490,7 +2490,7 @@ public final class ActiveServices { PackageManager.NOTIFY_PACKAGE_USE_SERVICE); app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE); app.thread.scheduleCreateService(r, r.serviceInfo, - mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), + mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo), app.getReportedProcState()); r.postNotification(); created = true; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b898a90dbb55..f7fe9e2455f1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -665,11 +665,6 @@ public class ActivityManagerService extends IActivityManager.Stub int mNextIsolatedProcessUid = 0; /** - * The currently running heavy-weight process, if any. - */ - ProcessRecord mHeavyWeightProcess = null; - - /** * Non-persistent appId whitelist for background restrictions */ int[] mBackgroundAppIdWhitelist = new int[] { @@ -826,12 +821,6 @@ public class ActivityManagerService extends IActivityManager.Stub final SparseArray<UidRecord> mValidateUids = new SparseArray<>(); /** - * Packages that the user has asked to have run in screen size - * compatibility mode instead of filling the screen. - */ - final CompatModePackages mCompatModePackages; - - /** * Set of IntentSenderRecord objects that are currently active. */ final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords @@ -1097,11 +1086,10 @@ public class ActivityManagerService extends IActivityManager.Stub volatile boolean mSystemReady = false; volatile boolean mOnBattery = false; volatile int mFactoryTest; + volatile boolean mBooting = false; - @GuardedBy("this") boolean mBooting = false; @GuardedBy("this") boolean mCallFinishBooting = false; @GuardedBy("this") boolean mBootAnimationComplete = false; - @GuardedBy("this") boolean mLaunchWarningShown = false; private @GuardedBy("this") boolean mCheckedForSetup = false; final Context mContext; @@ -1388,10 +1376,8 @@ public class ActivityManagerService extends IActivityManager.Stub long mLastWriteTime = 0; - /** - * Set to true after the system has finished booting. - */ - boolean mBooted = false; + /** Set to true after the system has finished booting. */ + volatile boolean mBooted = false; /** * Current boot phase. @@ -1441,8 +1427,6 @@ public class ActivityManagerService extends IActivityManager.Stub static final int PROC_START_TIMEOUT_MSG = 20; static final int KILL_APPLICATION_MSG = 22; static final int FINALIZE_PENDING_INTENT_MSG = 23; - static final int POST_HEAVY_NOTIFICATION_MSG = 24; - static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25; static final int SHOW_STRICT_MODE_VIOLATION_UI_MSG = 26; static final int CHECK_EXCESSIVE_POWER_USE_MSG = 27; static final int CLEAR_DNS_CACHE_MSG = 28; @@ -1451,7 +1435,6 @@ public class ActivityManagerService extends IActivityManager.Stub static final int DISPATCH_PROCESS_DIED_UI_MSG = 32; static final int REPORT_MEM_USAGE_MSG = 33; static final int UPDATE_TIME_PREFERENCE_MSG = 41; - static final int FINISH_BOOTING_MSG = 45; static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47; static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49; static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50; @@ -1758,65 +1741,6 @@ public class ActivityManagerService extends IActivityManager.Stub case FINALIZE_PENDING_INTENT_MSG: { ((PendingIntentRecord)msg.obj).completeFinalize(); } break; - case POST_HEAVY_NOTIFICATION_MSG: { - INotificationManager inm = NotificationManager.getService(); - if (inm == null) { - return; - } - - ActivityRecord root = (ActivityRecord)msg.obj; - final WindowProcessController process = root.app; - if (process == null) { - return; - } - - try { - Context context = mContext.createPackageContext(process.mInfo.packageName, 0); - String text = mContext.getString(R.string.heavy_weight_notification, - context.getApplicationInfo().loadLabel(context.getPackageManager())); - Notification notification = - new Notification.Builder(context, - SystemNotificationChannels.HEAVY_WEIGHT_APP) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) - .setWhen(0) - .setOngoing(true) - .setTicker(text) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)) - .setContentTitle(text) - .setContentText( - mContext.getText(R.string.heavy_weight_notification_detail)) - .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, - root.intent, PendingIntent.FLAG_CANCEL_CURRENT, null, - new UserHandle(root.userId))) - .build(); - try { - inm.enqueueNotificationWithTag("android", "android", null, - SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, - notification, root.userId); - } catch (RuntimeException e) { - Slog.w(ActivityManagerService.TAG, - "Error showing notification for heavy-weight app", e); - } catch (RemoteException e) { - } - } catch (NameNotFoundException e) { - Slog.w(TAG, "Unable to create context for heavy notification", e); - } - } break; - case CANCEL_HEAVY_NOTIFICATION_MSG: { - INotificationManager inm = NotificationManager.getService(); - if (inm == null) { - return; - } - try { - inm.cancelNotificationWithTag("android", null, - SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, msg.arg1); - } catch (RuntimeException e) { - Slog.w(ActivityManagerService.TAG, - "Error canceling notification for service", e); - } catch (RemoteException e) { - } - } break; case CHECK_EXCESSIVE_POWER_USE_MSG: { synchronized (ActivityManagerService.this) { checkExcessivePowerUsageLocked(); @@ -1853,17 +1777,6 @@ public class ActivityManagerService extends IActivityManager.Stub } break; } - case FINISH_BOOTING_MSG: { - if (msg.arg1 != 0) { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting"); - finishBooting(); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - } - if (msg.arg2 != 0) { - mAtmInternal.enableScreenAfterBoot(mBooted); - } - break; - } case SEND_LOCALE_TO_MOUNT_DAEMON_MSG: { try { Locale l = (Locale) msg.obj; @@ -2430,7 +2343,6 @@ public class ActivityManagerService extends IActivityManager.Stub mAppErrors = null; mAppOpsService = mInjector.getAppOpsService(null, null); mBatteryStatsService = null; - mCompatModePackages = null; mConstants = null; mHandler = null; mHandlerThread = null; @@ -2519,7 +2431,6 @@ public class ActivityManagerService extends IActivityManager.Stub } mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations")); - mCompatModePackages = new CompatModePackages(this, systemDir, mHandler); mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler); mActivityTaskManager = atm; @@ -3791,31 +3702,31 @@ public class ActivityManagerService extends IActivityManager.Stub return true; } - void updateUsageStats(ActivityRecord component, boolean resumed) { + void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) { if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, - "updateUsageStats: comp=" + component + "res=" + resumed); + "updateUsageStats: comp=" + activity + "res=" + resumed); final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED, - component.app.mUid, component.realActivity.getPackageName(), - component.realActivity.getShortClassName(), resumed ? + uid, activity.getPackageName(), + activity.getShortClassName(), resumed ? StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND : StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND); if (resumed) { if (mUsageStatsService != null) { - mUsageStatsService.reportEvent(component.realActivity, component.userId, + mUsageStatsService.reportEvent(activity, userId, UsageEvents.Event.MOVE_TO_FOREGROUND); } synchronized (stats) { - stats.noteActivityResumedLocked(component.app.mUid); + stats.noteActivityResumedLocked(uid); } } else { if (mUsageStatsService != null) { - mUsageStatsService.reportEvent(component.realActivity, component.userId, + mUsageStatsService.reportEvent(activity, userId, UsageEvents.Event.MOVE_TO_BACKGROUND); } synchronized (stats) { - stats.noteActivityPausedLocked(component.app.mUid); + stats.noteActivityPausedLocked(uid); } } } @@ -3895,8 +3806,8 @@ public class ActivityManagerService extends IActivityManager.Stub mCheckedForSetup = checked; } - CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { - return mCompatModePackages.compatibilityInfoForPackageLocked(ai); + CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) { + return mAtmInternal.compatibilityInfoForPackage(ai); } private void enforceNotIsolatedCaller(String caller) { @@ -3906,37 +3817,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public int getPackageScreenCompatMode(String packageName) { - enforceNotIsolatedCaller("getPackageScreenCompatMode"); - synchronized (this) { - return mCompatModePackages.getPackageScreenCompatModeLocked(packageName); - } - } - - @Override public void setPackageScreenCompatMode(String packageName, int mode) { - enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY, - "setPackageScreenCompatMode"); - synchronized (this) { - mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode); - } - } - - @Override - public boolean getPackageAskScreenCompat(String packageName) { - enforceNotIsolatedCaller("getPackageAskScreenCompat"); - synchronized (this) { - return mCompatModePackages.getPackageAskCompatModeLocked(packageName); - } - } - - @Override - public void setPackageAskScreenCompat(String packageName, boolean ask) { - enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY, - "setPackageAskScreenCompat"); - synchronized (this) { - mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask); - } + mActivityTaskManager.setPackageScreenCompatMode(packageName, mode); } private boolean hasUsageStatsPermission(String callingPackage) { @@ -4307,19 +4189,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, msg); throw new SecurityException(msg); } - - synchronized(this) { - final ProcessRecord proc = mHeavyWeightProcess; - if (proc == null) { - return; - } - - proc.getWindowProcessController().finishActivities(); - - mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG, - proc.userId, 0)); - mHeavyWeightProcess = null; - } + mAtmInternal.finishHeavyWeightApp(); } @Override @@ -5789,11 +5659,8 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } removeProcessNameLocked(name, uid); - if (mHeavyWeightProcess == app) { - mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG, - mHeavyWeightProcess.userId, 0)); - mHeavyWeightProcess = null; - } + mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController()); + boolean needRestart = false; if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) { int pid = app.pid; @@ -5851,11 +5718,7 @@ public class ActivityManagerService extends IActivityManager.Stub EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId, pid, app.uid, app.processName); removeProcessNameLocked(app.processName, app.uid); - if (mHeavyWeightProcess == app) { - mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG, - mHeavyWeightProcess.userId, 0)); - mHeavyWeightProcess = null; - } + mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController()); mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid); // Take care of any launching providers waiting for this process. cleanupAppInLaunchingProvidersLocked(app, true); @@ -6034,7 +5897,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc " + processName + " with config " + getGlobalConfiguration()); ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info; - app.compat = compatibilityInfoForPackageLocked(appInfo); + app.compat = compatibilityInfoForPackage(appInfo); ProfilerInfo profilerInfo = null; String preBindAgent = null; @@ -6231,7 +6094,7 @@ public class ActivityManagerService extends IActivityManager.Stub PackageManager.NOTIFY_PACKAGE_USE_BACKUP); try { thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, - compatibilityInfoForPackageLocked(mBackupTarget.appInfo), + compatibilityInfoForPackage(mBackupTarget.appInfo), mBackupTarget.backupMode); } catch (Exception e) { Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e); @@ -6264,11 +6127,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - void postFinishBooting(boolean finishBooting, boolean enableScreen) { - mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG, - finishBooting ? 1 : 0, enableScreen ? 1 : 0)); - } - @Override public void showBootMessage(final CharSequence msg, final boolean always) { if (Binder.getCallingUid() != myUid()) { @@ -6278,6 +6136,8 @@ public class ActivityManagerService extends IActivityManager.Stub } final void finishBooting() { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting"); + synchronized (this) { if (!mBootAnimationComplete) { mCallFinishBooting = true; @@ -6383,6 +6243,8 @@ public class ActivityManagerService extends IActivityManager.Stub }); mUserController.scheduleStartProfiles(); } + + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } @Override @@ -6393,9 +6255,7 @@ public class ActivityManagerService extends IActivityManager.Stub mBootAnimationComplete = true; } if (callFinishBooting) { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting"); finishBooting(); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } } @@ -6410,9 +6270,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (booting) { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting"); finishBooting(); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } if (enableScreen) { @@ -10852,7 +10710,7 @@ public class ActivityManagerService extends IActivityManager.Stub int clientTargetSdk) { outInfo.pid = app.pid; outInfo.uid = app.info.uid; - if (mHeavyWeightProcess == app) { + if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) { outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE; } if (app.isPersistent()) { @@ -11895,13 +11753,13 @@ public class ActivityManagerService extends IActivityManager.Stub TimeUtils.formatDuration(mActivityTaskManager.mPreviousProcessVisibleTime, sb); pw.println(sb); } - if (mHeavyWeightProcess != null && (dumpPackage == null - || mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) { + if (mActivityTaskManager.mHeavyWeightProcess != null && (dumpPackage == null + || mActivityTaskManager.mHeavyWeightProcess.mPkgList.contains(dumpPackage))) { if (needSep) { pw.println(); needSep = false; } - pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess); + pw.println(" mHeavyWeightProcess: " + mActivityTaskManager.mHeavyWeightProcess); } if (dumpAll && mPendingStarts.size() > 0) { if (needSep) pw.println(); @@ -11920,10 +11778,10 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" mConfigWillChange: " + mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange); } - if (mCompatModePackages.getPackages().size() > 0) { + if (mActivityTaskManager.mCompatModePackages.getPackages().size() > 0) { boolean printed = false; for (Map.Entry<String, Integer> entry - : mCompatModePackages.getPackages().entrySet()) { + : mActivityTaskManager.mCompatModePackages.getPackages().entrySet()) { String pkg = entry.getKey(); int mode = entry.getValue(); if (dumpPackage != null && !dumpPackage.equals(pkg)) { @@ -12305,12 +12163,13 @@ public class ActivityManagerService extends IActivityManager.Stub proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mActivityTaskManager.mPreviousProcessVisibleTime); } - if (mHeavyWeightProcess != null && (dumpPackage == null - || mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) { - mHeavyWeightProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC); + if (mActivityTaskManager.mHeavyWeightProcess != null && (dumpPackage == null + || mActivityTaskManager.mHeavyWeightProcess.mPkgList.contains(dumpPackage))) { + ((ProcessRecord) mActivityTaskManager.mHeavyWeightProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC); } - for (Map.Entry<String, Integer> entry : mCompatModePackages.getPackages().entrySet()) { + for (Map.Entry<String, Integer> entry + : mActivityTaskManager.mCompatModePackages.getPackages().entrySet()) { String pkg = entry.getKey(); int mode = entry.getValue(); if (dumpPackage == null || dumpPackage.equals(pkg)) { @@ -12563,8 +12422,8 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(); pw.println(" mHomeProcess: " + mActivityTaskManager.mHomeProcess); pw.println(" mPreviousProcess: " + mActivityTaskManager.mPreviousProcess); - if (mHeavyWeightProcess != null) { - pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess); + if (mActivityTaskManager.mHeavyWeightProcess != null) { + pw.println(" mHeavyWeightProcess: " + mActivityTaskManager.mHeavyWeightProcess); } return true; @@ -15196,11 +15055,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (!replacingPid) { removeProcessNameLocked(app.processName, app.uid, app); } - if (mHeavyWeightProcess == app) { - mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG, - mHeavyWeightProcess.userId, 0)); - mHeavyWeightProcess = null; - } + mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController()); } else if (!app.removed) { // This app is persistent, so we need to keep its record around. // If it is not already on the pending app list, add it there @@ -15584,7 +15439,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc); try { proc.thread.scheduleCreateBackupAgent(app, - compatibilityInfoForPackageLocked(app), backupMode); + compatibilityInfoForPackage(app), backupMode); } catch (RemoteException e) { // Will time out on the backup manager side } @@ -15683,7 +15538,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (proc.thread != null) { try { proc.thread.scheduleDestroyBackupAgent(appInfo, - compatibilityInfoForPackageLocked(appInfo)); + compatibilityInfoForPackage(appInfo)); } catch (Exception e) { Slog.e(TAG, "Exception when unbinding backup agent:"); e.printStackTrace(); @@ -16392,7 +16247,6 @@ public class ActivityManagerService extends IActivityManager.Stub mServices.forceStopPackageLocked(ssp, userId); mAtmInternal.onPackageUninstalled(ssp); - mCompatModePackages.handlePackageUninstalledLocked(ssp); mBatteryStatsService.notePackageUninstalled(ssp); } } else { @@ -16454,7 +16308,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); - mCompatModePackages.handlePackageAddedLocked(ssp, replacing); + mAtmInternal.onPackageAdded(ssp, replacing); try { ApplicationInfo ai = AppGlobals.getPackageManager(). @@ -16471,7 +16325,6 @@ public class ActivityManagerService extends IActivityManager.Stub Uri data = intent.getData(); String ssp; if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { - mCompatModePackages.handlePackageDataClearedLocked(ssp); mAtmInternal.onPackageDataCleared(ssp); } break; @@ -17816,7 +17669,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - if (app == mHeavyWeightProcess) { + if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) { if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) { // We don't want to kill the current heavy-weight process. adj = ProcessList.HEAVY_WEIGHT_APP_ADJ; @@ -21097,18 +20950,6 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityManagerService.this.trimApplications(); } - public int getPackageScreenCompatMode(ApplicationInfo ai) { - synchronized (ActivityManagerService.this) { - return mCompatModePackages.computeCompatModeLocked(ai); - } - } - - public void setPackageScreenCompatMode(ApplicationInfo ai, int mode) { - synchronized (ActivityManagerService.this) { - mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode); - } - } - public void closeSystemDialogs(String reason) { ActivityManagerService.this.closeSystemDialogs(reason); } @@ -21157,6 +20998,38 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public void updateCpuStats() { + synchronized (ActivityManagerService.this) { + ActivityManagerService.this.updateCpuStats(); + } + } + + @Override + public void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) { + synchronized (ActivityManagerService.this) { + ActivityManagerService.this.updateUsageStats(activity, uid, userId, resumed); + } + } + + @Override + public void updateForegroundTimeIfOnBattery( + String packageName, int uid, long cpuTimeDiff) { + synchronized (ActivityManagerService.this) { + if (!mBatteryStatsService.isOnBattery()) { + return; + } + final BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics(); + synchronized (bsi) { + final BatteryStatsImpl.Uid.Proc ps = + bsi.getProcessStatsLocked(uid, packageName); + if (ps != null) { + ps.addForegroundTimeLocked(cpuTimeDiff); + } + } + } + } + + @Override public void sendForegroundProfileChanged(int userId) { mUserController.sendForegroundProfileChanged(userId); } @@ -21216,6 +21089,31 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManagerService.this.getTaskForActivity(token, onlyRoot); } } + + @Override + public void setBooting(boolean booting) { + mBooting = booting; + } + + @Override + public boolean isBooting() { + return mBooting; + } + + @Override + public void setBooted(boolean booted) { + mBooted = booted; + } + + @Override + public boolean isBooted() { + return mBooted; + } + + @Override + public void finishBooting() { + ActivityManagerService.this.finishBooting(); + } } /** diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 2199bb71a5f5..77cfb124ec80 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -2326,7 +2326,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } final CompatibilityInfo compatInfo = - service.mAm.compatibilityInfoForPackageLocked(info.applicationInfo); + service.compatibilityInfoForPackageLocked(info.applicationInfo); final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(), diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 91e677b2b265..35a1eb8ff616 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -107,6 +107,7 @@ import static java.lang.Integer.MAX_VALUE; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityController; @@ -151,6 +152,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService.ItemMatcher; import com.android.server.wm.ConfigurationContainer; @@ -1514,14 +1516,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode()); - mService.mAm.updateCpuStats(); + mService.updateCpuStats(); if (prev.attachedToProcess()) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev); try { EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev), prev.shortComponentName, "userLeaving=" + userLeaving); - mService.mAm.updateUsageStats(prev, false); + mService.updateUsageStats(prev, false); mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(), prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, @@ -1683,20 +1685,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (prev != null) { prev.resumeKeyDispatchingLocked(); - if (prev.hasProcess() && prev.cpuTimeAtResume > 0 - && mService.mAm.mBatteryStatsService.isOnBattery()) { - long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume; - if (diff > 0) { - BatteryStatsImpl bsi = mService.mAm.mBatteryStatsService.getActiveStatistics(); - synchronized (bsi) { - BatteryStatsImpl.Uid.Proc ps = - bsi.getProcessStatsLocked(prev.info.applicationInfo.uid, - prev.info.packageName); - if (ps != null) { - ps.addForegroundTimeLocked(diff); - } - } - } + final long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume; + if (prev.hasProcess() && prev.cpuTimeAtResume > 0 && diff > 0) { + final Runnable r = PooledLambda.obtainRunnable( + ActivityManagerInternal::updateForegroundTimeIfOnBattery, + mService.mAmInternal, prev.info.packageName, prev.info.applicationInfo.uid, + diff); + mService.mH.post(r); } prev.cpuTimeAtResume = 0; // reset it } @@ -1713,7 +1708,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS); } - void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) { + private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) { if (!mStackSupervisor.mStoppingActivities.contains(r)) { mStackSupervisor.mStoppingActivities.add(r); @@ -2407,7 +2402,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai @GuardedBy("mService") private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { - if (!mService.mAm.mBooting && !mService.mAm.mBooted) { + if (!mService.isBooting() && !mService.isBooted()) { // Not ready yet! return false; } @@ -2691,7 +2686,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai lastStack == null ? null :lastStack.mResumedActivity; final ActivityState lastState = next.getState(); - mService.mAm.updateCpuStats(); + mService.updateCpuStats(); if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next + " (in existing)"); @@ -4166,7 +4161,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mWindowManager.notifyAppRelaunchesCleared(r.appToken); } - void removeTimeoutsForActivityLocked(ActivityRecord r) { + private void removeTimeoutsForActivityLocked(ActivityRecord r) { mStackSupervisor.removeTimeoutsForActivityLocked(r); mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); mHandler.removeMessages(STOP_TIMEOUT_MSG, r); @@ -4368,12 +4363,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (hadApp) { if (removeFromApp) { r.app.removeActivity(r); - if (mService.mAm.mHeavyWeightProcess != null - && mService.mAm.mHeavyWeightProcess.getWindowProcessController() == r.app - && !r.app.hasActivities()) { - mService.mAm.mHeavyWeightProcess = null; - mService.mAm.mHandler.sendEmptyMessage( - ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG); + if (!r.app.hasActivities()) { + mService.clearHeavyWeightProcessIfEquals(r.app); } if (!r.app.hasActivities()) { // Update any services we are bound to that might care about whether @@ -4564,7 +4555,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai r.getTask().taskId, r.shortComponentName, "proc died without state saved"); if (r.getState() == RESUMED) { - mService.mAm.updateUsageStats(r, false); + mService.updateUsageStats(r, false); } } } else { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 8649e83cd932..c887370024ee 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -686,7 +686,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); final Display[] displays = mDisplayManager.getDisplays(); - for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) { + for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) { final Display display = displays[displayNdx]; final ActivityDisplay activityDisplay = new ActivityDisplay(this, display); if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) { @@ -800,7 +800,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } final ActivityRecord r = topRunningActivityLocked(); - if (mService.mAm.mBooting || !mService.mAm.mBooted) { + if (mService.isBooting() || !mService.isBooted()) { if (r != null && r.idle) { checkFinishBootingLocked(); } @@ -832,7 +832,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } boolean resumeHomeStackTask(ActivityRecord prev, String reason) { - if (!mService.mAm.mBooting && !mService.mAm.mBooted) { + if (!mService.isBooting() && !mService.isBooted()) { // Not ready yet! return false; } @@ -1530,7 +1530,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D r.sleeping = false; r.forceNewConfig = false; mService.getAppWarningsLocked().onStartActivity(r); - r.compat = mService.mAm.compatibilityInfoForPackageLocked(r.info.applicationInfo); + r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo); ProfilerInfo profilerInfo = null; if (mService.mAm.mProfileApp != null && mService.mAm.mProfileApp.equals(app.processName)) { if (mService.mAm.mProfileProc == null || mService.mAm.mProfileProc == app) { @@ -1591,22 +1591,17 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 && mService.mAm.mHasHeavyWeightFeature) { - // This may be a heavy-weight process! Note that the package - // manager will ensure that only activity can run in the main - // process of the .apk, which is the only thing that will be - // considered heavy-weight. + // This may be a heavy-weight process! Note that the package manager will ensure + // that only activity can run in the main process of the .apk, which is the only + // thing that will be considered heavy-weight. if (app.processName.equals(app.info.packageName)) { - if (mService.mAm.mHeavyWeightProcess != null - && mService.mAm.mHeavyWeightProcess != app) { - Slog.w(TAG, "Starting new heavy weight process " + app + if (mService.mHeavyWeightProcess != null + && mService.mHeavyWeightProcess != proc) { + Slog.w(TAG, "Starting new heavy weight process " + proc + " when already running " - + mService.mAm.mHeavyWeightProcess); + + mService.mHeavyWeightProcess); } - mService.mAm.mHeavyWeightProcess = app; - Message msg = mService.mAm.mHandler.obtainMessage( - ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG); - msg.obj = r; - mService.mAm.mHandler.sendMessage(msg); + mService.setHeavyWeightProcess(r); } } @@ -2053,15 +2048,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ @GuardedBy("mService") private boolean checkFinishBootingLocked() { - final boolean booting = mService.mAm.mBooting; + final boolean booting = mService.isBooting(); boolean enableScreen = false; - mService.mAm.mBooting = false; - if (!mService.mAm.mBooted) { - mService.mAm.mBooted = true; + mService.setBooting(false); + if (!mService.isBooted()) { + mService.setBooted(true); enableScreen = true; } if (booting || enableScreen) { - mService.mAm.postFinishBooting(booting, enableScreen); + mService.postFinishBooting(booting, enableScreen); } return booting; } @@ -3682,7 +3677,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final ActivityStack stack = r.getStack(); if (isTopDisplayFocusedStack(stack)) { - mService.mAm.updateUsageStats(r, true); + mService.updateUsageStats(r, true); } if (allResumedActivitiesComplete()) { ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 7df255de9ddb..7da0519ef2f0 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1079,9 +1079,9 @@ class ActivityStarter { // This may be a heavy-weight process! Check to see if we already // have another, different heavy-weight process running. if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) { - final ProcessRecord heavy = mService.mAm.mHeavyWeightProcess; - if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid - || !heavy.processName.equals(aInfo.processName))) { + final WindowProcessController heavy = mService.mHeavyWeightProcess; + if (heavy != null && (heavy.mInfo.uid != aInfo.applicationInfo.uid + || !heavy.mName.equals(aInfo.processName))) { int appCallingUid = callingUid; if (caller != null) { ProcessRecord callerApp = mService.mAm.getRecordForAppLocked(caller); @@ -1109,8 +1109,7 @@ class ActivityStarter { } newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT, new IntentSender(target)); - heavy.getWindowProcessController().updateIntentForHeavyWeightActivity( - newIntent); + heavy.updateIntentForHeavyWeightActivity(newIntent); newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP, aInfo.packageName); newIntent.setFlags(intent.getFlags()); diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java index 6935703dbd04..9acb04be525b 100644 --- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java @@ -125,7 +125,11 @@ import android.app.IActivityController; import android.app.IActivityTaskManager; import android.app.IApplicationThread; import android.app.IAssistDataReceiver; +import android.app.INotificationManager; import android.app.ITaskStackListener; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.app.PictureInPictureParams; import android.app.ProfilerInfo; import android.app.RemoteAction; @@ -150,6 +154,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; +import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; @@ -208,10 +213,13 @@ import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ProcessMap; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.KeyguardDismissCallback; import com.android.internal.util.Preconditions; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.AppOpsService; import com.android.server.AttributeCache; import com.android.server.LocalServices; @@ -266,6 +274,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ActivityManagerInternal mAmInternal; UriGrantsManagerInternal mUgmInternal; private PackageManagerInternal mPmInternal; + private ActivityTaskManagerInternal mInternal; /* Global service lock used by the package the owns this service. */ Object mGlobalLock; ActivityStackSupervisor mStackSupervisor; @@ -278,6 +287,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final SparseArray<WindowProcessController> mPidMap = new SparseArray<>(); /** This is the process holding what we currently consider to be the "home" activity. */ WindowProcessController mHomeProcess; + /** The currently running heavy-weight process, if any. */ + WindowProcessController mHeavyWeightProcess = null; /** * This is the process holding the activity the user last visited that is in a different process * from the one they are currently in. @@ -475,6 +486,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private AppWarnings mAppWarnings; + /** + * Packages that the user has asked to have run in screen size + * compatibility mode instead of filling the screen. + */ + CompatModePackages mCompatModePackages; + private FontScaleSettingObserver mFontScaleSettingObserver; private final class FontScaleSettingObserver extends ContentObserver { @@ -607,8 +624,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mGlobalLock = mAm; mH = new H(mAm.mHandlerThread.getLooper()); mUiHandler = new UiHandler(); - mAppWarnings = new AppWarnings( - this, mUiContext, mH, mUiHandler, SystemServiceManager.ensureSystemDir()); + final File systemDir = SystemServiceManager.ensureSystemDir(); + mAppWarnings = new AppWarnings(this, mUiContext, mH, mUiHandler, systemDir); + mCompatModePackages = new CompatModePackages(this, systemDir, mH); mTempConfig.setToDefaults(); mTempConfig.setLocales(LocaleList.getDefault()); @@ -687,7 +705,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } private void start() { - LocalServices.addService(ActivityTaskManagerInternal.class, new LocalService()); + mInternal = new LocalService(); + LocalServices.addService(ActivityTaskManagerInternal.class, mInternal); } public static final class Lifecycle extends SystemService { @@ -1461,16 +1480,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public int getFrontActivityScreenCompatMode() { enforceNotIsolatedCaller("getFrontActivityScreenCompatMode"); - ApplicationInfo ai; synchronized (mGlobalLock) { final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked(); if (r == null) { return ActivityManager.COMPAT_MODE_UNKNOWN; } - ai = r.info.applicationInfo; + return mCompatModePackages.computeCompatModeLocked(r.info.applicationInfo); } - - return mAmInternal.getPackageScreenCompatMode(ai); } @Override @@ -1485,9 +1501,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return; } ai = r.info.applicationInfo; + mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode); } - - mAmInternal.setPackageScreenCompatMode(ai, mode); } @Override @@ -4112,7 +4127,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mVrController.shouldDisableNonVrUiLocked(); } - void applyUpdateVrModeLocked(ActivityRecord r) { + private void applyUpdateVrModeLocked(ActivityRecord r) { // VR apps are expected to run in a main display. If an app is turning on VR for // itself, but lives in a dynamic stack, then make sure that it is moved to the main // fullscreen stack before enabling VR Mode. @@ -4143,6 +4158,40 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { }); } + @Override + public int getPackageScreenCompatMode(String packageName) { + enforceNotIsolatedCaller("getPackageScreenCompatMode"); + synchronized (mGlobalLock) { + return mCompatModePackages.getPackageScreenCompatModeLocked(packageName); + } + } + + @Override + public void setPackageScreenCompatMode(String packageName, int mode) { + mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY, + "setPackageScreenCompatMode"); + synchronized (mGlobalLock) { + mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode); + } + } + + @Override + public boolean getPackageAskScreenCompat(String packageName) { + enforceNotIsolatedCaller("getPackageAskScreenCompat"); + synchronized (mGlobalLock) { + return mCompatModePackages.getPackageAskCompatModeLocked(packageName); + } + } + + @Override + public void setPackageAskScreenCompat(String packageName, boolean ask) { + mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY, + "setPackageAskScreenCompat"); + synchronized (mGlobalLock) { + mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask); + } + } + ActivityStack getTopDisplayFocusedStack() { return mStackSupervisor.getTopDisplayFocusedStack(); } @@ -4808,6 +4857,122 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mH.post(mAmInternal::updateOomAdj); } + void updateCpuStats() { + mH.post(mAmInternal::updateCpuStats); + } + + void updateUsageStats(ActivityRecord component, boolean resumed) { + final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats, + mAmInternal, component.realActivity, component.app.mUid, component.userId, resumed); + mH.sendMessage(m); + } + + void setBooting(boolean booting) { + mAmInternal.setBooting(booting); + } + + boolean isBooting() { + return mAmInternal.isBooting(); + } + + void setBooted(boolean booted) { + mAmInternal.setBooted(booted); + } + + boolean isBooted() { + return mAmInternal.isBooted(); + } + + void postFinishBooting(boolean finishBooting, boolean enableScreen) { + mH.post(() -> { + if (finishBooting) { + mAmInternal.finishBooting(); + } + if (enableScreen) { + mInternal.enableScreenAfterBoot(isBooted()); + } + }); + } + + void setHeavyWeightProcess(ActivityRecord root) { + mHeavyWeightProcess = root.app; + final Message m = PooledLambda.obtainMessage( + ActivityTaskManagerService::postHeavyWeightProcessNotification, this, + root.app, root.intent, root.userId); + mH.sendMessage(m); + } + + void clearHeavyWeightProcessIfEquals(WindowProcessController proc) { + if (mHeavyWeightProcess == null || mHeavyWeightProcess != proc) { + return; + } + + mHeavyWeightProcess = null; + final Message m = PooledLambda.obtainMessage( + ActivityTaskManagerService::cancelHeavyWeightProcessNotification, this, + proc.mUserId); + mH.sendMessage(m); + } + + private void cancelHeavyWeightProcessNotification(int userId) { + final INotificationManager inm = NotificationManager.getService(); + if (inm == null) { + return; + } + try { + inm.cancelNotificationWithTag("android", null, + SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, userId); + } catch (RuntimeException e) { + Slog.w(TAG, "Error canceling notification for service", e); + } catch (RemoteException e) { + } + + } + + private void postHeavyWeightProcessNotification( + WindowProcessController proc, Intent intent, int userId) { + if (proc == null) { + return; + } + + final INotificationManager inm = NotificationManager.getService(); + if (inm == null) { + return; + } + + try { + Context context = mContext.createPackageContext(proc.mInfo.packageName, 0); + String text = mContext.getString(R.string.heavy_weight_notification, + context.getApplicationInfo().loadLabel(context.getPackageManager())); + Notification notification = + new Notification.Builder(context, + SystemNotificationChannels.HEAVY_WEIGHT_APP) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setWhen(0) + .setOngoing(true) + .setTicker(text) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(text) + .setContentText( + mContext.getText(R.string.heavy_weight_notification_detail)) + .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, + intent, PendingIntent.FLAG_CANCEL_CURRENT, null, + new UserHandle(userId))) + .build(); + try { + inm.enqueueNotificationWithTag("android", "android", null, + SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, notification, userId); + } catch (RuntimeException e) { + Slog.w(TAG, "Error showing notification for heavy-weight app", e); + } catch (RemoteException e) { + } + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Unable to create context for heavy notification", e); + } + + } + // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities private void startTimeTrackingFocusedActivityLocked() { final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity(); @@ -4884,6 +5049,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mH.post(() -> mAmInternal.scheduleAppGcs()); } + CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { + return mCompatModePackages.compatibilityInfoForPackageLocked(ai); + } + /** * Returns the PackageManager. Used by classes hosted by {@link ActivityTaskManagerService}. The * PackageManager could be unavailable at construction time and therefore needs to be accessed @@ -5270,6 +5439,28 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public boolean isHeavyWeightProcess(WindowProcessController proc) { + synchronized (mGlobalLock) { + return proc == mHeavyWeightProcess; + } + } + + @Override + public void clearHeavyWeightProcessIfEquals(WindowProcessController proc) { + synchronized (mGlobalLock) { + ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(proc); + } + } + + @Override + public void finishHeavyWeightApp() { + synchronized (mGlobalLock) { + ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals( + mHeavyWeightProcess); + } + } + + @Override public boolean isSleeping() { synchronized (mGlobalLock) { return isSleepingLocked(); @@ -5378,6 +5569,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void onPackageDataCleared(String name) { synchronized (mGlobalLock) { + mCompatModePackages.handlePackageDataClearedLocked(name); mAppWarnings.onPackageDataCleared(name); } } @@ -5386,7 +5578,23 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void onPackageUninstalled(String name) { synchronized (mGlobalLock) { mAppWarnings.onPackageUninstalled(name); + mCompatModePackages.handlePackageUninstalledLocked(name); + } + } + + @Override + public void onPackageAdded(String name, boolean replacing) { + synchronized (mGlobalLock) { + mCompatModePackages.handlePackageAddedLocked(name, replacing); } } + + @Override + public CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) { + synchronized (mGlobalLock) { + return compatibilityInfoForPackageLocked(ai); + } + } + } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 0a7e127dde22..1387f457d2ed 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -302,7 +302,7 @@ public final class BroadcastQueue { mService.notifyPackageUse(r.intent.getComponent().getPackageName(), PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER); app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, - mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo), + mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId, app.getReportedProcState()); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java index 77efbfc0b387..536f3a93a3f1 100644 --- a/services/core/java/com/android/server/am/CompatModePackages.java +++ b/services/core/java/com/android/server/am/CompatModePackages.java @@ -51,13 +51,13 @@ public final class CompatModePackages { private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_AM; private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; - private final ActivityManagerService mService; + private final ActivityTaskManagerService mService; private final AtomicFile mFile; // Compatibility state: no longer ask user to select the mode. - public static final int COMPAT_FLAG_DONT_ASK = 1<<0; + private static final int COMPAT_FLAG_DONT_ASK = 1<<0; // Compatibility state: compatibility mode is enabled. - public static final int COMPAT_FLAG_ENABLED = 1<<1; + private static final int COMPAT_FLAG_ENABLED = 1<<1; private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>(); @@ -80,7 +80,7 @@ public final class CompatModePackages { } }; - public CompatModePackages(ActivityManagerService service, File systemDir, Handler handler) { + public CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler) { mService = service; mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"), "compat-mode"); mHandler = new CompatHandler(handler.getLooper()); @@ -317,12 +317,12 @@ public final class CompatModePackages { scheduleWrite(); - final ActivityStack stack = mService.mActivityTaskManager.getTopDisplayFocusedStack(); + final ActivityStack stack = mService.getTopDisplayFocusedStack(); ActivityRecord starting = stack.restartPackage(packageName); // Tell all processes that loaded this package about the change. - for (int i=mService.mLruProcesses.size()-1; i>=0; i--) { - ProcessRecord app = mService.mLruProcesses.get(i); + for (int i = mService.mAm.mLruProcesses.size() - 1; i >= 0; i--) { + final ProcessRecord app = mService.mAm.mLruProcesses.get(i); if (!app.pkgList.containsKey(packageName)) { continue; } @@ -346,10 +346,10 @@ public final class CompatModePackages { } } - void saveCompatModes() { + private void saveCompatModes() { HashMap<String, Integer> pkgs; - synchronized (mService) { - pkgs = new HashMap<String, Integer>(mPackages); + synchronized (mService.mGlobalLock) { + pkgs = new HashMap<>(mPackages); } FileOutputStream fos = null; diff --git a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java index d4932f9cf56e..2fdcb5106595 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java @@ -25,6 +25,9 @@ public class SystemAudioInitiationActionFromAvr extends HdmiCecFeatureAction { // State that waits for <Active Source> once send <Request Active Source>. private static final int STATE_WAITING_FOR_ACTIVE_SOURCE = 1; + // State that waits for TV supporting Audio System Mode or not + // once received <Active Source> + private static final int STATE_WAITING_FOR_TV_SUPPORT = 2; @VisibleForTesting static final int MAX_RETRY_COUNT = 5; @@ -60,6 +63,7 @@ public class SystemAudioInitiationActionFromAvr extends HdmiCecFeatureAction { if (physicalAddress != getSourcePath()) { audioSystem().setActiveSource(cmd.getSource(), physicalAddress); } + mState = STATE_WAITING_FOR_TV_SUPPORT; queryTvSystemAudioModeSupport(); return true; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index b5a9f7416237..326479039ac8 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -18,45 +18,11 @@ package com.android.server.inputmethod; import android.content.ComponentName; -import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethodClient; - /** * Input method manager local system service interface. */ public abstract class InputMethodManagerInternal { /** - * Called by the window manager service when a client process is being attached to the window - * manager service. - * - * <p>The caller must not have WindowManagerService lock. This method internally acquires - * InputMethodManagerService lock.</p> - * - * @param client {@link android.os.Binder} proxy that is associated with the singleton instance - * of {@link android.view.inputmethod.InputMethodManager} that runs on the client - * process - * @param inputContext communication channel for the dummy - * {@link android.view.inputmethod.InputConnection} - * @param uid UID of the client process - * @param pid PID of the client process - */ - public abstract void addClient(IInputMethodClient client, IInputContext inputContext, int uid, - int pid); - - /** - * Called by the window manager service when a client process is being attached to the window - * manager service. - * - * <p>The caller must not have WindowManagerService lock. This method internally acquires - * InputMethodManagerService lock.</p> - * - * @param client {@link android.os.Binder} proxy that is associated with the singleton instance - * of {@link android.view.inputmethod.InputMethodManager} that runs on the client - * process - */ - public abstract void removeClient(IInputMethodClient client); - - /** * Called by the power manager to tell the input method manager whether it * should start watching for wake events. */ @@ -78,15 +44,6 @@ public abstract class InputMethodManagerInternal { public static final InputMethodManagerInternal NOP = new InputMethodManagerInternal() { @Override - public void addClient(IInputMethodClient client, IInputContext inputContext, - int uid, int pid) { - } - - @Override - public void removeClient(IInputMethodClient client) { - } - - @Override public void setInteractive(boolean interactive) { } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index a043201100bd..6f5f90a781e3 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -401,12 +401,28 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + private static final class ClientDeathRecipient implements IBinder.DeathRecipient { + private final InputMethodManagerService mImms; + private final IInputMethodClient mClient; + + ClientDeathRecipient(InputMethodManagerService imms, IInputMethodClient client) { + mImms = imms; + mClient = client; + } + + @Override + public void binderDied() { + mImms.removeClient(mClient); + } + } + static final class ClientState { final IInputMethodClient client; final IInputContext inputContext; final int uid; final int pid; final InputBinding binding; + final ClientDeathRecipient clientDeathRecipient; boolean sessionRequested; SessionState curSession; @@ -419,12 +435,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } ClientState(IInputMethodClient _client, IInputContext _inputContext, - int _uid, int _pid) { + int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) { client = _client; inputContext = _inputContext; uid = _uid; pid = _pid; binding = new InputBinding(null, inputContext.asBinder(), uid, pid); + clientDeathRecipient = _clientDeathRecipient; } } @@ -1716,9 +1733,39 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - void addClient(ClientState clientState) { + /** + * Called by each application process as a preparation to start interacting with + * {@link InputMethodManagerService}. + * + * <p>As a general principle, IPCs from the application process that take + * {@link InputMethodClient} will be rejected without this step.</p> + * + * @param client {@link android.os.Binder} proxy that is associated with the singleton instance + * of {@link android.view.inputmethod.InputMethodManager} that runs on the client + * process + * @param inputContext communication channel for the dummy + * {@link android.view.inputmethod.InputConnection} + */ + @Override + public void addClient(IInputMethodClient client, IInputContext inputContext) { + final int callerUid = Binder.getCallingUid(); + final int callerPid = Binder.getCallingPid(); synchronized (mMethodMap) { - mClients.put(clientState.client.asBinder(), clientState); + // TODO: Optimize this linear search. + for (ClientState state : mClients.values()) { + if (state.uid == callerUid && state.pid == callerPid) { + throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid + + " is already registered"); + } + } + final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client); + try { + client.asBinder().linkToDeath(deathRecipient, 0); + } catch (RemoteException e) { + throw new IllegalStateException(e); + } + mClients.put(client.asBinder(), + new ClientState(client, inputContext, callerUid, callerPid, deathRecipient)); } } @@ -1726,6 +1773,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub synchronized (mMethodMap) { ClientState cs = mClients.remove(client.asBinder()); if (cs != null) { + client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0); clearClientSessionLocked(cs); if (mCurClient == cs) { if (mBoundToMethod) { @@ -4407,20 +4455,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public void addClient(IInputMethodClient client, IInputContext inputContext, int uid, - int pid) { - // Work around Bug 113877122: We need to handle this synchronously. Otherwise, some - // IMM binder calls from the client process before we register this client. - mService.addClient(new ClientState(client, inputContext, uid, pid)); - } - - @Override - public void removeClient(IInputMethodClient client) { - // Handle this synchronously to be consistent with addClient(). - mService.removeClient(client); - } - - @Override public void setInteractive(boolean interactive) { // Do everything in handler so as not to block the caller. mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0) diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 4dd2bf25ed67..25f52e7a8501 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -52,8 +52,6 @@ import android.provider.Settings.Global; import android.service.notification.Condition; import android.service.notification.ConditionProviderService; import android.service.notification.ZenModeConfig; -import android.service.notification.ZenModeConfig.EventInfo; -import android.service.notification.ZenModeConfig.ScheduleInfo; import android.service.notification.ZenModeConfig.ZenRule; import android.service.notification.ZenModeProto; import android.util.AndroidRuntimeException; @@ -119,8 +117,6 @@ public class ZenModeHelper { public static final long SUPPRESSED_EFFECT_ALL = SUPPRESSED_EFFECT_CALLS | SUPPRESSED_EFFECT_NOTIFICATIONS; - protected String mDefaultRuleEveryNightName; - protected String mDefaultRuleEventsName; @VisibleForTesting protected boolean mIsBootComplete; public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) { @@ -130,9 +126,9 @@ public class ZenModeHelper { mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mNotificationManager = context.getSystemService(NotificationManager.class); - mDefaultConfig = new ZenModeConfig(); - setDefaultZenRules(mContext); - mConfig = mDefaultConfig; + mDefaultConfig = readDefaultConfig(mContext.getResources()); + updateDefaultAutomaticRuleNames(); + mConfig = mDefaultConfig.copy(); mConfigs.put(UserHandle.USER_SYSTEM, mConfig); mSettingsObserver = new SettingsObserver(mHandler); @@ -311,7 +307,9 @@ public class ZenModeHelper { newConfig = mConfig.copy(); ZenRule rule = new ZenRule(); populateZenRule(automaticZenRule, rule, true); - newConfig.automaticRules.put(rule.id, rule); + if (newConfig.automaticRules.put(rule.id, rule) != null) { + rule.modified = true; + } if (setConfigLocked(newConfig, reason, rule.component, true)) { return rule.id; } else { @@ -341,7 +339,9 @@ public class ZenModeHelper { } } populateZenRule(automaticZenRule, rule, false); - newConfig.automaticRules.put(ruleId, rule); + if (newConfig.automaticRules.put(ruleId, rule) != null) { + rule.modified = true; + } return setConfigLocked(newConfig, reason, rule.component, true); } } @@ -413,17 +413,6 @@ public class ZenModeHelper { } } - public void setDefaultZenRules(Context context) { - mDefaultConfig = readDefaultConfig(context.getResources()); - appendDefaultRules(mDefaultConfig); - } - - private void appendDefaultRules (ZenModeConfig config) { - getDefaultRuleNames(); - appendDefaultEveryNightRule(config); - appendDefaultEventRules(config); - } - // Checks zen rule properties are the same (doesn't check creation time, name nor enabled) // used to check if default rules were customized or not private boolean ruleValuesEqual(AutomaticZenRule rule, ZenRule defaultRule) { @@ -437,22 +426,16 @@ public class ZenModeHelper { } protected void updateDefaultZenRules() { - ZenModeConfig configDefaultRules = new ZenModeConfig(); - appendDefaultRules(configDefaultRules); // "new" localized default rules - for (String ruleId : ZenModeConfig.DEFAULT_RULE_IDS) { - AutomaticZenRule currRule = getAutomaticZenRule(ruleId); - ZenRule defaultRule = configDefaultRules.automaticRules.get(ruleId); - // if default rule wasn't customized, use localized name instead of previous - if (ruleValuesEqual(currRule, defaultRule) && - !defaultRule.name.equals(currRule.getName())) { + updateDefaultAutomaticRuleNames(); + for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) { + ZenRule currRule = mConfig.automaticRules.get(defaultRule.id); + // if default rule wasn't modified, use localized name instead of previous + if (!currRule.modified && !defaultRule.name.equals(currRule.name)) { if (canManageAutomaticZenRule(defaultRule)) { if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name " - + "from " + currRule.getName() + " to " + defaultRule.name); + + "from " + currRule.name + " to " + defaultRule.name); // update default rule (if locale changed, name of rule will change) - AutomaticZenRule defaultAutoRule = createAutomaticZenRule(defaultRule); - // ensure enabled state is carried over from current rule - defaultAutoRule.setEnabled(currRule.isEnabled()); - updateAutomaticZenRule(ruleId, defaultAutoRule, + updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(defaultRule), "locale changed"); } } @@ -642,7 +625,9 @@ public class ZenModeHelper { // - doesn't already have default rules and // - all previous automatic rules were disabled config.automaticRules = new ArrayMap<>(); - appendDefaultRules(config); + for (ZenRule rule : mDefaultConfig.automaticRules.values()) { + config.automaticRules.put(rule.id, rule); + } reason += ", reset to default rules"; } @@ -854,12 +839,16 @@ public class ZenModeHelper { } } - private void getDefaultRuleNames() { - // on locale-change, these values differ - mDefaultRuleEveryNightName = mContext.getResources() - .getString(R.string.zen_mode_default_every_night_name); - mDefaultRuleEventsName = mContext.getResources() - .getString(R.string.zen_mode_default_events_name); + private void updateDefaultAutomaticRuleNames() { + for (ZenRule rule : mDefaultConfig.automaticRules.values()) { + if (ZenModeConfig.EVENTS_DEFAULT_RULE_ID.equals(rule.id)) { + rule.name = mContext.getResources() + .getString(R.string.zen_mode_default_events_name); + } else if (ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID.equals(rule.id)) { + rule.name = mContext.getResources() + .getString(R.string.zen_mode_default_every_night_name); + } + } } @VisibleForTesting @@ -1001,42 +990,6 @@ public class ZenModeHelper { return new ZenModeConfig(); } - private void appendDefaultEveryNightRule(ZenModeConfig config) { - if (config == null) return; - - final ScheduleInfo weeknights = new ScheduleInfo(); - weeknights.days = ZenModeConfig.ALL_DAYS; - weeknights.startHour = 22; - weeknights.endHour = 7; - weeknights.exitAtAlarm = true; - final ZenRule rule = new ZenRule(); - rule.enabled = false; - rule.name = mDefaultRuleEveryNightName; - rule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights); - rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; - rule.component = ScheduleConditionProvider.COMPONENT; - rule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID; - rule.creationTime = System.currentTimeMillis(); - config.automaticRules.put(rule.id, rule); - } - - private void appendDefaultEventRules(ZenModeConfig config) { - if (config == null) return; - - final EventInfo events = new EventInfo(); - events.calendar = null; // any calendar - events.reply = EventInfo.REPLY_YES_OR_MAYBE; - final ZenRule rule = new ZenRule(); - rule.enabled = false; - rule.name = mDefaultRuleEventsName; - rule.conditionId = ZenModeConfig.toEventConditionId(events); - rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; - rule.component = EventConditionProvider.COMPONENT; - rule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID; - rule.creationTime = System.currentTimeMillis(); - config.automaticRules.put(rule.id, rule); - } - private static int zenSeverity(int zen) { switch (zen) { case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return 1; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 361416adc4ef..a9f1b5c05a7f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2234,6 +2234,9 @@ class PackageManagerShellCommand extends ShellCommand { case "--install-location": sessionParams.installLocation = Integer.parseInt(getNextArg()); break; + case "--install-reason": + sessionParams.installReason = Integer.parseInt(getNextArg()); + break; case "--force-uuid": sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID; sessionParams.volumeUuid = getNextArg(); @@ -2742,8 +2745,8 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(""); pw.println(" install [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]"); pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]"); - pw.println(" [--originating-uri URI] [---referrer URI]"); - pw.println(" [--abi ABI_NAME] [--force-sdk]"); + pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]"); + pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]"); pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]"); pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [PATH|-]"); pw.println(" Install an application. Must provide the apk data to install, either as a"); @@ -2768,14 +2771,17 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" --full: cause the app to be installed as a non-ephemeral full app"); pw.println(" --install-location: force the install location:"); pw.println(" 0=auto, 1=internal only, 2=prefer external"); + pw.println(" --install-reason: indicates why the app is being installed:"); + pw.println(" 0=unknown, 1=admin policy, 2=device restore,"); + pw.println(" 3=device setup, 4=user request"); pw.println(" --force-uuid: force install on to disk volume with given UUID"); pw.println(" --force-sdk: allow install even when existing app targets platform"); pw.println(" codename but new one targets a final API level"); pw.println(""); pw.println(" install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]"); pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]"); - pw.println(" [--originating-uri URI] [---referrer URI]"); - pw.println(" [--abi ABI_NAME] [--force-sdk]"); + pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]"); + pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]"); pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]"); pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]"); pw.println(" Like \"install\", but starts an install session. Use \"install-write\""); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 3b19beb52e2e..91fd8d0e960d 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -125,6 +125,7 @@ import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; + import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; @@ -631,9 +632,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { InputConsumer mInputConsumer = null; - private final WindowFrames mWindowFrames = new WindowFrames(); - private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect(); - private static final Rect mTmpRect = new Rect(); + private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect(); + private static final Rect sTmpRect = new Rect(); + private static final Rect sTmpDockedFrame = new Rect(); + private static final Rect sTmpNavFrame = new Rect(); + private static final Rect sTmpLastParentFrame = new Rect(); WindowState mTopFullscreenOpaqueWindowState; WindowState mTopFullscreenOpaqueOrDimmingWindowState; @@ -4308,9 +4311,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDockLayer = 0x10000000; mStatusBarLayer = -1; - mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout); - mWindowFrames.setParentFrameWasClippedByDisplayCutout(false); - if (displayFrames.mDisplayId == DEFAULT_DISPLAY) { // For purposes of putting out fake window up to steal focus, we will // drive nav being hidden only by whether it is requested. @@ -4377,12 +4377,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return; } - mTmpRect.setEmpty(); - mWindowFrames.setFrames(displayFrames.mDock /* parentFrame */, - displayFrames.mDock /* displayFrame */, displayFrames.mDock /* overscanFrame */, - displayFrames.mDock /* contentFrame */, displayFrames.mDock /* visibleFrame */, - mTmpRect /* decorFrame */, displayFrames.mDock /* stableFrame */, - displayFrames.mDock /* outsetFrame */); + sTmpRect.setEmpty(); + sTmpDockedFrame.set(displayFrames.mDock); final int displayId = displayFrames.mDisplayId; final Rect dockFrame = displayFrames.mDock; @@ -4396,7 +4392,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { continue; } - w.computeFrameLw(mWindowFrames); + w.getWindowFrames().setFrames(sTmpDockedFrame /* parentFrame */, + sTmpDockedFrame /* displayFrame */, sTmpDockedFrame /* overscanFrame */, + sTmpDockedFrame /* contentFrame */, sTmpDockedFrame /* visibleFrame */, + sTmpRect /* decorFrame */, sTmpDockedFrame /* stableFrame */, + sTmpDockedFrame /* outsetFrame */); + w.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); + w.computeFrameLw(); final Rect frame = w.getFrameLw(); if (frame.left <= 0 && frame.top <= 0) { @@ -4448,17 +4450,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { return false; } // apply any navigation bar insets - mTmpRect.setEmpty(); - mWindowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */, + sTmpRect.setEmpty(); + mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */, displayFrames.mUnrestricted /* displayFrame */, displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */, - displayFrames.mStable /* visibleFrame */, mTmpRect /* decorFrame */, + displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */, displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */); - + mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); mStatusBarLayer = mStatusBar.getSurfaceLayer(); // Let the status bar determine its size. - mStatusBar.computeFrameLw(mWindowFrames); + mStatusBar.computeFrameLw(); // For layout, the status bar is always at the top with our fixed height. displayFrames.mStable.top = displayFrames.mUnrestricted.top @@ -4468,11 +4470,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { displayFrames.mDisplayCutoutSafe.top); // Tell the bar controller where the collapsed status bar content is - mTmpRect.set(mStatusBar.getContentFrameLw()); - mTmpRect.intersect(displayFrames.mDisplayCutoutSafe); - mTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset - mTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size - mStatusBarController.setContentFrame(mTmpRect); + sTmpRect.set(mStatusBar.getContentFrameLw()); + sTmpRect.intersect(displayFrames.mDisplayCutoutSafe); + sTmpRect.top = mStatusBar.getContentFrameLw().top; // Ignore top display cutout inset + sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size + mStatusBarController.setContentFrame(sTmpRect); boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; boolean statusBarTranslucent = (sysui @@ -4512,7 +4514,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return false; } - final Rect navigationFrame = mWindowFrames.mParentFrame; + final Rect navigationFrame = sTmpNavFrame; boolean transientNavBarShowing = mNavigationBarController.isTransientShowing(); // Force the navigation bar to its appropriate place and size. We need to do this directly, // instead of relying on it to bubble up from the nav bar, because this needs to change @@ -4523,7 +4525,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final Rect dockFrame = displayFrames.mDock; mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation); - final Rect cutoutSafeUnrestricted = mTmpRect; + final Rect cutoutSafeUnrestricted = sTmpRect; cutoutSafeUnrestricted.set(displayFrames.mUnrestricted); cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe); @@ -4605,15 +4607,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { displayFrames.mContent.set(dockFrame); mStatusBarLayer = mNavigationBar.getSurfaceLayer(); // And compute the final frame. - mTmpRect.setEmpty(); - mWindowFrames.setFrames(navigationFrame /* parentFrame */, + sTmpRect.setEmpty(); + mNavigationBar.getWindowFrames().setFrames(navigationFrame /* parentFrame */, navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */, displayFrames.mDisplayCutoutSafe /* contentFrame */, - navigationFrame /* visibleFrame */, mTmpRect /* decorFrame */, + navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */, navigationFrame /* stableFrame */, displayFrames.mDisplayCutoutSafe /* outsetFrame */); - - mNavigationBar.computeFrameLw(mWindowFrames); + mNavigationBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout); + mNavigationBar.computeFrameLw(); mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw()); if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame); @@ -4744,17 +4746,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs); final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs); - final Rect pf = mWindowFrames.mParentFrame; - final Rect df = mWindowFrames.mDisplayFrame; - final Rect of = mWindowFrames.mOverscanFrame; - final Rect cf = mWindowFrames.mContentFrame; - final Rect vf = mWindowFrames.mVisibleFrame; - final Rect dcf = mWindowFrames.mDecorFrame; - final Rect sf = mWindowFrames.mStableFrame; + final WindowFrames windowFrames = win.getWindowFrames(); + + windowFrames.setHasOutsets(false); + sTmpLastParentFrame.set(windowFrames.mParentFrame); + final Rect pf = windowFrames.mParentFrame; + final Rect df = windowFrames.mDisplayFrame; + final Rect of = windowFrames.mOverscanFrame; + final Rect cf = windowFrames.mContentFrame; + final Rect vf = windowFrames.mVisibleFrame; + final Rect dcf = windowFrames.mDecorFrame; + final Rect sf = windowFrames.mStableFrame; dcf.setEmpty(); - mWindowFrames.mOutsetFrame.setEmpty(); - mWindowFrames.setParentFrameWasClippedByDisplayCutout(false); - mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout); + windowFrames.setParentFrameWasClippedByDisplayCutout(false); + windowFrames.setDisplayCutout(displayFrames.mDisplayCutout); final boolean hasNavBar = (isDefaultDisplay && mDefaultDisplayPolicy.hasNavigationBar() && mNavigationBar != null && mNavigationBar.isVisibleLw()); @@ -4774,7 +4779,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { cf.set(displayFrames.mDock); of.set(displayFrames.mDock); df.set(displayFrames.mDock); - pf.set(displayFrames.mDock); + windowFrames.mParentFrame.set(displayFrames.mDock); // IM dock windows layout below the nav bar... pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom; // ...with content insets above the nav bar @@ -4876,9 +4881,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { ? displayFrames.mRestricted.bottom : displayFrames.mUnrestricted.bottom; - if (DEBUG_LAYOUT) Slog.v(TAG, String.format( - "Laying out status bar window: (%d,%d - %d,%d)", - pf.left, pf.top, pf.right, pf.bottom)); + if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf); } else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0 && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) { // Asking to layout into the overscan region, so give it that pure @@ -4950,17 +4953,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { pf.bottom = df.bottom = of.bottom = cf.bottom = displayFrames.mRestricted.bottom; } - if (DEBUG_LAYOUT) Slog.v(TAG, String.format( - "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)", - pf.left, pf.top, pf.right, pf.bottom)); + if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf); } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) { // The navigation bar has Real Ultimate Power. of.set(displayFrames.mUnrestricted); df.set(displayFrames.mUnrestricted); pf.set(displayFrames.mUnrestricted); - if (DEBUG_LAYOUT) Slog.v(TAG, String.format( - "Laying out navigation bar window: (%d,%d - %d,%d)", - pf.left, pf.top, pf.right, pf.bottom)); + if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf); } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT) && ((fl & FLAG_FULLSCREEN) != 0)) { // Fullscreen secure system overlays get what they ask for. Screenshot region @@ -5087,7 +5086,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in // the cutout safe zone. if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { - final Rect displayCutoutSafeExceptMaybeBars = mTmpDisplayCutoutSafeExceptMaybeBarsRect; + final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect; displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe); if (layoutInScreen && layoutInsetDecor && !requestedFullscreen && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) { @@ -5122,9 +5121,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // They will later be cropped or shifted using the displayFrame in WindowState, // which prevents overlap with the DisplayCutout. if (!attachedInParent && !floatingInScreenWindow) { - mTmpRect.set(pf); + sTmpRect.set(pf); pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars); - mWindowFrames.setParentFrameWasClippedByDisplayCutout(!mTmpRect.equals(pf)); + windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf)); } // Make sure that NO_LIMITS windows clipped to the display don't extend under the // cutout. @@ -5152,8 +5151,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // apply the outsets to floating dialogs, because they wouldn't make sense there. final boolean useOutsets = shouldUseOutsets(attrs, fl); if (isDefaultDisplay && useOutsets) { - final Rect osf = mWindowFrames.mOutsetFrame; + final Rect osf = windowFrames.mOutsetFrame; osf.set(cf.left, cf.top, cf.right, cf.bottom); + windowFrames.setHasOutsets(true); int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources()); if (outset > 0) { int rotation = displayFrames.mRotation; @@ -5180,9 +5180,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { + " cf=" + cf.toShortString() + " vf=" + vf.toShortString() + " dcf=" + dcf.toShortString() + " sf=" + sf.toShortString() - + " osf=" + mWindowFrames.mOutsetFrame.toShortString()); + + " osf=" + windowFrames.mOutsetFrame.toShortString() + " " + win); + + if (!sTmpLastParentFrame.equals(pf)) { + windowFrames.setContentChanged(true); + } - win.computeFrameLw(mWindowFrames); + win.computeFrameLw(); // Dock windows carve out the bottom of the screen, so normal windows // can't appear underneath them. if (type == TYPE_INPUT_METHOD && win.isVisibleLw() diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index b55adeb32729..1fcdd63c4488 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -63,12 +63,10 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; import static java.lang.annotation.RetentionPolicy.SOURCE; -import android.Manifest; import android.annotation.IntDef; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; -import android.content.pm.ActivityInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Rect; @@ -95,7 +93,6 @@ import com.android.internal.policy.IShortcutService; import com.android.server.wm.DisplayFrames; import com.android.server.wm.DisplayRotation; import com.android.server.wm.WindowFrames; -import com.android.server.wm.utils.WmDisplayCutout; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -201,10 +198,8 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * getFrame() if so desired. Must be called with the window manager * lock held. * - * @param windowFrames Container for all the window frames that affect how the window is - * laid out. */ - public void computeFrameLw(WindowFrames windowFrames); + public void computeFrameLw(); /** * Retrieve the current frame of the window that has been assigned by @@ -477,6 +472,11 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * Writes {@link com.android.server.wm.IdentifierProto} to stream. */ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId); + + /** + * @return The {@link WindowFrames} associated with this {@link WindowState} + */ + WindowFrames getWindowFrames(); } /** @@ -1167,7 +1167,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { default void layoutWindowLw( WindowState win, WindowState attached, DisplayFrames displayFrames) {} - /** * Return the layout hints for a newly added window. These values are computed on the * most recent layout, so they are not guaranteed to be correct. diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java index 180f34355c94..9f69702911c9 100644 --- a/services/core/java/com/android/server/security/VerityUtils.java +++ b/services/core/java/com/android/server/security/VerityUtils.java @@ -23,11 +23,11 @@ import android.annotation.NonNull; import android.os.SharedMemory; import android.system.ErrnoException; import android.system.Os; +import android.util.Pair; +import android.util.Slog; import android.util.apk.ApkSignatureVerifier; import android.util.apk.ByteBufferFactory; import android.util.apk.SignatureNotFoundException; -import android.util.Pair; -import android.util.Slog; import java.io.FileDescriptor; import java.io.IOException; @@ -85,7 +85,7 @@ abstract public class VerityUtils { */ public static byte[] generateFsverityRootHash(@NonNull String apkPath) throws NoSuchAlgorithmException, DigestException, IOException { - return ApkSignatureVerifier.generateFsverityRootHash(apkPath); + return ApkSignatureVerifier.generateApkVerityRootHash(apkPath); } /** diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 5e3fe0a119ff..bfa03ca9f2be 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -1031,7 +1031,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { looperStats.reset(); for (LooperStats.ExportedEntry entry : entries) { StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(1000); // uid collection not implemented yet + e.writeInt(entry.workSourceUid); e.writeString(entry.handlerClassName); e.writeString(entry.threadName); e.writeString(entry.messageName); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 83075ed24e33..524ca170336f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -23,6 +23,8 @@ import android.app.IActivityManager; import android.app.IApplicationThread; import android.content.ComponentName; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.res.CompatibilityInfo; import android.os.Bundle; import android.os.IBinder; import android.os.SystemClock; @@ -267,6 +269,9 @@ public abstract class ActivityTaskManagerInternal { public abstract void onProcessRemoved(String name, int uid); public abstract void onCleanUpApplicationRecord(WindowProcessController proc); public abstract int getTopProcessState(); + public abstract boolean isHeavyWeightProcess(WindowProcessController proc); + public abstract void clearHeavyWeightProcessIfEquals(WindowProcessController proc); + public abstract void finishHeavyWeightApp(); public abstract boolean isSleeping(); public abstract boolean isShuttingDown(); @@ -281,4 +286,7 @@ public abstract class ActivityTaskManagerInternal { public abstract void onPackageDataCleared(String name); public abstract void onPackageUninstalled(String name); + public abstract void onPackageAdded(String name, boolean replacing); + + public abstract CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index d518549859a0..6aca4640f160 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -571,7 +571,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (!w.mLayoutAttached) { if (mTmpInitial) { //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); - w.mContentChanged = false; + w.resetContentChanged(); } if (w.mAttrs.type == TYPE_DREAM) { // Don't layout windows behind a dream, so that if it does stuff like hide @@ -616,7 +616,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo || w.mLayoutNeeded) { if (mTmpInitial) { //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); - w.mContentChanged = false; + w.resetContentChanged(); } w.mLayoutNeeded = false; w.prelayout(); @@ -699,7 +699,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final WindowStateAnimator winAnimator = w.mWinAnimator; //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing"); - w.mContentChanged = false; + w.resetContentChanged(); // Moved from updateWindowsAndWallpaperLocked(). if (w.mHasSurface) { @@ -3251,7 +3251,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // TODO(b/68392460): We should screenshot Task controls directly // but it's difficult at the moment as the Task doesn't have the // correct size set. - final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, 0, 1, inRotation, rot); + final Bitmap bitmap = SurfaceControl.screenshot(frame, dw, dh, inRotation, rot); if (bitmap == null) { Slog.w(TAG_WM, "Failed to take screenshot"); return null; diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index f9a71d3d850d..acc9c038bd9c 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -38,7 +38,6 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.Trace; import android.os.UserHandle; import android.util.MergedConfiguration; @@ -56,10 +55,6 @@ import android.view.SurfaceSession; import android.view.WindowManager; import com.android.internal.os.logging.MetricsLoggerWrapper; -import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethodClient; -import com.android.server.LocalServices; -import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.WindowManagerService.H; import java.io.PrintWriter; @@ -73,8 +68,6 @@ import java.util.Set; class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final WindowManagerService mService; final IWindowSessionCallback mCallback; - final IInputMethodClient mClient; - final InputMethodManagerInternal mInputMethodManagerInternal; final int mUid; final int mPid; private final String mStringName; @@ -95,17 +88,9 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { private String mPackageName; private String mRelayoutTag; - public Session(WindowManagerService service, IWindowSessionCallback callback, - IInputMethodClient client, IInputContext inputContext) { + public Session(WindowManagerService service, IWindowSessionCallback callback) { mService = service; mCallback = callback; - mClient = client; - // Depending on the timing when Session object gets called and SystemServer#mFactoryTestMode - // this could be null, right? - final InputMethodManagerInternal immInternal = - LocalServices.getService(InputMethodManagerInternal.class); - mInputMethodManagerInternal = - immInternal != null ? immInternal : InputMethodManagerInternal.NOP; mUid = Binder.getCallingUid(); mPid = Binder.getCallingPid(); mLastReportedAnimatorScale = service.getCurrentAnimatorScale(); @@ -134,12 +119,11 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { sb.append("}"); mStringName = sb.toString(); - mInputMethodManagerInternal.addClient(client, inputContext, mUid, mPid); try { - client.asBinder().linkToDeath(this, 0); + mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { // The caller has died, so we can just forget about this. - mInputMethodManagerInternal.removeClient(client); + // Hmmm, should we call killSessionLocked()?? } } @@ -159,9 +143,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void binderDied() { - mInputMethodManagerInternal.removeClient(mClient); synchronized(mService.mWindowMap) { - mClient.asBinder().unlinkToDeath(this, 0); + mCallback.asBinder().unlinkToDeath(this, 0); mClientDead = true; killSessionLocked(); } diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java index cbe7d9d05e28..20a874b7d0e8 100644 --- a/services/core/java/com/android/server/wm/WindowFrames.java +++ b/services/core/java/com/android/server/wm/WindowFrames.java @@ -36,7 +36,6 @@ import android.annotation.NonNull; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; import android.view.DisplayCutout; -import android.view.WindowManager; import com.android.server.wm.utils.InsetUtils; import com.android.server.wm.utils.WmDisplayCutout; @@ -191,6 +190,10 @@ public class WindowFrames { private final Rect mTmpRect = new Rect(); + private boolean mHasOutsets; + + private boolean mContentChanged; + public WindowFrames() { } @@ -237,11 +240,9 @@ public class WindowFrames { * Calculates the outsets for this windowFrame. The outsets are calculated by the area between * the {@link #mOutsetFrame} and the {@link #mContentFrame}. If there are no outsets, then * {@link #mOutsets} is set to empty. - * - * @param hasOutsets Whether this frame has outsets. */ - void calculateOutsets(boolean hasOutsets) { - if (hasOutsets) { + void calculateOutsets() { + if (mHasOutsets) { InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets); } else { mOutsets.setEmpty(); @@ -249,7 +250,8 @@ public class WindowFrames { } /** - * Calculate the insets for the type {@link WindowManager.LayoutParams#TYPE_DOCK_DIVIDER} + * Calculate the insets for the type + * {@link android.view.WindowManager.LayoutParams#TYPE_DOCK_DIVIDER} * * @param cutoutInsets The insets for the cutout. */ @@ -367,6 +369,28 @@ public class WindowFrames { mLastContentInsets.set(-1, -1, -1, -1); } + /** + * Sets whether the frame has outsets. + */ + public void setHasOutsets(boolean hasOutsets) { + mHasOutsets = hasOutsets; + } + + /** + * Sets whether the content has changed. This means that either the size or parent frame has + * changed. + */ + public void setContentChanged(boolean contentChanged) { + mContentChanged = contentChanged; + } + + /** + * @see #setContentChanged(boolean) + */ + boolean hasContentChanged() { + return mContentChanged; + } + public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); mParentFrame.writeToProto(proto, PARENT_FRAME); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 711f66ad4d9a..71ce1d9e2ea7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -232,8 +232,6 @@ import com.android.internal.policy.IShortcutService; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.LatencyTracker; -import com.android.internal.view.IInputContext; -import com.android.internal.view.IInputMethodClient; import com.android.internal.view.WindowManagerPolicyThread; import com.android.server.AnimationThread; import com.android.server.DisplayThread; @@ -5037,12 +5035,8 @@ public class WindowManagerService extends IWindowManager.Stub // ------------------------------------------------------------- @Override - public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client, - IInputContext inputContext) { - if (client == null) throw new IllegalArgumentException("null client"); - if (inputContext == null) throw new IllegalArgumentException("null inputContext"); - Session session = new Session(this, callback, client, inputContext); - return session; + public IWindowSession openSession(IWindowSessionCallback callback) { + return new Session(this, callback); } @Override @@ -5075,9 +5069,7 @@ public class WindowManagerService extends IWindowManager.Stub throw new SecurityException("Must hold permission " + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - if (displayId != DEFAULT_DISPLAY) { - throw new IllegalArgumentException("Can only set the default display"); - } + final long ident = Binder.clearCallingIdentity(); try { synchronized(mWindowMap) { @@ -5110,9 +5102,7 @@ public class WindowManagerService extends IWindowManager.Stub throw new SecurityException("Must hold permission " + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - if (displayId != DEFAULT_DISPLAY) { - throw new IllegalArgumentException("Can only set the default display"); - } + final long ident = Binder.clearCallingIdentity(); try { synchronized(mWindowMap) { @@ -5192,9 +5182,7 @@ public class WindowManagerService extends IWindowManager.Stub throw new SecurityException("Must hold permission " + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - if (displayId != DEFAULT_DISPLAY) { - throw new IllegalArgumentException("Can only set the default display"); - } + final long ident = Binder.clearCallingIdentity(); try { synchronized(mWindowMap) { @@ -5241,9 +5229,6 @@ public class WindowManagerService extends IWindowManager.Stub throw new SecurityException("Must hold permission " + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - if (displayId != DEFAULT_DISPLAY) { - throw new IllegalArgumentException("Can only set the default display"); - } final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "setForcedDisplayDensityForUser", @@ -5272,9 +5257,6 @@ public class WindowManagerService extends IWindowManager.Stub throw new SecurityException("Must hold permission " + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - if (displayId != DEFAULT_DISPLAY) { - throw new IllegalArgumentException("Can only set the default display"); - } final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "clearForcedDisplayDensityForUser", diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 6e0ccfd5d2db..831418b4b2b4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -20,7 +20,6 @@ import static android.os.Build.IS_USER; import android.graphics.Point; import android.graphics.Rect; -import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ShellCommand; import android.os.UserHandle; @@ -28,10 +27,6 @@ import android.util.DisplayMetrics; import android.view.Display; import android.view.IWindowManager; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -87,21 +82,48 @@ public class WindowManagerShellCommand extends ShellCommand { return -1; } + private int getDisplayId(String opt) { + int displayId = Display.DEFAULT_DISPLAY; + String option = "-d".equals(opt) ? opt : getNextOption(); + if (option != null && "-d".equals(option)) { + try { + displayId = Integer.parseInt(getNextArgRequired()); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad number " + e); + } catch (IllegalArgumentException e) { + getErrPrintWriter().println("Error: " + e); + } + } + return displayId; + } + + private void printInitialDisplaySize(PrintWriter pw , int displayId) { + final Point initialSize = new Point(); + final Point baseSize = new Point(); + + try { + mInterface.getInitialDisplaySize(displayId, initialSize); + mInterface.getBaseDisplaySize(displayId, baseSize); + pw.println("Physical size: " + initialSize.x + "x" + initialSize.y); + if (!initialSize.equals(baseSize)) { + pw.println("Override size: " + baseSize.x + "x" + baseSize.y); + } + } catch (RemoteException e) { + // Can't call getInitialDisplaySize() on IWindowManager or + // Can't call getBaseDisplaySize() on IWindowManager + pw.println("Remote exception: " + e); + } + } + private int runDisplaySize(PrintWriter pw) throws RemoteException { String size = getNextArg(); int w, h; + final int displayId = getDisplayId(size); if (size == null) { - Point initialSize = new Point(); - Point baseSize = new Point(); - try { - mInterface.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize); - mInterface.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize); - pw.println("Physical size: " + initialSize.x + "x" + initialSize.y); - if (!initialSize.equals(baseSize)) { - pw.println("Override size: " + baseSize.x + "x" + baseSize.y); - } - } catch (RemoteException e) { - } + printInitialDisplaySize(pw, displayId); + return 0; + } else if ("-d".equals(size)) { + printInitialDisplaySize(pw, displayId); return 0; } else if ("reset".equals(size)) { w = h = -1; @@ -114,8 +136,8 @@ public class WindowManagerShellCommand extends ShellCommand { String wstr = size.substring(0, div); String hstr = size.substring(div+1); try { - w = parseDimension(wstr); - h = parseDimension(hstr); + w = parseDimension(wstr, displayId); + h = parseDimension(hstr, displayId); } catch (NumberFormatException e) { getErrPrintWriter().println("Error: bad number " + e); return -1; @@ -123,27 +145,38 @@ public class WindowManagerShellCommand extends ShellCommand { } if (w >= 0 && h >= 0) { - // TODO(multidisplay): For now Configuration only applies to main screen. - mInterface.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h); + mInterface.setForcedDisplaySize(displayId, w, h); } else { - mInterface.clearForcedDisplaySize(Display.DEFAULT_DISPLAY); + mInterface.clearForcedDisplaySize(displayId); } return 0; } + private void printInitialDisplayDensity(PrintWriter pw , int displayId) { + try { + final int initialDensity = mInterface.getInitialDisplayDensity(displayId); + final int baseDensity = mInterface.getBaseDisplayDensity(displayId); + pw.println("Physical density: " + initialDensity); + if (initialDensity != baseDensity) { + pw.println("Override density: " + baseDensity); + } + } catch (RemoteException e) { + // Can't call getInitialDisplayDensity() on IWindowManager or + // Can't call getBaseDisplayDensity() on IWindowManager + pw.println("Remote exception: " + e); + } + } + private int runDisplayDensity(PrintWriter pw) throws RemoteException { String densityStr = getNextArg(); int density; + final int displayId = getDisplayId(densityStr); + if (densityStr == null) { - try { - int initialDensity = mInterface.getInitialDisplayDensity(Display.DEFAULT_DISPLAY); - int baseDensity = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY); - pw.println("Physical density: " + initialDensity); - if (initialDensity != baseDensity) { - pw.println("Override density: " + baseDensity); - } - } catch (RemoteException e) { - } + printInitialDisplayDensity(pw, displayId); + return 0; + } else if ("-d".equals(densityStr)) { + printInitialDisplayDensity(pw, displayId); return 0; } else if ("reset".equals(densityStr)) { density = -1; @@ -161,11 +194,10 @@ public class WindowManagerShellCommand extends ShellCommand { } if (density > 0) { - // TODO(multidisplay): For now Configuration only applies to main screen. - mInterface.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density, + mInterface.setForcedDisplayDensityForUser(displayId, density, UserHandle.USER_CURRENT); } else { - mInterface.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, + mInterface.clearForcedDisplayDensityForUser(displayId, UserHandle.USER_CURRENT); } return 0; @@ -174,6 +206,7 @@ public class WindowManagerShellCommand extends ShellCommand { private int runDisplayOverscan(PrintWriter pw) throws RemoteException { String overscanStr = getNextArgRequired(); Rect rect = new Rect(); + final int displayId = getDisplayId(overscanStr); if ("reset".equals(overscanStr)) { rect.set(0, 0, 0, 0); } else { @@ -190,17 +223,16 @@ public class WindowManagerShellCommand extends ShellCommand { rect.bottom = Integer.parseInt(matcher.group(4)); } - mInterface.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, - rect.bottom); + mInterface.setOverscan(displayId, rect.left, rect.top, rect.right, rect.bottom); return 0; } private int runDisplayScaling(PrintWriter pw) throws RemoteException { String scalingStr = getNextArgRequired(); if ("auto".equals(scalingStr)) { - mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0); + mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 0); } else if ("off".equals(scalingStr)) { - mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1); + mInterface.setForcedDisplayScalingMode(getDisplayId(scalingStr), 1); } else { getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'"); return -1; @@ -213,14 +245,14 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } - private int parseDimension(String s) throws NumberFormatException { + private int parseDimension(String s, int displayId) throws NumberFormatException { if (s.endsWith("px")) { return Integer.parseInt(s.substring(0, s.length() - 2)); } if (s.endsWith("dp")) { int density; try { - density = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY); + density = mInterface.getBaseDisplayDensity(displayId); } catch (RemoteException e) { density = DisplayMetrics.DENSITY_DEFAULT; } @@ -236,14 +268,14 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println("Window manager (window) commands:"); pw.println(" help"); pw.println(" Print this help text."); - pw.println(" size [reset|WxH|WdpxHdp]"); + pw.println(" size [reset|WxH|WdpxHdp] [-d DISPLAY_ID]"); pw.println(" Return or override display size."); pw.println(" width and height in pixels unless suffixed with 'dp'."); - pw.println(" density [reset|DENSITY]"); + pw.println(" density [reset|DENSITY] [-d DISPLAY_ID]"); pw.println(" Return or override display density."); - pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM]"); + pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM] [-d DISPLAY ID]"); pw.println(" Set overscan area for display."); - pw.println(" scaling [off|auto]"); + pw.println(" scaling [off|auto] [-d DISPLAY_ID]"); pw.println(" Set display scaling mode."); pw.println(" dismiss-keyguard"); pw.println(" Dismiss the keyguard, prompting user for auth if necessary."); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 5272b663c2aa..f1ddda777e8f 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -359,8 +359,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private final Rect mInsetFrame = new Rect(); - boolean mContentChanged; - // If a window showing a wallpaper: the requested offset for the // wallpaper; if a wallpaper window: the currently applied offset. float mWallpaperX = -1; @@ -787,7 +785,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } @Override - public void computeFrameLw(WindowFrames windowFrames) { + public void computeFrameLw() { if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) { // This window is being replaced and either already got information that it's being // removed or we are still waiting for some information. Because of this we don't @@ -796,8 +794,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } mHaveFrame = true; - mWindowFrames.setParentFrameWasClippedByDisplayCutout( - windowFrames.parentFrameWasClippedByDisplayCutout()); final Task task = getTask(); final boolean inFullscreenContainer = inFullscreenContainer(); @@ -827,10 +823,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final int layoutYDiff; if (inFullscreenContainer || layoutInParentFrame()) { // We use the parent frame as the containing frame for fullscreen and child windows - mWindowFrames.mContainingFrame.set(windowFrames.mParentFrame); - mWindowFrames.mDisplayFrame.set(windowFrames.mDisplayFrame); - layoutDisplayFrame = windowFrames.mDisplayFrame; - layoutContainingFrame = windowFrames.mParentFrame; + mWindowFrames.mContainingFrame.set(mWindowFrames.mParentFrame); + layoutDisplayFrame = mWindowFrames.mDisplayFrame; + layoutContainingFrame = mWindowFrames.mParentFrame; layoutXDiff = 0; layoutYDiff = 0; } else { @@ -849,17 +844,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // IME is up and obscuring this window. Adjust the window position so it is visible. if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) { if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom - > windowFrames.mContentFrame.bottom) { + > mWindowFrames.mContentFrame.bottom) { // In freeform we want to move the top up directly. // TODO: Investigate why this is contentFrame not parentFrame. mWindowFrames.mContainingFrame.top -= mWindowFrames.mContainingFrame.bottom - - windowFrames.mContentFrame.bottom; + - mWindowFrames.mContentFrame.bottom; } else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom - > windowFrames.mParentFrame.bottom) { + > mWindowFrames.mParentFrame.bottom) { // But in docked we want to behave like fullscreen and behave as if the task // were given smaller bounds for the purposes of layout. Skip adjustments for // the pinned stack, they are handled separately in the PinnedStackController. - mWindowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom; + mWindowFrames.mContainingFrame.bottom = mWindowFrames.mParentFrame.bottom; } } @@ -868,7 +863,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // if it wasn't set already. No need to intersect it with the (visible) // "content frame" since it is allowed to be outside the visible desktop. if (mWindowFrames.mContainingFrame.isEmpty()) { - mWindowFrames.mContainingFrame.set(windowFrames.mContentFrame); + mWindowFrames.mContainingFrame.set(mWindowFrames.mContentFrame); } } @@ -878,10 +873,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // PIP edge case: When going from pinned to fullscreen, we apply a // tempInsetFrame for the full task - but we're still at the start of the animation. // To prevent a jump if there's a letterbox, restrict to the parent frame. - mInsetFrame.intersectUnchecked(windowFrames.mParentFrame); - mWindowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame); + mInsetFrame.intersectUnchecked(mWindowFrames.mParentFrame); + mWindowFrames.mContainingFrame.intersectUnchecked(mWindowFrames.mParentFrame); } + layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame); mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame); layoutXDiff = !mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left @@ -892,41 +888,24 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP layoutContainingFrame = !mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame; mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight); - subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, - windowFrames.mDisplayFrame, mTmpRect); + subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame, + mTmpRect); if (!layoutInParentFrame()) { subtractInsets(mWindowFrames.mContainingFrame, layoutContainingFrame, - windowFrames.mParentFrame, mTmpRect); - subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame, + mWindowFrames.mParentFrame, mTmpRect); + subtractInsets(mInsetFrame, layoutContainingFrame, mWindowFrames.mParentFrame, mTmpRect); } - layoutDisplayFrame = windowFrames.mDisplayFrame; layoutDisplayFrame.intersect(layoutContainingFrame); } final int pw = mWindowFrames.mContainingFrame.width(); final int ph = mWindowFrames.mContainingFrame.height(); - if (!mWindowFrames.mParentFrame.equals(windowFrames.mParentFrame)) { - //Slog.i(TAG_WM, "Window " + this + " content frame from " + mParentFrame - // + " to " + parentFrame); - mWindowFrames.mParentFrame.set(windowFrames.mParentFrame); - mContentChanged = true; - } if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) { mLastRequestedWidth = mRequestedWidth; mLastRequestedHeight = mRequestedHeight; - mContentChanged = true; - } - - mWindowFrames.mOverscanFrame.set(windowFrames.mOverscanFrame); - mWindowFrames.mContentFrame.set(windowFrames.mContentFrame); - mWindowFrames.mVisibleFrame.set(windowFrames.mVisibleFrame); - mWindowFrames.mDecorFrame.set(windowFrames.mDecorFrame); - mWindowFrames.mStableFrame.set(windowFrames.mStableFrame); - final boolean hasOutsets = !windowFrames.mOutsetFrame.isEmpty(); - if (hasOutsets) { - mWindowFrames.mOutsetFrame.set(windowFrames.mOutsetFrame); + mWindowFrames.setContentChanged(true); } final int fw = mWindowFrames.mFrame.width(); @@ -935,7 +914,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame); // Calculate the outsets before the content frame gets shrinked to the window frame. - mWindowFrames.calculateOutsets(hasOutsets); + mWindowFrames.calculateOutsets(); // Make sure the content and visible frames are inside of the // final window frame. @@ -997,7 +976,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (mAttrs.type == TYPE_DOCK_DIVIDER) { - final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo( + final WmDisplayCutout c = mWindowFrames.mDisplayCutout.calculateRelativeTo( mWindowFrames.mDisplayFrame); mWindowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets()); } else { @@ -1006,7 +985,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } mWindowFrames.setDisplayCutout( - windowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame)); + mWindowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame)); // Offset the actual frame by the amount layout frame is off. mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff); @@ -1733,7 +1712,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * sense to call from performLayoutAndPlaceSurfacesLockedInner().) */ private boolean hasMoved() { - return mHasSurface && (mContentChanged || mMovedByResize) + return mHasSurface && (mWindowFrames.hasContentChanged() || mMovedByResize) && !mAnimatingExit && (mWindowFrames.mFrame.top != mWindowFrames.mLastFrame.top || mWindowFrames.mFrame.left != mWindowFrames.mLastFrame.left) @@ -4762,6 +4741,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mWindowFrames.mVisibleInsets; } + @Override + public WindowFrames getWindowFrames() { + return mWindowFrames; + } + + void resetContentChanged() { + mWindowFrames.setContentChanged(false); + } + private final class MoveAnimationSpec implements AnimationSpec { private final long mDuration; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 4c71d65115f0..e76afa3c144a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -6019,7 +6019,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { success = mUserManagerInternal.removeUserEvenWhenDisallowed(userId); if (!success) { Slog.w(LOG_TAG, "Couldn't remove user " + userId); - } else if (isManagedProfile(userId)) { + } else if (isManagedProfile(userId) && !TextUtils.isEmpty(wipeReasonForUser)) { sendWipeProfileNotification(wipeReasonForUser); } } catch (RemoteException re) { @@ -6034,7 +6034,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty"); enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId()); final ActiveAdmin admin; diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index 9d09f5c44a28..0e8200992a14 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -439,6 +439,10 @@ public class ActivityTestsBase { } @Override + void updateUsageStats(ActivityRecord component, boolean resumed) { + } + + @Override final protected ActivityStackSupervisor createStackSupervisor() { final ActivityStackSupervisor supervisor = spy(createTestSupervisor()); final KeyguardController keyguardController = mock(KeyguardController.class); @@ -497,10 +501,6 @@ public class ActivityTestsBase { } @Override - void updateUsageStats(ActivityRecord component, boolean resumed) { - } - - @Override Configuration getGlobalConfiguration() { return mContext.getResources().getConfiguration(); } diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java index b238e4376b1d..d34f951ffd61 100644 --- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java +++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java @@ -30,7 +30,7 @@ import com.android.server.wm.utils.WmDisplayCutout; public class FakeWindowState implements WindowManagerPolicy.WindowState { - private WindowFrames windowFrames; + private WindowFrames mWindowFrames = new WindowFrames(); public WindowManager.LayoutParams attrs; public int displayId; @@ -53,41 +53,40 @@ public class FakeWindowState implements WindowManagerPolicy.WindowState { } @Override - public void computeFrameLw(WindowFrames windowFrames) { - this.windowFrames = windowFrames; + public void computeFrameLw() { } @Override public Rect getFrameLw() { - return windowFrames.mParentFrame; + return mWindowFrames.mParentFrame; } @Override public Rect getDisplayFrameLw() { - return windowFrames.mDisplayFrame; + return mWindowFrames.mDisplayFrame; } @Override public Rect getOverscanFrameLw() { - return windowFrames.mOverscanFrame; + return mWindowFrames.mOverscanFrame; } @Override public Rect getContentFrameLw() { - return windowFrames.mContentFrame; + return mWindowFrames.mContentFrame; } @Override public Rect getVisibleFrameLw() { - return windowFrames.mVisibleFrame; + return mWindowFrames.mVisibleFrame; } public Rect getStableFrame() { - return windowFrames.mStableFrame; + return mWindowFrames.mStableFrame; } public Rect getDecorFrame() { - return windowFrames.mDecorFrame; + return mWindowFrames.mDecorFrame; } @Override @@ -252,6 +251,11 @@ public class FakeWindowState implements WindowManagerPolicy.WindowState { } @Override + public WindowFrames getWindowFrames() { + return mWindowFrames; + } + + @Override public boolean isInputMethodTarget() { return false; } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java index e64823085d35..088672973524 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java @@ -202,8 +202,8 @@ public class WindowFrameTests extends WindowTestsBase { // When mFrame extends past cf, the content insets are // the difference between mFrame and ContentFrame. Visible // and stable frames work the same way. - final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect); - w.computeFrameLw(windowFrames); + w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect); + w.computeFrameLw(); assertFrame(w, 0, 0, 1000, 1000); assertContentInset(w, 0, topContentInset, 0, bottomContentInset); assertVisibleInset(w, 0, topVisibleInset, 0, bottomVisibleInset); @@ -217,7 +217,7 @@ public class WindowFrameTests extends WindowTestsBase { w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT w.mRequestedWidth = 100; w.mRequestedHeight = 100; - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertFrame(w, 100, 100, 200, 200); assertContentInset(w, 0, 0, 0, 0); // In this case the frames are shrunk to the window frame. @@ -238,8 +238,8 @@ public class WindowFrameTests extends WindowTestsBase { // Here the window has FILL_PARENT, FILL_PARENT // so we expect it to fill the entire available frame. - final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf); - w.computeFrameLw(windowFrames); + w.getWindowFrames().setFrames(pf, pf, pf, pf, pf, pf, pf, pf); + w.computeFrameLw(); assertFrame(w, 0, 0, 1000, 1000); // It can select various widths and heights within the bounds. @@ -247,14 +247,14 @@ public class WindowFrameTests extends WindowTestsBase { // and we use mRequestedWidth/mRequestedHeight w.mAttrs.width = 300; w.mAttrs.height = 300; - w.computeFrameLw(windowFrames); + w.computeFrameLw(); // Explicit width and height without requested width/height // gets us nothing. assertFrame(w, 0, 0, 0, 0); w.mRequestedWidth = 300; w.mRequestedHeight = 300; - w.computeFrameLw(windowFrames); + w.computeFrameLw(); // With requestedWidth/Height we can freely choose our size within the // parent bounds. assertFrame(w, 0, 0, 300, 300); @@ -267,14 +267,14 @@ public class WindowFrameTests extends WindowTestsBase { w.mRequestedWidth = -1; w.mAttrs.width = 100; w.mAttrs.height = 100; - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertFrame(w, 0, 0, 100, 100); w.mAttrs.flags = 0; // But sizes too large will be clipped to the containing frame w.mRequestedWidth = 1200; w.mRequestedHeight = 1200; - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertFrame(w, 0, 0, 1000, 1000); // Before they are clipped though windows will be shifted @@ -282,7 +282,7 @@ public class WindowFrameTests extends WindowTestsBase { w.mAttrs.y = 300; w.mRequestedWidth = 1000; w.mRequestedHeight = 1000; - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertFrame(w, 0, 0, 1000, 1000); // If there is room to move around in the parent frame the window will be shifted according @@ -292,16 +292,16 @@ public class WindowFrameTests extends WindowTestsBase { w.mRequestedWidth = 300; w.mRequestedHeight = 300; w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP; - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertFrame(w, 700, 0, 1000, 300); w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM; - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertFrame(w, 700, 700, 1000, 1000); // Window specified x and y are interpreted as offsets in the opposite // direction of gravity w.mAttrs.x = 100; w.mAttrs.y = 100; - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertFrame(w, 600, 600, 900, 900); } @@ -322,8 +322,9 @@ public class WindowFrameTests extends WindowTestsBase { w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); - final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect); - w.computeFrameLw(windowFrames); + final WindowFrames windowFrames = w.getWindowFrames(); + windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect); + w.computeFrameLw(); // For non fullscreen tasks the containing frame is based off the // task bounds not the parent frame. assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); @@ -336,7 +337,7 @@ public class WindowFrameTests extends WindowTestsBase { final int cfBottom = logicalHeight / 2; final Rect cf = new Rect(0, 0, cfRight, cfBottom); windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); int contentInsetRight = taskRight - cfRight; int contentInsetBottom = taskBottom - cfBottom; @@ -354,7 +355,7 @@ public class WindowFrameTests extends WindowTestsBase { final int insetBottom = insetTop + (taskBottom - taskTop); task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom); windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); contentInsetRight = insetRight - cfRight; contentInsetBottom = insetBottom - cfBottom; @@ -384,13 +385,14 @@ public class WindowFrameTests extends WindowTestsBase { // We use a decor content frame with insets to produce cropping. Rect dcf = new Rect(cf); - final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect); - w.computeFrameLw(windowFrames); + final WindowFrames windowFrames = w.getWindowFrames(); + windowFrames.setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect); + w.computeFrameLw(); assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom); windowFrames.mDecorFrame.setEmpty(); // Likewise with no decor frame we would get no crop - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight); // Now we set up a window which doesn't fill the entire decor frame. @@ -404,7 +406,7 @@ public class WindowFrameTests extends WindowTestsBase { w.mAttrs.height = logicalHeight / 2; w.mRequestedWidth = logicalWidth / 2; w.mRequestedHeight = logicalHeight / 2; - w.computeFrameLw(windowFrames); + w.computeFrameLw(); // Normally the crop is shrunk from the decor frame // to the computed window frame. @@ -437,8 +439,9 @@ public class WindowFrameTests extends WindowTestsBase { w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); - final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect); - w.computeFrameLw(windowFrames); + final WindowFrames windowFrames = w.getWindowFrames(); + windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect); + w.computeFrameLw(); // For non fullscreen tasks the containing frame is based off the // task bounds not the parent frame. assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); @@ -455,7 +458,7 @@ public class WindowFrameTests extends WindowTestsBase { pf.set(0, 0, logicalWidth, logicalHeight); task.mFullscreenForTest = true; windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertFrame(w, cf.left, cf.top, cf.right, cf.bottom); assertContentFrame(w, cf); assertContentInset(w, 0, 0, 0, 0); @@ -473,9 +476,10 @@ public class WindowFrameTests extends WindowTestsBase { final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height()); - final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf); + final WindowFrames windowFrames = w.getWindowFrames(); + windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf); windowFrames.setDisplayCutout(cutout); - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50); assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0); @@ -497,9 +501,10 @@ public class WindowFrameTests extends WindowTestsBase { final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height()); - final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf); + final WindowFrames windowFrames = w.getWindowFrames(); + windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf); windowFrames.setDisplayCutout(cutout); - w.computeFrameLw(windowFrames); + w.computeFrameLw(); assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50); assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java index b43d9a671751..6af3ea763c13 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java @@ -391,14 +391,14 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testDisplayCutoutIsCalculatedRelativeToFrame() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - WindowFrames wf = new WindowFrames(); + WindowFrames wf = app.getWindowFrames(); wf.mParentFrame.set(7, 10, 185, 380); wf.mDisplayFrame.set(wf.mParentFrame); final DisplayCutout cutout = new DisplayCutout(new Rect(0, 15, 0, 22), Arrays.asList(new Rect(95, 0, 105, 15), new Rect(95, 378, 105, 400))); wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400))); - app.computeFrameLw(wf); + app.computeFrameLw(); assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20))); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 91f4bc837eaa..702161e48b75 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -45,6 +45,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; +import android.content.res.XmlResourceParser; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioManagerInternal; @@ -60,6 +61,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArrayMap; +import android.util.Log; import android.util.Xml; import com.android.internal.R; @@ -74,12 +76,16 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -88,7 +94,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ConditionProviders mConditionProviders; @Mock NotificationManager mNotificationManager; - @Mock private Resources mResources; + private Resources mResources; private TestableLooper mTestableLooper; private ZenModeHelper mZenModeHelperSpy; private Context mContext; @@ -101,11 +107,17 @@ public class ZenModeHelperTest extends UiServiceTestCase { mTestableLooper = TestableLooper.get(this); mContext = spy(getContext()); mContentResolver = mContext.getContentResolver(); - when(mContext.getResources()).thenReturn(mResources); - when(mResources.getString(R.string.zen_mode_default_every_night_name)).thenReturn("night"); - when(mResources.getString(R.string.zen_mode_default_events_name)).thenReturn("events"); - when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager); + mResources = spy(mContext.getResources()); + try { + when(mResources.getXml(R.xml.default_zen_mode_config)).thenReturn( + getDefaultConfigParser()); + } catch (Exception e) { + Log.d("ZenModeHelperTest", "Couldn't mock default zen mode config xml file err=" + + e.toString()); + } + + when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager); mConditionProviders = new ConditionProviders(mContext, new UserProfiles(), AppGlobals.getPackageManager()); mConditionProviders.addSystemProvider(new CountdownConditionProvider()); @@ -113,6 +125,30 @@ public class ZenModeHelperTest extends UiServiceTestCase { mConditionProviders)); } + private XmlResourceParser getDefaultConfigParser() throws IOException, XmlPullParserException { + String xml = "<zen version=\"8\" user=\"0\">\n" + + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" " + + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" " + + "visualScreenOff=\"true\" alarms=\"true\" " + + "media=\"true\" system=\"false\" />\n" + + "<automatic ruleId=\"EVENTS_DEFAULT_RULE\" enabled=\"false\" snoozing=\"false\"" + + " name=\"Event\" zen=\"1\"" + + " component=\"android/com.android.server.notification.EventConditionProvider\"" + + " conditionId=\"condition://android/event?userId=-10000&calendar=&" + + "reply=1\"/>\n" + + "<automatic ruleId=\"EVERY_NIGHT_DEFAULT_RULE\" enabled=\"false\"" + + " snoozing=\"false\" name=\"Sleeping\" zen=\"1\"" + + " component=\"android/com.android.server.notification.ScheduleConditionProvider\"" + + " conditionId=\"condition://android/schedule?days=1.2.3.4.5.6.7 &start=22.0" + + "&end=7.0&exitAtAlarm=true\"/>" + + "<disallow visualEffects=\"511\" />" + + "</zen>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); + parser.nextTag(); + return new XmlResourceParserImpl(parser); + } + private ByteArrayOutputStream writeXmlAndPurge(boolean forBackup, Integer version) throws Exception { XmlSerializer serializer = new FastXmlSerializer(); @@ -649,8 +685,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { customRule.id = "customRule"; customRule.name = "Custom Rule"; customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; - customRule.component = new ComponentName("test", "test"); customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo); + customRule.component = new ComponentName("android", "ScheduleConditionProvider"); automaticRules.put("customRule", customRule); mZenModeHelperSpy.mConfig.automaticRules = automaticRules; @@ -662,8 +698,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); mZenModeHelperSpy.readXml(parser, true); - assertEquals(original.hashCode(), mZenModeHelperSpy.mConfig.hashCode()); assertEquals(original, mZenModeHelperSpy.mConfig); + assertEquals(original.hashCode(), mZenModeHelperSpy.mConfig.hashCode()); } @Test @@ -678,6 +714,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { customRule.name = "Custom Rule"; customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights); + customRule.component = new ComponentName("android", "ScheduleConditionProvider"); enabledAutoRule.put("customRule", customRule); mZenModeHelperSpy.mConfig.automaticRules = enabledAutoRule; @@ -842,6 +879,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { customRule.name = "Custom Rule"; customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights); + customRule.component = new ComponentName("android", "ScheduleConditionProvider"); disabledAutoRule.put("customRule", customRule); mZenModeHelperSpy.mConfig.automaticRules = disabledAutoRule; @@ -877,6 +915,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { customRule.name = "Custom Rule"; customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo); + customRule.component = new ComponentName("android", "ScheduleConditionProvider"); automaticRules.put("customRule", customRule); ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule(); @@ -886,6 +925,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { defaultScheduleRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId( defaultScheduleRuleInfo); + customRule.component = new ComponentName("android", "ScheduleConditionProvider"); defaultScheduleRule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID; automaticRules.put(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID, defaultScheduleRule); @@ -922,6 +962,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { customRule.name = "Custom Rule"; customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo); + customRule.component = new ComponentName("android", "ScheduleConditionProvider"); automaticRules.put("customRule", customRule); ZenModeConfig.ZenRule defaultScheduleRule = new ZenModeConfig.ZenRule(); @@ -1015,4 +1056,294 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertTrue(mZenModeHelperSpy.mConfig.allowRepeatCallers); assertEquals(SUPPRESSED_EFFECT_BADGE, mZenModeHelperSpy.mConfig.suppressedVisualEffects); } + + /** + * Wrapper to use XmlPullParser as XmlResourceParser for Resources.getXml() + */ + final class XmlResourceParserImpl implements XmlResourceParser { + private XmlPullParser parser; + + public XmlResourceParserImpl(XmlPullParser parser) { + this.parser = parser; + } + + public int getEventType() throws XmlPullParserException { + return parser.getEventType(); + } + + @Override + public void setFeature(String name, boolean state) throws XmlPullParserException { + parser.setFeature(name, state); + } + + @Override + public boolean getFeature(String name) { + return false; + } + + @Override + public void setProperty(String name, Object value) throws XmlPullParserException { + parser.setProperty(name, value); + } + + @Override + public Object getProperty(String name) { + return parser.getProperty(name); + } + + @Override + public void setInput(Reader in) throws XmlPullParserException { + parser.setInput(in); + } + + @Override + public void setInput(InputStream inputStream, String inputEncoding) + throws XmlPullParserException { + parser.setInput(inputStream, inputEncoding); + } + + @Override + public String getInputEncoding() { + return parser.getInputEncoding(); + } + + @Override + public void defineEntityReplacementText(String entityName, String replacementText) + throws XmlPullParserException { + parser.defineEntityReplacementText(entityName, replacementText); + } + + @Override + public int getNamespaceCount(int depth) throws XmlPullParserException { + return parser.getNamespaceCount(depth); + } + + @Override + public String getNamespacePrefix(int pos) throws XmlPullParserException { + return parser.getNamespacePrefix(pos); + } + + @Override + public String getNamespaceUri(int pos) throws XmlPullParserException { + return parser.getNamespaceUri(pos); + } + + @Override + public String getNamespace(String prefix) { + return parser.getNamespace(prefix); + } + + @Override + public int getDepth() { + return parser.getDepth(); + } + + @Override + public String getPositionDescription() { + return parser.getPositionDescription(); + } + + @Override + public int getLineNumber() { + return parser.getLineNumber(); + } + + @Override + public int getColumnNumber() { + return parser.getColumnNumber(); + } + + @Override + public boolean isWhitespace() throws XmlPullParserException { + return parser.isWhitespace(); + } + + @Override + public String getText() { + return parser.getText(); + } + + @Override + public char[] getTextCharacters(int[] holderForStartAndLength) { + return parser.getTextCharacters(holderForStartAndLength); + } + + @Override + public String getNamespace() { + return parser.getNamespace(); + } + + @Override + public String getName() { + return parser.getName(); + } + + @Override + public String getPrefix() { + return parser.getPrefix(); + } + + @Override + public boolean isEmptyElementTag() throws XmlPullParserException { + return false; + } + + @Override + public int getAttributeCount() { + return parser.getAttributeCount(); + } + + public int next() throws IOException, XmlPullParserException { + return parser.next(); + } + + @Override + public int nextToken() throws XmlPullParserException, IOException { + return parser.next(); + } + + @Override + public void require(int type, String namespace, String name) + throws XmlPullParserException, IOException { + parser.require(type, namespace, name); + } + + @Override + public String nextText() throws XmlPullParserException, IOException { + return parser.nextText(); + } + + @Override + public String getAttributeNamespace(int index) { + return ""; + } + + @Override + public String getAttributeName(int index) { + return parser.getAttributeName(index); + } + + @Override + public String getAttributePrefix(int index) { + return parser.getAttributePrefix(index); + } + + @Override + public String getAttributeType(int index) { + return parser.getAttributeType(index); + } + + @Override + public boolean isAttributeDefault(int index) { + return parser.isAttributeDefault(index); + } + + @Override + public String getAttributeValue(int index) { + return parser.getAttributeValue(index); + } + + @Override + public String getAttributeValue(String namespace, String name) { + return parser.getAttributeValue(namespace, name); + } + + @Override + public int getAttributeNameResource(int index) { + return 0; + } + + @Override + public int getAttributeListValue(String namespace, String attribute, String[] options, + int defaultValue) { + return 0; + } + + @Override + public boolean getAttributeBooleanValue(String namespace, String attribute, + boolean defaultValue) { + return false; + } + + @Override + public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) { + return 0; + } + + @Override + public int getAttributeIntValue(String namespace, String attribute, int defaultValue) { + return 0; + } + + @Override + public int getAttributeUnsignedIntValue(String namespace, String attribute, + int defaultValue) { + return 0; + } + + @Override + public float getAttributeFloatValue(String namespace, String attribute, + float defaultValue) { + return 0; + } + + @Override + public int getAttributeListValue(int index, String[] options, int defaultValue) { + return 0; + } + + @Override + public boolean getAttributeBooleanValue(int index, boolean defaultValue) { + return false; + } + + @Override + public int getAttributeResourceValue(int index, int defaultValue) { + return 0; + } + + @Override + public int getAttributeIntValue(int index, int defaultValue) { + return 0; + } + + @Override + public int getAttributeUnsignedIntValue(int index, int defaultValue) { + return 0; + } + + @Override + public float getAttributeFloatValue(int index, float defaultValue) { + return 0; + } + + @Override + public String getIdAttribute() { + return null; + } + + @Override + public String getClassAttribute() { + return null; + } + + @Override + public int getIdAttributeResourceValue(int defaultValue) { + return 0; + } + + @Override + public int getStyleAttribute() { + return 0; + } + + @Override + public void close() { + } + + @Override + public int nextTag() throws IOException, XmlPullParserException { + return parser.nextTag(); + } + } } diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 91e24a955013..e40bae18d4f0 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -603,7 +603,7 @@ public class SmsMessage { /** * Returns the message body as a String, if it exists and is text based. - * @return message body is there is one, otherwise null + * @return message body if there is one, otherwise null */ public String getMessageBody() { return mWrappedSmsMessage.getMessageBody(); diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index e9423f75c9e1..777b850bc9c5 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -86,9 +86,8 @@ public class SubscriptionManager { /** @hide */ public static final int INVALID_PHONE_INDEX = -1; - /** An invalid slot identifier */ - /** @hide */ - public static final int INVALID_SIM_SLOT_INDEX = -1; + /** Indicates invalid sim slot. This can be returned by {@link #getSlotIndex(int)}. */ + public static final int INVALID_SIM_SLOT_INDEX = -2; /** Indicates the caller wants the default sub id. */ /** @hide */ @@ -139,9 +138,8 @@ public class SubscriptionManager { /** @hide */ public static final String SIM_SLOT_INDEX = "sim_id"; - /** SIM is not inserted */ - /** @hide */ - public static final int SIM_NOT_INSERTED = -1; + /** Indicates SIM is not inserted. This can be returned by {@link #getSlotIndex(int)}. */ + public static final int SIM_NOT_INSERTED = -3; /** * TelephonyProvider column name for user displayed name. @@ -1264,16 +1262,22 @@ public class SubscriptionManager { /** * Get slotIndex associated with the subscription. - * @return slotIndex as a positive integer or a negative value if an error either - * SIM_NOT_INSERTED or < 0 if an invalid slot index - * @hide - */ - @UnsupportedAppUsage - public static int getSlotIndex(int subId) { - if (!isValidSubscriptionId(subId)) { + * + * @param subscriptionId the unique SubscriptionInfo index in database + * @return slotIndex as a positive integer or a negative value, + * <ol> + * <li>{@link #INVALID_SUBSCRIPTION_ID} if the supplied subscriptionId is invalid </li> + * <li>{@link #SIM_NOT_INSERTED} if sim is not inserted </li> + * <li>{@link #INVALID_SIM_SLOT_INDEX} if the supplied subscriptionId doesn't have an + * associated slot index </li> + * </ol> + */ + public static int getSlotIndex(int subscriptionId) { + if (!isValidSubscriptionId(subscriptionId)) { if (DBG) { - logd("[getSlotIndex]- fail"); + logd("[getSlotIndex]- supplied subscriptionId is invalid. "); } + return INVALID_SUBSCRIPTION_ID; } int result = INVALID_SIM_SLOT_INDEX; @@ -1281,7 +1285,7 @@ public class SubscriptionManager { try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); if (iSub != null) { - result = iSub.getSlotIndex(subId); + result = iSub.getSlotIndex(subscriptionId); } } catch (RemoteException ex) { // ignore it diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index b5d1f0658edd..a8bcbe33966a 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -69,6 +69,7 @@ import com.android.internal.telephony.IAnas; import com.android.internal.telephony.IPhoneSubInfo; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.ITelephonyRegistry; +import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.TelephonyProperties; @@ -5789,21 +5790,46 @@ public class TelephonyManager { * @param persistSelection whether the selection will persist until reboot. If true, only allows * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume * normal network selection next time. - * @return true on success; false on any failure. + * @return {@code true} on success; {@code false} on any failure. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) { + return setNetworkSelectionModeManual( + new OperatorInfo( + "" /* operatorAlphaLong */, "" /* operatorAlphaShort */, operatorNumeric), + persistSelection); + } + + /** + * Ask the radio to connect to the input network and change selection mode to manual. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + * <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 operatorInfo included the PLMN id, long name, short name of the operator to attach to. + * @param persistSelection whether the selection will persist until reboot. If true, only allows + * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume + * normal network selection next time. + * @return {@code true} on success; {@code true} on any failure. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public boolean setNetworkSelectionModeManual( + OperatorInfo operatorInfo, boolean persistSelection) { try { ITelephony telephony = getITelephony(); if (telephony != null) { return telephony.setNetworkSelectionModeManual( - getSubId(), operatorNumeric, persistSelection); + getSubId(), operatorInfo, persistSelection); } } catch (RemoteException ex) { Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex); - } catch (NullPointerException ex) { - Rlog.e(TAG, "setNetworkSelectionModeManual NPE", ex); } return false; } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index c0bccde2cda3..afb1d82b5e9f 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -865,14 +865,15 @@ interface ITelephony { * Ask the radio to connect to the input network and change selection mode to manual. * * @param subId the id of the subscription. - * @param operatorNumeric the PLMN of the operator to attach to. - * @param persistSelection Whether the selection will persist until reboot. If true, only allows + * @param operatorInfo the operator inforamtion, included the PLMN, long name and short name of + * the operator to attach to. + * @param persistSelection whether the selection will persist until reboot. If true, only allows * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume * normal network selection next time. - * @return true if the request suceeded. + * @return {@code true} on success; {@code true} on any failure. */ - boolean setNetworkSelectionModeManual(int subId, in String operatorNumeric, - boolean persistSelection); + boolean setNetworkSelectionModeManual( + int subId, in OperatorInfo operatorInfo, boolean persisSelection); /** * Set the preferred network type. diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java index d0245a0a07b4..a47e2b026021 100644 --- a/telephony/java/com/android/internal/telephony/OperatorInfo.java +++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java @@ -21,7 +21,7 @@ import android.os.Parcel; import android.os.Parcelable; /** - * {@hide} + * @hide */ public class OperatorInfo implements Parcelable { public enum State { diff --git a/tests/ImfTest/Android.mk b/tests/ImfTest/Android.mk deleted file mode 100644 index a8f5b0867c65..000000000000 --- a/tests/ImfTest/Android.mk +++ /dev/null @@ -1,16 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -# Only compile source java files in this apk. -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := ImfTest -LOCAL_PRIVATE_PLATFORM_APIS := true - -include $(BUILD_PACKAGE) - -# Use the following include to make our test apk. -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tests/ImfTest/AndroidManifest.xml b/tests/ImfTest/AndroidManifest.xml deleted file mode 100644 index 82dbe753534c..000000000000 --- a/tests/ImfTest/AndroidManifest.xml +++ /dev/null @@ -1,146 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.imftest"> - - <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> - - <application> - - <activity android:name=".samples.InputTypeActivity" android:label="Input Type Activity"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.ButtonActivity" android:label="Button Activity"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.BigEditTextActivityNonScrollablePanScan" android:label="Big ET !Scroll Pan/Scan"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.ManyEditTextActivityNoScrollPanScan" android:label="ManyEditTextActivityNoScrollPanScan"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.BigEditTextActivityNonScrollableResize" android:label="Big ET !Scroll Resize"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.BigEditTextActivityScrollablePanScan" android:label="Big ET Scroll Pan/Scan"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.BigEditTextActivityScrollableResize" android:label="Big ET Scroll Resize"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.EditTextActivityDialog" android:label="ET Dialog"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.ManyEditTextActivityScrollPanScan" android:label="ManyEditTextActivityScrollPanScan"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.ManyEditTextActivityScrollResize" android:label="ManyEditTextActivityScrollResize"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.BottomEditTextActivityPanScan" android:label="BottomEditTextActivityPanScan"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.BottomEditTextActivityResize" android:label="BottomEditTextActivityResize"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.OneEditTextActivitySelected" android:label="OneEditTextActivitySelected"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.OneEditTextActivityNotSelected" android:label="OneEditTextActivityNotSelected"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.AutoCompleteTextViewActivityPortrait" android:label="AutoCompleteTextViewActivityPortrait" android:screenOrientation="portrait"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.AutoCompleteTextViewActivityLandscape" android:label="AutoCompleteTextViewActivityLandscape" android:screenOrientation="landscape"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - <activity android:name=".samples.DialogActivity" android:label="DialogActivity" android:screenOrientation="portrait"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER" /> - <category android:name="android.intent.category.IMF_TEST" /> - </intent-filter> - </activity> - - </application> - -</manifest> diff --git a/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml b/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml deleted file mode 100644 index 1a2b7eb0407e..000000000000 --- a/tests/ImfTest/res/layout/dialog_edit_text_no_scroll.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2009 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:padding="20dip" - android:orientation="vertical"> - - <View - android:id="@+id/blank" - android:layout_height="0dip" - android:layout_width="match_parent" - android:layout_weight="1"/> - - <EditText - android:id="@+id/dialog_edit_text" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:scrollHorizontally="true" - android:textAppearance="?android:attr/textAppearanceMedium" /> - -</LinearLayout> diff --git a/tests/ImfTest/res/layout/full_screen_edit_text.xml b/tests/ImfTest/res/layout/full_screen_edit_text.xml deleted file mode 100644 index e760ac181245..000000000000 --- a/tests/ImfTest/res/layout/full_screen_edit_text.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/samples/SampleCode/res/layout/baseline_1.xml -** -** Copyright 2009, 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. -*/ ---> - -<EditText xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/data" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:minLines="15" - android:gravity="top"/> - diff --git a/tests/ImfTest/res/layout/one_edit_text_activity.xml b/tests/ImfTest/res/layout/one_edit_text_activity.xml deleted file mode 100644 index 055822823bf5..000000000000 --- a/tests/ImfTest/res/layout/one_edit_text_activity.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/samples/SampleCode/res/layout/baseline_1.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" -> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:baselineAligned="false"> - - <View android:id="@+id/blank" - android:layout_height="0dip" - android:layout_width="match_parent" - android:layout_weight="1" - /> - - <EditText android:id="@+id/dialog_edit_text" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:scrollHorizontally="true" - android:textAppearance="?android:attr/textAppearanceMedium" - /> - </LinearLayout> - - <View - android:layout_width="match_parent" - android:layout_height="1dip" - android:background="@android:drawable/divider_horizontal_dark" - /> -</LinearLayout> diff --git a/tests/ImfTest/res/layout/sample_edit_text.xml b/tests/ImfTest/res/layout/sample_edit_text.xml deleted file mode 100644 index 3ff676727662..000000000000 --- a/tests/ImfTest/res/layout/sample_edit_text.xml +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/samples/SampleCode/res/layout/baseline_1.xml -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" -> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="?android:attr/listPreferredItemHeight" - android:orientation="horizontal" - android:baselineAligned="false" - android:gravity="center_vertical" - > - - <TextView android:id="@+id/label" - android:layout_width="100dip" - android:layout_height="wrap_content" - android:gravity="right|center_vertical" - /> - - <EditText android:id="@+id/data" - android:layout_width="0dip" - android:layout_weight="1" - android:layout_height="wrap_content" - android:layout_marginLeft="8dip" - /> - </LinearLayout> - - <View - android:layout_width="match_parent" - android:layout_height="1dip" - android:background="@android:drawable/divider_horizontal_dark" - /> -</LinearLayout> diff --git a/tests/ImfTest/res/values/config.xml b/tests/ImfTest/res/values/config.xml deleted file mode 100644 index 5ae40a381523..000000000000 --- a/tests/ImfTest/res/values/config.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright (c) 2009, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ ---> -<resources> - <bool name="def_expect_ime_autopop">false</bool> -</resources> diff --git a/tests/ImfTest/res/values/strings.xml b/tests/ImfTest/res/values/strings.xml deleted file mode 100644 index fc87480788d6..000000000000 --- a/tests/ImfTest/res/values/strings.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Strings for sample activities --> - <string name="normal_edit_text_label">Normal</string> - <string name="uri_edit_text_label">Uri</string> - <string name="email_address_edit_text_label">Email Address</string> - <string name="email_subject_edit_text_label">Email Subject</string> - <string name="email_content_edit_text_label">Email Content</string> - <string name="person_name_edit_text_label">Person Name</string> - <string name="postal_address_edit_text_label">Postal Address</string> - <string name="password_edit_text_label">Password</string> - <string name="search_string_edit_text_label">Search String</string> - <string name="web_edit_text_label">Web Edit Text</string> - <string name="signed_number_edit_text_label">Signed Number</string> - <string name="decimal_number_edit_text_label">Decimal Number</string> - <string name="phone_number_edit_text_label">Phone Number</string> - <string name="normal_datetime_edit_text_label">Datetime</string> - <string name="date_edit_text_label">Date</string> - <string name="time_edit_text_label">Time</string> - <string name="cap_chars_edit_text_label">Cap Chars</string> - <string name="cap_words_edit_text_label">Cap Words</string> - <string name="multiline_edit_text_label">Multiline</string> - <string name="search_edit_text_label">Search (flag)</string> - <string name="cap_sentences_edit_text_label">Cap Sentences</string> - <string name="auto_complete_edit_text_label">Auto Complete</string> - <string name="auto_correct_edit_text_label">Auto Correct</string> - <string name="test_dialog">Test Dialog</string> - <string name="open_dialog_scrollable">open scrollable dialog</string> - <string name="open_dialog_nonscrollable">open nonscrollable dialog</string> - - -</resources> diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java deleted file mode 100644 index 6115fd5a0fd0..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.os.RemoteException; -import android.provider.MediaStore; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.AutoCompleteTextView; -import android.widget.ArrayAdapter; -import android.content.Intent; -import android.content.pm.ActivityInfo; - -import com.android.internal.R; - -/* - * Activity with AutoCompleteTextView forced to landscape mode - */ -public class AutoCompleteTextViewActivityLandscape extends Activity -{ - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - setContentView(R.layout.auto_complete_list); - - ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, - android.R.layout.simple_dropdown_item_1line, COUNTRIES); - AutoCompleteTextView textView = findViewById(R.id.edit); - textView.setAdapter(adapter); - } - - static final String[] COUNTRIES = new String[] { - "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", - "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", - "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", - "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", - "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", - "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", - "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi", - "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde", - "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", - "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", - "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia", - "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic", - "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", - "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland", - "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia", - "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar", - "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau", - "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary", - "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica", - "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos", - "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", - "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", - "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova", - "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", - "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", - "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas", - "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", - "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar", - "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena", - "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon", - "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal", - "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", - "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea", - "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden", - "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas", - "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", - "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda", - "Ukraine", "United Arab Emirates", "United Kingdom", - "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", - "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara", - "Yemen", "Yugoslavia", "Zambia", "Zimbabwe" - }; -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java deleted file mode 100644 index 253c50fe5810..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.AutoCompleteTextView; -import android.widget.ArrayAdapter; - -import com.android.internal.R; - -/* - * Activity with AutoCompleteTextView (Candidate bar should not appear) - */ -public class AutoCompleteTextViewActivityPortrait extends Activity -{ - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - setContentView(R.layout.auto_complete_list); - - ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, - android.R.layout.simple_dropdown_item_1line, COUNTRIES); - AutoCompleteTextView textView = findViewById(R.id.edit); - textView.setAdapter(adapter); - } - - static final String[] COUNTRIES = new String[] { - "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", - "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", - "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", - "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", - "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", - "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", - "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi", - "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde", - "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", - "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", - "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia", - "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic", - "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", - "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland", - "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia", - "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar", - "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau", - "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary", - "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica", - "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos", - "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", - "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", - "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova", - "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", - "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", - "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas", - "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", - "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar", - "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena", - "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon", - "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal", - "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", - "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea", - "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden", - "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas", - "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", - "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda", - "Ukraine", "United Arab Emirates", "United Kingdom", - "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", - "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara", - "Yemen", "Yugoslavia", "Zambia", "Zimbabwe" - }; -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java deleted file mode 100644 index 033082fbab42..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScan.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import com.android.imftest.R; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.LinearLayout; - -public class BigEditTextActivityNonScrollablePanScan extends Activity { - - private View mRootView; - private View mDefaultFocusedView; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - - mRootView = new LinearLayout(this); - ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL); - mRootView.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - View view = getLayoutInflater().inflate( - R.layout.full_screen_edit_text, ((LinearLayout) mRootView), false); - - ((LinearLayout) mRootView).addView(view); - - mDefaultFocusedView = view.findViewById(R.id.data); - - setContentView(mRootView); - } - - public View getRootView() { - return mRootView; - } - - public View getDefaultFocusedView() { - return mDefaultFocusedView; - } - -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java deleted file mode 100644 index 8a16deab9a40..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResize.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import com.android.imftest.R; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.LinearLayout; - -public class BigEditTextActivityNonScrollableResize extends Activity { - - private View mRootView; - private View mDefaultFocusedView; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - - mRootView = new LinearLayout(this); - ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL); - mRootView.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - View view = getLayoutInflater().inflate( - R.layout.full_screen_edit_text, ((LinearLayout) mRootView), false); - - ((LinearLayout) mRootView).addView(view); - - mDefaultFocusedView = view.findViewById(R.id.data); - - setContentView(mRootView); - } - - public View getRootView() { - return mRootView; - } - - public View getDefaultFocusedView() { - return mDefaultFocusedView; - } - -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java deleted file mode 100644 index b4fdc4c07584..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScan.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import com.android.imftest.R; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.widget.ScrollView; - -public class BigEditTextActivityScrollablePanScan extends Activity { - - private View mRootView; - private View mDefaultFocusedView; - private LinearLayout mLayout; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - - mRootView = new ScrollView(this); - ((ScrollView) mRootView).setFillViewport(true); - mRootView.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - mLayout = new LinearLayout(this); - mLayout.setOrientation(LinearLayout.VERTICAL); - mLayout.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - View view = getLayoutInflater().inflate( - R.layout.full_screen_edit_text, ((ScrollView) mRootView), false); - - mLayout.addView(view); - - ((ScrollView) mRootView).addView(mLayout); - mDefaultFocusedView = view.findViewById(R.id.data); - - setContentView(mRootView); - } - - public View getRootView() { - return mRootView; - } - - public View getDefaultFocusedView() { - return mDefaultFocusedView; - } - -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java b/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java deleted file mode 100644 index 757b6b5d57ce..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/BigEditTextActivityScrollableResize.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import com.android.imftest.R; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.widget.ScrollView; - -public class BigEditTextActivityScrollableResize extends Activity { - - private View mRootView; - private View mDefaultFocusedView; - private LinearLayout mLayout; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - - mRootView = new ScrollView(this); - ((ScrollView) mRootView).setFillViewport(true); - mRootView.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - mLayout = new LinearLayout(this); - mLayout.setOrientation(LinearLayout.VERTICAL); - mLayout.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - View view = getLayoutInflater().inflate( - R.layout.full_screen_edit_text, ((ScrollView) mRootView), false); - - mLayout.addView(view); - - ((ScrollView) mRootView).addView(mLayout); - mDefaultFocusedView = view.findViewById(R.id.data); - - setContentView(mRootView); - } - - public View getRootView() { - return mRootView; - } - - public View getDefaultFocusedView() { - return mDefaultFocusedView; - } - -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java deleted file mode 100644 index 91a329d25f90..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityPanScan.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.widget.EditText; -import android.widget.ScrollView; -import android.widget.TextView; - -import com.android.imftest.R; - -/* - * Activity with EditText at the bottom (Pan&Scan) - */ -public class BottomEditTextActivityPanScan extends Activity -{ - private View mRootView; - private View mDefaultFocusedView; - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - mRootView = new LinearLayout(this); - ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL); - - View view = getLayoutInflater().inflate(R.layout.one_edit_text_activity, ((LinearLayout) mRootView), false); - mDefaultFocusedView = view.findViewById(R.id.dialog_edit_text); - ((LinearLayout) mRootView).addView(view); - - setContentView(mRootView); - this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - } - - public View getRootView() { - return mRootView; - } - - public View getDefaultFocusedView() { - return mDefaultFocusedView; - } -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java b/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java deleted file mode 100644 index c4c41bc7d4ee..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/BottomEditTextActivityResize.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.widget.EditText; -import android.widget.ScrollView; -import android.widget.TextView; - -import com.android.imftest.R; - -/* - * Activity with EditText at the bottom (Resize) - */ -public class BottomEditTextActivityResize extends Activity -{ - private View mRootView; - private View mDefaultFocusedView; - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - mRootView = new LinearLayout(this); - ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL); - - View view = getLayoutInflater().inflate(R.layout.one_edit_text_activity, ((LinearLayout) mRootView), false); - mDefaultFocusedView = view.findViewById(R.id.dialog_edit_text); - ((LinearLayout) mRootView).addView(view); - - setContentView(mRootView); - this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - } - - public View getRootView() { - return mRootView; - } - - public View getDefaultFocusedView() { - return mDefaultFocusedView; - } -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java b/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java deleted file mode 100644 index dbaedf9f0af1..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.View; -import android.widget.LinearLayout; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.Button; -import android.widget.TextView; - -public class ButtonActivity extends Activity -{ - static boolean mKeyboardIsActive = false; - public static final int BUTTON_ID = 0; - private View mRootView; - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - final ButtonActivity instance = this; - - final Button myButton = new Button(this); - myButton.setClickable(true); - myButton.setText("Keyboard UP!"); - myButton.setId(BUTTON_ID); - myButton.setFocusableInTouchMode(true); - myButton.setOnClickListener(new View.OnClickListener() - { - public void onClick (View v) - { - InputMethodManager imm = InputMethodManager.getInstance(); - if (mKeyboardIsActive) - { - imm.hideSoftInputFromInputMethod(v.getWindowToken(), 0); - myButton.setText("Keyboard UP!"); - - } - else - { - myButton.requestFocusFromTouch(); - imm.showSoftInput(v, 0); - myButton.setText("Keyboard DOWN!"); - } - - mKeyboardIsActive = !mKeyboardIsActive; - } - }); - - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - layout.addView(myButton); - setContentView(layout); - mRootView = layout; - } - - public View getRootView() { - return mRootView; - } -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java b/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java deleted file mode 100644 index 3ed03862a04f..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/DialogActivity.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.EditText; -import android.widget.Button; -import android.view.LayoutInflater; -import android.app.Dialog; - -public class DialogActivity extends Activity { - - private static final int DIALOG_WITHOUT_EDITTEXT = 0; - private static final int DIALOG_WITH_EDITTEXT = 1; - - private LinearLayout mLayout; - private LayoutInflater mInflater; - private Button mButton1; - private Button mButton2; - private EditText mEditText; - - - @Override - protected void onCreate(Bundle icicle) - { - super.onCreate(icicle); - - mLayout = new LinearLayout(this); - mLayout.setOrientation(LinearLayout.VERTICAL); - mLayout.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - mButton1 = new Button(this); - mButton1.setText("Dialog WITHOUT EditText");//(R.string.open_dialog_scrollable); - mButton1.setOnClickListener(new View.OnClickListener() - { - public void onClick(View v) - { - showDialog(DIALOG_WITHOUT_EDITTEXT); - } - }); - - mButton2 = new Button(this); - mButton2.setText("Dialog WITH EditText");//(R.string.open_dialog_nonscrollable); - mButton2.setOnClickListener(new View.OnClickListener() - { - public void onClick(View v) - { - showDialog(DIALOG_WITH_EDITTEXT); - } - }); - - mEditText = new EditText(this); - mLayout.addView(mEditText); - mLayout.addView(mButton1); - mLayout.addView(mButton2); - - setContentView(mLayout); - } - - @Override - protected Dialog onCreateDialog(int id) - { - switch (id) - { - case DIALOG_WITHOUT_EDITTEXT: - return createDialog(false); - case DIALOG_WITH_EDITTEXT: - return createDialog(true); - } - - return super.onCreateDialog(id); - } - - protected Dialog createDialog(boolean bEditText) - { - LinearLayout layout; - layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - - if(bEditText) - { - EditText editText; - editText = new EditText(this); - layout.addView(editText); - } - - Dialog d = new Dialog(this); - d.setTitle("The DIALOG!!!"); - d.setCancelable(true); - d.setContentView(layout); - return d; - } - - } diff --git a/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java b/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java deleted file mode 100644 index 2591b7c96ae9..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/EditTextActivityDialog.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import com.android.imftest.R; - -import android.app.Activity; -import android.app.Dialog; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.ScrollView; - -public class EditTextActivityDialog extends Activity { - - private static final int SCROLLABLE_DIALOG_ID = 0; - private static final int NONSCROLLABLE_DIALOG_ID = 1; - - private LinearLayout mLayout; - private ScrollView mScrollView; - private LayoutInflater mInflater; - private Button mButton1; - private Button mButton2; - - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - mLayout = new LinearLayout(this); - mLayout.setOrientation(LinearLayout.VERTICAL); - mLayout.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - mButton1 = new Button(this); - mButton1.setText(R.string.open_dialog_scrollable); - mButton1.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - showDialog(SCROLLABLE_DIALOG_ID); - } - }); - - mButton2 = new Button(this); - mButton2.setText(R.string.open_dialog_nonscrollable); - mButton2.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - showDialog(NONSCROLLABLE_DIALOG_ID); - } - }); - - mLayout.addView(mButton1); - mLayout.addView(mButton2); - - setContentView(mLayout); - } - - @Override - protected Dialog onCreateDialog(int id) { - switch (id) { - case SCROLLABLE_DIALOG_ID: - return createDialog(true); - case NONSCROLLABLE_DIALOG_ID: - return createDialog(false); - } - - return super.onCreateDialog(id); - } - - protected Dialog createDialog(boolean scrollable) { - View layout; - EditText editText; - - if (scrollable) { - layout = new ScrollView(EditTextActivityDialog.this); - ((ScrollView) layout).setMinimumHeight(mLayout.getHeight()); - - ((ScrollView) layout).addView(( - LinearLayout) View.inflate(EditTextActivityDialog.this, - R.layout.dialog_edit_text_no_scroll, null)); - } else { - layout = View.inflate(EditTextActivityDialog.this, - R.layout.dialog_edit_text_no_scroll, null); - } - - Dialog d = new Dialog(EditTextActivityDialog.this); - d.setTitle(getString(R.string.test_dialog)); - d.setCancelable(true); - d.setContentView(layout); - return d; - } - -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java b/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java deleted file mode 100644 index 299e6bb2778e..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/InputTypeActivity.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import com.android.imftest.R; - -import android.app.Activity; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.TextView; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; - -public class InputTypeActivity extends Activity { - - private LinearLayout mLayout; - private ScrollView mScrollView; - private LayoutInflater mInflater; - private ViewGroup mParent; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - mScrollView = new ScrollView(this); - - mLayout = new LinearLayout(this); - mLayout.setOrientation(LinearLayout.VERTICAL); - mLayout.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - mInflater = getLayoutInflater(); - mParent = mLayout; - - /* Normal Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL, - R.string.normal_edit_text_label)); - - /* Normal Edit Text w/Cap Chars Flag*/ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS, - R.string.cap_chars_edit_text_label)); - - /* Normal Edit Text w/Cap Words Flag*/ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS, - R.string.cap_words_edit_text_label)); - - /* Normal Edit Text w/Cap Multiline Flag */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE, - R.string.multiline_edit_text_label)); - - /* Normal Edit Text w/Cap Sentences Flag */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES, - R.string.cap_sentences_edit_text_label)); - - /* Normal Edit Text w/Auto-complete Flag */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE, - R.string.auto_complete_edit_text_label)); - - /* Normal Edit Text w/Auto-correct Flag */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_NORMAL|EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT, - R.string.auto_correct_edit_text_label)); - - /* Uri Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_URI, - R.string.uri_edit_text_label)); - - /* Email Address Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, - R.string.email_address_edit_text_label)); - - /* Email Subject Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT, - R.string.email_subject_edit_text_label)); - - /* Email Content Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE, - R.string.email_content_edit_text_label)); - - /* Person Name Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME, - R.string.person_name_edit_text_label)); - - /* Postal Address Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, - R.string.postal_address_edit_text_label)); - - /* Password Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_PASSWORD, - R.string.password_edit_text_label)); - - /* Web Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_TEXT|EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT, - R.string.web_edit_text_label)); - - /* Signed Number Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_NUMBER|EditorInfo.TYPE_NUMBER_FLAG_SIGNED, - R.string.signed_number_edit_text_label)); - - /* Decimal Number Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_NUMBER|EditorInfo.TYPE_NUMBER_FLAG_DECIMAL, - R.string.decimal_number_edit_text_label)); - - /* Phone Number Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_PHONE, - R.string.phone_number_edit_text_label)); - - /* Normal Datetime Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_DATETIME|EditorInfo.TYPE_DATETIME_VARIATION_NORMAL, - R.string.normal_datetime_edit_text_label)); - - /* Date Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_DATETIME|EditorInfo.TYPE_DATETIME_VARIATION_DATE, - R.string.date_edit_text_label)); - - /* Time Edit Text */ - mLayout.addView(buildEntryView(EditorInfo.TYPE_CLASS_DATETIME|EditorInfo.TYPE_DATETIME_VARIATION_TIME, - R.string.time_edit_text_label)); - - mScrollView.addView(mLayout); - setContentView(mScrollView); - } - - private View buildEntryView(int inputType, int label) { - - - View view = mInflater.inflate(R.layout.sample_edit_text, mParent, false); - - EditText editText = (EditText) view.findViewById(R.id.data); - editText.setInputType(inputType); - - TextView textView = (TextView) view.findViewById(R.id.label); - textView.setText(label); - - return view; - } - -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java deleted file mode 100644 index 646e4805f32e..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScan.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.Button; -import android.widget.TextView; -import android.widget.ScrollView; - -import com.android.internal.R; - -/* - * Full screen of EditTexts (Non-Scrollable, Pan&Scan) - */ -public class ManyEditTextActivityNoScrollPanScan extends Activity -{ - public static final int NUM_EDIT_TEXTS = 9; - - private View mRootView; - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - mRootView = new LinearLayout(this); - ((LinearLayout) mRootView).setOrientation(LinearLayout.VERTICAL); - - for (int i=0; i<NUM_EDIT_TEXTS; i++) - { - final EditText editText = new EditText(this); - editText.setText(String.valueOf(i)); - editText.setId(i); - ((LinearLayout) mRootView).addView(editText); - } - setContentView(mRootView); - this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - } - - public View getRootView() { - return mRootView; - } - -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java deleted file mode 100644 index 0387e1ec5d13..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScan.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.Button; -import android.widget.TextView; -import android.widget.ScrollView; - -import com.android.internal.R; - -/* - * Full screen of EditTexts (Scrollable, Pan&Scan) - */ -public class ManyEditTextActivityScrollPanScan extends Activity -{ - public static final int NUM_EDIT_TEXTS = 12; - - private View mRootView; - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - mRootView = new ScrollView(this); - - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - - for (int i=0; i<NUM_EDIT_TEXTS; i++) - { - final EditText editText = new EditText(this); - editText.setText(String.valueOf(i)); - editText.setId(i); - layout.addView(editText); - } - - ((ScrollView) mRootView).addView(layout); - setContentView(mRootView); - this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - } - - public View getRootView() { - return mRootView; - } -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java b/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java deleted file mode 100644 index 7793b553d746..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/ManyEditTextActivityScrollResize.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.widget.EditText; -import android.widget.ScrollView; - -/* - * Full screen of EditTexts (Scrollable, Resize) - */ -public class ManyEditTextActivityScrollResize extends Activity -{ - public static final int NUM_EDIT_TEXTS = 12; - - private View mRootView; - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - mRootView = new ScrollView(this); - - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - - for (int i=0; i<NUM_EDIT_TEXTS; i++) - { - final EditText editText = new EditText(this); - editText.setText(String.valueOf(i)); - editText.setId(i); - layout.addView(editText); - } - - ((ScrollView) mRootView).addView(layout); - setContentView(mRootView); - this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - } - - public View getRootView() { - return mRootView; - } -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java deleted file mode 100644 index c4be21c7f2d8..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivityNotSelected.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.os.Debug; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.Button; -import android.widget.TextView; -import android.widget.ScrollView; - -import com.android.internal.R; - -/* - * Activity with non-EditText view selected initially - */ -public class OneEditTextActivityNotSelected extends Activity -{ - private View mRootView; - private View mDefaultFocusedView; - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - mRootView = new ScrollView(this); - - EditText editText = new EditText(this); - Button button = new Button(this); - button.setText("The focus is here."); - button.setFocusableInTouchMode(true); - button.requestFocus(); - mDefaultFocusedView = button; - layout.addView(button); - layout.addView(editText); - - ((ScrollView) mRootView).addView(layout); - setContentView(mRootView); - } - - public View getRootView() { - return mRootView; - } - - public View getDefaultFocusedView() { - return mDefaultFocusedView; - } -} diff --git a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java b/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java deleted file mode 100644 index 64882aa30eb1..000000000000 --- a/tests/ImfTest/src/com/android/imftest/samples/OneEditTextActivitySelected.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2009 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.imftest.samples; - -import android.app.Activity; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.LinearLayout; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.Button; -import android.widget.TextView; -import android.widget.ScrollView; - -import com.android.internal.R; - -/* - * Activity with EditText selected initially - */ -public class OneEditTextActivitySelected extends Activity -{ - private View mRootView; - private View mDefaultFocusedView; - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - mRootView = new ScrollView(this); - - EditText editText = new EditText(this); - editText.requestFocus(); - mDefaultFocusedView = editText; - layout.addView(editText); - - ((ScrollView) mRootView).addView(layout); - setContentView(mRootView); - - // set to resize so IME is always shown (and also so - // ImfBaseTestCase#destructiveCheckImeInitialState thinks it should always be shown - this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - } - - public View getRootView() { - return mRootView; - } - - public View getDefaultFocusedView() { - return mDefaultFocusedView; - } -} diff --git a/tests/ImfTest/tests/Android.mk b/tests/ImfTest/tests/Android.mk deleted file mode 100644 index 14186d7a5a87..000000000000 --- a/tests/ImfTest/tests/Android.mk +++ /dev/null @@ -1,18 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -# Include all test java files. -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base -LOCAL_STATIC_JAVA_LIBRARIES := junit - -LOCAL_PACKAGE_NAME := ImfTestTests -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_INSTRUMENTATION_FOR := ImfTest - -include $(BUILD_PACKAGE) diff --git a/tests/ImfTest/tests/AndroidManifest.xml b/tests/ImfTest/tests/AndroidManifest.xml deleted file mode 100644 index c02fa0b212a5..000000000000 --- a/tests/ImfTest/tests/AndroidManifest.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2007 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 name must be unique so suffix with "tests" so package loader doesn't ignore us --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.imftest.tests"> - - <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> - - <application> - <uses-library android:name="android.test.runner" /> - </application> - - <!-- - This declares that this app uses the instrumentation test runner targeting - the package of com.android.imftest. To run the tests use the command: - "adb shell am instrument -w com.android.imftest.tests/android.test.InstrumentationTestRunner" - --> - <instrumentation android:name="android.test.InstrumentationTestRunner" - android:targetPackage="com.android.imftest" - android:label="imf tests"/> - -</manifest> diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java deleted file mode 100644 index 2db11c56e7e3..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollablePanScanTests.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; -import android.view.View; - -import com.android.imftest.R; - - -public class BigEditTextActivityNonScrollablePanScanTests extends ImfBaseTestCase<BigEditTextActivityNonScrollablePanScan> { - - public final String TAG = "BigEditTextActivityNonScrollablePanScanTests"; - - public BigEditTextActivityNonScrollablePanScanTests() { - super(BigEditTextActivityNonScrollablePanScan.class); - } - - @LargeTest - public void testAppAdjustmentPanScan() { - // Give the IME 2 seconds to appear. - pause(2000); - - View rootView = ((BigEditTextActivityNonScrollablePanScan) mTargetActivity).getRootView(); - View servedView = ((BigEditTextActivityNonScrollablePanScan) mTargetActivity).getDefaultFocusedView(); - - assertNotNull(rootView); - assertNotNull(servedView); - - destructiveCheckImeInitialState(rootView, servedView); - - verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java deleted file mode 100644 index 1050794af642..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityNonScrollableResizeTests.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; -import android.view.View; - -import com.android.imftest.R; - - -public class BigEditTextActivityNonScrollableResizeTests extends ImfBaseTestCase<BigEditTextActivityNonScrollableResize> { - - public final String TAG = "BigEditTextActivityNonScrollableResizeTests"; - - public BigEditTextActivityNonScrollableResizeTests() { - super(BigEditTextActivityNonScrollableResize.class); - } - - @LargeTest - public void testAppAdjustmentPanScan() { // Give the IME 2 seconds to appear. - pause(2000); - - View rootView = ((BigEditTextActivityNonScrollableResize) mTargetActivity).getRootView(); - View servedView = ((BigEditTextActivityNonScrollableResize) mTargetActivity).getDefaultFocusedView(); - - assertNotNull(rootView); - assertNotNull(servedView); - - destructiveCheckImeInitialState(rootView, servedView); - - verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java deleted file mode 100644 index 1e848b051439..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollablePanScanTests.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; -import android.view.View; - -import com.android.imftest.R; - - -public class BigEditTextActivityScrollablePanScanTests extends ImfBaseTestCase<BigEditTextActivityScrollablePanScan> { - - public final String TAG = "BigEditTextActivityScrollablePanScanTests"; - - public BigEditTextActivityScrollablePanScanTests() { - super(BigEditTextActivityScrollablePanScan.class); - } - - @LargeTest - public void testAppAdjustmentPanScan() { // Give the IME 2 seconds to appear. - pause(2000); - - View rootView = ((BigEditTextActivityScrollablePanScan) mTargetActivity).getRootView(); - View servedView = ((BigEditTextActivityScrollablePanScan) mTargetActivity).getDefaultFocusedView(); - - assertNotNull(rootView); - assertNotNull(servedView); - - destructiveCheckImeInitialState(rootView, servedView); - - verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java deleted file mode 100644 index de607d659ebe..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/BigEditTextActivityScrollableResizeTests.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; -import android.view.View; - -import com.android.imftest.R; - - -public class BigEditTextActivityScrollableResizeTests extends ImfBaseTestCase<BigEditTextActivityScrollableResize> { - - public final String TAG = "BigEditTextActivityScrollableResizeTests"; - - public BigEditTextActivityScrollableResizeTests() { - super(BigEditTextActivityScrollableResize.class); - } - - @LargeTest - public void testAppAdjustmentPanScan() { - // Give the IME 2 seconds to appear. - pause(2000); - - View rootView = ((BigEditTextActivityScrollableResize) mTargetActivity).getRootView(); - View servedView = ((BigEditTextActivityScrollableResize) mTargetActivity).getDefaultFocusedView(); - - assertNotNull(rootView); - assertNotNull(servedView); - - destructiveCheckImeInitialState(rootView, servedView); - - verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java deleted file mode 100644 index c52190552e4f..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityPanScanTests.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; -import android.view.View; - -import com.android.imftest.R; - - -public class BottomEditTextActivityPanScanTests extends ImfBaseTestCase<BottomEditTextActivityPanScan> { - - public final String TAG = "BottomEditTextActivityPanScanTests"; - - public BottomEditTextActivityPanScanTests() { - super(BottomEditTextActivityPanScan.class); - } - - @LargeTest - public void testAppAdjustmentPanScan() { - // Give the IME 2 seconds to appear. - pause(2000); - - View rootView = ((BottomEditTextActivityPanScan) mTargetActivity).getRootView(); - View servedView = ((BottomEditTextActivityPanScan) mTargetActivity).getDefaultFocusedView(); - - assertNotNull(rootView); - assertNotNull(servedView); - - destructiveCheckImeInitialState(rootView, servedView); - - verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java deleted file mode 100644 index 9a69fd509844..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/BottomEditTextActivityResizeTests.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; -import android.view.View; - -import com.android.imftest.R; - - -public class BottomEditTextActivityResizeTests extends ImfBaseTestCase<BottomEditTextActivityResize> { - - public final String TAG = "BottomEditTextActivityResizeTests"; - - public BottomEditTextActivityResizeTests() { - super(BottomEditTextActivityResize.class); - } - - @LargeTest - public void testAppAdjustmentResize() { - // Give the IME 2 seconds to appear. - pause(2000); - - View rootView = ((BottomEditTextActivityResize) mTargetActivity).getRootView(); - View servedView = ((BottomEditTextActivityResize) mTargetActivity).getDefaultFocusedView(); - - assertNotNull(rootView); - assertNotNull(servedView); - - destructiveCheckImeInitialState(rootView, servedView); - - verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java deleted file mode 100644 index f6f97b517090..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/ButtonActivityTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; -import android.view.KeyEvent; -import android.widget.Button; - - -public class ButtonActivityTest extends ImfBaseTestCase<ButtonActivity> { - - final public String TAG = "ButtonActivityTest"; - - public ButtonActivityTest() { - super(ButtonActivity.class); - } - - @LargeTest - public void testButtonActivatesIme() { - - final Button button = (Button) mTargetActivity.findViewById(ButtonActivity.BUTTON_ID); - - // Push button - // Bring the target EditText into focus. - mTargetActivity.runOnUiThread(new Runnable() { - public void run() { - button.requestFocus(); - } - }); - - sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); - - // Give it a couple seconds - pause(2000); - - // We should have initialized imm.mServedView and imm.mCurrentTextBoxAttribute - assertTrue(mImm.isActive()); - // imm.mServedInputConnection should be null since Button doesn't override onCreateInputConnection(). - assertFalse(mImm.isAcceptingText()); - - destructiveCheckImeInitialState(mTargetActivity.getRootView(), button); - - } -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java deleted file mode 100644 index 32f80a3ad2d5..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.app.Activity; -import android.app.KeyguardManager; -import android.content.Context; -import android.content.Intent; -import android.content.res.Configuration; -import android.os.SystemClock; -import android.test.InstrumentationTestCase; -import android.view.KeyEvent; -import android.view.View; -import android.view.WindowManager; -import android.view.inputmethod.InputMethodManager; - -import com.android.imftest.R; - -public abstract class ImfBaseTestCase<T extends Activity> extends InstrumentationTestCase { - - /* - * The amount of time we are willing to wait for the IME to appear after a user action - * before we give up and fail the test. - */ - public final long WAIT_FOR_IME = 5000; - - /* - * Unfortunately there is now way for us to know how tall the IME is, - * so we have to hard code a minimum and maximum value. - */ - public final int IME_MIN_HEIGHT = 150; - public final int IME_MAX_HEIGHT = 300; - - protected InputMethodManager mImm; - protected T mTargetActivity; - protected boolean mExpectAutoPop; - private Class<T> mTargetActivityClass; - - public ImfBaseTestCase(Class<T> activityClass) { - mTargetActivityClass = activityClass; - } - - @Override - public void setUp() throws Exception { - super.setUp(); - final String packageName = getInstrumentation().getTargetContext().getPackageName(); - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - mTargetActivity = launchActivityWithIntent(packageName, mTargetActivityClass, intent); - // expect ime to auto pop up if device has no hard keyboard - int keyboardType = mTargetActivity.getResources().getConfiguration().keyboard; - mExpectAutoPop = (keyboardType == Configuration.KEYBOARD_NOKEYS || - keyboardType == Configuration.KEYBOARD_UNDEFINED); - - mImm = InputMethodManager.getInstance(); - - KeyguardManager keyguardManager = - (KeyguardManager) getInstrumentation().getContext().getSystemService( - Context.KEYGUARD_SERVICE); - keyguardManager.newKeyguardLock("imftest").disableKeyguard(); - } - - // Utility test methods - public void verifyEditTextAdjustment(final View editText, int rootViewHeight) { - - int[] origLocation = new int[2]; - int[] newLocation = new int[2]; - - // Tell the keyboard to go away. - mImm.hideSoftInputFromWindow(editText.getWindowToken(), 0); - - // Bring the target EditText into focus. - mTargetActivity.runOnUiThread(new Runnable() { - public void run() { - editText.requestFocus(); - } - }); - - // Get the original location of the EditText. - editText.getLocationOnScreen(origLocation); - - // Tap the EditText to bring up the IME. - sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); - - // Wait until the EditText pops above the IME or until we hit the timeout. - editText.getLocationOnScreen(newLocation); - long timeoutTime = SystemClock.uptimeMillis() + WAIT_FOR_IME; - while (newLocation[1] > rootViewHeight - IME_MIN_HEIGHT && SystemClock.uptimeMillis() < timeoutTime) { - editText.getLocationOnScreen(newLocation); - pause(100); - } - - assertTrue(newLocation[1] <= rootViewHeight - IME_MIN_HEIGHT); - - // Tell the keyboard to go away. - mImm.hideSoftInputFromWindow(editText.getWindowToken(), 0); - } - - public void destructiveCheckImeInitialState(View rootView, View servedView) { - int windowSoftInputMode = mTargetActivity.getWindow().getAttributes().softInputMode; - int adjustMode = windowSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; - if (mExpectAutoPop && adjustMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { - assertTrue(destructiveCheckImeUp(rootView, servedView)); - } else { - assertFalse(destructiveCheckImeUp(rootView, servedView)); - } - } - - public boolean destructiveCheckImeUp(View rootView, View servedView) { - int origHeight; - int newHeight; - - origHeight = rootView.getHeight(); - - // Tell the keyboard to go away. - mImm.hideSoftInputFromWindow(servedView.getWindowToken(), 0); - - // Give it five seconds to adjust - newHeight = rootView.getHeight(); - long timeoutTime = SystemClock.uptimeMillis() + WAIT_FOR_IME; - while (Math.abs(newHeight - origHeight) < IME_MIN_HEIGHT && SystemClock.uptimeMillis() < timeoutTime) { - newHeight = rootView.getHeight(); - } - - return (Math.abs(origHeight - newHeight) >= IME_MIN_HEIGHT); - } - - void pause(int millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - } - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java deleted file mode 100644 index 278efb1f68db..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityBaseTestCase.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.app.Activity; -import android.widget.EditText; - - -public abstract class ManyEditTextActivityBaseTestCase<T extends Activity> extends ImfBaseTestCase<T> { - - public ManyEditTextActivityBaseTestCase(Class<T> activityClass){ - super(activityClass); - } - - public abstract void testAllEditTextsAdjust(); - - public void verifyAllEditTextAdjustment(int numEditTexts, int rootViewHeight) { - - for (int i = 0; i < numEditTexts; i++) { - final EditText lastEditText = (EditText) mTargetActivity.findViewById(i); - verifyEditTextAdjustment(lastEditText, rootViewHeight); - } - - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java deleted file mode 100644 index 4f8d14e88ad5..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityNoScrollPanScanTests.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; - - -public class ManyEditTextActivityNoScrollPanScanTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityNoScrollPanScan> { - - public final String TAG = "ManyEditTextActivityNoScrollPanScanTests"; - - public ManyEditTextActivityNoScrollPanScanTests() { - super(ManyEditTextActivityNoScrollPanScan.class); - } - - - @LargeTest - public void testAllEditTextsAdjust() { - verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS, - mTargetActivity.getRootView().getMeasuredHeight()); - } -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java deleted file mode 100644 index 7f98f7fbdf21..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollPanScanTests.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; - - -public class ManyEditTextActivityScrollPanScanTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityScrollPanScan> { - - public final String TAG = "ManyEditTextActivityScrollPanScanTests"; - - - public ManyEditTextActivityScrollPanScanTests() { - super(ManyEditTextActivityScrollPanScan.class); - } - - @LargeTest - public void testAllEditTextsAdjust() { - verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS, - mTargetActivity.getRootView().getMeasuredHeight()); - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java deleted file mode 100644 index 68dae87ea5c9..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/ManyEditTextActivityScrollResizeTests.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; - - -public class ManyEditTextActivityScrollResizeTests extends ManyEditTextActivityBaseTestCase<ManyEditTextActivityScrollResize> { - - public final String TAG = "ManyEditTextActivityScrollResizeTests"; - - - public ManyEditTextActivityScrollResizeTests() { - super(ManyEditTextActivityScrollResize.class); - } - - @LargeTest - public void testAllEditTextsAdjust() { - verifyAllEditTextAdjustment(mTargetActivity.NUM_EDIT_TEXTS, - mTargetActivity.getRootView().getMeasuredHeight()); - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java deleted file mode 100644 index 6147d3c5c6c4..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivityNotSelectedTests.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import android.test.suitebuilder.annotation.LargeTest; -import android.view.View; - - -public class OneEditTextActivityNotSelectedTests extends ImfBaseTestCase<OneEditTextActivityNotSelected> { - - public final String TAG = "OneEditTextActivityNotSelectedTests"; - - public OneEditTextActivityNotSelectedTests() { - super(OneEditTextActivityNotSelected.class); - } - - @LargeTest - public void testSoftKeyboardNoAutoPop() { - - // Give the IME 2 seconds to appear. - pause(2000); - - assertFalse(mImm.isAcceptingText()); - - View rootView = ((OneEditTextActivityNotSelected) mTargetActivity).getRootView(); - View servedView = ((OneEditTextActivityNotSelected) mTargetActivity).getDefaultFocusedView(); - - assertNotNull(rootView); - assertNotNull(servedView); - - destructiveCheckImeInitialState(rootView, servedView); - - verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); - } - -} diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java b/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java deleted file mode 100644 index 42fcd66f7922..000000000000 --- a/tests/ImfTest/tests/src/com/android/imftest/samples/OneEditTextActivitySelectedTests.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2007 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.imftest.samples; - -import com.android.imftest.R; - -import android.test.suitebuilder.annotation.LargeTest; -import android.view.KeyEvent; -import android.view.View; - - -public class OneEditTextActivitySelectedTests extends ImfBaseTestCase<OneEditTextActivitySelected> { - - public final String TAG = "OneEditTextActivitySelectedTests"; - - public OneEditTextActivitySelectedTests() { - super(OneEditTextActivitySelected.class); - } - - @LargeTest - public void testSoftKeyboardAutoPop() { - - // Give the IME 2 seconds to appear. - pause(2000); - - assertTrue(mImm.isAcceptingText()); - - View rootView = ((OneEditTextActivitySelected) mTargetActivity).getRootView(); - View servedView = ((OneEditTextActivitySelected) mTargetActivity).getDefaultFocusedView(); - - assertNotNull(rootView); - assertNotNull(servedView); - - destructiveCheckImeInitialState(rootView, servedView); - - verifyEditTextAdjustment(servedView, rootView.getMeasuredHeight()); - } - -} diff --git a/tools/aapt2/development.md b/tools/aapt2/development.md new file mode 100644 index 000000000000..8ee873a0e40f --- /dev/null +++ b/tools/aapt2/development.md @@ -0,0 +1,11 @@ +# AAPT2 development + +## Building +All build targets can be found in `Android.bp` file. The main ones are `make -j aapt2` and `make -j aapt2_tests` + +`make -j aapt2` will create an aapt2 executable in `out/host/linux-x86/bin/aapt2` (on Linux). This `aapt2` executable will then be used for all the apps in the platform. + +Static version of the tool (without shared libraries) can be built with `make -j static_sdk_tools dist DIST_DIR=$OUTPUT_DIRECTORY BUILD_HOST_static=1`. Note, in addition to aapt2 this command will also output other statically built tools to the `$OUTPUT_DIRECTORY`. + +## Running tests +Build `make -j aapt2_tests` and then (on Linux) execute `out/host/linux-x86/nativetest64/aapt2_tests/aapt2_tests`
\ No newline at end of file |