Merge "Remove refs to BatteryStats in ProcessRecord"
diff --git a/Android.bp b/Android.bp
index 459f179..3957f7e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -479,6 +479,8 @@
"media/java/android/media/tv/ITvRemoteServiceInput.aidl",
"media/java/android/service/media/IMediaBrowserService.aidl",
"media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
+ "telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl",
+ "telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl",
"telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl",
"telecomm/java/com/android/internal/telecom/ICallScreeningService.aidl",
"telecomm/java/com/android/internal/telecom/IVideoCallback.aidl",
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index e126fb8..2b8b8f2 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -23,6 +23,8 @@
import com.android.internal.os.BinderCallsStats;
+import java.util.Random;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -45,7 +47,7 @@
@Before
public void setUp() {
- mBinderCallsStats = new BinderCallsStats();
+ mBinderCallsStats = new BinderCallsStats(new Random());
}
@After
diff --git a/api/current.txt b/api/current.txt
index e3db256..9a47a66 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -40959,6 +40959,16 @@
field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
}
+ public abstract class CallRedirectionService extends android.app.Service {
+ ctor public CallRedirectionService();
+ method public final void cancelCall();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+ method public final void placeCallUnmodified();
+ method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
+ }
+
public abstract class CallScreeningService extends android.app.Service {
ctor public CallScreeningService();
method public android.os.IBinder onBind(android.content.Intent);
@@ -70711,7 +70721,7 @@
ctor public InflaterInputStream(java.io.InputStream);
method protected void fill() throws java.io.IOException;
field protected byte[] buf;
- field protected boolean closed;
+ field protected deprecated boolean closed;
field protected java.util.zip.Inflater inf;
field protected int len;
}
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1119eb3..0c241fc 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -403,7 +403,7 @@
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 [--proto]\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");
@@ -567,12 +567,17 @@
int argCount = args.size();
bool good = false;
bool proto = false;
+ bool includeCurrentBucket = false;
int uid;
string name;
if (!std::strcmp("--proto", args[argCount-1].c_str())) {
proto = true;
argCount -= 1;
}
+ if (!std::strcmp("--include_current_bucket", args[argCount-1].c_str())) {
+ includeCurrentBucket = true;
+ argCount -= 1;
+ }
if (argCount == 2) {
// Automatically pick the UID
uid = IPCThreadState::self()->getCallingUid();
@@ -600,7 +605,7 @@
if (good) {
vector<uint8_t> data;
mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
- false /* include_current_bucket*/, ADB_DUMP, &data);
+ includeCurrentBucket, ADB_DUMP, &data);
if (proto) {
for (size_t i = 0; i < data.size(); i ++) {
fprintf(out, "%c", data[i]);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 279ed02..bf033a7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -49,7 +49,7 @@
oneof pushed {
// For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
BleScanStateChanged ble_scan_state_changed = 2;
- // 3 is available for use
+ ProcessStateChanged process_state_changed = 3;
BleScanResultReceived ble_scan_result_received = 4;
SensorStateChanged sensor_state_changed = 5;
GpsScanStateChanged gps_scan_state_changed = 6;
@@ -60,7 +60,12 @@
LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11;
MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12;
WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13;
- // 14 - 19 are available
+ ActivityManagerSleepStateChanged activity_manager_sleep_state_changed = 14;
+ MemoryFactorStateChanged memory_factor_state_changed = 15;
+ ExcessiveCpuUsageReported excessive_cpu_usage_reported = 16;
+ CachedKillReported cached_kill_reported = 17;
+ ProcessMemoryStatReported process_memory_stat_reported = 18;
+ // 19 is available
BatterySaverModeStateChanged battery_saver_mode_state_changed = 20;
DeviceIdleModeStateChanged device_idle_mode_state_changed = 21;
DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22;
@@ -126,7 +131,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10023
+ // Next: 10024
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -151,6 +156,7 @@
FullBatteryCapacity full_battery_capacity = 10020;
Temperature temperature = 10021;
BinderCalls binder_calls = 10022;
+ BinderCallsExceptions binder_calls_exceptions = 10023;
}
// DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -210,7 +216,8 @@
}
/**
- * Logs that the state of a process state, as per the activity manager, has changed.
+ * Logs that the process state of the uid, as determined by ActivityManager
+ * (i.e. the highest process state of that uid's processes) has changed.
*
* Logged from:
* frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -223,6 +230,112 @@
}
/**
+ * Logs process state change of a process, as per the activity manager.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
+ */
+message ProcessStateChanged {
+ optional int32 uid = 1;
+ optional string process_name = 2;
+ optional string package_name = 3;
+ // TODO: remove this when validation is done
+ optional int64 version = 5;
+ // The state, from frameworks/base/core/proto/android/app/enums.proto.
+ optional android.app.ProcessStateEnum state = 4;
+}
+
+/**
+ * Logs when ActivityManagerService sleep state is changed.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+ */
+message ActivityManagerSleepStateChanged {
+ // TODO: import frameworks proto
+ enum State {
+ UNKNOWN = 0;
+ ASLEEP = 1;
+ AWAKE = 2;
+ }
+ optional State state = 1 [(stateFieldOption).option = EXCLUSIVE];
+}
+
+/**
+ * Logs when system memory state changes.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message MemoryFactorStateChanged {
+ // TODO: import frameworks proto
+ enum State {
+ MEMORY_UNKNOWN = 0;
+ NORMAL = 1; // normal.
+ MODERATE = 2; // moderate memory pressure.
+ LOW = 3; // low memory.
+ CRITICAL = 4; // critical memory.
+
+ }
+ optional State factor = 1 [(stateFieldOption).option = EXCLUSIVE];
+}
+
+/**
+ * Logs when app is using too much cpu, according to ActivityManagerService.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message ExcessiveCpuUsageReported {
+ optional int32 uid = 1;
+ optional string process_name = 2;
+ optional string package_name = 3;
+ // package version. TODO: remove this when validation is done
+ optional int64 version = 4;
+}
+
+/**
+ * Logs when a cached process is killed, along with its pss.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message CachedKillReported {
+ optional int32 uid = 1;
+ optional string process_name = 2;
+ optional string package_name = 3;
+ // TODO: remove this when validation is done
+ optional int64 version = 5;
+ optional int64 pss = 4;
+}
+
+/**
+ * Logs when memory stats of a process is reported.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
+ */
+message ProcessMemoryStatReported {
+ optional int32 uid = 1;
+ optional string process_name = 2;
+ optional string package_name = 3;
+ //TODO: remove this when validation is done
+ optional int64 version = 9;
+ optional int64 pss = 4;
+ optional int64 uss = 5;
+ optional int64 rss = 6;
+ enum Type {
+ ADD_PSS_INTERNAL_SINGLE = 0;
+ ADD_PSS_INTERNAL_ALL_MEM = 1;
+ ADD_PSS_INTERNAL_ALL_POLL = 2;
+ ADD_PSS_EXTERNAL = 3;
+ ADD_PSS_EXTERNAL_SLOW = 4;
+ }
+ optional Type type = 7;
+ optional int64 duration = 8;
+}
+
+/**
* Logs that a process started, finished, crashed, or ANRed.
*
* Logged from:
@@ -1982,35 +2095,67 @@
*
* Binder stats are cumulative from boot unless somebody reset the data using
* > adb shell dumpsys binder_calls_stats --reset
+ *
+ * Next tag: 14
*/
message BinderCalls {
- // TODO(gaillard): figure out if binder call stats includes data from isolated uids, if a uid
- // gets recycled and we have isolated uids, we might attribute the data incorrectly.
- // TODO(gaillard): there is a high dimensions cardinality, figure out if we should drop the less
- // commonly used APIs.
- optional int32 uid = 1 [(is_uid) = true];
- // Fully qualified class name of the API call.
- optional string service_class_name = 2;
- // Method name of the API call. It can also be a transaction code if we cannot resolve it to a
- // name. See Binder#getTransactionName.
- optional string service_method_name = 3;
- // Total number of API calls.
- optional int64 call_count = 4;
- // Number of exceptions thrown by the API.
- optional int64 exception_count = 5;
- // Total latency of all API calls.
- // Average can be computed using total_latency_micros / call_count.
- optional int64 total_latency_micros = 6;
- // Maximum latency of one API call.
- optional int64 max_latency_micros = 7;
- // Total CPU usage of all API calls.
- optional int64 total_cpu_micros = 8;
- // Maximum CPU usage of one API call.
- optional int64 max_cpu_micros = 9;
- // Maximum parcel reply size of one API call.
- optional int64 max_reply_size_bytes = 10;
- // Maximum parcel request size of one API call.
- optional int64 max_request_size_bytes = 11;
+ optional int32 uid = 1 [(is_uid) = true];
+ // Fully qualified class name of the API call.
+ //
+ // This is a system server class name.
+ //
+ // TODO(gaillard): figure out if binder call stats includes data from isolated uids, if a uid
+ // gets recycled and we have isolated uids, we might attribute the data incorrectly.
+ // TODO(gaillard): there is a high dimensions cardinality, figure out if we should drop the less
+ // commonly used APIs.
+ optional string service_class_name = 2;
+ // Method name of the API call. It can also be a transaction code if we cannot
+ // resolve it to a name. See Binder#getTransactionName.
+ //
+ // This is a system server method name.
+ optional string service_method_name = 3;
+ // Total number of API calls.
+ optional int64 call_count = 4;
+ // True if the screen was interactive PowerManager#isInteractive at the end of the call.
+ optional bool screen_interactive = 13;
+ // Total number of API calls we have data recorded for. If we collected data for all the calls,
+ // call_count will be equal to recorded_call_count.
+ //
+ // If recorded_call_count is different than call_count, it means data collection has been
+ // sampled. All the fields below will be sampled in this case.
+ optional int64 recorded_call_count = 12;
+ // Number of exceptions thrown by the API.
+ optional int64 recorded_exception_count = 5;
+ // Total latency of all API calls.
+ // Average can be computed using total_latency_micros / recorded_call_count.
+ optional int64 recorded_total_latency_micros = 6;
+ // Maximum latency of one API call.
+ optional int64 recorded_max_latency_micros = 7;
+ // Total CPU usage of all API calls.
+ // Average can be computed using total_cpu_micros / recorded_call_count.
+ // Total can be computed using total_cpu_micros / recorded_call_count * call_count.
+ optional int64 recorded_total_cpu_micros = 8;
+ // Maximum CPU usage of one API call.
+ optional int64 recorded_max_cpu_micros = 9;
+ // Maximum parcel reply size of one API call.
+ optional int64 recorded_max_reply_size_bytes = 10;
+ // Maximum parcel request size of one API call.
+ optional int64 recorded_max_request_size_bytes = 11;
+}
+
+/**
+ * Pulls the statistics of exceptions during calls to Binder.
+ *
+ * Binder stats are cumulative from boot unless somebody reset the data using
+ * > adb shell dumpsys binder_calls_stats --reset
+ */
+message BinderCallsExceptions {
+ // Exception class name, e.g. java.lang.IllegalArgumentException.
+ //
+ // This is an exception class name thrown by the system server.
+ optional string exception_class_name = 1;
+ // Total number of exceptions.
+ optional int64 exception_count = 2;
}
/**
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index d81e2dc..3e9077b 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -3210,6 +3210,7 @@
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEARCHABLE:Ljava/lang/String;
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEASON_DISPLAY_NUMBER:Ljava/lang/String;
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEASON_TITLE:Ljava/lang/String;
+Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SERIES_ID:Ljava/lang/String;
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SHORT_DESCRIPTION:Ljava/lang/String;
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_THUMBNAIL_URI:Ljava/lang/String;
Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_TITLE:Ljava/lang/String;
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
new file mode 100644
index 0000000..05de3e8
--- /dev/null
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -0,0 +1,60 @@
+/*
+ * 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.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a class member, that is not part of the SDK, is used by apps.
+ * Since the member is not part of the SDK, such use is not supported.
+ *
+ * This annotation acts as a heads up that changing a given method or field
+ * may affect apps, potentially breaking them when the next Android version is
+ * released. In some cases, for members that are heavily used, this annotation
+ * may imply restrictions on changes to the member.
+ *
+ * This annotation also results in access to the member being permitted by the
+ * runtime, with a warning being generated in debug builds.
+ *
+ * For more details, see go/UnsupportedAppUsage.
+ *
+ * {@hide}
+ */
+@Retention(CLASS)
+@Target({CONSTRUCTOR, METHOD, FIELD})
+public @interface UnsupportedAppUsage {
+
+ /**
+ * Associates a bug tracking the work to add a public alternative to this API. Optional.
+ *
+ * @return ID of the associated tracking bug
+ */
+ long trackingBug() default 0;
+
+ /**
+ * For debug use only. The expected dex signature to be generated for this API, used to verify
+ * parts of the build process.
+ *
+ * @return A dex API signature.
+ */
+ String expectedSignature() default "";
+}
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 69852f3..6f73d02 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -33,7 +33,7 @@
// Perms.
void grantSlicePermission(String callingPkg, String toPkg, in Uri uri);
void revokeSlicePermission(String callingPkg, String toPkg, in Uri uri);
- int checkSlicePermission(in Uri uri, String pkg, int pid, int uid,
+ int checkSlicePermission(in Uri uri, String callingPkg, String pkg, int pid, int uid,
in String[] autoGrantPermissions);
void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 26498e8..955093d 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -430,7 +430,8 @@
*/
public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
try {
- return mService.checkSlicePermission(uri, null, pid, uid, null);
+ return mService.checkSlicePermission(uri, mContext.getPackageName(), null, pid, uid,
+ null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -487,7 +488,8 @@
if (pkg == null) {
throw new SecurityException("No pkg specified");
}
- int result = mService.checkSlicePermission(uri, pkg, pid, uid, autoGrantPermissions);
+ int result = mService.checkSlicePermission(uri, mContext.getPackageName(), pkg, pid,
+ uid, autoGrantPermissions);
if (result == PERMISSION_DENIED) {
throw new SecurityException("User " + uid + " does not have slice permission for "
+ uri + ".");
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 0fb84b7..b7f5cdf 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -24,8 +24,6 @@
import android.os.StrictMode;
import android.util.Log;
-import libcore.net.UriCodec;
-
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@@ -1959,7 +1957,8 @@
if (s == null) {
return null;
}
- return UriCodec.decode(s, false, StandardCharsets.UTF_8, false);
+ return UriCodec.decode(
+ s, false /* convertPlus */, StandardCharsets.UTF_8, false /* throwOnFailure */);
}
/**
diff --git a/core/java/android/net/UriCodec.java b/core/java/android/net/UriCodec.java
new file mode 100644
index 0000000..e1470e0
--- /dev/null
+++ b/core/java/android/net/UriCodec.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2015 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.net;
+
+import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Decodes “application/x-www-form-urlencoded” content.
+ *
+ * @hide
+ */
+public final class UriCodec {
+
+ private UriCodec() {}
+
+ /**
+ * Interprets a char as hex digits, returning a number from -1 (invalid char) to 15 ('f').
+ */
+ private static int hexCharToValue(char c) {
+ if ('0' <= c && c <= '9') {
+ return c - '0';
+ }
+ if ('a' <= c && c <= 'f') {
+ return 10 + c - 'a';
+ }
+ if ('A' <= c && c <= 'F') {
+ return 10 + c - 'A';
+ }
+ return -1;
+ }
+
+ private static URISyntaxException unexpectedCharacterException(
+ String uri, String name, char unexpected, int index) {
+ String nameString = (name == null) ? "" : " in [" + name + "]";
+ return new URISyntaxException(
+ uri, "Unexpected character" + nameString + ": " + unexpected, index);
+ }
+
+ private static char getNextCharacter(String uri, int index, int end, String name)
+ throws URISyntaxException {
+ if (index >= end) {
+ String nameString = (name == null) ? "" : " in [" + name + "]";
+ throw new URISyntaxException(
+ uri, "Unexpected end of string" + nameString, index);
+ }
+ return uri.charAt(index);
+ }
+
+ /**
+ * Decode a string according to the rules of this decoder.
+ *
+ * - if {@code convertPlus == true} all ‘+’ chars in the decoded output are converted to ‘ ‘
+ * (white space)
+ * - if {@code throwOnFailure == true}, an {@link IllegalArgumentException} is thrown for
+ * invalid inputs. Else, U+FFFd is emitted to the output in place of invalid input octets.
+ */
+ public static String decode(
+ String s, boolean convertPlus, Charset charset, boolean throwOnFailure) {
+ StringBuilder builder = new StringBuilder(s.length());
+ appendDecoded(builder, s, convertPlus, charset, throwOnFailure);
+ return builder.toString();
+ }
+
+ /**
+ * Character to be output when there's an error decoding an input.
+ */
+ private static final char INVALID_INPUT_CHARACTER = '\ufffd';
+
+ private static void appendDecoded(
+ StringBuilder builder,
+ String s,
+ boolean convertPlus,
+ Charset charset,
+ boolean throwOnFailure) {
+ CharsetDecoder decoder = charset.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .replaceWith("\ufffd")
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ // Holds the bytes corresponding to the escaped chars being read (empty if the last char
+ // wasn't a escaped char).
+ ByteBuffer byteBuffer = ByteBuffer.allocate(s.length());
+ int i = 0;
+ while (i < s.length()) {
+ char c = s.charAt(i);
+ i++;
+ switch (c) {
+ case '+':
+ flushDecodingByteAccumulator(
+ builder, decoder, byteBuffer, throwOnFailure);
+ builder.append(convertPlus ? ' ' : '+');
+ break;
+ case '%':
+ // Expect two characters representing a number in hex.
+ byte hexValue = 0;
+ for (int j = 0; j < 2; j++) {
+ try {
+ c = getNextCharacter(s, i, s.length(), null /* name */);
+ } catch (URISyntaxException e) {
+ // Unexpected end of input.
+ if (throwOnFailure) {
+ throw new IllegalArgumentException(e);
+ } else {
+ flushDecodingByteAccumulator(
+ builder, decoder, byteBuffer, throwOnFailure);
+ builder.append(INVALID_INPUT_CHARACTER);
+ return;
+ }
+ }
+ i++;
+ int newDigit = hexCharToValue(c);
+ if (newDigit < 0) {
+ if (throwOnFailure) {
+ throw new IllegalArgumentException(
+ unexpectedCharacterException(s, null /* name */, c, i - 1));
+ } else {
+ flushDecodingByteAccumulator(
+ builder, decoder, byteBuffer, throwOnFailure);
+ builder.append(INVALID_INPUT_CHARACTER);
+ break;
+ }
+ }
+ hexValue = (byte) (hexValue * 0x10 + newDigit);
+ }
+ byteBuffer.put(hexValue);
+ break;
+ default:
+ flushDecodingByteAccumulator(builder, decoder, byteBuffer, throwOnFailure);
+ builder.append(c);
+ }
+ }
+ flushDecodingByteAccumulator(builder, decoder, byteBuffer, throwOnFailure);
+ }
+
+ private static void flushDecodingByteAccumulator(
+ StringBuilder builder,
+ CharsetDecoder decoder,
+ ByteBuffer byteBuffer,
+ boolean throwOnFailure) {
+ if (byteBuffer.position() == 0) {
+ return;
+ }
+ byteBuffer.flip();
+ try {
+ builder.append(decoder.decode(byteBuffer));
+ } catch (CharacterCodingException e) {
+ if (throwOnFailure) {
+ throw new IllegalArgumentException(e);
+ } else {
+ builder.append(INVALID_INPUT_CHARACTER);
+ }
+ } finally {
+ // Use the byte buffer to write again.
+ byteBuffer.flip();
+ byteBuffer.limit(byteBuffer.capacity());
+ }
+ }
+}
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index b6c6bdc..5a7a83f 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -151,14 +151,14 @@
@Override
public String toString() {
return new StringBuilder(Condition.class.getSimpleName()).append('[')
- .append("id=").append(id)
- .append(",summary=").append(summary)
- .append(",line1=").append(line1)
- .append(",line2=").append(line2)
- .append(",icon=").append(icon)
- .append(",state=").append(stateToString(state))
- .append(",flags=").append(flags)
- .append(']').toString();
+ .append("state=").append(stateToString(state))
+ .append(",id=").append(id)
+ .append(",summary=").append(summary)
+ .append(",line1=").append(line1)
+ .append(",line2=").append(line2)
+ .append(",icon=").append(icon)
+ .append(",flags=").append(flags)
+ .append(']').toString();
}
/** @hide */
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index f52234d..5ac36afc 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -240,11 +240,29 @@
.append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
.append(",suppressedVisualEffects=").append(suppressedVisualEffects)
.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd)
- .append(",automaticRules=").append(automaticRules)
- .append(",manualRule=").append(manualRule)
+ .append(",\nautomaticRules=").append(rulesToString())
+ .append(",\nmanualRule=").append(manualRule)
.append(']').toString();
}
+ private String rulesToString() {
+ if (automaticRules.isEmpty()) {
+ return "{}";
+ }
+
+ StringBuilder buffer = new StringBuilder(automaticRules.size() * 28);
+ buffer.append('{');
+ for (int i = 0; i < automaticRules.size(); i++) {
+ if (i > 0) {
+ buffer.append(",\n");
+ }
+ Object value = automaticRules.valueAt(i);
+ buffer.append(value);
+ }
+ buffer.append('}');
+ return buffer.toString();
+ }
+
private Diff diff(ZenModeConfig to) {
final Diff d = new Diff();
if (to == null) {
@@ -1027,10 +1045,10 @@
public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
final boolean isSchedule = conditionId != null
- && conditionId.getScheme().equals(Condition.SCHEME)
- && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
+ && Condition.SCHEME.equals(conditionId.getScheme())
+ && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
&& conditionId.getPathSegments().size() == 1
- && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH);
+ && ZenModeConfig.SCHEDULE_PATH.equals(conditionId.getPathSegments().get(0));
if (!isSchedule) return null;
final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
@@ -1128,10 +1146,10 @@
public static EventInfo tryParseEventConditionId(Uri conditionId) {
final boolean isEvent = conditionId != null
- && conditionId.getScheme().equals(Condition.SCHEME)
- && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
+ && Condition.SCHEME.equals(conditionId.getScheme())
+ && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
&& conditionId.getPathSegments().size() == 1
- && conditionId.getPathSegments().get(0).equals(EVENT_PATH);
+ && EVENT_PATH.equals(conditionId.getPathSegments().get(0));
if (!isEvent) return null;
final EventInfo rt = new EventInfo();
rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL);
@@ -1340,14 +1358,14 @@
@Override
public String toString() {
return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
- .append("enabled=").append(enabled)
+ .append("id=").append(id)
+ .append(",enabled=").append(String.valueOf(enabled).toUpperCase())
.append(",snoozing=").append(snoozing)
.append(",name=").append(name)
.append(",zenMode=").append(Global.zenModeToString(zenMode))
.append(",conditionId=").append(conditionId)
.append(",condition=").append(condition)
.append(",component=").append(component)
- .append(",id=").append(id)
.append(",creationTime=").append(creationTime)
.append(",enabler=").append(enabler)
.append(']').toString();
@@ -1479,7 +1497,7 @@
final int N = lines.size();
for (int i = 0; i < N; i++) {
if (i > 0) {
- sb.append(',');
+ sb.append(",\n");
}
sb.append(lines.get(i));
}
diff --git a/core/java/android/util/Base64OutputStream.java b/core/java/android/util/Base64OutputStream.java
index 4535d1c..8378705 100644
--- a/core/java/android/util/Base64OutputStream.java
+++ b/core/java/android/util/Base64OutputStream.java
@@ -117,8 +117,10 @@
out.flush();
}
} catch (IOException e) {
- if (thrown != null) {
+ if (thrown == null) {
thrown = e;
+ } else {
+ thrown.addSuppressed(e);
}
}
diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
index bc4a19d..11aee2f 100644
--- a/core/java/android/util/jar/StrictJarFile.java
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -390,6 +390,7 @@
public static class ZipInflaterInputStream extends InflaterInputStream {
private final ZipEntry entry;
private long bytesRead = 0;
+ private boolean closed;
public ZipInflaterInputStream(InputStream is, Inflater inf, int bsize, ZipEntry entry) {
super(is, inf, bsize);
@@ -424,6 +425,12 @@
}
return super.available() == 0 ? 0 : (int) (entry.getSize() - bytesRead);
}
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ closed = true;
+ }
}
/**
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index f87c081..63c7dd0 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -38,6 +38,7 @@
import java.util.List;
import java.util.Map;
import java.util.Queue;
+import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.ToDoubleFunction;
@@ -57,7 +58,7 @@
private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
private static final CallSession NOT_ENABLED = new CallSession();
- private static final BinderCallsStats sInstance = new BinderCallsStats();
+ private static final BinderCallsStats sInstance = new BinderCallsStats(new Random());
private volatile boolean mEnabled = ENABLED_DEFAULT;
private volatile boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
@@ -68,12 +69,12 @@
private final ArrayMap<String, Integer> mExceptionCounts = new ArrayMap<>();
private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>();
private final Object mLock = new Object();
+ private final Random mRandom;
private long mStartTime = System.currentTimeMillis();
- @GuardedBy("mLock")
- private UidEntry mSampledEntries = new UidEntry(-1);
@VisibleForTesting // Use getInstance() instead.
- public BinderCallsStats() {
+ public BinderCallsStats(Random random) {
+ this.mRandom = random;
}
public CallSession callStarted(Binder binder, int code) {
@@ -82,7 +83,7 @@
private CallSession callStarted(String className, int code, @Nullable String methodName) {
if (!mEnabled) {
- return NOT_ENABLED;
+ return NOT_ENABLED;
}
CallSession s = mCallSessionsPool.poll();
@@ -98,16 +99,12 @@
s.timeStarted = -1;
synchronized (mLock) {
- if (mDetailedTracking) {
- s.cpuTimeStarted = getThreadTimeMicro();
- s.timeStarted = getElapsedRealtimeMicro();
- } else {
- s.sampledCallStat = mSampledEntries.getOrCreate(s.callStat);
- if (s.sampledCallStat.callCount % mPeriodicSamplingInterval == 0) {
- s.cpuTimeStarted = getThreadTimeMicro();
- s.timeStarted = getElapsedRealtimeMicro();
- }
+ if (!mDetailedTracking && !shouldTrackCall()) {
+ return s;
}
+
+ s.cpuTimeStarted = getThreadTimeMicro();
+ s.timeStarted = getElapsedRealtimeMicro();
}
return s;
}
@@ -115,7 +112,7 @@
public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
Preconditions.checkNotNull(s);
if (s == NOT_ENABLED) {
- return;
+ return;
}
processCallEnded(s, parcelRequestSize, parcelReplySize);
@@ -128,60 +125,42 @@
private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
synchronized (mLock) {
if (!mEnabled) {
- return;
+ return;
}
- long duration;
- long latencyDuration;
- if (mDetailedTracking) {
- duration = getThreadTimeMicro() - s.cpuTimeStarted;
- latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
- } else {
- CallStat cs = s.sampledCallStat;
- // Non-negative time signals beginning of the new sampling interval
- if (s.cpuTimeStarted >= 0) {
- duration = getThreadTimeMicro() - s.cpuTimeStarted;
- latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
- } else {
- // callCount is always incremented, but time only once per sampling interval
- long samplesCount = cs.callCount / mPeriodicSamplingInterval + 1;
- duration = cs.cpuTimeMicros / samplesCount;
- latencyDuration = cs.latencyMicros / samplesCount;
- }
- }
-
- int callingUid = getCallingUid();
-
+ final int callingUid = getCallingUid();
UidEntry uidEntry = mUidEntries.get(callingUid);
if (uidEntry == null) {
uidEntry = new UidEntry(callingUid);
mUidEntries.put(callingUid, uidEntry);
}
-
- CallStat callStat;
- if (mDetailedTracking) {
- // Find CallStat entry and update its total time
- callStat = uidEntry.getOrCreate(s.callStat);
- callStat.exceptionCount += s.exceptionThrown ? 1 : 0;
- callStat.maxRequestSizeBytes =
- Math.max(callStat.maxRequestSizeBytes, parcelRequestSize);
- callStat.maxReplySizeBytes =
- Math.max(callStat.maxReplySizeBytes, parcelReplySize);
- } else {
- // update sampled timings in the beginning of each interval
- callStat = s.sampledCallStat;
- }
+ uidEntry.callCount++;
+ CallStat callStat = uidEntry.getOrCreate(s.callStat);
callStat.callCount++;
- callStat.methodName = s.callStat.methodName;
- if (s.cpuTimeStarted >= 0) {
+
+ // Non-negative time signals we need to record data for this call.
+ final boolean recordCall = s.cpuTimeStarted >= 0;
+ if (recordCall) {
+ final long duration = getThreadTimeMicro() - s.cpuTimeStarted;
+ final long latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
+ uidEntry.cpuTimeMicros += duration;
+ uidEntry.recordedCallCount++;
+
+ callStat.recordedCallCount++;
+ callStat.methodName = s.callStat.methodName;
callStat.cpuTimeMicros += duration;
callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
callStat.latencyMicros += latencyDuration;
- callStat.maxLatencyMicros = Math.max(callStat.maxLatencyMicros, latencyDuration);
+ callStat.maxLatencyMicros =
+ Math.max(callStat.maxLatencyMicros, latencyDuration);
+ if (mDetailedTracking) {
+ callStat.exceptionCount += s.exceptionThrown ? 1 : 0;
+ callStat.maxRequestSizeBytes =
+ Math.max(callStat.maxRequestSizeBytes, parcelRequestSize);
+ callStat.maxReplySizeBytes =
+ Math.max(callStat.maxReplySizeBytes, parcelReplySize);
+ }
}
-
- uidEntry.cpuTimeMicros += duration;
- uidEntry.callCount++;
}
}
@@ -195,28 +174,28 @@
public void callThrewException(CallSession s, Exception exception) {
Preconditions.checkNotNull(s);
if (!mEnabled) {
- return;
+ return;
}
s.exceptionThrown = true;
try {
String className = exception.getClass().getName();
synchronized (mLock) {
if (mExceptionCounts.size() >= MAX_EXCEPTION_COUNT_SIZE) {
- className = EXCEPTION_COUNT_OVERFLOW_NAME;
+ className = EXCEPTION_COUNT_OVERFLOW_NAME;
}
Integer count = mExceptionCounts.get(className);
mExceptionCounts.put(className, count == null ? 1 : count + 1);
}
} catch (RuntimeException e) {
- // Do not propagate the exception. We do not want to swallow original exception.
- Log.wtf(TAG, "Unexpected exception while updating mExceptionCounts", e);
+ // Do not propagate the exception. We do not want to swallow original exception.
+ Log.wtf(TAG, "Unexpected exception while updating mExceptionCounts", e);
}
}
public ArrayList<ExportedCallStat> getExportedCallStats() {
// We do not collect all the data if detailed tracking is off.
if (!mDetailedTracking) {
- return new ArrayList<ExportedCallStat>();
+ return new ArrayList<ExportedCallStat>();
}
ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>();
@@ -229,11 +208,12 @@
exported.uid = entry.uid;
exported.className = stat.className;
exported.methodName = stat.methodName == null
- ? String.valueOf(stat.msg) : stat.methodName;
+ ? String.valueOf(stat.msg) : stat.methodName;
exported.cpuTimeMicros = stat.cpuTimeMicros;
exported.maxCpuTimeMicros = stat.maxCpuTimeMicros;
exported.latencyMicros = stat.latencyMicros;
exported.maxLatencyMicros = stat.maxLatencyMicros;
+ exported.recordedCallCount = stat.recordedCallCount;
exported.callCount = stat.callCount;
exported.maxRequestSizeBytes = stat.maxRequestSizeBytes;
exported.maxReplySizeBytes = stat.maxReplySizeBytes;
@@ -254,14 +234,16 @@
private void dumpLocked(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
if (!mEnabled) {
- pw.println("Binder calls stats disabled.");
- return;
+ pw.println("Binder calls stats disabled.");
+ return;
}
long totalCallsCount = 0;
+ long totalRecordedCallsCount = 0;
long totalCpuTime = 0;
pw.print("Start time: ");
pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartTime));
+ pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
List<UidEntry> entries = new ArrayList<>();
int uidEntriesSize = mUidEntries.size();
@@ -269,70 +251,53 @@
UidEntry e = mUidEntries.valueAt(i);
entries.add(e);
totalCpuTime += e.cpuTimeMicros;
+ totalRecordedCallsCount += e.recordedCallCount;
totalCallsCount += e.callCount;
}
entries.sort(Comparator.<UidEntry>comparingDouble(value -> value.cpuTimeMicros).reversed());
String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) ";
StringBuilder sb = new StringBuilder();
- if (mDetailedTracking) {
- pw.println("Per-UID raw data " + datasetSizeDesc
- + "(package/uid, call_desc, cpu_time_micros, max_cpu_time_micros, "
- + "latency_time_micros, max_latency_time_micros, exception_count, "
- + "max_request_size_bytes, max_reply_size_bytes, call_count):");
- List<UidEntry> topEntries = verbose ? entries
- : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
- for (UidEntry uidEntry : topEntries) {
- for (CallStat e : uidEntry.getCallStatsList()) {
- sb.setLength(0);
- sb.append(" ")
- .append(uidToString(uidEntry.uid, appIdToPkgNameMap))
- .append(",").append(e)
- .append(',').append(e.cpuTimeMicros)
- .append(',').append(e.maxCpuTimeMicros)
- .append(',').append(e.latencyMicros)
- .append(',').append(e.maxLatencyMicros)
- .append(',').append(e.exceptionCount)
- .append(',').append(e.maxRequestSizeBytes)
- .append(',').append(e.maxReplySizeBytes)
- .append(',').append(e.callCount);
- pw.println(sb);
- }
- }
- pw.println();
- } else {
- pw.println("Sampled stats " + datasetSizeDesc
- + "(call_desc, cpu_time, call_count, exception_count):");
- List<CallStat> sampledStatsList = mSampledEntries.getCallStatsList();
- // Show all if verbose, otherwise 90th percentile
- if (!verbose) {
- sampledStatsList = getHighestValues(sampledStatsList,
- value -> value.cpuTimeMicros, 0.9);
- }
- for (CallStat e : sampledStatsList) {
+ List<UidEntry> topEntries = verbose ? entries
+ : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
+ pw.println("Per-UID raw data " + datasetSizeDesc
+ + "(package/uid, call_desc, cpu_time_micros, max_cpu_time_micros, "
+ + "latency_time_micros, max_latency_time_micros, exception_count, "
+ + "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, "
+ + "call_count):");
+ for (UidEntry uidEntry : topEntries) {
+ for (CallStat e : uidEntry.getCallStatsList()) {
sb.setLength(0);
- sb.append(" ").append(e)
- .append(',').append(e.cpuTimeMicros * mPeriodicSamplingInterval)
- .append(',').append(e.callCount)
- .append(',').append(e.exceptionCount);
+ sb.append(" ")
+ .append(uidToString(uidEntry.uid, appIdToPkgNameMap))
+ .append(',').append(e)
+ .append(',').append(e.cpuTimeMicros)
+ .append(',').append(e.maxCpuTimeMicros)
+ .append(',').append(e.latencyMicros)
+ .append(',').append(e.maxLatencyMicros)
+ .append(',').append(mDetailedTracking ? e.exceptionCount : '_')
+ .append(',').append(mDetailedTracking ? e.maxRequestSizeBytes : '_')
+ .append(',').append(mDetailedTracking ? e.maxReplySizeBytes : '_')
+ .append(',').append(e.recordedCallCount)
+ .append(',').append(e.callCount);
pw.println(sb);
}
- pw.println();
}
+ pw.println();
pw.println("Per-UID Summary " + datasetSizeDesc
- + "(cpu_time, % of total cpu_time, call_count, exception_count, package/uid):");
+ + "(cpu_time, % of total cpu_time, recorded_call_count, call_count, package/uid):");
List<UidEntry> summaryEntries = verbose ? entries
: getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
for (UidEntry entry : summaryEntries) {
String uidStr = uidToString(entry.uid, appIdToPkgNameMap);
- pw.println(String.format(" %10d %3.0f%% %8d %3d %s",
- entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime, entry.callCount,
- entry.exceptionCount, uidStr));
+ pw.println(String.format(" %10d %3.0f%% %8d %8d %s",
+ entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
+ entry.recordedCallCount, entry.callCount, uidStr));
}
pw.println();
pw.println(String.format(" Summary: total_cpu_time=%d, "
- + "calls_count=%d, avg_call_cpu_time=%.0f",
- totalCpuTime, totalCallsCount, (double)totalCpuTime / totalCallsCount));
+ + "calls_count=%d, avg_call_cpu_time=%.0f",
+ totalCpuTime, totalCallsCount, (double)totalCpuTime / totalRecordedCallsCount));
pw.println();
pw.println("Exceptions thrown (exception_count, class_name):");
@@ -340,10 +305,15 @@
// We cannot use new ArrayList(Collection) constructor because MapCollections does not
// implement toArray method.
mExceptionCounts.entrySet().iterator().forEachRemaining(
- (e) -> exceptionEntries.add(Pair.create(e.getKey(), e.getValue())));
+ (e) -> exceptionEntries.add(Pair.create(e.getKey(), e.getValue())));
exceptionEntries.sort((e1, e2) -> Integer.compare(e2.second, e1.second));
for (Pair<String, Integer> entry : exceptionEntries) {
- pw.println(String.format(" %6d %s", entry.second, entry.first));
+ pw.println(String.format(" %6d %s", entry.second, entry.first));
+ }
+
+ if (!mDetailedTracking && mPeriodicSamplingInterval != 1) {
+ pw.println("");
+ pw.println("/!\\ Displayed data is sampled. See sampling interval at the top.");
}
}
@@ -366,16 +336,20 @@
return SystemClock.elapsedRealtimeNanos() / 1000;
}
+ private boolean shouldTrackCall() {
+ return mRandom.nextInt() % mPeriodicSamplingInterval == 0;
+ }
+
public static BinderCallsStats getInstance() {
return sInstance;
}
public void setDetailedTracking(boolean enabled) {
synchronized (mLock) {
- if (enabled != mDetailedTracking) {
- mDetailedTracking = enabled;
- reset();
- }
+ if (enabled != mDetailedTracking) {
+ mDetailedTracking = enabled;
+ reset();
+ }
}
}
@@ -401,7 +375,6 @@
synchronized (mLock) {
mUidEntries.clear();
mExceptionCounts.clear();
- mSampledEntries.mCallStats.clear();
mStartTime = System.currentTimeMillis();
}
}
@@ -418,6 +391,7 @@
public long latencyMicros;
public long maxLatencyMicros;
public long callCount;
+ public long recordedCallCount;
public long maxRequestSizeBytes;
public long maxReplySizeBytes;
public long exceptionCount;
@@ -430,11 +404,21 @@
// Method name might be null when we cannot resolve the transaction code. For instance, if
// the binder was not generated by AIDL.
public @Nullable String methodName;
+ // Number of calls for which we collected data for. We do not record data for all the calls
+ // when sampling is on.
+ public long recordedCallCount;
+ // Real number of total calls.
+ public long callCount;
+ // Total CPU of all for all the recorded calls.
+ // Approximate total CPU usage can be computed by
+ // cpuTimeMicros * callCount / recordedCallCount
public long cpuTimeMicros;
public long maxCpuTimeMicros;
+ // Total latency of all for all the recorded calls.
+ // Approximate average latency can be computed by
+ // latencyMicros * callCount / recordedCallCount
public long latencyMicros;
public long maxLatencyMicros;
- public long callCount;
// The following fields are only computed if mDetailedTracking is set.
public long maxRequestSizeBytes;
public long maxReplySizeBytes;
@@ -455,7 +439,6 @@
}
CallStat callStat = (CallStat) o;
-
return msg == callStat.msg && (className.equals(callStat.className));
}
@@ -477,15 +460,20 @@
long timeStarted;
boolean exceptionThrown;
final CallStat callStat = new CallStat();
- CallStat sampledCallStat;
}
@VisibleForTesting
public static class UidEntry {
int uid;
- public long cpuTimeMicros;
+ // Number of calls for which we collected data for. We do not record data for all the calls
+ // when sampling is on.
+ public long recordedCallCount;
+ // Real number of total calls.
public long callCount;
- public int exceptionCount;
+ // Total CPU of all for all the recorded calls.
+ // Approximate total CPU usage can be computed by
+ // cpuTimeMicros * callCount / recordedCallCount
+ public long cpuTimeMicros;
UidEntry(int uid) {
this.uid = uid;
@@ -551,11 +539,6 @@
}
@VisibleForTesting
- public UidEntry getSampledEntries() {
- return mSampledEntries;
- }
-
- @VisibleForTesting
public ArrayMap<String, Integer> getExceptionCounts() {
return mExceptionCounts;
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index a9cd5c8..e37e650 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -30,14 +30,13 @@
import android.util.Slog;
import com.android.internal.logging.AndroidConfig;
import com.android.server.NetworkManagementSocketTagger;
+import dalvik.system.RuntimeHooks;
import dalvik.system.VMRuntime;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;
-import java.util.TimeZone;
import java.util.logging.LogManager;
-import org.apache.harmony.luni.internal.util.TimezoneGetter;
/**
* Main entry point for runtime initialization. Not for
@@ -195,19 +194,13 @@
* the default handler, but not the pre handler.
*/
LoggingHandler loggingHandler = new LoggingHandler();
- Thread.setUncaughtExceptionPreHandler(loggingHandler);
+ RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
/*
- * Install a TimezoneGetter subclass for ZoneInfo.db
+ * Install a time zone supplier that uses the Android persistent time zone system property.
*/
- TimezoneGetter.setInstance(new TimezoneGetter() {
- @Override
- public String getId() {
- return SystemProperties.get("persist.sys.timezone");
- }
- });
- TimeZone.setDefault(null);
+ RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));
/*
* Sets handler for java.util.logging to use Android log facilities.
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index da19560..8f87f91 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -21,9 +21,6 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.icu.impl.CacheValue;
-import android.icu.text.DecimalFormatSymbols;
-import android.icu.util.ULocale;
import android.opengl.EGL14;
import android.os.Build;
import android.os.Environment;
@@ -122,9 +119,9 @@
static void preload(TimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
- bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");
- beginIcuCachePinning();
- bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning
+ bootTimingsTraceLog.traceBegin("BeginPreload");
+ beginPreload();
+ bootTimingsTraceLog.traceEnd(); // BeginPreload
bootTimingsTraceLog.traceBegin("PreloadClasses");
preloadClasses();
bootTimingsTraceLog.traceEnd(); // PreloadClasses
@@ -142,7 +139,7 @@
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
- endIcuCachePinning();
+ endPreload();
warmUpJcaProviders();
Log.d(TAG, "end preload");
@@ -156,27 +153,16 @@
preload(new TimingsTraceLog("ZygoteInitTiming_lazy", Trace.TRACE_TAG_DALVIK));
}
- private static void beginIcuCachePinning() {
- // Pin ICU data in memory from this point that would normally be held by soft references.
- // Without this, any references created immediately below or during class preloading
- // would be collected when the Zygote GC runs in gcAndFinalize().
- Log.i(TAG, "Installing ICU cache reference pinning...");
+ private static void beginPreload() {
+ Log.i(TAG, "Calling ZygoteHooks.beginPreload()");
- CacheValue.setStrength(CacheValue.Strength.STRONG);
-
- Log.i(TAG, "Preloading ICU data...");
- // Explicitly exercise code to cache data apps are likely to need.
- ULocale[] localesToPin = { ULocale.ROOT, ULocale.US, ULocale.getDefault() };
- for (ULocale uLocale : localesToPin) {
- new DecimalFormatSymbols(uLocale);
- }
+ ZygoteHooks.onBeginPreload();
}
- private static void endIcuCachePinning() {
- // All cache references created by ICU from this point will be soft.
- CacheValue.setStrength(CacheValue.Strength.SOFT);
+ private static void endPreload() {
+ ZygoteHooks.onEndPreload();
- Log.i(TAG, "Uninstalled ICU cache reference pinning...");
+ Log.i(TAG, "Called ZygoteHooks.endPreload()");
}
private static void preloadSharedLibraries() {
@@ -436,15 +422,8 @@
* softly- and final-reachable objects, along with any other garbage.
* This is only useful just before a fork().
*/
- /*package*/ static void gcAndFinalize() {
- final VMRuntime runtime = VMRuntime.getRuntime();
-
- /* runFinalizationSync() lets finalizers be called in Zygote,
- * which doesn't have a HeapWorker thread.
- */
- System.gc();
- runtime.runFinalizationSync();
- System.gc();
+ private static void gcAndFinalize() {
+ ZygoteHooks.gcAndFinalize();
}
/**
diff --git a/core/tests/coretests/src/android/net/UriCodecTest.java b/core/tests/coretests/src/android/net/UriCodecTest.java
new file mode 100644
index 0000000..7fe9402
--- /dev/null
+++ b/core/tests/coretests/src/android/net/UriCodecTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 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.net;
+
+import junit.framework.TestCase;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tests for {@link UriCodec}
+ */
+public class UriCodecTest extends TestCase {
+
+ public void testDecode_emptyString_returnsEmptyString() {
+ assertEquals("", UriCodec.decode("",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */));
+ }
+
+ public void testDecode_wrongHexDigit_fails() {
+ try {
+ // %p in the end.
+ UriCodec.decode("ab%2f$%C4%82%25%e0%a1%80%p",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */);
+ fail("Expected URISyntaxException");
+ } catch (IllegalArgumentException expected) {
+ // Expected.
+ }
+ }
+
+ public void testDecode_secondHexDigitWrong_fails() {
+ try {
+ // %1p in the end.
+ UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%1p",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */);
+ fail("Expected URISyntaxException");
+ } catch (IllegalArgumentException expected) {
+ // Expected.
+ }
+ }
+
+ public void testDecode_endsWithPercent_fails() {
+ try {
+ // % in the end.
+ UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */);
+ fail("Expected URISyntaxException");
+ } catch (IllegalArgumentException expected) {
+ // Expected.
+ }
+ }
+
+ public void testDecode_dontThrowException_appendsUnknownCharacter() {
+ assertEquals("ab/$\u0102%\u0840\ufffd",
+ UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ false /* throwOnFailure */));
+ }
+
+ public void testDecode_convertPlus() {
+ assertEquals("ab/$\u0102% \u0840",
+ UriCodec.decode("ab%2f$%c4%82%25+%e0%a1%80",
+ true /* convertPlus */,
+ StandardCharsets.UTF_8,
+ false /* throwOnFailure */));
+ }
+
+ // Last character needs decoding (make sure we are flushing the buffer with chars to decode).
+ public void testDecode_lastCharacter() {
+ assertEquals("ab/$\u0102%\u0840",
+ UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */));
+ }
+
+ // Check that a second row of encoded characters is decoded properly (internal buffers are
+ // reset properly).
+ public void testDecode_secondRowOfEncoded() {
+ assertEquals("ab/$\u0102%\u0840aa\u0840",
+ UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80aa%e0%a1%80",
+ false /* convertPlus */,
+ StandardCharsets.UTF_8,
+ true /* throwOnFailure */));
+ }
+}
diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java
index 3aee583..af608c3 100644
--- a/core/tests/coretests/src/android/util/Base64Test.java
+++ b/core/tests/coretests/src/android/util/Base64Test.java
@@ -18,13 +18,18 @@
import android.support.test.filters.LargeTest;
+import junit.framework.TestCase;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Random;
-import junit.framework.TestCase;
+import java.util.stream.Collectors;
@LargeTest
public class Base64Test extends TestCase {
@@ -530,4 +535,74 @@
assertEquals(plain, actual);
}
}
+
+ public void testOutputStream_ioExceptionDuringClose() {
+ OutputStream out = new OutputStream() {
+ @Override public void write(int b) throws IOException { }
+ @Override public void close() throws IOException {
+ throw new IOException("close()");
+ }
+ };
+ OutputStream out2 = new Base64OutputStream(out, Base64.DEFAULT);
+ try {
+ out2.close();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testOutputStream_ioExceptionDuringCloseAndWrite() {
+ OutputStream out = new OutputStream() {
+ @Override public void write(int b) throws IOException {
+ throw new IOException("write()");
+ }
+ @Override public void write(byte[] b) throws IOException {
+ throw new IOException("write()");
+ }
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ throw new IOException("write()");
+ }
+ @Override public void close() throws IOException {
+ throw new IOException("close()");
+ }
+ };
+ OutputStream out2 = new Base64OutputStream(out, Base64.DEFAULT);
+ try {
+ out2.close();
+ fail();
+ } catch (IOException expected) {
+ // Base64OutputStream write()s pending (possibly empty) data
+ // before close(), so the IOE from write() should be thrown and
+ // any later exception suppressed.
+ assertEquals("write()", expected.getMessage());
+ Throwable[] suppressed = expected.getSuppressed();
+ List<String> suppressedMessages = Arrays.asList(suppressed).stream()
+ .map((e) -> e.getMessage())
+ .collect(Collectors.toList());
+ assertEquals(Collections.singletonList("close()"), suppressedMessages);
+ }
+ }
+
+ public void testOutputStream_ioExceptionDuringWrite() {
+ OutputStream out = new OutputStream() {
+ @Override public void write(int b) throws IOException {
+ throw new IOException("write()");
+ }
+ @Override public void write(byte[] b) throws IOException {
+ throw new IOException("write()");
+ }
+ @Override public void write(byte[] b, int off, int len) throws IOException {
+ throw new IOException("write()");
+ }
+ };
+ // Base64OutputStream write()s pending (possibly empty) data
+ // before close(), so the IOE from write() should be thrown.
+ OutputStream out2 = new Base64OutputStream(out, Base64.DEFAULT);
+ try {
+ out2.close();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index d46c154..07e2af8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -33,6 +33,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import static org.junit.Assert.assertEquals;
@@ -48,6 +49,7 @@
public void testDetailedOff() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(false);
+ bcs.setSamplingInterval(5);
Binder binder = new Binder();
BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
@@ -58,44 +60,31 @@
assertEquals(1, uidEntries.size());
BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
Assert.assertNotNull(uidEntry);
+ List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
assertEquals(1, uidEntry.callCount);
+ assertEquals(1, uidEntry.recordedCallCount);
assertEquals(10, uidEntry.cpuTimeMicros);
- assertEquals("Detailed tracking off - no entries should be returned",
- 0, uidEntry.getCallStatsList().size());
+ assertEquals(binder.getClass().getName(), callStatsList.get(0).className);
+ assertEquals(1, callStatsList.get(0).msg);
- BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries();
- List<BinderCallsStats.CallStat> sampledCallStatsList = sampledEntries.getCallStatsList();
- assertEquals(1, sampledCallStatsList.size());
-
-
- assertEquals(1, sampledCallStatsList.get(0).callCount);
- assertEquals(10, sampledCallStatsList.get(0).cpuTimeMicros);
- assertEquals(binder.getClass().getName(), sampledCallStatsList.get(0).className);
- assertEquals(1, sampledCallStatsList.get(0).msg);
-
+ // CPU usage is sampled, should not be tracked here.
callSession = bcs.callStarted(binder, 1);
bcs.time += 20;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-
- uidEntry = bcs.getUidEntries().get(TEST_UID);
assertEquals(2, uidEntry.callCount);
- // When sampling is enabled, cpu time is only measured for the first transaction in the
- // sampling interval, for others an average duration of previous transactions is used as
- // approximation
- assertEquals(20, uidEntry.cpuTimeMicros);
- sampledCallStatsList = sampledEntries.getCallStatsList();
- assertEquals(1, sampledCallStatsList.size());
+ assertEquals(1, uidEntry.recordedCallCount);
+ assertEquals(10, uidEntry.cpuTimeMicros);
+ assertEquals(1, callStatsList.size());
callSession = bcs.callStarted(binder, 2);
bcs.time += 50;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
uidEntry = bcs.getUidEntries().get(TEST_UID);
assertEquals(3, uidEntry.callCount);
-
- // This is the first transaction of a new type, so the real CPU time will be measured
- assertEquals(70, uidEntry.cpuTimeMicros);
- sampledCallStatsList = sampledEntries.getCallStatsList();
- assertEquals(2, sampledCallStatsList.size());
+ assertEquals(1, uidEntry.recordedCallCount);
+ // Still sampled even for another API.
+ callStatsList = uidEntry.getCallStatsList();
+ assertEquals(2, callStatsList.size());
}
@Test
@@ -116,10 +105,6 @@
assertEquals(10, uidEntry.cpuTimeMicros);
assertEquals(1, uidEntry.getCallStatsList().size());
- BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries();
- assertEquals("Sampling is not used when detailed tracking on",
- 0, bcs.getSampledEntries().getCallStatsList().size());
-
List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
assertEquals(1, callStatsList.get(0).callCount);
assertEquals(10, callStatsList.get(0).cpuTimeMicros);
@@ -216,13 +201,57 @@
BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
Assert.assertNotNull(uidEntry);
assertEquals(3, uidEntry.callCount);
- assertEquals(70, uidEntry.cpuTimeMicros);
- assertEquals("Detailed tracking off - no entries should be returned",
- 0, uidEntry.getCallStatsList().size());
+ assertEquals(60 /* 10 + 50 */, uidEntry.cpuTimeMicros);
- BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries();
- List<BinderCallsStats.CallStat> sampledCallStatsList = sampledEntries.getCallStatsList();
- assertEquals(1, sampledCallStatsList.size());
+ List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
+ assertEquals(1, callStatsList.size());
+ BinderCallsStats.CallStat callStats = callStatsList.get(0);
+ assertEquals(3, callStats.callCount);
+ assertEquals(2, callStats.recordedCallCount);
+ assertEquals(60, callStats.cpuTimeMicros);
+ assertEquals(50, callStats.maxCpuTimeMicros);
+ assertEquals(0, callStats.maxRequestSizeBytes);
+ assertEquals(0, callStats.maxReplySizeBytes);
+ }
+
+ @Test
+ public void testSamplingWithDifferentApis() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(false);
+ bcs.setSamplingInterval(2);
+
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 2 /* another method */);
+ bcs.time += 1000; // shoud be ignored.
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+
+ SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+ assertEquals(1, uidEntries.size());
+ BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+ assertEquals(2, uidEntry.callCount);
+ assertEquals(1, uidEntry.recordedCallCount);
+ assertEquals(10, uidEntry.cpuTimeMicros);
+
+ List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
+ assertEquals(2, callStatsList.size());
+
+ BinderCallsStats.CallStat callStats = callStatsList.get(0);
+ assertEquals(1, callStats.callCount);
+ assertEquals(1, callStats.recordedCallCount);
+ assertEquals(10, callStats.cpuTimeMicros);
+ assertEquals(10, callStats.maxCpuTimeMicros);
+
+ // Only call count should is tracked, rest is sampled.
+ callStats = callStatsList.get(1);
+ assertEquals(1, callStats.callCount);
+ assertEquals(0, callStats.recordedCallCount);
+ assertEquals(0, callStats.cpuTimeMicros);
+ assertEquals(0, callStats.maxCpuTimeMicros);
}
@Test
@@ -374,6 +403,7 @@
assertEquals(20, stat.latencyMicros);
assertEquals(20, stat.maxLatencyMicros);
assertEquals(1, stat.callCount);
+ assertEquals(1, stat.recordedCallCount);
assertEquals(REQUEST_SIZE, stat.maxRequestSizeBytes);
assertEquals(REPLY_SIZE, stat.maxReplySizeBytes);
assertEquals(0, stat.exceptionCount);
@@ -385,6 +415,14 @@
long elapsedTime = 0;
TestBinderCallsStats() {
+ // Make random generator not random.
+ super(new Random() {
+ int mCallCount = 0;
+
+ public int nextInt() {
+ return mCallCount++;
+ }
+ });
}
@Override
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index bac7a4d..1b15dbd 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -15,32 +15,38 @@
*/
#include "CanvasTransform.h"
+#include "utils/Color.h"
#include "Properties.h"
+#include <ui/ColorSpace.h>
#include <SkColorFilter.h>
#include <SkPaint.h>
-#include <log/log.h>
+
+#include <algorithm>
+#include <cmath>
namespace android::uirenderer {
static SkColor makeLight(SkColor color) {
- SkScalar hsv[3];
- SkColorToHSV(color, hsv);
- if (hsv[1] > .2f) return color;
- // hsv[1] *= .85f;
- // hsv[2] = std::min(1.0f, std::max(hsv[2], 1 - hsv[2]) * 1.3f);
- hsv[2] = std::max(hsv[2], 1.1f - hsv[2]);
- return SkHSVToColor(SkColorGetA(color), hsv);
+ Lab lab = sRGBToLab(color);
+ float invertedL = std::min(110 - lab.L, 100.0f);
+ if (invertedL > lab.L) {
+ lab.L = invertedL;
+ return LabToSRGB(lab, SkColorGetA(color));
+ } else {
+ return color;
+ }
}
static SkColor makeDark(SkColor color) {
- SkScalar hsv[3];
- SkColorToHSV(color, hsv);
- if (hsv[1] > .2f) return color;
- // hsv[1] *= .85f;
- // hsv[2] = std::max(0.0f, std::min(hsv[2], 1 - hsv[2]) * .7f);
- hsv[2] = std::min(hsv[2], 1.1f - hsv[2]);
- return SkHSVToColor(SkColorGetA(color), hsv);
+ Lab lab = sRGBToLab(color);
+ float invertedL = std::min(110 - lab.L, 100.0f);
+ if (invertedL < lab.L) {
+ lab.L = invertedL;
+ return LabToSRGB(lab, SkColorGetA(color));
+ } else {
+ return color;
+ }
}
static SkColor transformColor(ColorTransform transform, SkColor color) {
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 6408ce6..ab80d3d 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -286,7 +286,7 @@
eglDestroySyncKHR(display, fence);
}
- return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
+ return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
}
}; // namespace android::uirenderer
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index a401b3f..7a8d026 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -17,6 +17,7 @@
#include "Caches.h"
#include "HardwareBitmapUploader.h"
+#include "Properties.h"
#include "renderthread/RenderProxy.h"
#include "utils/Color.h"
@@ -34,6 +35,7 @@
#include <SkToSRGBColorFilter.h>
#include <limits>
+#include <SkHighContrastFilter.h>
namespace android {
@@ -195,11 +197,13 @@
mPixelStorage.ashmem.size = mappedSize;
}
-Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info)
+Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette)
: SkPixelRef(info.width(), info.height(), nullptr,
bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride())
, mInfo(validateAlpha(info))
- , mPixelStorageType(PixelStorageType::Hardware) {
+ , mPixelStorageType(PixelStorageType::Hardware)
+ , mPalette(palette)
+ , mPaletteGenerationId(getGenerationID()) {
mPixelStorage.hardware.buffer = buffer;
buffer->incStrong(buffer);
setImmutable(); // HW bitmaps are always immutable
@@ -326,7 +330,106 @@
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;
}
+class MinMaxAverage {
+public:
+
+ void add(float sample) {
+ if (mCount == 0) {
+ mMin = sample;
+ mMax = sample;
+ } else {
+ mMin = std::min(mMin, sample);
+ mMax = std::max(mMax, sample);
+ }
+ mTotal += sample;
+ mCount++;
+ }
+
+ float average() {
+ return mTotal / mCount;
+ }
+
+ float min() {
+ return mMin;
+ }
+
+ float max() {
+ return mMax;
+ }
+
+ float delta() {
+ return mMax - mMin;
+ }
+
+private:
+ float mMin = 0.0f;
+ float mMax = 0.0f;
+ float mTotal = 0.0f;
+ int mCount = 0;
+};
+
+BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes) {
+ ATRACE_CALL();
+
+ SkPixmap pixmap{info, addr, rowBytes};
+
+ // TODO: This calculation of converting to HSV & tracking min/max is probably overkill
+ // Experiment with something simpler since we just want to figure out if it's "color-ful"
+ // and then the average perceptual lightness.
+
+ MinMaxAverage hue, saturation, value;
+ int sampledCount = 0;
+
+ // Sample a grid of 100 pixels to get an overall estimation of the colors in play
+ const int x_step = std::max(1, pixmap.width() / 10);
+ const int y_step = std::max(1, pixmap.height() / 10);
+ for (int x = 0; x < pixmap.width(); x += x_step) {
+ for (int y = 0; y < pixmap.height(); y += y_step) {
+ SkColor color = pixmap.getColor(x, y);
+ if (!info.isOpaque() && SkColorGetA(color) < 75) {
+ continue;
+ }
+
+ sampledCount++;
+ float hsv[3];
+ SkColorToHSV(color, hsv);
+ hue.add(hsv[0]);
+ saturation.add(hsv[1]);
+ value.add(hsv[2]);
+ }
+ }
+
+ // TODO: Tune the coverage threshold
+ if (sampledCount < 5) {
+ ALOGV("Not enough samples, only found %d for image sized %dx%d, format = %d, alpha = %d",
+ sampledCount, info.width(), info.height(), (int) info.colorType(), (int) info.alphaType());
+ return BitmapPalette::Unknown;
+ }
+
+ ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = %f]",
+ sampledCount,
+ hue.min(), hue.max(), hue.average(),
+ saturation.min(), saturation.max(), saturation.average());
+
+ if (hue.delta() <= 20 && saturation.delta() <= .1f) {
+ if (value.average() >= .5f) {
+ return BitmapPalette::Light;
+ } else {
+ return BitmapPalette::Dark;
+ }
+ }
+ return BitmapPalette::Unknown;
+}
+
} // namespace android
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index dbd4456..d268042 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -34,6 +34,12 @@
Hardware,
};
+enum class BitmapPalette {
+ Unknown,
+ Light,
+ Dark,
+};
+
namespace uirenderer {
namespace renderthread {
class RenderThread;
@@ -63,7 +69,7 @@
Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
size_t rowBytes);
Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
- Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
+ Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette = BitmapPalette::Unknown);
int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); }
@@ -103,6 +109,20 @@
*/
sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter);
+ static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes);
+
+ static BitmapPalette computePalette(const SkBitmap& bitmap) {
+ return computePalette(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes());
+ }
+
+ BitmapPalette palette() {
+ if (!isHardware() && mPaletteGenerationId != getGenerationID()) {
+ mPalette = computePalette(info(), pixels(), rowBytes());
+ mPaletteGenerationId = getGenerationID();
+ }
+ return mPalette;
+ }
+
private:
virtual ~Bitmap();
void* getStorage() const;
@@ -111,6 +131,9 @@
const PixelStorageType mPixelStorageType;
+ BitmapPalette mPalette = BitmapPalette::Unknown;
+ uint32_t mPaletteGenerationId = -1;
+
bool mHasHardwareMipMap = false;
union {
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 75740e8..a3e7859 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -16,8 +16,10 @@
#include "Color.h"
-
#include <utils/Log.h>
+#include <ui/ColorSpace.h>
+
+#include <algorithm>
#include <cmath>
namespace android {
@@ -107,5 +109,97 @@
}
}
+template<typename T>
+static constexpr T clamp(T x, T min, T max) {
+ return x < min ? min : x > max ? max : x;
+}
+
+//static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
+static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
+static const mat3 BRADFORD = mat3{
+ float3{ 0.8951f, -0.7502f, 0.0389f},
+ float3{ 0.2664f, 1.7135f, -0.0685f},
+ float3{-0.1614f, 0.0367f, 1.0296f}
+};
+
+static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
+ float3 srcLMS = matrix * srcWhitePoint;
+ float3 dstLMS = matrix * dstWhitePoint;
+ return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
+}
+
+namespace LabColorSpace {
+
+static constexpr float A = 216.0f / 24389.0f;
+static constexpr float B = 841.0f / 108.0f;
+static constexpr float C = 4.0f / 29.0f;
+static constexpr float D = 6.0f / 29.0f;
+
+float3 toXyz(const Lab& lab) {
+ float3 v { lab.L, lab.a, lab.b };
+ v[0] = clamp(v[0], 0.0f, 100.0f);
+ v[1] = clamp(v[1], -128.0f, 128.0f);
+ v[2] = clamp(v[2], -128.0f, 128.0f);
+
+ float fy = (v[0] + 16.0f) / 116.0f;
+ float fx = fy + (v[1] * 0.002f);
+ float fz = fy - (v[2] * 0.005f);
+ float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
+ float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
+ float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
+
+ v[0] = X * ILLUMINANT_D50_XYZ[0];
+ v[1] = Y * ILLUMINANT_D50_XYZ[1];
+ v[2] = Z * ILLUMINANT_D50_XYZ[2];
+
+ return v;
+}
+
+Lab fromXyz(const float3& v) {
+ float X = v[0] / ILLUMINANT_D50_XYZ[0];
+ float Y = v[1] / ILLUMINANT_D50_XYZ[1];
+ float Z = v[2] / ILLUMINANT_D50_XYZ[2];
+
+ float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C;
+ float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C;
+ float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C;
+
+ float L = 116.0f * fy - 16.0f;
+ float a = 500.0f * (fx - fy);
+ float b = 200.0f * (fy - fz);
+
+ return Lab {
+ clamp(L, 0.0f, 100.0f),
+ clamp(a, -128.0f, 128.0f),
+ clamp(b, -128.0f, 128.0f)
+ };
+}
+
+};
+
+Lab sRGBToLab(SkColor color) {
+ auto colorSpace = ColorSpace::sRGB();
+ float3 rgb;
+ rgb.r = SkColorGetR(color) / 255.0f;
+ rgb.g = SkColorGetG(color) / 255.0f;
+ rgb.b = SkColorGetB(color) / 255.0f;
+ float3 xyz = colorSpace.rgbToXYZ(rgb);
+ float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
+ xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz;
+ return LabColorSpace::fromXyz(xyz);
+}
+
+SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) {
+ auto colorSpace = ColorSpace::sRGB();
+ float3 xyz = LabColorSpace::toXyz(lab);
+ float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
+ xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz;
+ float3 rgb = colorSpace.xyzToRGB(xyz);
+ return SkColorSetARGB(alpha,
+ static_cast<uint8_t>(rgb.r * 255),
+ static_cast<uint8_t>(rgb.g * 255),
+ static_cast<uint8_t>(rgb.b * 255));
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 2bec1f5..3c13a54 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -114,6 +114,16 @@
bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
+
+struct Lab {
+ float L;
+ float a;
+ float b;
+};
+
+Lab sRGBToLab(SkColor color);
+SkColor LabToSRGB(const Lab& lab, SkAlpha alpha);
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ee9e732..30a07ef 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -734,6 +734,11 @@
* calling Activity. If a Looper is specified with a {@link LocationListener}
* then callbacks are made on the supplied Looper thread.
*
+ * <p> When location callbacks are invoked, the system will hold a wakelock
+ * on your application's behalf for some period of time, but not
+ * indefinitely. If your application requires a long running wakelock
+ * within the location callback, you should acquire it yourself.
+ *
* <p class="note"> Prior to Jellybean, the minTime parameter was
* only a hint, and some location provider implementations ignored it.
* From Jellybean and onwards it is mandatory for Android compatible
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
deleted file mode 100644
index 95edc5e..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.drawer;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.settingslib.applications.InterestingConfigChanges;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-public class CategoryManager {
-
- public static final String SETTING_PKG = "com.android.settings";
-
- private static final String TAG = "CategoryManager";
-
- private static CategoryManager sInstance;
- private final InterestingConfigChanges mInterestingConfigChanges;
-
- // Tile cache (key: <packageName, activityName>, value: tile)
- private final Map<Pair<String, String>, Tile> mTileByComponentCache;
-
- // Tile cache (key: category key, value: category)
- private final Map<String, DashboardCategory> mCategoryByKeyMap;
-
- private List<DashboardCategory> mCategories;
- private String mExtraAction;
-
- public static CategoryManager get(Context context) {
- return get(context, null);
- }
-
- public static CategoryManager get(Context context, String action) {
- if (sInstance == null) {
- sInstance = new CategoryManager(context, action);
- }
- return sInstance;
- }
-
- CategoryManager(Context context, String action) {
- mTileByComponentCache = new ArrayMap<>();
- mCategoryByKeyMap = new ArrayMap<>();
- mInterestingConfigChanges = new InterestingConfigChanges();
- mInterestingConfigChanges.applyNewConfig(context.getResources());
- mExtraAction = action;
- }
-
- public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) {
- tryInitCategories(context, SETTING_PKG);
-
- return mCategoryByKeyMap.get(categoryKey);
- }
-
- public synchronized List<DashboardCategory> getCategories(Context context) {
- tryInitCategories(context, SETTING_PKG);
- return mCategories;
- }
-
- public synchronized void reloadAllCategories(Context context, String settingPkg) {
- final boolean forceClearCache = mInterestingConfigChanges.applyNewConfig(
- context.getResources());
- mCategories = null;
- tryInitCategories(context, forceClearCache, settingPkg);
- }
-
- public synchronized void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
- if (mCategories == null) {
- Log.w(TAG, "Category is null, skipping blacklist update");
- }
- for (int i = 0; i < mCategories.size(); i++) {
- DashboardCategory category = mCategories.get(i);
- for (int j = 0; j < category.getTilesCount(); j++) {
- Tile tile = category.getTile(j);
- if (tileBlacklist.contains(tile.intent.getComponent())) {
- category.removeTile(j--);
- }
- }
- }
- }
-
- private synchronized void tryInitCategories(Context context, String settingPkg) {
- // Keep cached tiles by default. The cache is only invalidated when InterestingConfigChange
- // happens.
- tryInitCategories(context, false /* forceClearCache */, settingPkg);
- }
-
- private synchronized void tryInitCategories(Context context, boolean forceClearCache,
- String settingPkg) {
- if (mCategories == null) {
- if (forceClearCache) {
- mTileByComponentCache.clear();
- }
- mCategoryByKeyMap.clear();
- mCategories = TileUtils.getCategories(context, mTileByComponentCache,
- mExtraAction, settingPkg);
- for (DashboardCategory category : mCategories) {
- mCategoryByKeyMap.put(category.key, category);
- }
- backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
- sortCategories(context, mCategoryByKeyMap);
- filterDuplicateTiles(mCategoryByKeyMap);
- }
- }
-
- @VisibleForTesting
- synchronized void backwardCompatCleanupForCategory(
- Map<Pair<String, String>, Tile> tileByComponentCache,
- Map<String, DashboardCategory> categoryByKeyMap) {
- // A package can use a) CategoryKey, b) old category keys, c) both.
- // Check if a package uses old category key only.
- // If yes, map them to new category key.
-
- // Build a package name -> tile map first.
- final Map<String, List<Tile>> packageToTileMap = new HashMap<>();
- for (Entry<Pair<String, String>, Tile> tileEntry : tileByComponentCache.entrySet()) {
- final String packageName = tileEntry.getKey().first;
- List<Tile> tiles = packageToTileMap.get(packageName);
- if (tiles == null) {
- tiles = new ArrayList<>();
- packageToTileMap.put(packageName, tiles);
- }
- tiles.add(tileEntry.getValue());
- }
-
- for (Entry<String, List<Tile>> entry : packageToTileMap.entrySet()) {
- final List<Tile> tiles = entry.getValue();
- // Loop map, find if all tiles from same package uses old key only.
- boolean useNewKey = false;
- boolean useOldKey = false;
- for (Tile tile : tiles) {
- if (CategoryKey.KEY_COMPAT_MAP.containsKey(tile.category)) {
- useOldKey = true;
- } else {
- useNewKey = true;
- break;
- }
- }
- // Uses only old key, map them to new keys one by one.
- if (useOldKey && !useNewKey) {
- for (Tile tile : tiles) {
- final String newCategoryKey = CategoryKey.KEY_COMPAT_MAP.get(tile.category);
- tile.category = newCategoryKey;
- // move tile to new category.
- DashboardCategory newCategory = categoryByKeyMap.get(newCategoryKey);
- if (newCategory == null) {
- newCategory = new DashboardCategory();
- categoryByKeyMap.put(newCategoryKey, newCategory);
- }
- newCategory.addTile(tile);
- }
- }
- }
- }
-
- /**
- * Sort the tiles injected from all apps such that if they have the same priority value,
- * they wil lbe sorted by package name.
- * <p/>
- * A list of tiles are considered sorted when their priority value decreases in a linear
- * scan.
- */
- @VisibleForTesting
- synchronized void sortCategories(Context context,
- Map<String, DashboardCategory> categoryByKeyMap) {
- for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
- categoryEntry.getValue().sortTiles(context.getPackageName());
- }
- }
-
- /**
- * Filter out duplicate tiles from category. Duplicate tiles are the ones pointing to the
- * same intent.
- */
- @VisibleForTesting
- synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
- for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
- final DashboardCategory category = categoryEntry.getValue();
- final int count = category.getTilesCount();
- final Set<ComponentName> components = new ArraySet<>();
- for (int i = count - 1; i >= 0; i--) {
- final Tile tile = category.getTile(i);
- if (tile.intent == null) {
- continue;
- }
- final ComponentName tileComponent = tile.intent.getComponent();
- if (components.contains(tileComponent)) {
- category.removeTile(i);
- } else {
- components.add(tileComponent);
- }
- }
- }
- }
-
- /**
- * Sort priority value for tiles within a single {@code DashboardCategory}.
- *
- * @see #sortCategories(Context, Map)
- */
- private synchronized void sortCategoriesForExternalTiles(Context context,
- DashboardCategory dashboardCategory) {
- dashboardCategory.sortTiles(context.getPackageName());
-
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index f54ba8c..06f1456 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -35,6 +35,8 @@
import android.util.Log;
import android.util.Pair;
+import androidx.annotation.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -48,6 +50,8 @@
private static final boolean DEBUG_TIMING = false;
private static final String LOG_TAG = "TileUtils";
+ @VisibleForTesting
+ static final String SETTING_PKG = "com.android.settings";
/**
* Settings will search for system activities of this action and add them as a top level
@@ -200,7 +204,7 @@
* categories
*/
public static List<DashboardCategory> getCategories(Context context,
- Map<Pair<String, String>, Tile> cache, String extraAction, String settingPkg) {
+ Map<Pair<String, String>, Tile> cache, String extraAction) {
final long startTime = System.currentTimeMillis();
boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
!= 0;
@@ -210,22 +214,18 @@
// TODO: Needs much optimization, too many PM queries going on here.
if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
// Only add Settings for this user.
- getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true,
- settingPkg);
+ getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true);
getTilesForAction(context, user, OPERATOR_SETTINGS, cache,
- OPERATOR_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
+ OPERATOR_DEFAULT_CATEGORY, tiles, false, true);
getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
- MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
+ MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true);
}
if (setup) {
- getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false,
- settingPkg);
- getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false,
- settingPkg);
- if (extraAction != null) {
- getTilesForAction(context, user, extraAction, cache, null, tiles, false,
- settingPkg);
- }
+ getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);
+ getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false);
+ if (extraAction != null) {
+ getTilesForAction(context, user, extraAction, cache, null, tiles, false);
+ }
}
}
@@ -256,19 +256,18 @@
private static void getTilesForAction(Context context,
UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
- String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
- String settingPkg) {
+ String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings) {
getTilesForAction(context, user, action, addedCache, defaultCategory, outTiles,
- requireSettings, requireSettings, settingPkg);
+ requireSettings, requireSettings);
}
private static void getTilesForAction(Context context,
UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
- boolean usePriority, String settingPkg) {
+ boolean usePriority) {
Intent intent = new Intent(action);
if (requireSettings) {
- intent.setPackage(settingPkg);
+ intent.setPackage(SETTING_PKG);
}
getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
usePriority, true, true);
@@ -281,7 +280,6 @@
PackageManager pm = context.getPackageManager();
List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
PackageManager.GET_META_DATA, user.getIdentifier());
- Map<String, IContentProvider> providerMap = new HashMap<>();
for (ResolveInfo resolved : results) {
if (!resolved.system) {
// Do not allow any app to add to settings, only system ones.
@@ -312,7 +310,7 @@
tile.priority = usePriority ? resolved.priority : 0;
tile.metaData = activityInfo.metaData;
updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
- pm, providerMap, forceTintExternalIcon);
+ pm, forceTintExternalIcon);
if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
addedCache.put(key, tile);
}
@@ -328,11 +326,10 @@
private static boolean updateTileData(Context context, Tile tile,
ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm,
- Map<String, IContentProvider> providerMap, boolean forceTintExternalIcon) {
+ boolean forceTintExternalIcon) {
if (applicationInfo.isSystemApp()) {
boolean forceTintIcon = false;
int icon = 0;
- Pair<String, Integer> iconFromUri = null;
CharSequence title = null;
String summary = null;
String keyHint = null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
index 92044c3..7ed357c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
@@ -36,7 +36,8 @@
"/system/etc/NOTICE.xml.gz",
"/vendor/etc/NOTICE.xml.gz",
"/odm/etc/NOTICE.xml.gz",
- "/oem/etc/NOTICE.xml.gz"};
+ "/oem/etc/NOTICE.xml.gz",
+ "/product/etc/NOTICE.xml.gz"};
private static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
deleted file mode 100644
index 4efcb7e..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.drawer;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Pair;
-
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.shadows.ShadowApplication;
-
-import java.util.HashMap;
-import java.util.Map;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-public class CategoryManagerTest {
-
- private Context mContext;
- private CategoryManager mCategoryManager;
- private Map<Pair<String, String>, Tile> mTileByComponentCache;
- private Map<String, DashboardCategory> mCategoryByKeyMap;
-
- @Before
- public void setUp() {
- mContext = ShadowApplication.getInstance().getApplicationContext();
- mTileByComponentCache = new HashMap<>();
- mCategoryByKeyMap = new HashMap<>();
- mCategoryManager = CategoryManager.get(mContext);
- }
-
- @Test
- public void getInstance_shouldBeSingleton() {
- assertThat(mCategoryManager).isSameAs(CategoryManager.get(mContext));
- }
-
- @Test
- public void backwardCompatCleanupForCategory_shouldNotChangeCategoryForNewKeys() {
- final Tile tile1 = new Tile();
- final Tile tile2 = new Tile();
- tile1.category = CategoryKey.CATEGORY_ACCOUNT;
- tile2.category = CategoryKey.CATEGORY_ACCOUNT;
- final DashboardCategory category = new DashboardCategory();
- category.addTile(tile1);
- category.addTile(tile2);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_ACCOUNT, category);
- mTileByComponentCache.put(new Pair<>("PACKAGE", "1"), tile1);
- mTileByComponentCache.put(new Pair<>("PACKAGE", "2"), tile2);
-
- mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-
- assertThat(mCategoryByKeyMap.size()).isEqualTo(1);
- assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT)).isNotNull();
- }
-
- @Test
- public void backwardCompatCleanupForCategory_shouldNotChangeCategoryForMixedKeys() {
- final Tile tile1 = new Tile();
- final Tile tile2 = new Tile();
- final String oldCategory = "com.android.settings.category.wireless";
- tile1.category = CategoryKey.CATEGORY_ACCOUNT;
- tile2.category = oldCategory;
- final DashboardCategory category1 = new DashboardCategory();
- category1.addTile(tile1);
- final DashboardCategory category2 = new DashboardCategory();
- category2.addTile(tile2);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_ACCOUNT, category1);
- mCategoryByKeyMap.put(oldCategory, category2);
- mTileByComponentCache.put(new Pair<>("PACKAGE", "CLASS1"), tile1);
- mTileByComponentCache.put(new Pair<>("PACKAGE", "CLASS2"), tile2);
-
- mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-
- assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
- assertThat(
- mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).getTilesCount()).isEqualTo(1);
- assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
- }
-
- @Test
- public void backwardCompatCleanupForCategory_shouldChangeCategoryForOldKeys() {
- final Tile tile1 = new Tile();
- final String oldCategory = "com.android.settings.category.wireless";
- tile1.category = oldCategory;
- final DashboardCategory category1 = new DashboardCategory();
- category1.addTile(tile1);
- mCategoryByKeyMap.put(oldCategory, category1);
- mTileByComponentCache.put(new Pair<>("PACKAGE", "CLASS1"), tile1);
-
- mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-
- // Added 1 more category to category map.
- assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
- // The new category map has CATEGORY_NETWORK type now, which contains 1 tile.
- assertThat(
- mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).getTilesCount()).isEqualTo(1);
- // Old category still exists.
- assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
- }
-
- @Test
- public void sortCategories_singlePackage_shouldReorderBasedOnPriority() {
- // Create some fake tiles that are not sorted.
- final String testPackage = "com.android.test";
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile1.priority = 100;
- final Tile tile2 = new Tile();
- tile2.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class2"));
- tile2.priority = 50;
- final Tile tile3 = new Tile();
- tile3.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class3"));
- tile3.priority = 200;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- // Sort their priorities
- mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
- mCategoryByKeyMap);
-
- // Verify they are now sorted.
- assertThat(category.getTile(0)).isSameAs(tile3);
- assertThat(category.getTile(1)).isSameAs(tile1);
- assertThat(category.getTile(2)).isSameAs(tile2);
- }
-
- @Test
- public void sortCategories_multiPackage_shouldReorderBasedOnPackageAndPriority() {
- // Create some fake tiles that are not sorted.
- final String testPackage1 = "com.android.test1";
- final String testPackage2 = "com.android.test2";
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent =
- new Intent().setComponent(new ComponentName(testPackage2, "class1"));
- tile1.priority = 100;
- final Tile tile2 = new Tile();
- tile2.intent =
- new Intent().setComponent(new ComponentName(testPackage1, "class2"));
- tile2.priority = 100;
- final Tile tile3 = new Tile();
- tile3.intent =
- new Intent().setComponent(new ComponentName(testPackage1, "class3"));
- tile3.priority = 50;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- // Sort their priorities
- mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
- mCategoryByKeyMap);
-
- // Verify they are now sorted.
- assertThat(category.getTile(0)).isSameAs(tile2);
- assertThat(category.getTile(1)).isSameAs(tile1);
- assertThat(category.getTile(2)).isSameAs(tile3);
- }
-
- @Test
- public void sortCategories_internalPackageTiles_shouldSkipTileForInternalPackage() {
- // Create some fake tiles that are not sorted.
- final String testPackage =
- ShadowApplication.getInstance().getApplicationContext().getPackageName();
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile1.priority = 100;
- final Tile tile2 = new Tile();
- tile2.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class2"));
- tile2.priority = 100;
- final Tile tile3 = new Tile();
- tile3.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class3"));
- tile3.priority = 50;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- // Sort their priorities
- mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
- mCategoryByKeyMap);
-
- // Verify the sorting order is not changed
- assertThat(category.getTile(0)).isSameAs(tile1);
- assertThat(category.getTile(1)).isSameAs(tile2);
- assertThat(category.getTile(2)).isSameAs(tile3);
- }
-
- @Test
- public void sortCategories_internalAndExternalPackageTiles_shouldRetainPriorityOrdering() {
- // Inject one external tile among internal tiles.
- final String testPackage =
- ShadowApplication.getInstance().getApplicationContext().getPackageName();
- final String testPackage2 = "com.google.test2";
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent = new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile1.priority = 2;
- final Tile tile2 = new Tile();
- tile2.intent = new Intent().setComponent(new ComponentName(testPackage, "class2"));
- tile2.priority = 1;
- final Tile tile3 = new Tile();
- tile3.intent = new Intent().setComponent(new ComponentName(testPackage2, "class0"));
- tile3.priority = 0;
- final Tile tile4 = new Tile();
- tile4.intent = new Intent().setComponent(new ComponentName(testPackage, "class3"));
- tile4.priority = -1;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- category.addTile(tile4);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- // Sort their priorities
- mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
- mCategoryByKeyMap);
-
- // Verify the sorting order is not changed
- assertThat(category.getTile(0)).isSameAs(tile1);
- assertThat(category.getTile(1)).isSameAs(tile2);
- assertThat(category.getTile(2)).isSameAs(tile3);
- assertThat(category.getTile(3)).isSameAs(tile4);
- }
-
- @Test
- public void sortCategories_samePriority_internalPackageTileShouldTakePrecedence() {
- // Inject one external tile among internal tiles with same priority.
- final String testPackage =
- ShadowApplication.getInstance().getApplicationContext().getPackageName();
- final String testPackage2 = "com.google.test2";
- final String testPackage3 = "com.abcde.test3";
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent = new Intent().setComponent(new ComponentName(testPackage2, "class1"));
- tile1.priority = 1;
- final Tile tile2 = new Tile();
- tile2.intent = new Intent().setComponent(new ComponentName(testPackage, "class2"));
- tile2.priority = 1;
- final Tile tile3 = new Tile();
- tile3.intent = new Intent().setComponent(new ComponentName(testPackage3, "class3"));
- tile3.priority = 1;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- // Sort their priorities
- mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
- mCategoryByKeyMap);
-
- // Verify the sorting order is internal first, follow by package name ordering
- assertThat(category.getTile(0)).isSameAs(tile2);
- assertThat(category.getTile(1)).isSameAs(tile3);
- assertThat(category.getTile(2)).isSameAs(tile1);
- }
-
- @Test
- public void filterTiles_noDuplicate_noChange() {
- // Create some unique tiles
- final String testPackage =
- ShadowApplication.getInstance().getApplicationContext().getPackageName();
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile1.priority = 100;
- final Tile tile2 = new Tile();
- tile2.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class2"));
- tile2.priority = 100;
- final Tile tile3 = new Tile();
- tile3.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class3"));
- tile3.priority = 50;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
-
- assertThat(category.getTilesCount()).isEqualTo(3);
- }
-
- @Test
- public void filterTiles_hasDuplicate_shouldOnlyKeepUniqueTiles() {
- // Create tiles pointing to same intent.
- final String testPackage =
- ShadowApplication.getInstance().getApplicationContext().getPackageName();
- final DashboardCategory category = new DashboardCategory();
- final Tile tile1 = new Tile();
- tile1.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile1.priority = 100;
- final Tile tile2 = new Tile();
- tile2.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile2.priority = 100;
- final Tile tile3 = new Tile();
- tile3.intent =
- new Intent().setComponent(new ComponentName(testPackage, "class1"));
- tile3.priority = 50;
- category.addTile(tile1);
- category.addTile(tile2);
- category.addTile(tile3);
- mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
-
- mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
-
- assertThat(category.getTilesCount()).isEqualTo(1);
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 31621af..a1c48f0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -177,8 +177,7 @@
event -> testAction.equals(event.getAction())), anyInt(), anyInt()))
.thenReturn(info);
- List<DashboardCategory> categoryList = TileUtils.getCategories(
- mContext, cache, testAction, CategoryManager.SETTING_PKG);
+ List<DashboardCategory> categoryList = TileUtils.getCategories(mContext, cache, testAction);
assertThat(categoryList.get(0).getTile(0).category).isEqualTo(testCategory);
}
@@ -193,12 +192,12 @@
userHandleList.add(new UserHandle(ActivityManager.getCurrentUser()));
when(mUserManager.getUserProfiles()).thenReturn(userHandleList);
- TileUtils.getCategories(mContext, cache, null /* action */, CategoryManager.SETTING_PKG);
+ TileUtils.getCategories(mContext, cache, null /* action */);
verify(mPackageManager, atLeastOnce()).queryIntentActivitiesAsUser(
intentCaptor.capture(), anyInt(), anyInt());
assertThat(intentCaptor.getAllValues().get(0).getPackage())
- .isEqualTo(CategoryManager.SETTING_PKG);
+ .isEqualTo(TileUtils.SETTING_PKG);
}
@Test
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 2a22bf2..38d266d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -154,12 +154,11 @@
}
public void fadeInTranslating() {
- float translation = mShelfIcons.getTranslationY();
- mShelfIcons.setTranslationY(translation - mShelfAppearTranslation);
+ mShelfIcons.setTranslationY(-mShelfAppearTranslation);
mShelfIcons.setAlpha(0);
mShelfIcons.animate()
.setInterpolator(Interpolators.DECELERATE_QUINT)
- .translationY(translation)
+ .translationY(0)
.setDuration(SHELF_IN_TRANSLATION_DURATION)
.start();
mShelfIcons.animate()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index e804460..4df1e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -118,7 +118,7 @@
mPanel.startExpandingFromPeek();
// This call needs to be after the expansion start otherwise we will get a
// flicker of one frame as it's not expanded yet.
- mHeadsUpManager.unpinAll();
+ mHeadsUpManager.unpinAll(true);
mPanel.clearNotificationEffects();
endMotion();
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 6694b93a3..677dd73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -389,13 +389,23 @@
/**
* Unpins all pinned Heads Up Notifications.
+ * @param userUnPinned The unpinned action is trigger by user real operation.
*/
- public void unpinAll() {
+ public void unpinAll(boolean userUnPinned) {
for (String key : mHeadsUpEntries.keySet()) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
setEntryPinned(entry, false /* isPinned */);
// maybe it got un sticky
entry.updateEntry(false /* updatePostTime */);
+
+ // when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
+ // on the screen.
+ if (userUnPinned && entry.entry != null && entry.entry.row != null) {
+ ExpandableNotificationRow row = entry.entry.row;
+ if (row.mustStayOnScreen()) {
+ row.setHeadsUpIsVisible();
+ }
+ }
}
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 65ad596..ad80cc26 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -123,8 +123,7 @@
}
public void destroy() {
- mHandler.sendMessage(obtainMessage(
- RemoteFillService::handleDestroy, this));
+ mHandler.sendMessage(obtainMessage(RemoteFillService::handleDestroy, this));
}
private void handleDestroy() {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 18255c5..8f59ca9 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -839,7 +839,8 @@
// FillServiceCallbacks
@Override
public void onServiceDied(RemoteFillService service) {
- // TODO(b/337565347): implement
+ Slog.w(TAG, "removing session because service died");
+ forceRemoveSelfLocked();
}
// AutoFillUiCallback
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 74d8755..9631e46 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2271,6 +2271,7 @@
return ruleName;
}
+ @GuardedBy("mRulesLock")
private @NonNull SparseIntArray getUidFirewallRulesLR(int chain) {
switch (chain) {
case FIREWALL_CHAIN_STANDBY:
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 17257ee..91942a5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3332,6 +3332,14 @@
// Turn this condition on to cause killing to happen regularly, for testing.
if (proc.baseProcessTracker != null) {
proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
+ for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ proc.lastCachedPss, holder.appVersion);
+ }
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
} else if (proc != null && !keepIfLarge
@@ -3341,6 +3349,14 @@
if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
if (proc.baseProcessTracker != null) {
proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
+ for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ proc.lastCachedPss, holder.appVersion);
+ }
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
}
@@ -5365,6 +5381,19 @@
infos[i].getTotalUss(), infos[i].getTotalRss(), false,
ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime-startTime,
proc.pkgList.mPkgList);
+ for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ infos[i].getTotalPss(),
+ infos[i].getTotalUss(),
+ infos[i].getTotalRss(),
+ ProcessStats.ADD_PSS_EXTERNAL_SLOW,
+ endTime-startTime,
+ holder.appVersion);
+ }
}
}
}
@@ -5395,6 +5424,16 @@
// Record this for posterity if the process has been stable.
proc.baseProcessTracker.addPss(pss[i], tmpUss[0], tmpUss[2], false,
ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList.mPkgList);
+ for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ pss[i], tmpUss[0], tmpUss[2],
+ ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime,
+ holder.appVersion);
+ }
}
}
}
@@ -15232,6 +15271,16 @@
// Record this for posterity if the process has been stable.
r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime-startTime, r.pkgList.mPkgList);
+ for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ r.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ myTotalPss, myTotalUss, myTotalRss, reportType,
+ endTime-startTime,
+ holder.appVersion);
+ }
}
}
@@ -15730,6 +15779,15 @@
// Record this for posterity if the process has been stable.
r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
reportType, endTime-startTime, r.pkgList.mPkgList);
+ for (int ipkg = r.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = r.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ r.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ myTotalPss, myTotalUss, myTotalRss, reportType, endTime-startTime,
+ holder.appVersion);
+ }
}
}
@@ -19759,6 +19817,15 @@
proc.lastPssTime = now;
proc.baseProcessTracker.addPss(
pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
+ for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.PROCESS_MEMORY_STAT_REPORTED,
+ proc.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ pss, uss, rss, statType, pssDuration,
+ holder.appVersion);
+ }
if (DEBUG_PSS) Slog.d(TAG_PSS,
"pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
+ " state=" + ProcessList.makeProcStateString(procState));
@@ -20110,6 +20177,14 @@
app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
+ " dur=" + checkDur + " limit=" + cpuLimit, true);
app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList);
+ for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg);
+ StatsLog.write(StatsLog.EXCESSIVE_CPU_USAGE_REPORTED,
+ app.info.uid,
+ holder.state.getName(),
+ holder.state.getPackage(),
+ holder.appVersion);
+ }
}
}
app.lastCpuTime = app.curCpuTime;
@@ -20945,6 +21020,7 @@
}
if (memFactor != mLastMemoryLevel) {
EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
+ StatsLog.write(StatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
}
mLastMemoryLevel = memFactor;
mLastNumProcesses = mLruProcesses.size();
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index dbade5f..de732c7 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -4676,6 +4676,8 @@
// will wake up stacks or put them to sleep as appropriate.
if (wasSleeping) {
mSleeping = false;
+ StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
+ StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__AWAKE);
startTimeTrackingFocusedActivityLocked();
mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
mStackSupervisor.comeOutOfSleepIfNeededLocked();
@@ -4686,6 +4688,8 @@
}
} else if (!mSleeping && shouldSleep) {
mSleeping = true;
+ StatsLog.write(StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED,
+ StatsLog.ACTIVITY_MANAGER_SLEEP_STATE_CHANGED__STATE__ASLEEP);
if (mCurAppTimeTracker != null) {
mCurAppTimeTracker.stop();
}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index d9a8818..2541352 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -15,6 +15,8 @@
*/
package com.android.server.am;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_LINK_LAYER_STATS;
+
import android.annotation.Nullable;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
@@ -410,8 +412,11 @@
if (mWifiManager != null) {
try {
- wifiReceiver = new SynchronousResultReceiver("wifi");
- mWifiManager.requestActivityInfo(wifiReceiver);
+ // Only fetch WiFi power data if it is supported.
+ if ((mWifiManager.getSupportedFeatures() & WIFI_FEATURE_LINK_LAYER_STATS) != 0) {
+ wifiReceiver = new SynchronousResultReceiver("wifi");
+ mWifiManager.requestActivityInfo(wifiReceiver);
+ }
} catch (RemoteException e) {
// Oh well.
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 211b8ff..b33ce2b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -43,6 +43,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -538,6 +539,12 @@
if (origBase != null) {
origBase.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+ pkgList.valueAt(ipkg).appVersion);
+ }
origBase.makeInactive();
}
baseProcessTracker = tracker.getProcessStateLocked(info.packageName, uid,
@@ -567,6 +574,12 @@
if (origBase != null) {
origBase.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+ pkgList.valueAt(ipkg).appVersion);
+ }
origBase.makeInactive();
}
baseProcessTracker = null;
@@ -829,6 +842,12 @@
public void forceProcessStateUpTo(int newState) {
if (mRepProcState > newState) {
curProcState = mRepProcState = newState;
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(mRepProcState),
+ pkgList.valueAt(ipkg).appVersion);
+ }
}
}
@@ -841,6 +860,12 @@
long now = SystemClock.uptimeMillis();
baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), now, pkgList.mPkgList);
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+ pkgList.valueAt(ipkg).appVersion);
+ }
if (N != 1) {
for (int i=0; i<N; i++) {
ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
@@ -892,6 +917,12 @@
void setReportedProcState(int repProcState) {
mRepProcState = repProcState;
+ for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
+ uid, processName, pkgList.keyAt(ipkg),
+ ActivityManager.processStateAmToProto(mRepProcState),
+ pkgList.valueAt(ipkg).appVersion);
+ }
mWindowProcessController.setReportedProcState(repProcState);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 7ba8e14..b796abd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -66,11 +66,7 @@
Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
mSystemAudioActivated ? "true" : "false");
}
- if (setSystemAudioMode(false)) {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildSetSystemAudioMode(
- mAddress, Constants.ADDR_BROADCAST, false));
- }
+ terminateSystemAudioMode();
}
@Override
@@ -102,6 +98,18 @@
}
}
+ @ServiceThreadOnly
+ protected boolean handleActiveSource(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ int logicalAddress = message.getSource();
+ int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+ ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
+ if (!mActiveSource.equals(activeSource)) {
+ setActiveSource(activeSource);
+ }
+ return true;
+ }
+
@Override
@ServiceThreadOnly
protected int getPreferredAddress() {
@@ -320,6 +328,21 @@
}
}
+ protected void terminateSystemAudioMode() {
+ // remove pending initiation actions
+ removeAction(SystemAudioInitiationActionFromAvr.class);
+ if (!isSystemAudioActivated()) {
+ return;
+ }
+
+ if (setSystemAudioMode(false)) {
+ // send <Set System Audio Mode> [“Off”]
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ mAddress, Constants.ADDR_BROADCAST, false));
+ }
+ }
+
/** Reports if System Audio Mode is supported by the connected TV */
interface TvSystemAudioModeSupportedCallback {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ebb3209..76f9695 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -628,6 +628,7 @@
mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
}
+ @GuardedBy("mUidRulesFirstLock")
void updatePowerSaveWhitelistUL() {
try {
int[] whitelist = mDeviceIdleController.getAppIdWhitelistExceptIdle();
@@ -654,6 +655,7 @@
*
* @return whether any uid has been whitelisted.
*/
+ @GuardedBy("mUidRulesFirstLock")
boolean addDefaultRestrictBackgroundWhitelistUidsUL() {
final List<UserInfo> users = mUserManager.getUsers();
final int numberUsers = users.size();
@@ -666,6 +668,7 @@
return changed;
}
+ @GuardedBy("mUidRulesFirstLock")
private boolean addDefaultRestrictBackgroundWhitelistUidsUL(int userId) {
final SystemConfig sysConfig = SystemConfig.getInstance();
final PackageManager pm = mContext.getPackageManager();
@@ -1120,6 +1123,7 @@
* Check {@link NetworkPolicy} against current {@link INetworkStatsService}
* to show visible notifications as needed.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
void updateNotificationsNL() {
if (LOGV) Slog.v(TAG, "updateNotificationsNL()");
Trace.traceBegin(TRACE_TAG_NETWORK, "updateNotificationsNL");
@@ -1266,6 +1270,7 @@
* @return relevant subId, or {@link #INVALID_SUBSCRIPTION_ID} when no
* matching subId found.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private int findRelevantSubIdNL(NetworkTemplate template) {
// Mobile template is relevant when any active subscriber matches
for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
@@ -1285,6 +1290,7 @@
* Notify that given {@link NetworkTemplate} is over
* {@link NetworkPolicy#limitBytes}, potentially showing dialog to user.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private void notifyOverLimitNL(NetworkTemplate template) {
if (!mOverLimitNotified.contains(template)) {
mContext.startActivity(buildNetworkOverLimitIntent(mContext.getResources(), template));
@@ -1292,6 +1298,7 @@
}
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
private void notifyUnderLimitNL(NetworkTemplate template) {
mOverLimitNotified.remove(template);
}
@@ -1462,6 +1469,7 @@
* @param subId that has its associated NetworkPolicy updated if necessary
* @return if any policies were updated
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private boolean maybeUpdateMobilePolicyCycleAL(int subId, String subscriberId) {
if (LOGV) Slog.v(TAG, "maybeUpdateMobilePolicyCycleAL()");
@@ -1619,6 +1627,7 @@
* @param shouldNormalizePolicies true iff network policies need to be normalized after the
* update.
*/
+ @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
void handleNetworkPoliciesUpdateAL(boolean shouldNormalizePolicies) {
if (shouldNormalizePolicies) {
normalizePoliciesNL();
@@ -1633,6 +1642,7 @@
* Proactively control network data connections when they exceed
* {@link NetworkPolicy#limitBytes}.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
void updateNetworkEnabledNL() {
if (LOGV) Slog.v(TAG, "updateNetworkEnabledNL()");
Trace.traceBegin(TRACE_TAG_NETWORK, "updateNetworkEnabledNL");
@@ -1773,6 +1783,7 @@
* {@link NetworkPolicy} that need to be enforced. When matches found, set
* remaining quota based on usage cycle and historical stats.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
void updateNetworkRulesNL() {
if (LOGV) Slog.v(TAG, "updateNetworkRulesNL()");
Trace.traceBegin(TRACE_TAG_NETWORK, "updateNetworkRulesNL");
@@ -1957,6 +1968,7 @@
* Once any {@link #mNetworkPolicy} are loaded from disk, ensure that we
* have at least a default mobile policy defined.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private void ensureActiveMobilePolicyAL() {
if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyAL()");
if (mSuppressDefaultPolicy) return;
@@ -1977,6 +1989,7 @@
* @param subscriberId that we check for an existing policy
* @return true if a mobile network policy was added, or false one already existed.
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private boolean ensureActiveMobilePolicyAL(int subId, String subscriberId) {
// Poke around to see if we already have a policy
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
@@ -2036,6 +2049,7 @@
*
* @return if the policy was modified
*/
+ @GuardedBy("mNetworkPoliciesSecondLock")
private boolean updateDefaultMobilePolicyAL(int subId, NetworkPolicy policy) {
if (!policy.inferred) {
if (LOGD) Slog.d(TAG, "Ignoring user-defined policy " + policy);
@@ -2091,6 +2105,7 @@
}
}
+ @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
private void readPolicyAL() {
if (LOGV) Slog.v(TAG, "readPolicyAL()");
@@ -2322,6 +2337,7 @@
* Perform upgrade step of moving any user-defined meterness overrides over
* into {@link WifiConfiguration}.
*/
+ @GuardedBy({"mNetworkPoliciesSecondLock", "mUidRulesFirstLock"})
private void upgradeWifiMeteredOverrideAL() {
boolean modified = false;
final WifiManager wm = mContext.getSystemService(WifiManager.class);
@@ -2352,6 +2368,7 @@
}
}
+ @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
void writePolicyAL() {
if (LOGV) Slog.v(TAG, "writePolicyAL()");
@@ -2523,6 +2540,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void setUidPolicyUncheckedUL(int uid, int oldPolicy, int policy, boolean persist) {
setUidPolicyUncheckedUL(uid, policy, false);
@@ -2554,6 +2572,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void setUidPolicyUncheckedUL(int uid, int policy, boolean persist) {
if (policy == POLICY_NONE) {
mUidPolicy.delete(uid);
@@ -2601,6 +2620,7 @@
* Removes any persistable state associated with given {@link UserHandle}, persisting
* if any changes that are made.
*/
+ @GuardedBy("mUidRulesFirstLock")
boolean removeUserStateUL(int userId, boolean writePolicy) {
mLogger.removingUserState(userId);
@@ -2702,10 +2722,12 @@
}
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
private void normalizePoliciesNL() {
normalizePoliciesNL(getNetworkPolicies(mContext.getOpPackageName()));
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
private void normalizePoliciesNL(NetworkPolicy[] policies) {
mNetworkPolicy.clear();
for (NetworkPolicy policy : policies) {
@@ -2795,6 +2817,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void setRestrictBackgroundUL(boolean restrictBackground) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setRestrictBackgroundUL");
try {
@@ -3447,11 +3470,13 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private boolean isUidForegroundOnRestrictBackgroundUL(int uid) {
final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
return isProcStateAllowedWhileOnRestrictBackground(procState);
}
+ @GuardedBy("mUidRulesFirstLock")
private boolean isUidForegroundOnRestrictPowerUL(int uid) {
final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
return isProcStateAllowedWhileIdleOrPowerSaveMode(procState);
@@ -3467,6 +3492,7 @@
* {@link #updateRulesForDataUsageRestrictionsUL(int)} and
* {@link #updateRulesForPowerRestrictionsUL(int)}
*/
+ @GuardedBy("mUidRulesFirstLock")
private void updateUidStateUL(int uid, int uidState) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL");
try {
@@ -3493,6 +3519,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void removeUidStateUL(int uid) {
final int index = mUidState.indexOfKey(uid);
if (index >= 0) {
@@ -3537,6 +3564,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRulesForPowerSaveUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL");
try {
@@ -3547,10 +3575,12 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRuleForRestrictPowerUL(int uid) {
updateRulesForWhitelistedPowerSaveUL(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRulesForDeviceIdleUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForDeviceIdleUL");
try {
@@ -3561,12 +3591,14 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRuleForDeviceIdleUL(int uid) {
updateRulesForWhitelistedPowerSaveUL(uid, mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
}
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForWhitelistedPowerSaveUL(boolean enabled, int chain,
SparseIntArray rules) {
if (enabled) {
@@ -3611,6 +3643,7 @@
* {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is
* whitelisted.
*/
+ @GuardedBy("mUidRulesFirstLock")
private boolean isWhitelistedBatterySaverUL(int uid, boolean deviceIdleMode) {
final int appId = UserHandle.getAppId(uid);
boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
@@ -3623,6 +3656,7 @@
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
if (enabled) {
final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid,
@@ -3635,6 +3669,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRulesForAppIdleUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForAppIdleUL");
try {
@@ -3664,6 +3699,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
void updateRuleForAppIdleUL(int uid) {
if (!isUidValidForBlacklistRules(uid)) return;
@@ -3687,6 +3723,7 @@
* Toggle the firewall standby chain and inform listeners if the uid rules have effectively
* changed.
*/
+ @GuardedBy("mUidRulesFirstLock")
void updateRulesForAppIdleParoleUL() {
boolean paroled = mUsageStats.isAppIdleParoleOn();
boolean enableChain = !paroled;
@@ -3719,6 +3756,7 @@
* Update rules that might be changed by {@link #mRestrictBackground},
* {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value.
*/
+ @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
private void updateRulesForGlobalChangeAL(boolean restrictedNetworksChanged) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
@@ -3740,6 +3778,7 @@
}
// TODO: rename / document to make it clear these are global (not app-specific) rules
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForRestrictPowerUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL");
try {
@@ -3751,6 +3790,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForRestrictBackgroundUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictBackgroundUL");
try {
@@ -3771,6 +3811,7 @@
}
// TODO: refactor / consolidate all those updateXyz methods, there are way too many of them...
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForAllAppsUL(@RestrictType int type) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL-" + type);
@@ -3822,6 +3863,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForTempWhitelistChangeUL(int appId) {
final List<UserInfo> users = mUserManager.getUsers();
final int numUsers = users.size();
@@ -3886,6 +3928,7 @@
/**
* Clears all state - internal and external - associated with an UID.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void onUidDeletedUL(int uid) {
// First cleanup in-memory state synchronously...
mUidRules.delete(uid);
@@ -3914,6 +3957,7 @@
*
* <p>This method changes both the external firewall rules and the internal state.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void updateRestrictionRulesForUidUL(int uid) {
// Methods below only changes the firewall rules for the power-related modes.
updateRuleForDeviceIdleUL(uid);
@@ -4104,6 +4148,7 @@
* <p>
* <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void updateRulesForPowerRestrictionsUL(int uid) {
final int oldUidRules = mUidRules.get(uid, RULE_NONE);
@@ -4540,6 +4585,7 @@
* @param uidRules new UID rules; if {@code null}, only toggles chain state.
* @param toggle whether the chain should be enabled, disabled, or not changed.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules,
@ChainToggleType int toggle) {
if (uidRules != null) {
@@ -4606,6 +4652,7 @@
/**
* Add or remove a uid to the firewall blacklist for all network ifaces.
*/
+ @GuardedBy("mUidRulesFirstLock")
private void enableFirewallChainUL(int chain, boolean enable) {
if (mFirewallChainStates.indexOfKey(chain) >= 0 &&
mFirewallChainStates.get(chain) == enable) {
@@ -4717,6 +4764,7 @@
mHandler.getLooper().getQueue().addIdleHandler(handler);
}
+ @GuardedBy("mUidRulesFirstLock")
@VisibleForTesting
public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
mRestrictBackgroundPowerState = result;
@@ -5048,6 +5096,7 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
private boolean isRestrictedByAdminUL(int uid) {
final Set<Integer> restrictedUids = mMeteredRestrictedUids.get(
UserHandle.getUserId(uid));
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index c0fbfbb..18f4bc7 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -150,6 +150,7 @@
try {
provider.onConnected();
} catch (RemoteException e) {
+ Slog.e(TAG, "can't connect to service " + info, e);
// we tried
}
if (mCallback != null) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index dfc61d9..432d17c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -582,16 +582,15 @@
updatedChannel.setLockscreenVisibility(
NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
}
- if (!fromUser) {
- updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
- }
if (fromUser) {
updatedChannel.lockFields(channel.getUserLockedFields());
lockFieldsForUpdate(channel, updatedChannel);
+ } else {
+ updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
}
r.channels.put(updatedChannel.getId(), updatedChannel);
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) {
+ if (onlyHasDefaultChannel(pkg, uid)) {
// copy settings to app level so they are inherited by new channels
// when the app migrates
r.importance = updatedChannel.getImportance();
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 6760875..b016faf 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -36,7 +36,8 @@
public class ZenLog {
private static final String TAG = "ZenLog";
- private static final boolean DEBUG = Build.IS_DEBUGGABLE;
+ // the ZenLog is *very* verbose, so be careful about setting this to true
+ private static final boolean DEBUG = false;
private static final int SIZE = Build.IS_DEBUGGABLE ? 100 : 20;
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 63c0baf..8013f7a 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -59,7 +59,8 @@
pw.print(prefix); pw.print("mSubscriptions="); pw.println(mSubscriptions);
}
- public void evaluateConfig(ZenModeConfig config, boolean processSubscriptions) {
+ public void evaluateConfig(ZenModeConfig config, ComponentName trigger,
+ boolean processSubscriptions) {
if (config == null) return;
if (config.manualRule != null && config.manualRule.condition != null
&& !config.manualRule.isTrueOrUnknown()) {
@@ -67,9 +68,9 @@
config.manualRule = null;
}
final ArraySet<Uri> current = new ArraySet<>();
- evaluateRule(config.manualRule, current, processSubscriptions);
+ evaluateRule(config.manualRule, current, null, processSubscriptions);
for (ZenRule automaticRule : config.automaticRules.values()) {
- evaluateRule(automaticRule, current, processSubscriptions);
+ evaluateRule(automaticRule, current, trigger, processSubscriptions);
updateSnoozing(automaticRule);
}
@@ -102,7 +103,7 @@
@Override
public void onServiceAdded(ComponentName component) {
if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
- mHelper.setConfig(mHelper.getConfig(), "zmc.onServiceAdded");
+ mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded");
}
@Override
@@ -110,17 +111,22 @@
if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition);
ZenModeConfig config = mHelper.getConfig();
if (config == null) return;
+ ComponentName trigger = null;
boolean updated = updateCondition(id, condition, config.manualRule);
for (ZenRule automaticRule : config.automaticRules.values()) {
updated |= updateCondition(id, condition, automaticRule);
updated |= updateSnoozing(automaticRule);
+ if (updated) {
+ trigger = automaticRule.component;
+ }
}
if (updated) {
- mHelper.setConfig(config, "conditionChanged");
+ mHelper.setConfig(config, trigger, "conditionChanged");
}
}
- private void evaluateRule(ZenRule rule, ArraySet<Uri> current, boolean processSubscriptions) {
+ private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger,
+ boolean processSubscriptions) {
if (rule == null || rule.conditionId == null) return;
final Uri id = rule.conditionId;
boolean isSystemCondition = false;
@@ -146,7 +152,8 @@
if (current != null) {
current.add(id);
}
- if (processSubscriptions) {
+ if (processSubscriptions && trigger != null && trigger.equals(rule.component)) {
+ if (DEBUG) Log.d(TAG, "Subscribing to " + rule.component);
if (mConditionProviders.subscribeIfNecessary(rule.component, rule.conditionId)) {
synchronized (mSubscriptions) {
mSubscriptions.put(rule.conditionId, rule.component);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 1954ed4..9c5e064 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -225,7 +225,7 @@
config.user = user;
}
synchronized (mConfig) {
- setConfigLocked(config, reason);
+ setConfigLocked(config, null, reason);
}
cleanUpZenRules();
}
@@ -312,7 +312,7 @@
ZenRule rule = new ZenRule();
populateZenRule(automaticZenRule, rule, true);
newConfig.automaticRules.put(rule.id, rule);
- if (setConfigLocked(newConfig, reason, true)) {
+ if (setConfigLocked(newConfig, reason, rule.component, true)) {
return rule.id;
} else {
throw new AndroidRuntimeException("Could not create rule");
@@ -342,7 +342,7 @@
}
populateZenRule(automaticZenRule, rule, false);
newConfig.automaticRules.put(ruleId, rule);
- return setConfigLocked(newConfig, reason, true);
+ return setConfigLocked(newConfig, reason, rule.component, true);
}
}
@@ -360,7 +360,7 @@
throw new SecurityException(
"Cannot delete rules not owned by your condition provider");
}
- return setConfigLocked(newConfig, reason, true);
+ return setConfigLocked(newConfig, reason, null, true);
}
}
@@ -376,7 +376,7 @@
newConfig.automaticRules.removeAt(i);
}
}
- return setConfigLocked(newConfig, reason, true);
+ return setConfigLocked(newConfig, reason, null, true);
}
}
@@ -537,7 +537,7 @@
newRule.enabler = caller;
newConfig.manualRule = newRule;
}
- setConfigLocked(newConfig, reason, setRingerMode);
+ setConfigLocked(newConfig, reason, null, setRingerMode);
}
}
@@ -644,7 +644,7 @@
}
if (DEBUG) Log.d(TAG, reason);
synchronized (mConfig) {
- setConfigLocked(config, reason);
+ setConfigLocked(config, null, reason);
}
}
}
@@ -673,7 +673,7 @@
synchronized (mConfig) {
final ZenModeConfig newConfig = mConfig.copy();
newConfig.applyNotificationPolicy(policy);
- setConfigLocked(newConfig, "setNotificationPolicy");
+ setConfigLocked(newConfig, null, "setNotificationPolicy");
}
}
@@ -697,7 +697,7 @@
}
}
}
- setConfigLocked(newConfig, "cleanUpZenRules");
+ setConfigLocked(newConfig, null, "cleanUpZenRules");
}
}
@@ -710,17 +710,19 @@
}
}
- public boolean setConfigLocked(ZenModeConfig config, String reason) {
- return setConfigLocked(config, reason, true /*setRingerMode*/);
+ public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
+ String reason) {
+ return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/);
}
- public void setConfig(ZenModeConfig config, String reason) {
+ public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason) {
synchronized (mConfig) {
- setConfigLocked(config, reason);
+ setConfigLocked(config, triggeringComponent, reason);
}
}
- private boolean setConfigLocked(ZenModeConfig config, String reason, boolean setRingerMode) {
+ private boolean setConfigLocked(ZenModeConfig config, String reason,
+ ComponentName triggeringComponent, boolean setRingerMode) {
final long identity = Binder.clearCallingIdentity();
try {
if (config == null || !config.isValid()) {
@@ -733,7 +735,8 @@
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
return true;
}
- mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config
+ // may modify config
+ mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
mConfigs.put(config.user, config);
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
ZenLog.traceConfig(reason, mConfig, config);
@@ -746,7 +749,7 @@
dispatchOnPolicyChanged();
}
mConfig = config;
- mHandler.postApplyConfig(config, reason, setRingerMode);
+ mHandler.postApplyConfig(config, reason, triggeringComponent, setRingerMode);
return true;
} catch (SecurityException e) {
Log.wtf(TAG, "Invalid rule in config", e);
@@ -756,13 +759,14 @@
}
}
- private void applyConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
+ private void applyConfig(ZenModeConfig config, String reason,
+ ComponentName triggeringComponent, boolean setRingerMode) {
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
if (!evaluateZenMode(reason, setRingerMode)) {
applyRestrictions(); // evaluateZenMode will also apply restrictions if changed
}
- mConditions.evaluateConfig(config, true /*processSubscriptions*/);
+ mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/);
}
private int getZenModeSetting() {
@@ -1268,13 +1272,16 @@
private final class ConfigMessageData {
public final ZenModeConfig config;
+ public ComponentName triggeringComponent;
public final String reason;
public final boolean setRingerMode;
- ConfigMessageData(ZenModeConfig config, String reason, boolean setRingerMode) {
+ ConfigMessageData(ZenModeConfig config, String reason,
+ ComponentName triggeringComponent, boolean setRingerMode) {
this.config = config;
this.reason = reason;
this.setRingerMode = setRingerMode;
+ this.triggeringComponent = triggeringComponent;
}
}
@@ -1294,9 +1301,10 @@
sendEmptyMessageDelayed(MSG_METRICS, METRICS_PERIOD_MS);
}
- private void postApplyConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
+ private void postApplyConfig(ZenModeConfig config, String reason,
+ ComponentName triggeringComponent, boolean setRingerMode) {
sendMessage(obtainMessage(MSG_APPLY_CONFIG,
- new ConfigMessageData(config, reason, setRingerMode)));
+ new ConfigMessageData(config, reason, triggeringComponent, setRingerMode)));
}
@Override
@@ -1311,7 +1319,7 @@
case MSG_APPLY_CONFIG:
ConfigMessageData applyConfigData = (ConfigMessageData) msg.obj;
applyConfig(applyConfigData.config, applyConfigData.reason,
- applyConfigData.setRingerMode);
+ applyConfigData.triggeringComponent, applyConfigData.setRingerMode);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b0be4a9..55b1940 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -34,6 +34,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.DexoptUtils;
@@ -289,7 +290,8 @@
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
- profileName, dexMetadataPath, getReasonName(compilationReason));
+ profileName, dexMetadataPath,
+ getAugmentedReasonName(compilationReason, dexMetadataPath != null));
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -302,6 +304,12 @@
}
}
+ private String getAugmentedReasonName(int compilationReason, boolean useDexMetadata) {
+ String annotation = useDexMetadata
+ ? ArtManagerService.DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION : "";
+ return getReasonName(compilationReason) + annotation;
+ }
+
/**
* Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}.
*
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3919f50..30224d2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -452,7 +452,6 @@
static final int SCAN_NEW_INSTALL = 1<<2;
static final int SCAN_UPDATE_TIME = 1<<3;
static final int SCAN_BOOTING = 1<<4;
- static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<6;
static final int SCAN_REQUIRE_KNOWN = 1<<7;
static final int SCAN_MOVE = 1<<8;
static final int SCAN_INITIAL = 1<<9;
@@ -475,7 +474,6 @@
SCAN_NEW_INSTALL,
SCAN_UPDATE_TIME,
SCAN_BOOTING,
- SCAN_DELETE_DATA_ON_FAILURES,
SCAN_REQUIRE_KNOWN,
SCAN_MOVE,
SCAN_INITIAL,
@@ -8951,15 +8949,20 @@
}
}
- final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
+ final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
+ if (scanResult.success) {
+ synchronized (mPackages) {
+ commitScanResultLocked(scanResult);
+ }
+ }
if (shouldHideSystemApp) {
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
}
- return scannedPkg;
+ return scanResult.pkgSetting.pkg;
}
private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) {
@@ -9985,7 +9988,7 @@
}
@GuardedBy({"mInstallLock", "mPackages"})
- private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg,
+ private List<ScanResult> scanPackageTracedLI(PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
@@ -10002,16 +10005,16 @@
scanFlags &= ~SCAN_CHECK_ONLY;
}
- final PackageParser.Package scannedPkg;
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ final List<ScanResult> scanResults = new ArrayList<>(1 + childCount);
try {
// Scan the parent
- scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user);
+ scanResults.add(scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user));
// Scan the children
- final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
- scanPackageNewLI(childPkg, parseFlags,
- scanFlags, currentTime, user);
+ scanResults.add(scanPackageNewLI(childPkg, parseFlags,
+ scanFlags, currentTime, user));
}
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -10021,11 +10024,13 @@
return scanPackageTracedLI(pkg, parseFlags, scanFlags, currentTime, user);
}
- return scannedPkg;
+ return scanResults;
}
/** The result of a package scan. */
private static class ScanResult {
+ /** The request that initiated the scan that produced this result. */
+ public final ScanRequest request;
/** Whether or not the package scan was successful */
public final boolean success;
/**
@@ -10036,9 +10041,10 @@
/** ABI code paths that have changed in the package scan */
@Nullable public final List<String> changedAbiCodePath;
public ScanResult(
- boolean success,
+ ScanRequest request, boolean success,
@Nullable PackageSetting pkgSetting,
@Nullable List<String> changedAbiCodePath) {
+ this.request = request;
this.success = success;
this.pkgSetting = pkgSetting;
this.changedAbiCodePath = changedAbiCodePath;
@@ -10181,7 +10187,7 @@
// method. Also, we need to solve the problem of potentially creating a new shared user
// setting. That can probably be done later and patch things up after the fact.
@GuardedBy({"mInstallLock", "mPackages"})
- private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg,
+ private ScanResult scanPackageNewLI(@NonNull PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
@@ -10217,28 +10223,30 @@
+ " packages=" + sharedUserSetting.packages);
}
}
+ final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
+ pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
+ originalPkgSetting, realPkgName, parseFlags, scanFlags,
+ (pkg == mPlatformPackage), user);
+ return scanPackageOnlyLI(request, mFactoryTest, currentTime);
+ }
+ }
- boolean scanSucceeded = false;
- try {
- final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
- pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
- originalPkgSetting, realPkgName, parseFlags, scanFlags,
- (pkg == mPlatformPackage), user);
- final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime);
- if (result.success) {
- commitScanResultsLocked(request, result);
+
+ private void commitSuccessfulScanResults(@NonNull List<ScanResult> results)
+ throws PackageManagerException {
+ synchronized(mPackages) {
+ for (ScanResult result : results) {
+ // failures should have been caught earlier, but in case it wasn't,
+ // let's double check
+ if (!result.success) {
+ throw new PackageManagerException(
+ "Scan failed for " + result.request.pkg.packageName);
}
- scanSucceeded = true;
- } finally {
- if (!scanSucceeded && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
- // DELETE_DATA_ON_FAILURES is only used by frozen paths
- destroyAppDataLIF(pkg, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
- destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
- }
+ }
+ for (ScanResult result : results) {
+ commitScanResultLocked(result);
}
}
- return pkg;
}
/**
@@ -10249,8 +10257,8 @@
* possible and the system is not left in an inconsistent state.
*/
@GuardedBy({"mPackages", "mInstallLock"})
- private void commitScanResultsLocked(@NonNull ScanRequest request, @NonNull ScanResult result)
- throws PackageManagerException {
+ private void commitScanResultLocked(@NonNull ScanResult result) throws PackageManagerException {
+ final ScanRequest request = result.request;
final PackageParser.Package pkg = request.pkg;
final PackageParser.Package oldPkg = request.oldPkg;
final @ParseFlags int parseFlags = request.parseFlags;
@@ -10831,7 +10839,7 @@
pkgSetting.volumeUuid = volumeUuid;
}
- return new ScanResult(true, pkgSetting, changedAbiCodePath);
+ return new ScanResult(request, true, pkgSetting, changedAbiCodePath);
}
/**
@@ -16384,14 +16392,18 @@
}
try {
- PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
+ final PackageParser.Package newPackage;
+ List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
-
+ commitSuccessfulScanResults(scanResults);
+ // TODO(b/109941548): Child packages may return >1 result with the first being the base;
+ // we need to treat child packages as an atomic install and remove this hack
+ final ScanResult basePackageScanResult = scanResults.get(0);
+ newPackage = basePackageScanResult.pkgSetting.pkg;
updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
prepareAppDataAfterInstallLIF(newPackage);
-
} else {
// Remove package from internal structures, but keep around any
// data that might have already existed
@@ -16399,6 +16411,9 @@
PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
}
} catch (PackageManagerException e) {
+ destroyAppDataLIF(pkg, UserHandle.USER_ALL,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
@@ -16655,8 +16670,10 @@
| StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
try {
- final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
+ final List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags,
scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
+ commitSuccessfulScanResults(scanResults);
+ final PackageParser.Package newPackage = scanResults.get(0).pkgSetting.pkg;
updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
installReason);
@@ -16794,8 +16811,11 @@
PackageParser.Package newPackage = null;
try {
+ final List<ScanResult> scanResults =
+ scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
// Add the package to the internal data structures
- newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
+ commitSuccessfulScanResults(scanResults);
+ newPackage = scanResults.get(0).pkgSetting.pkg;
// Set the update and install times
PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;
@@ -16852,7 +16872,9 @@
}
// Add back the old system package
try {
- scanPackageTracedLI(deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
+ final List<ScanResult> scanResults = scanPackageTracedLI(
+ deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
+ commitSuccessfulScanResults(scanResults);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
}
@@ -17663,7 +17685,7 @@
replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
installerPackageName, res, args.installReason);
} else {
- installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
+ installNewPackageLIF(pkg, parseFlags, scanFlags,
args.user, installerPackageName, volumeUuid, res, args.installReason);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d06437e..5878762 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2419,30 +2419,30 @@
private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
boolean logSuccess) throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- final ParcelFileDescriptor fd;
- if (STDIN_PATH.equals(inPath)) {
- fd = new ParcelFileDescriptor(getInFileDescriptor());
- } else if (inPath != null) {
- fd = openFileForSystem(inPath, "r");
- if (fd == null) {
- return -1;
- }
- sizeBytes = fd.getStatSize();
- if (sizeBytes < 0) {
- getErrPrintWriter().println("Unable to get size of: " + inPath);
- return -1;
- }
- } else {
- fd = new ParcelFileDescriptor(getInFileDescriptor());
- }
- if (sizeBytes <= 0) {
- getErrPrintWriter().println("Error: must specify a APK size");
- return 1;
- }
-
PackageInstaller.Session session = null;
try {
+ final PrintWriter pw = getOutPrintWriter();
+ final ParcelFileDescriptor fd;
+ if (STDIN_PATH.equals(inPath)) {
+ fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+ } else if (inPath != null) {
+ fd = openFileForSystem(inPath, "r");
+ if (fd == null) {
+ return -1;
+ }
+ sizeBytes = fd.getStatSize();
+ if (sizeBytes < 0) {
+ getErrPrintWriter().println("Unable to get size of: " + inPath);
+ return -1;
+ }
+ } else {
+ fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+ }
+ if (sizeBytes <= 0) {
+ getErrPrintWriter().println("Error: must specify a APK size");
+ return 1;
+ }
+
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));
session.write(splitName, 0, sizeBytes, fd);
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 0ba7822..21daa39 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -519,6 +519,11 @@
private static final int TRON_COMPILATION_REASON_AB_OTA = 6;
private static final int TRON_COMPILATION_REASON_INACTIVE = 7;
private static final int TRON_COMPILATION_REASON_SHARED = 8;
+ private static final int TRON_COMPILATION_REASON_INSTALL_WITH_DEX_METADATA = 9;
+
+ // The annotation to add as a suffix to the compilation reason when dexopt was
+ // performed with dex metadata.
+ public static final String DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION = "-dm";
/**
* Convert the compilation reason to an int suitable to be logged to TRON.
@@ -534,6 +539,10 @@
case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
case "shared" : return TRON_COMPILATION_REASON_SHARED;
+ // This is a special marker for dex metadata installation that does not
+ // have an equivalent as a system property.
+ case "install" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
+ return TRON_COMPILATION_REASON_INSTALL_WITH_DEX_METADATA;
default: return TRON_COMPILATION_REASON_UNKNOWN;
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 8aed957..8a6fbaa 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2059,6 +2059,7 @@
}
}
+ @GuardedBy({"mSettings.mLock", "mLock"})
private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
int size = 0;
for (BasePermission perm : mSettings.mPermissions.values()) {
@@ -2067,6 +2068,7 @@
return size;
}
+ @GuardedBy({"mSettings.mLock", "mLock"})
private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
// We calculate the max size of permissions defined by this uid and throw
// if that plus the size of 'info' would exceed our stated maximum.
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index ded2c15..73775b4 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -219,12 +219,12 @@
}
@Override
- public int checkSlicePermission(Uri uri, String pkg, int pid, int uid,
+ public int checkSlicePermission(Uri uri, String callingPkg, String pkg, int pid, int uid,
String[] autoGrantPermissions) {
int userId = UserHandle.getUserId(uid);
if (pkg == null) {
for (String p : mContext.getPackageManager().getPackagesForUid(uid)) {
- if (checkSlicePermission(uri, p, pid, uid, autoGrantPermissions)
+ if (checkSlicePermission(uri, callingPkg, p, pid, uid, autoGrantPermissions)
== PERMISSION_GRANTED) {
return PERMISSION_GRANTED;
}
@@ -237,9 +237,9 @@
if (mPermissions.hasPermission(pkg, userId, uri)) {
return PackageManager.PERMISSION_GRANTED;
}
- if (autoGrantPermissions != null) {
+ if (autoGrantPermissions != null && callingPkg != null) {
// Need to own the Uri to call in with permissions to grant.
- enforceOwner(pkg, uri, userId);
+ enforceOwner(callingPkg, uri, userId);
for (String perm : autoGrantPermissions) {
if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
int providerUser = ContentProvider.getUserIdFromUri(uri, userId);
@@ -391,7 +391,7 @@
}
protected int checkAccess(String pkg, Uri uri, int uid, int pid) {
- return checkSlicePermission(uri, pkg, uid, pid, null);
+ return checkSlicePermission(uri, null, pkg, uid, pid, null);
}
private String getProviderPkg(Uri uri, int user) {
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index c7c24a5..2175772 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1179,6 +1179,7 @@
}
}
+ @GuardedBy("StatsCompanionService.sStatsdLock")
private void forgetEverythingLocked() {
sStatsd = null;
mContext.unregisterReceiver(mAppUpdateReceiver);
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index c1c32c2..774a3bc 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -708,8 +708,10 @@
@Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mLock) {
- clearClientLocked();
- mRemoteInstance = null;
+ if (mRemoteInstance != null) {
+ clearClientLocked();
+ mRemoteInstance = null;
+ }
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index b392935..cde8e63 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -21,15 +21,13 @@
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
import static com.google.common.truth.Truth.assertThat;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertEquals;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
+import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import java.util.ArrayList;
import org.junit.Before;
import org.junit.Test;
@@ -139,52 +137,56 @@
mMusicMute = true;
mMusicMaxVolume = 20;
int scaledVolume = VolumeControlAction.scaleToCecVolume(10, mMusicMaxVolume);
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder.buildReportAudioStatus(
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildReportAudioStatus(
ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
HdmiCecMessage messageGive = HdmiCecMessageBuilder.buildGiveAudioStatus(
ADDR_TV, ADDR_AUDIO_SYSTEM);
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive));
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
}
@Test
public void handleGiveSystemAudioModeStatus_originalOff() {
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
HdmiCecMessage messageGive = HdmiCecMessageBuilder
.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
}
@Test
public void handleRequestArcInitiate() {
// TODO(b/80296911): Add tests when finishing handler impl.
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
.buildInitiateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
HdmiCecMessage message = HdmiCecMessageBuilder
.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message));
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
}
@Test
public void handleRequestArcTermination() {
// TODO(b/80297105): Add tests when finishing handler impl.
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
.buildTerminateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
.buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(messageRequestOff));
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(messageRequestOff))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
}
@Test
@@ -196,28 +198,31 @@
.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
// Check if originally off
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
// Check if correctly turned on
- expectMessage = HdmiCecMessageBuilder
+ expectedMessage = HdmiCecMessageBuilder
.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet));
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
- assertFalse(mMusicMute);
+ assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+ assertThat(mMusicMute).isEqualTo(false);
}
@Test
public void handleSystemAudioModeRequest_turnOffByTv() {
- assertFalse(mMusicMute);
+ assertThat(mMusicMute).isEqualTo(false);
// Check if feature correctly turned off
HdmiCecMessage messageGive = HdmiCecMessageBuilder
@@ -225,18 +230,20 @@
HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
.buildSystemAudioModeRequest(ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
.buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff));
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
- expectMessage = HdmiCecMessageBuilder
+ expectedMessage = HdmiCecMessageBuilder
.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
- assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
- assertTrue(mMusicMute);
+ assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+ assertThat(mMusicMute).isEqualTo(true);
}
@Test
@@ -247,10 +254,10 @@
// Check if standby correctly turns off the feature
mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
.buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
- assertEquals(expectMessage, mNativeWrapper.getResultMessage());
- assertTrue(mMusicMute);
+ assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+ assertThat(mMusicMute).isEqualTo(true);
}
@Test
@@ -296,4 +303,50 @@
assertThat(mHdmiCecLocalDeviceAudioSystem
.getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
}
+
+ @Test
+ public void handleActiveSource_updateActiveSource() {
+ HdmiCecMessage message = HdmiCecMessageBuilder
+ .buildActiveSource(ADDR_TV, 0x0000);
+ ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message))
+ .isEqualTo(true);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().equals(expectedActiveSource))
+ .isEqualTo(true);
+ }
+
+ @Test
+ public void terminateSystemAudioMode_systemAudioModeOff() {
+ mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
+
+ mMusicMute = false;
+ HdmiCecMessage message = HdmiCecMessageBuilder
+ .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+
+ mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
+ assertThat(mMusicMute).isEqualTo(false);
+ assertThat(mNativeWrapper.getResultMessage()).isNotEqualTo(message);
+ }
+
+ @Test
+ public void terminateSystemAudioMode_systemAudioModeOn() {
+ mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(true);
+
+ mMusicMute = false;
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
+ .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+
+ mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
+ assertThat(mMusicMute).isEqualTo(true);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 02d5869..73adf25 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -93,11 +94,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PreferencesHelperTest extends UiServiceTestCase {
- private static final String PKG = "com.android.server.notification";
- private static final int UID = 0;
+ private static final int UID_N_MR1 = 0;
private static final UserHandle USER = UserHandle.of(0);
- private static final String UPDATED_PKG = "updatedPkg";
- private static final int UID2 = 1111;
+ private static final int UID_O = 1111;
private static final String SYSTEM_PKG = "android";
private static final int SYSTEM_UID= 1000;
private static final UserHandle USER2 = UserHandle.of(10);
@@ -130,16 +129,16 @@
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
final ApplicationInfo upgrade = new ApplicationInfo();
upgrade.targetSdkVersion = Build.VERSION_CODES.O;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
- when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(upgrade);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(upgrade);
when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
- when(mPm.getPackageUidAsUser(eq(PKG), anyInt())).thenReturn(UID);
- when(mPm.getPackageUidAsUser(eq(UPDATED_PKG), anyInt())).thenReturn(UID2);
+ when(mPm.getPackageUidAsUser(eq(PKG_N_MR1), anyInt())).thenReturn(UID_N_MR1);
+ when(mPm.getPackageUidAsUser(eq(PKG_O), anyInt())).thenReturn(UID_O);
when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
PackageInfo info = mock(PackageInfo.class);
info.signatures = new Signature[] {mock(Signature.class)};
when(mPm.getPackageInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(info);
- when(mPm.getPackageInfoAsUser(eq(PKG), anyInt(), anyInt()))
+ when(mPm.getPackageInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt()))
.thenReturn(mock(PackageInfo.class));
when(mContext.getResources()).thenReturn(
InstrumentationRegistry.getContext().getResources());
@@ -151,7 +150,7 @@
TestableContentResolver contentResolver = getContext().getContentResolver();
contentResolver.setFallbackToExisting(false);
Secure.putIntForUser(contentResolver,
- Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID));
+ Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID_N_MR1));
ContentProvider testContentProvider = mock(ContentProvider.class);
when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
@@ -270,28 +269,29 @@
channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
channel2.setLightColor(Color.BLUE);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
- mHelper.setShowBadge(PKG, UID, true);
- mHelper.setAppImportanceLocked(PKG, UID);
+ mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
+ mHelper.setAppImportanceLocked(PKG_N_MR1, UID_N_MR1);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false, channel1.getId(),
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, channel1.getId(),
channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
- mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
+ mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
loadStreamXml(baos, false);
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertTrue(mHelper.getIsAppImportanceLocked(PKG, UID));
- assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
+ assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
compareChannels(channel2,
- mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
List<NotificationChannelGroup> actualGroups =
- mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, false, true).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -332,36 +332,36 @@
NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
channel3.enableVibration(true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, false, false);
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
+ mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
- mHelper.setShowBadge(PKG, UID, true);
+ mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
- mHelper.setImportance(UPDATED_PKG, UID2, IMPORTANCE_NONE);
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel1.getId(),
channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
- mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG, UPDATED_PKG},
- new int[]{UID, UID2});
+ mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1, PKG_O},
+ new int[]{UID_N_MR1, UID_O});
- mHelper.setShowBadge(UPDATED_PKG, UID2, true);
+ mHelper.setShowBadge(PKG_O, UID_O, true);
loadStreamXml(baos, true);
- assertEquals(IMPORTANCE_NONE, mHelper.getImportance(UPDATED_PKG, UID2));
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
+ assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_O, UID_O));
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
compareChannels(channel2,
- mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
compareChannels(channel3,
- mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
List<NotificationChannelGroup> actualGroups =
- mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, false, true).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -388,9 +388,9 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
// Testing that in restore we are given the canonical version
loadStreamXml(baos, true);
@@ -414,13 +414,13 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
loadStreamXml(baos, true);
NotificationChannel actualChannel = mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false);
+ PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertEquals(localUri, actualChannel.getSound());
}
@@ -435,13 +435,13 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
loadStreamXml(baos, true);
NotificationChannel actualChannel = mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false);
+ PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
}
@@ -456,7 +456,7 @@
when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
String id = "id";
String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
- + "<package name=\"com.android.server.notification\" show_badge=\"true\">\n"
+ + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
+ "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" "
+ "sound=\"" + SOUND_URI + "\" "
+ "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n"
@@ -467,7 +467,7 @@
loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true);
- NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG, UID, id, false);
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, id, false);
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
}
@@ -476,13 +476,13 @@
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
channel.setSound(null, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel.getId());
loadStreamXml(baos, true);
NotificationChannel actualChannel = mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false);
+ PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertEquals(null, actualChannel.getSound());
}
@@ -498,19 +498,20 @@
new NotificationChannel("id3", "name3", IMPORTANCE_LOW);
channel3.setGroup(ncg.getId());
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel1.getId());
- mHelper.deleteNotificationChannelGroup(PKG, UID, ncg.getId());
- assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId());
+ mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg.getId());
+ assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel1.getId(),
channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
- mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
+ mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
@@ -518,21 +519,21 @@
parser.nextTag();
mHelper.readXml(parser, true);
- assertNull(mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
- assertNull(mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
- assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG, UID));
- //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID));
- assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+ assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
+ assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
+ assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG_N_MR1, UID_N_MR1));
+ //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG_N_MR1, UID_N_MR1));
+ assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
}
@Test
public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception {
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
NotificationChannel.DEFAULT_CHANNEL_ID);
loadStreamXml(baos, false);
- final NotificationChannel updated = mHelper.getNotificationChannel(PKG, UID,
+ final NotificationChannel updated = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false);
assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance());
assertFalse(updated.canBypassDnd());
@@ -542,28 +543,29 @@
@Test
public void testChannelXml_defaultChannelUpdatedApp_userSettings() throws Exception {
- final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
+ final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
+ UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false);
defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
NotificationChannel.DEFAULT_CHANNEL_ID);
loadStreamXml(baos, false);
assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel(
- PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
+ PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
}
@Test
public void testChannelXml_upgradeCreateDefaultChannel() throws Exception {
final String preupgradeXml = "<ranking version=\"1\">\n"
- + "<package name=\"" + PKG
+ + "<package name=\"" + PKG_N_MR1
+ "\" importance=\"" + NotificationManager.IMPORTANCE_HIGH
+ "\" priority=\"" + Notification.PRIORITY_MAX + "\" visibility=\""
- + Notification.VISIBILITY_SECRET + "\"" +" uid=\"" + UID + "\" />\n"
- + "<package name=\"" + UPDATED_PKG + "\" uid=\"" + UID2 + "\" visibility=\""
+ + Notification.VISIBILITY_SECRET + "\"" +" uid=\"" + UID_N_MR1 + "\" />\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" visibility=\""
+ Notification.VISIBILITY_PRIVATE + "\" />\n"
+ "</ranking>";
XmlPullParser parser = Xml.newPullParser();
@@ -573,7 +575,7 @@
mHelper.readXml(parser, false);
final NotificationChannel updated1 =
- mHelper.getNotificationChannel(PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance());
assertTrue(updated1.canBypassDnd());
assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility());
@@ -583,71 +585,71 @@
updated1.getUserLockedFields());
// No Default Channel created for updated packages
- assertEquals(null, mHelper.getNotificationChannel(UPDATED_PKG, UID2,
+ assertEquals(null, mHelper.getNotificationChannel(PKG_O, UID_O,
NotificationChannel.DEFAULT_CHANNEL_ID, false));
}
@Test
public void testChannelXml_upgradeDeletesDefaultChannel() throws Exception {
final NotificationChannel defaultChannel = mHelper.getNotificationChannel(
- PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
assertTrue(defaultChannel != null);
ByteArrayOutputStream baos =
- writeXmlAndPurge(PKG, UID, false, NotificationChannel.DEFAULT_CHANNEL_ID);
+ writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, NotificationChannel.DEFAULT_CHANNEL_ID);
// Load package at higher sdk.
final ApplicationInfo upgraded = new ApplicationInfo();
upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(upgraded);
loadStreamXml(baos, false);
// Default Channel should be gone.
- assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
+ assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false));
}
@Test
public void testDeletesDefaultChannelAfterChannelIsCreated() throws Exception {
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
// Load package at higher sdk.
final ApplicationInfo upgraded = new ApplicationInfo();
upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(upgraded);
loadStreamXml(baos, false);
// Default Channel should be gone.
- assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
+ assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false));
}
@Test
public void testLoadingOldChannelsDoesNotDeleteNewlyCreatedChannels() throws Exception {
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
loadStreamXml(baos, false);
// Should still have the newly created channel that wasn't in the xml.
- assertTrue(mHelper.getNotificationChannel(PKG, UID, "bananas", false) != null);
+ assertTrue(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "bananas", false) != null);
}
@Test
public void testCreateChannel_blocked() throws Exception {
- mHelper.setImportance(PKG, UID, IMPORTANCE_NONE);
+ mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_NONE);
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
}
@Test
public void testCreateChannel_badImportance() throws Exception {
try {
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1),
true, false);
fail("Was allowed to create a channel with invalid importance");
@@ -655,7 +657,7 @@
// yay
}
try {
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED),
true, false);
fail("Was allowed to create a channel with invalid importance");
@@ -663,16 +665,16 @@
// yay
}
try {
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1),
true, false);
fail("Was allowed to create a channel with invalid importance");
} catch (IllegalArgumentException e) {
// yay
}
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true, false);
- mHelper.createNotificationChannel(PKG, UID,
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false);
}
@@ -687,7 +689,7 @@
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.createNotificationChannel(PKG, UID, channel, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, false, false);
// same id, try to update all fields
final NotificationChannel channel2 =
@@ -697,70 +699,109 @@
channel2.setBypassDnd(false);
channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
- mHelper.updateNotificationChannel(PKG, UID, channel2, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true);
// all fields should be changed
- assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
+ assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false));
verify(mHandler, times(1)).requestSort();
}
@Test
public void testUpdate_preUpgrade_updatesAppFields() throws Exception {
- mHelper.setImportance(PKG, UID, IMPORTANCE_UNSPECIFIED);
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
+ mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_UNSPECIFIED);
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
- mHelper.getPackageVisibility(PKG, UID));
- assertFalse(mHelper.getIsAppImportanceLocked(PKG, UID));
+ mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
+ assertFalse(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
NotificationChannel defaultChannel = mHelper.getNotificationChannel(
- PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
defaultChannel.setShowBadge(false);
defaultChannel.setImportance(IMPORTANCE_NONE);
defaultChannel.setBypassDnd(true);
defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.setAppImportanceLocked(PKG, UID);
- mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
+ mHelper.setAppImportanceLocked(PKG_N_MR1, UID_N_MR1);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
// ensure app level fields are changed
- assertFalse(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_MAX, mHelper.getPackagePriority(PKG, UID));
- assertEquals(Notification.VISIBILITY_SECRET, mHelper.getPackageVisibility(PKG, UID));
- assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG, UID));
- assertTrue(mHelper.getIsAppImportanceLocked(PKG, UID));
+ assertFalse(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.PRIORITY_MAX, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.VISIBILITY_SECRET, mHelper.getPackageVisibility(PKG_N_MR1,
+ UID_N_MR1));
+ assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_N_MR1, UID_N_MR1));
+ assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
}
@Test
public void testUpdate_postUpgrade_noUpdateAppFields() throws Exception {
final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, false, false);
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, false, false);
+ assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_O, UID_O));
assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
- mHelper.getPackageVisibility(PKG, UID));
+ mHelper.getPackageVisibility(PKG_O, UID_O));
channel.setShowBadge(false);
channel.setImportance(IMPORTANCE_NONE);
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.updateNotificationChannel(PKG, UID, channel, true);
+ mHelper.updateNotificationChannel(PKG_O, UID_O, channel, true);
// ensure app level fields are not changed
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
+ assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_O, UID_O));
assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
- mHelper.getPackageVisibility(PKG, UID));
- assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
+ mHelper.getPackageVisibility(PKG_O, UID_O));
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_O,
+ UID_O));
+ assertFalse(mHelper.getIsAppImportanceLocked(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testUpdate_preUpgrade_noUpdateAppFieldsWithMultipleChannels() throws Exception {
+ final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, false, false);
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+ assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+ mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
+
+ channel.setShowBadge(false);
+ channel.setImportance(IMPORTANCE_NONE);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
+
+ NotificationChannel defaultChannel = mHelper.getNotificationChannel(
+ PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+
+ defaultChannel.setShowBadge(false);
+ defaultChannel.setImportance(IMPORTANCE_NONE);
+ defaultChannel.setBypassDnd(true);
+ defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
+
+ // ensure app level fields are not changed
+ assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+ assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+ mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
+ UID_N_MR1));
}
@Test
public void testGetNotificationChannel_ReturnsNullForUnknownChannel() throws Exception {
- assertEquals(null, mHelper.getNotificationChannel(PKG, UID, "garbage", false));
+ assertEquals(null, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "garbage", false));
}
@Test
@@ -778,10 +819,10 @@
}
channel.lockFields(lockMask);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
NotificationChannel savedChannel =
- mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertEquals(channel.getName(), savedChannel.getName());
assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
@@ -807,10 +848,10 @@
}
channel.lockFields(lockMask);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
NotificationChannel savedChannel =
- mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertEquals(channel.getName(), savedChannel.getName());
assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
@@ -833,103 +874,103 @@
@Test
public void testLockFields_soundAndVibration() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setSound(new Uri.Builder().scheme("test").build(),
new AudioAttributes.Builder().build());
update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_SOUND,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
.getUserLockedFields());
NotificationChannel update2 = getChannel();
update2.enableVibration(true);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_SOUND
| NotificationChannel.USER_LOCKED_VIBRATION,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
.getUserLockedFields());
}
@Test
public void testLockFields_vibrationAndLights() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setVibrationPattern(new long[]{7945, 46 ,246});
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_VIBRATION,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
.getUserLockedFields());
final NotificationChannel update2 = getChannel();
update2.enableLights(true);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_VIBRATION
| NotificationChannel.USER_LOCKED_LIGHTS,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
.getUserLockedFields());
}
@Test
public void testLockFields_lightsAndImportance() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
update1.setLightColor(Color.GREEN);
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_LIGHTS,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
.getUserLockedFields());
final NotificationChannel update2 = getChannel();
update2.setImportance(IMPORTANCE_DEFAULT);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_LIGHTS
| NotificationChannel.USER_LOCKED_IMPORTANCE,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
.getUserLockedFields());
}
@Test
public void testLockFields_visibilityAndDndAndBadge() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
assertEquals(0,
- mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false)
.getUserLockedFields());
final NotificationChannel update1 = getChannel();
update1.setBypassDnd(true);
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update1, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update1.getId(), false)
.getUserLockedFields());
final NotificationChannel update2 = getChannel();
update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update2, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_VISIBILITY,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update2.getId(), false)
.getUserLockedFields());
final NotificationChannel update3 = getChannel();
update3.setShowBadge(false);
- mHelper.updateNotificationChannel(PKG, UID, update3, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update3, true);
assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_VISIBILITY
| NotificationChannel.USER_LOCKED_SHOW_BADGE,
- mHelper.getNotificationChannel(PKG, UID, update3.getId(), false)
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update3.getId(), false)
.getUserLockedFields());
}
@Test
public void testDeleteNonExistentChannel() throws Exception {
- mHelper.deleteNotificationChannelGroup(PKG, UID, "does not exist");
+ mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, "does not exist");
}
@Test
@@ -942,16 +983,16 @@
channel.enableVibration(true);
channel.setVibrationPattern(new long[]{100, 67, 145, 156});
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
// Does not return deleted channel
NotificationChannel response =
- mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), false);
assertNull(response);
// Returns deleted channel
- response = mHelper.getNotificationChannel(PKG, UID, channel.getId(), true);
+ response = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId(), true);
compareChannels(channel, response);
assertTrue(response.isDeleted());
}
@@ -971,14 +1012,14 @@
NotificationChannel channel2 =
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
channelMap.put(channel2.getId(), channel2);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
// Returns only non-deleted channels
List<NotificationChannel> channels =
- mHelper.getNotificationChannels(PKG, UID, false).getList();
+ mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList();
assertEquals(2, channels.size()); // Default channel + non-deleted channel
for (NotificationChannel nc : channels) {
if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
@@ -987,7 +1028,7 @@
}
// Returns deleted channels too
- channels = mHelper.getNotificationChannels(PKG, UID, true).getList();
+ channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList();
assertEquals(3, channels.size()); // Includes default channel
for (NotificationChannel nc : channels) {
if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
@@ -1004,15 +1045,15 @@
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
- mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId());
- assertEquals(2, mHelper.getDeletedChannelCount(PKG, UID));
- assertEquals(0, mHelper.getDeletedChannelCount("pkg2", UID2));
+ assertEquals(2, mHelper.getDeletedChannelCount(PKG_N_MR1, UID_N_MR1));
+ assertEquals(0, mHelper.getDeletedChannelCount("pkg2", UID_O));
}
@Test
@@ -1023,14 +1064,14 @@
new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_NONE);
NotificationChannel channel3 =
new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_NONE);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId());
- assertEquals(1, mHelper.getBlockedChannelCount(PKG, UID));
- assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID2));
+ assertEquals(1, mHelper.getBlockedChannelCount(PKG_N_MR1, UID_N_MR1));
+ assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID_O));
}
@Test
@@ -1039,7 +1080,7 @@
// expected result: areChannelsBypassingDnd = false
// setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1048,18 +1089,18 @@
// expected result: areChannelsBypassingDnd = true
NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel2.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, true);
assertTrue(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
// delete channels
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
- mHelper.deleteNotificationChannel(PKG, UID, channel2.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId());
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1071,7 +1112,7 @@
// expected result: areChannelsBypassingDnd = false
// setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1079,7 +1120,7 @@
// update channel so it CAN bypass dnd:
// expected result: areChannelsBypassingDnd = true
channel.setBypassDnd(true);
- mHelper.updateNotificationChannel(PKG, UID, channel, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
assertTrue(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1087,7 +1128,7 @@
// update channel so it can't bypass dnd:
// expected result: areChannelsBypassingDnd = false
channel.setBypassDnd(false);
- mHelper.updateNotificationChannel(PKG, UID, channel, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1124,33 +1165,33 @@
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel.setVibrationPattern(vibration);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
newChannel.setVibrationPattern(new long[]{100});
- mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel, true, false);
// No long deleted, using old settings
compareChannels(channel,
- mHelper.getNotificationChannel(PKG, UID, newChannel.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel.getId(), false));
}
@Test
public void testOnlyHasDefaultChannel() throws Exception {
- assertTrue(mHelper.onlyHasDefaultChannel(PKG, UID));
- assertFalse(mHelper.onlyHasDefaultChannel(UPDATED_PKG, UID2));
+ assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
- assertFalse(mHelper.onlyHasDefaultChannel(PKG, UID));
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
}
@Test
public void testCreateChannel_defaultChannelId() throws Exception {
try {
- mHelper.createNotificationChannel(PKG, UID, new NotificationChannel(
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true, false);
fail("Allowed to create default channel");
} catch (IllegalArgumentException e) {
@@ -1165,17 +1206,17 @@
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel.setVibrationPattern(vibration);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
newChannel.setVibrationPattern(new long[]{100});
- mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel, true, false);
// Old settings not overridden
compareChannels(channel,
- mHelper.getNotificationChannel(PKG, UID, newChannel.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel.getId(), false));
}
@Test
@@ -1184,9 +1225,9 @@
final NotificationChannel channel = new NotificationChannel("id2", "name2",
NotificationManager.IMPORTANCE_DEFAULT);
channel.setSound(sound, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
assertEquals(sound, mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false).getSound());
+ PKG_N_MR1, UID_N_MR1, channel.getId(), false).getSound());
}
@Test
@@ -1196,13 +1237,13 @@
NotificationChannel channel2 =
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false);
- mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
+ mHelper.permanentlyDeleteNotificationChannels(PKG_N_MR1, UID_N_MR1);
// Only default channel remains
- assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
+ assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size());
}
@Test
@@ -1218,28 +1259,28 @@
new NotificationChannel("deleted", "belongs to deleted", IMPORTANCE_DEFAULT);
groupedAndDeleted.setGroup("totally");
- mHelper.createNotificationChannelGroup(PKG, UID, notDeleted, true);
- mHelper.createNotificationChannelGroup(PKG, UID, deleted, true);
- mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true, false);
- mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true, false);
- mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true, false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, deleted, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedButNotDeleted, true, false);
- mHelper.deleteNotificationChannelGroup(PKG, UID, deleted.getId());
+ mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, deleted.getId());
- assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), PKG, UID));
- assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG, UID));
+ assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), PKG_N_MR1, UID_N_MR1));
+ assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1));
- assertNull(mHelper.getNotificationChannel(PKG, UID, groupedAndDeleted.getId(), false));
+ assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), false));
compareChannels(groupedAndDeleted,
- mHelper.getNotificationChannel(PKG, UID, groupedAndDeleted.getId(), true));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), true));
compareChannels(groupedButNotDeleted,
- mHelper.getNotificationChannel(PKG, UID, groupedButNotDeleted.getId(), false));
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedButNotDeleted.getId(), false));
compareChannels(nonGroupedNonDeletedChannel, mHelper.getNotificationChannel(
- PKG, UID, nonGroupedNonDeletedChannel.getId(), false));
+ PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel.getId(), false));
// notDeleted
- assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID).size());
+ assertEquals(1, mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1).size());
verify(mHandler, never()).requestSort();
}
@@ -1253,11 +1294,11 @@
final ApplicationInfo legacy = new ApplicationInfo();
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
// create records with the default channel for all user 0 and user 1 uids
- mHelper.getImportance(PKG, user0Uids[i]);
- mHelper.getImportance(PKG, user1Uids[i]);
+ mHelper.getImportance(PKG_N_MR1, user0Uids[i]);
+ mHelper.getImportance(PKG_N_MR1, user1Uids[i]);
}
mHelper.onUserRemoved(1);
@@ -1265,12 +1306,12 @@
// user 0 records remain
for (int i = 0; i < user0Uids.length; i++) {
assertEquals(1,
- mHelper.getNotificationChannels(PKG, user0Uids[i], false).getList().size());
+ mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false).getList().size());
}
// user 1 records are gone
for (int i = 0; i < user1Uids.length; i++) {
assertEquals(0,
- mHelper.getNotificationChannels(PKG, user1Uids[i], false).getList().size());
+ mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false).getList().size());
}
}
@@ -1279,71 +1320,77 @@
// Deleted
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
+ mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
- assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
+ assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size());
// Not deleted
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
- mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
- assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
+ mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
+ assertEquals(2, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
}
@Test
public void testOnPackageChanged_packageRemoval_importance() throws Exception {
- mHelper.setImportance(PKG, UID, NotificationManager.IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_N_MR1, UID_N_MR1, NotificationManager.IMPORTANCE_HIGH);
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
+ mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
- assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
+ UID_N_MR1));
}
@Test
public void testOnPackageChanged_packageRemoval_groups() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
+ mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
+ UID_N_MR1});
assertEquals(0,
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList().size());
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList().size());
}
@Test
public void testOnPackageChange_downgradeTargetSdk() throws Exception {
// create channel as api 26
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
+ mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
// install new app version targeting 25
final ApplicationInfo legacy = new ApplicationInfo();
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
- when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(legacy);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(legacy);
mHelper.onPackagesChanged(
- false, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
+ false, UserHandle.USER_SYSTEM, new String[]{PKG_O}, new int[]{UID_O});
// make sure the default channel was readded
- //assertEquals(2, mHelper.getNotificationChannels(UPDATED_PKG, UID2, false).getList().size());
+ //assertEquals(2, mHelper.getNotificationChannels(PKG_O, UID_O, false).getList().size());
assertNotNull(mHelper.getNotificationChannel(
- UPDATED_PKG, UID2, NotificationChannel.DEFAULT_CHANNEL_ID, false));
+ PKG_O, UID_O, NotificationChannel.DEFAULT_CHANNEL_ID, false));
}
@Test
public void testRecordDefaults() throws Exception {
- assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
- assertEquals(true, mHelper.canShowBadge(PKG, UID));
- assertEquals(1, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
+ UID_N_MR1));
+ assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
+ assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
}
@Test
public void testCreateGroup() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG, UID).iterator().next());
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1).iterator().next());
verify(mHandler, never()).requestSort();
}
@@ -1353,7 +1400,7 @@
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup("garbage");
try {
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
fail("Created a channel with a bad group");
} catch (IllegalArgumentException e) {
}
@@ -1362,45 +1409,45 @@
@Test
public void testCannotCreateChannel_goodGroup() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
assertEquals(ncg.getId(),
- mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false).getGroup());
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false).getGroup());
}
@Test
public void testGetChannelGroups() throws Exception {
NotificationChannelGroup unused = new NotificationChannelGroup("unused", "s");
- mHelper.createNotificationChannelGroup(PKG, UID, unused, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, unused, true);
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
NotificationChannel channel1a =
new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1a.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1a, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1a, true, false);
NotificationChannel channel2 =
new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
channel2.setGroup(ncg2.getId());
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false);
NotificationChannel channel3 =
new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false);
List<NotificationChannelGroup> actual =
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
assertEquals(3, actual.size());
for (NotificationChannelGroup group : actual) {
if (group.getId() == null) {
@@ -1426,19 +1473,19 @@
@Test
public void testGetChannelGroups_noSideEffects() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
channel1.setImportance(IMPORTANCE_LOW);
- mHelper.updateNotificationChannel(PKG, UID, channel1, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true);
List<NotificationChannelGroup> actual =
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true).getList();
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
@@ -1451,14 +1498,14 @@
@Test
public void testCreateChannel_updateName() throws Exception {
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
- NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
+ NotificationChannel actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
assertEquals("hello", actual.getName());
nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
- actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
assertEquals("goodbye", actual.getName());
assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
@@ -1468,17 +1515,17 @@
@Test
public void testCreateChannel_addToGroup() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("group", "");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
- NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
+ NotificationChannel actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
assertNull(actual.getGroup());
nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
nc.setGroup(group.getId());
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
- actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
assertNotNull(actual.getGroup());
assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
@@ -1500,7 +1547,7 @@
String pkgName = "pkg" + i;
int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
for (int j = 0; j < numChannels; j++) {
- mHelper.createNotificationChannel(pkgName, UID,
+ mHelper.createNotificationChannel(pkgName, UID_N_MR1,
new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false);
}
expectedChannels.put(pkgName, numChannels);
@@ -1508,7 +1555,7 @@
// delete the first channel of the first package
String pkg = expectedChannels.keyAt(0);
- mHelper.deleteNotificationChannel("pkg" + 0, UID, "0");
+ mHelper.deleteNotificationChannel("pkg" + 0, UID_N_MR1, "0");
// dump should not include deleted channels
int count = expectedChannels.get(pkg);
expectedChannels.put(pkg, count - 1);
@@ -1566,7 +1613,8 @@
@Test
public void testOnLocaleChanged_updatesDefaultChannels() throws Exception {
String newLabel = "bananas!";
- final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
+ final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
+ UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false);
assertFalse(newLabel.equals(defaultChannel.getName()));
@@ -1577,56 +1625,56 @@
mHelper.onLocaleChanged(mContext, USER.getIdentifier());
- assertEquals(newLabel, mHelper.getNotificationChannel(PKG, UID,
+ assertEquals(newLabel, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1,
NotificationChannel.DEFAULT_CHANNEL_ID, false).getName());
}
@Test
public void testIsGroupBlocked_noGroup() throws Exception {
- assertFalse(mHelper.isGroupBlocked(PKG, UID, null));
+ assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, null));
- assertFalse(mHelper.isGroupBlocked(PKG, UID, "non existent group"));
+ assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, "non existent group"));
}
@Test
public void testIsGroupBlocked_notBlocked() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
- assertFalse(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
}
@Test
public void testIsGroupBlocked_blocked() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
group.setBlocked(true);
- mHelper.createNotificationChannelGroup(PKG, UID, group, false);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, false);
- assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
}
@Test
public void testIsGroup_appCannotResetBlock() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
NotificationChannelGroup group2 = group.clone();
group2.setBlocked(true);
- mHelper.createNotificationChannelGroup(PKG, UID, group2, false);
- assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group2, false);
+ assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
NotificationChannelGroup group3 = group.clone();
group3.setBlocked(false);
- mHelper.createNotificationChannelGroup(PKG, UID, group3, true);
- assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group3, true);
+ assertTrue(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId()));
}
@Test
public void testGetNotificationChannelGroupWithChannels() throws Exception {
NotificationChannelGroup group = new NotificationChannelGroup("group", "");
NotificationChannelGroup other = new NotificationChannelGroup("something else", "");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
- mHelper.createNotificationChannelGroup(PKG, UID, other, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, other, true);
NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
a.setGroup(group.getId());
@@ -1636,20 +1684,20 @@
c.setGroup(group.getId());
NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, a, true, false);
- mHelper.createNotificationChannel(PKG, UID, b, true, false);
- mHelper.createNotificationChannel(PKG, UID, c, true, false);
- mHelper.createNotificationChannel(PKG, UID, d, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, c.getId());
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, a, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, b, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, c, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, d, true, false);
+ mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, c.getId());
NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
- PKG, UID, group.getId(), true);
+ PKG_N_MR1, UID_N_MR1, group.getId(), true);
assertEquals(2, retrieved.getChannels().size());
compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
compareChannels(c, findChannel(retrieved.getChannels(), c.getId()));
retrieved = mHelper.getNotificationChannelGroupWithChannels(
- PKG, UID, group.getId(), false);
+ PKG_N_MR1, UID_N_MR1, group.getId(), false);
assertEquals(1, retrieved.getChannels().size());
compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
}
@@ -1670,9 +1718,9 @@
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
test.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, UID, test, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, test, true, true);
- assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+ assertTrue(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "A", false).canBypassDnd());
}
@Test
@@ -1680,9 +1728,9 @@
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
test.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, 1000, test, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, 1000, test, true, false);
- assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
+ assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 1000, "A", false).canBypassDnd());
}
@Test
@@ -1701,23 +1749,23 @@
@Test
public void testDndPkgCanBypassDnd_update() throws Exception {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, test, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, test, true, true);
NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
update.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, UID, update, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true, true);
- assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+ assertTrue(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "A", false).canBypassDnd());
}
@Test
public void testNormalPkgCannotBypassDnd_update() {
NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, 1000, test, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, 1000, test, true, false);
NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
update.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, 1000, update, true, false);
- assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
+ mHelper.createNotificationChannel(PKG_N_MR1, 1000, update, true, false);
+ assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 1000, "A", false).canBypassDnd());
}
@Test
@@ -1727,16 +1775,16 @@
@Test
public void testGetBlockedAppCount_noAppsForUserId() {
- mHelper.setEnabled(PKG, 100, false);
+ mHelper.setEnabled(PKG_N_MR1, 100, false);
assertEquals(0, mHelper.getBlockedAppCount(9));
}
@Test
public void testGetBlockedAppCount_appsForUserId() {
- mHelper.setEnabled(PKG, 1020, false);
- mHelper.setEnabled(PKG, 1030, false);
- mHelper.setEnabled(PKG, 1060, false);
- mHelper.setEnabled(PKG, 1000, true);
+ mHelper.setEnabled(PKG_N_MR1, 1020, false);
+ mHelper.setEnabled(PKG_N_MR1, 1030, false);
+ mHelper.setEnabled(PKG_N_MR1, 1060, false);
+ mHelper.setEnabled(PKG_N_MR1, 1000, true);
assertEquals(3, mHelper.getBlockedAppCount(0));
}
}
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 2ecda48..2a22600 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -116,7 +116,7 @@
mZenModeHelperSpy.writeXml(serializer, forBackup, version);
serializer.endDocument();
serializer.flush();
- mZenModeHelperSpy.setConfig(new ZenModeConfig(), "writing xml");
+ mZenModeHelperSpy.setConfig(new ZenModeConfig(), null, "writing xml");
return baos;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 1db8967..6d4f5f8 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -120,7 +120,8 @@
.thenReturn(PERMISSION_DENIED);
when(mContextSpy.checkPermission("perm2", Process.myPid(), Process.myUid()))
.thenReturn(PERMISSION_GRANTED);
- mService.checkSlicePermission(TEST_URI, mContext.getPackageName(), Process.myPid(),
+ mService.checkSlicePermission(TEST_URI, mContext.getPackageName(),
+ mContext.getPackageName(), Process.myPid(),
Process.myUid(), testPerms);
verify(mContextSpy).checkPermission(eq("perm1"), eq(Process.myPid()), eq(Process.myUid()));
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
new file mode 100644
index 0000000..9c874bf
--- /dev/null
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom;
+
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.ICallRedirectionService;
+import com.android.internal.telecom.ICallRedirectionAdapter;
+
+/**
+ * This service can be implemented to interact between Telecom and its implementor
+ * for making outgoing call with optional redirection/cancellation purposes.
+ *
+ * <p>
+ * Below is an example manifest registration for a {@code CallRedirectionService}.
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourCallRedirectionServiceImplementation"
+ * android:permission="android.permission.BIND_REDIRECTION_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telecom.CallRedirectionService"/>
+ * </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ */
+public abstract class CallRedirectionService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
+
+ /**
+ * An adapter to inform Telecom the response from the implementor of the Call
+ * Redirection service
+ */
+ private ICallRedirectionAdapter mCallRedirectionAdapter;
+
+ /**
+ * Telecom calls this method to inform the implemented {@link CallRedirectionService} of
+ * a new outgoing call which is being placed.
+ * @param handle the phone number dialed by the user
+ * @param targetPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+ */
+ public abstract void onPlaceCall(Uri handle, PhoneAccountHandle targetPhoneAccount);
+
+ /**
+ * The implemented {@link CallRedirectionService} calls this method to response a request
+ * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that no changes
+ * are required to the outgoing call, and that the call should be placed as-is.
+ */
+ public final void placeCallUnmodified() {
+ try {
+ mCallRedirectionAdapter.placeCallUnmodified();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * The implemented {@link CallRedirectionService} calls this method to response a request
+ * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that changes
+ * are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing call.
+ * @param handle the new phone number to dial
+ * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call.
+ * If {@code null}, no change will be made to the
+ * {@link PhoneAccountHandle} used to place the call.
+ */
+ public final void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) {
+ try {
+ mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * The implemented {@link CallRedirectionService} calls this method to response a request
+ * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that an outgoing
+ * call should be canceled entirely.
+ */
+ public final void cancelCall() {
+ try {
+ mCallRedirectionAdapter.cancelCall();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * A handler message to process the attempt to place call with redirection service from Telecom
+ */
+ private static final int MSG_PLACE_CALL = 1;
+
+ /**
+ * A handler to process the attempt to place call with redirection service from Telecom
+ */
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PLACE_CALL:
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1;
+ onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ }
+ };
+
+ private final class CallRedirectionBinder extends ICallRedirectionService.Stub {
+
+ /**
+ * Telecom calls this method to inform the CallRedirectionService of a new outgoing call
+ * which is about to be placed.
+ * @param handle the phone number dialed by the user
+ * @param targetPhoneAccount the URI of the number the user dialed
+ */
+ @Override
+ public void placeCall(ICallRedirectionAdapter adapter, Uri handle,
+ PhoneAccountHandle targetPhoneAccount) {
+ Log.v(this, "placeCall");
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = adapter;
+ args.arg2 = handle;
+ args.arg3 = targetPhoneAccount;
+ mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget();
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.v(this, "onBind");
+ return new CallRedirectionBinder();
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.v(this, "onUnbind");
+ return false;
+ }
+}
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
new file mode 100644
index 0000000..46bf983
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import android.net.Uri;
+import android.telecom.PhoneAccountHandle;
+
+/**
+ * Internal remote callback interface for call redirection services.
+ *
+ * @see android.telecom.CallRedirectionService
+ *
+ * {@hide}
+ */
+oneway interface ICallRedirectionAdapter {
+ void cancelCall();
+
+ void placeCallUnmodified();
+
+ void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount);
+}
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
new file mode 100644
index 0000000..d8d360b
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import android.net.Uri;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.internal.telecom.ICallRedirectionAdapter;
+
+/**
+ * Internal remote interface for a call redirection service.
+ *
+ * @see android.telecom.CallRedirectionService
+ *
+ * @hide
+ */
+oneway interface ICallRedirectionService {
+ void placeCall(in ICallRedirectionAdapter adapter, in Uri handle,
+ in PhoneAccountHandle targetPhoneAccount);
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cabf444..2a68044 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2012,6 +2012,15 @@
public static final String KEY_UNDELIVERED_SMS_MESSAGE_EXPIRATION_TIME =
"undelivered_sms_message_expiration_time";
+ /**
+ * Specifies a carrier-defined {@link CallRedirectionService} which Telecom will bind
+ * to for outgoing calls. An empty string indicates that no carrier-defined
+ * {@link CallRedirectionService} is specified.
+ * @hide
+ */
+ public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING =
+ "call_redirection_service_component_name_string";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index 3aab3fc..bffeb17 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -124,6 +125,14 @@
mTimeStamp = timeStamp;
}
+ /** @hide */
+ @NonNull
+ public abstract CellIdentity getCellIdentity();
+
+ /** @hide */
+ @NonNull
+ public abstract CellSignalStrength getCellSignalStrength();
+
/**
* Gets the connection status of this cell.
*
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index 6403bc5..8b8d1bb 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -45,6 +45,7 @@
this.mCellSignalStrengthCdma = ci.mCellSignalStrengthCdma.copy();
}
+ @Override
public CellIdentityCdma getCellIdentity() {
return mCellIdentityCdma;
}
@@ -53,6 +54,7 @@
mCellIdentityCdma = cid;
}
+ @Override
public CellSignalStrengthCdma getCellSignalStrength() {
return mCellSignalStrengthCdma;
}
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index a3a9b31..f7af1b2 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -45,6 +45,7 @@
this.mCellSignalStrengthGsm = ci.mCellSignalStrengthGsm.copy();
}
+ @Override
public CellIdentityGsm getCellIdentity() {
return mCellIdentityGsm;
}
@@ -53,6 +54,7 @@
mCellIdentityGsm = cid;
}
+ @Override
public CellSignalStrengthGsm getCellSignalStrength() {
return mCellSignalStrengthGsm;
}
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index b892e89..97d856e 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -45,6 +45,7 @@
this.mCellSignalStrengthLte = ci.mCellSignalStrengthLte.copy();
}
+ @Override
public CellIdentityLte getCellIdentity() {
if (DBG) log("getCellIdentity: " + mCellIdentityLte);
return mCellIdentityLte;
@@ -55,6 +56,7 @@
mCellIdentityLte = cid;
}
+ @Override
public CellSignalStrengthLte getCellSignalStrength() {
if (DBG) log("getCellSignalStrength: " + mCellSignalStrengthLte);
return mCellSignalStrengthLte;
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
index 7084c51..4fb1bce 100644
--- a/telephony/java/android/telephony/CellInfoTdscdma.java
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -48,6 +48,7 @@
this.mCellSignalStrengthTdscdma = ci.mCellSignalStrengthTdscdma.copy();
}
+ @Override
public CellIdentityTdscdma getCellIdentity() {
return mCellIdentityTdscdma;
}
@@ -56,6 +57,7 @@
mCellIdentityTdscdma = cid;
}
+ @Override
public CellSignalStrengthTdscdma getCellSignalStrength() {
return mCellSignalStrengthTdscdma;
}
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index 005f3d3..4f9dcb1 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -47,6 +47,7 @@
this.mCellSignalStrengthWcdma = ci.mCellSignalStrengthWcdma.copy();
}
+ @Override
public CellIdentityWcdma getCellIdentity() {
return mCellIdentityWcdma;
}
@@ -55,6 +56,7 @@
mCellIdentityWcdma = cid;
}
+ @Override
public CellSignalStrengthWcdma getCellSignalStrength() {
return mCellSignalStrengthWcdma;
}
diff --git a/telephony/java/com/android/internal/telephony/ISmsBaseImpl.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
similarity index 70%
rename from telephony/java/com/android/internal/telephony/ISmsBaseImpl.java
rename to telephony/java/com/android/internal/telephony/ISmsImplBase.java
index cc1d105..1cdf44d 100644
--- a/telephony/java/com/android/internal/telephony/ISmsBaseImpl.java
+++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
@@ -1,4 +1,5 @@
-/* Copyright (C) 2018 The Android Open Source Project
+/*
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,17 +12,19 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
*/
package com.android.internal.telephony;
import android.app.PendingIntent;
import android.net.Uri;
-import java.lang.UnsupportedOperationException;
+
import java.util.List;
-public class ISmsBaseImpl extends ISms.Stub {
+/**
+ * Base class for ISms that facilitates forward compatibility with new features.
+ */
+public class ISmsImplBase extends ISms.Stub {
@Override
public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPkg) {
@@ -29,45 +32,42 @@
}
@Override
- public boolean updateMessageOnIccEfForSubscriber(int subId, String callingPkg,
- int messageIndex, int newStatus, byte[] pdu) throws UnsupportedOperationException {
+ public boolean updateMessageOnIccEfForSubscriber(int subId, String callingPkg, int messageIndex,
+ int newStatus, byte[] pdu) {
throw new UnsupportedOperationException();
}
@Override
public boolean copyMessageToIccEfForSubscriber(int subId, String callingPkg, int status,
- byte[] pdu, byte[] smsc) throws UnsupportedOperationException {
+ byte[] pdu, byte[] smsc) {
throw new UnsupportedOperationException();
}
@Override
public void sendDataForSubscriber(int subId, String callingPkg, String destAddr,
String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
- PendingIntent deliveryIntent) throws UnsupportedOperationException {
+ PendingIntent deliveryIntent) {
throw new UnsupportedOperationException();
}
@Override
public void sendDataForSubscriberWithSelfPermissions(int subId, String callingPkg,
- String destAddr, String scAddr, int destPort, byte[] data,
- PendingIntent sentIntent, PendingIntent deliveryIntent)
- throws UnsupportedOperationException {
+ String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+ PendingIntent deliveryIntent) {
throw new UnsupportedOperationException();
}
@Override
public void sendTextForSubscriber(int subId, String callingPkg, String destAddr,
String scAddr, String text, PendingIntent sentIntent,
- PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp)
- throws UnsupportedOperationException {
+ PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp) {
throw new UnsupportedOperationException();
}
@Override
public void sendTextForSubscriberWithSelfPermissions(int subId, String callingPkg,
String destAddr, String scAddr, String text, PendingIntent sentIntent,
- PendingIntent deliveryIntent, boolean persistMessage)
- throws UnsupportedOperationException {
+ PendingIntent deliveryIntent, boolean persistMessage) {
throw new UnsupportedOperationException();
}
@@ -75,15 +75,13 @@
public void sendTextForSubscriberWithOptions(int subId, String callingPkg, String destAddr,
String scAddr, String text, PendingIntent sentIntent,
PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp,
- int priority, boolean expectMore, int validityPeriod)
- throws UnsupportedOperationException {
+ int priority, boolean expectMore, int validityPeriod) {
throw new UnsupportedOperationException();
}
@Override
public void injectSmsPduForSubscriber(
- int subId, byte[] pdu, String format, PendingIntent receivedIntent)
- throws UnsupportedOperationException {
+ int subId, byte[] pdu, String format, PendingIntent receivedIntent) {
throw new UnsupportedOperationException();
}
@@ -91,8 +89,7 @@
public void sendMultipartTextForSubscriber(int subId, String callingPkg,
String destinationAddress, String scAddress,
List<String> parts, List<PendingIntent> sentIntents,
- List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp)
- throws UnsupportedOperationException {
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
throw new UnsupportedOperationException();
}
@@ -101,99 +98,94 @@
String destinationAddress, String scAddress,
List<String> parts, List<PendingIntent> sentIntents,
List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp,
- int priority, boolean expectMore, int validityPeriod)
- throws UnsupportedOperationException {
+ int priority, boolean expectMore, int validityPeriod) {
throw new UnsupportedOperationException();
}
@Override
- public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
- throws UnsupportedOperationException {
+ public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType) {
throw new UnsupportedOperationException();
}
@Override
- public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
- throws UnsupportedOperationException {
+ public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier,
+ int ranType) {
throw new UnsupportedOperationException();
}
@Override
public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
- int endMessageId, int ranType) throws UnsupportedOperationException {
+ int endMessageId, int ranType) {
throw new UnsupportedOperationException();
}
@Override
public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
- int endMessageId, int ranType) throws UnsupportedOperationException {
+ int endMessageId, int ranType) {
throw new UnsupportedOperationException();
}
@Override
- public int getPremiumSmsPermission(String packageName) throws UnsupportedOperationException {
+ public int getPremiumSmsPermission(String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public int getPremiumSmsPermissionForSubscriber(int subId, String packageName)
- throws UnsupportedOperationException {
+ public int getPremiumSmsPermissionForSubscriber(int subId, String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public void setPremiumSmsPermission(String packageName, int permission) throws UnsupportedOperationException {
+ public void setPremiumSmsPermission(String packageName, int permission) {
throw new UnsupportedOperationException();
}
@Override
public void setPremiumSmsPermissionForSubscriber(int subId, String packageName,
- int permission) throws UnsupportedOperationException {
+ int permission) {
throw new UnsupportedOperationException();
}
@Override
- public boolean isImsSmsSupportedForSubscriber(int subId) throws UnsupportedOperationException {
+ public boolean isImsSmsSupportedForSubscriber(int subId) {
throw new UnsupportedOperationException();
}
@Override
- public boolean isSmsSimPickActivityNeeded(int subId) throws UnsupportedOperationException {
+ public boolean isSmsSimPickActivityNeeded(int subId) {
throw new UnsupportedOperationException();
}
@Override
- public int getPreferredSmsSubscription() throws UnsupportedOperationException {
+ public int getPreferredSmsSubscription() {
throw new UnsupportedOperationException();
}
@Override
- public String getImsSmsFormatForSubscriber(int subId) throws UnsupportedOperationException {
+ public String getImsSmsFormatForSubscriber(int subId) {
throw new UnsupportedOperationException();
}
@Override
- public boolean isSMSPromptEnabled() throws UnsupportedOperationException {
+ public boolean isSMSPromptEnabled() {
throw new UnsupportedOperationException();
}
@Override
public void sendStoredText(int subId, String callingPkg, Uri messageUri, String scAddress,
- PendingIntent sentIntent, PendingIntent deliveryIntent)
- throws UnsupportedOperationException {
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
throw new UnsupportedOperationException();
}
@Override
public void sendStoredMultipartText(int subId, String callingPkg, Uri messageUri,
- String scAddress, List<PendingIntent> sentIntents,
- List<PendingIntent> deliveryIntents) throws UnsupportedOperationException {
+ String scAddress, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents) {
throw new UnsupportedOperationException();
}
@Override
- public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent)
- throws UnsupportedOperationException {
+ public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent) {
throw new UnsupportedOperationException();
}
}
diff --git a/tests/ActivityViewTest/Android.mk b/tests/ActivityViewTest/Android.mk
new file mode 100644
index 0000000..9c80764
--- /dev/null
+++ b/tests/ActivityViewTest/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := ActivityViewTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ActivityViewTest/AndroidManifest.xml b/tests/ActivityViewTest/AndroidManifest.xml
new file mode 100644
index 0000000..de54cc9
--- /dev/null
+++ b/tests/ActivityViewTest/AndroidManifest.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.test.activityview">
+ <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+ <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING"/>
+ <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
+
+ <uses-sdk android:targetSdkVersion="27"/>
+ <application android:label="ActivityViewTest">
+ <activity android:name=".ActivityViewMainActivity"
+ android:label="AV Main"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".ActivityViewActivity"
+ android:label="AV"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ </activity>
+
+ <activity android:name=".ActivityViewResizeActivity"
+ android:label="AV Resize"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ </activity>
+
+ <activity android:name=".ActivityViewScrollActivity"
+ android:label="AV Scroll"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ </activity>
+
+ <activity android:name=".ActivityViewTestActivity"
+ android:resizeableActivity="true"
+ android:theme="@*android:style/Theme.NoTitleBar"
+ android:exported="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_activity.xml
new file mode 100644
index 0000000..67c01f8
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_activity.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#cfd8dc">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/activity_launch_button"
+ android:layout_width="200dp"
+ android:layout_height="wrap_content"
+ android:text="Launch test activity" />
+
+ <Button
+ android:id="@+id/activity_pick_launch_button"
+ android:layout_width="200dp"
+ android:layout_height="wrap_content"
+ android:text="Launch from picker" />
+
+ </LinearLayout>
+
+ <ActivityView
+ android:id="@+id/activity_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
new file mode 100644
index 0000000..ba2e911
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/activity_view_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Test ActivityView"
+ android:textAllCaps="false"/>
+
+ <Button
+ android:id="@+id/scroll_activity_view_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Test Scroll ActivityView"
+ android:textAllCaps="false"/>
+
+ <Button
+ android:id="@+id/resize_activity_view_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Test Resize ActivityView"
+ android:textAllCaps="false"/>
+
+</LinearLayout>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml
new file mode 100644
index 0000000..18d86e3
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#cfd8dc">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/activity_launch_button"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:text="Launch" />
+
+ <Button
+ android:id="@+id/activity_resize_button"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:text="Resize" />
+ </LinearLayout>
+
+ <SeekBar
+ android:id="@+id/activity_view_seek_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <ActivityView
+ android:id="@+id/activity_view"
+ android:layout_width="match_parent"
+ android:layout_height="600dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml
new file mode 100644
index 0000000..879c2c20
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@+id/activity_launch_button"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:text="Launch" />
+
+ <ScrollView
+ android:id="@+id/activity_view_host_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:color="#cfd8dc">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="300dp"
+ android:layout_gravity="center_horizontal"
+ android:background="#eeeeee" />
+
+ <ActivityView
+ android:id="@+id/activity_view"
+ android:layout_width="match_parent"
+ android:layout_height="300dp"
+ android:background="#fce4ec" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="300dp"
+ android:layout_gravity="center_horizontal"
+ android:background="#eeeeee" />
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
new file mode 100644
index 0000000..f7ec562
--- /dev/null
+++ b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#ffe0b2">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:orientation="vertical"
+ android:background="#00000000" >
+ <TextView
+ android:id="@+id/test_activity_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/black"
+ android:background="#00000000"
+ android:gravity="center" />
+ <TextView
+ android:id="@+id/test_activity_touch_state"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/black"
+ android:background="#00000000"
+ android:gravity="center" />
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/test_activity_width_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/black"
+ android:background="#00000000"
+ android:gravity="center" />
+
+ <TextView
+ android:id="@+id/test_activity_height_text"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:textColor="@android:color/black"
+ android:background="#00000000"
+ android:gravity="center" />
+
+ <View
+ android:id="@+id/touch_intercept_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#00000000"
+ />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
new file mode 100644
index 0000000..1548d6e
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
@@ -0,0 +1,51 @@
+/**
+ * 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.google.android.test.activityview;
+
+import android.app.Activity;
+import android.app.ActivityView;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+
+public class ActivityViewActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_activity);
+
+ final ActivityView activityView = findViewById(R.id.activity_view);
+ final Button launchButton = findViewById(R.id.activity_launch_button);
+ launchButton.setOnClickListener(v -> {
+ final Intent intent = new Intent(this, ActivityViewTestActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ activityView.startActivity(intent);
+ });
+ final Button pickActivityLaunchButton = findViewById(R.id.activity_pick_launch_button);
+ pickActivityLaunchButton.setOnClickListener(v -> {
+ final Intent intent = Intent.makeMainActivity(null);
+ final Intent chooser = Intent.createChooser(intent,
+ "Pick an app to launch in ActivityView");
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ activityView.startActivity(chooser);
+ }
+ });
+ }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
new file mode 100644
index 0000000..66f0c6a
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
@@ -0,0 +1,39 @@
+/**
+ * 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.google.android.test.activityview;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class ActivityViewMainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_main_activity);
+
+ findViewById(R.id.activity_view_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ActivityViewActivity.class)));
+
+ findViewById(R.id.scroll_activity_view_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ActivityViewScrollActivity.class)));
+
+ findViewById(R.id.resize_activity_view_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ActivityViewResizeActivity.class)));
+ }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java
new file mode 100644
index 0000000..8860a77
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java
@@ -0,0 +1,79 @@
+/**
+ * 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.google.android.test.activityview;
+
+import android.app.Activity;
+import android.app.ActivityView;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+public class ActivityViewResizeActivity extends Activity {
+ private static final int SMALL_SIZE = 600;
+ private static final int LARGE_SIZE = 1200;
+
+ private ActivityView mActivityView;
+
+ private boolean mFlipSize;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_resize_activity);
+
+ mActivityView = findViewById(R.id.activity_view);
+
+ final Button launchButton = findViewById(R.id.activity_launch_button);
+ launchButton.setOnClickListener(v -> {
+ final Intent intent = new Intent(this, ActivityViewTestActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ mActivityView.startActivity(intent);
+ });
+ final Button resizeButton = findViewById(R.id.activity_resize_button);
+ if (resizeButton != null) {
+ resizeButton.setOnClickListener(v -> {
+ LinearLayout.LayoutParams params =
+ (LinearLayout.LayoutParams) mActivityView.getLayoutParams();
+ params.height = mFlipSize ? SMALL_SIZE : LARGE_SIZE;
+ mFlipSize = !mFlipSize;
+ mActivityView.setLayoutParams(params);
+ });
+ }
+ final SeekBar seekBar = findViewById(R.id.activity_view_seek_bar);
+ if (seekBar != null) {
+ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ final LinearLayout.LayoutParams params =
+ (LinearLayout.LayoutParams) mActivityView.getLayoutParams();
+ params.height = SMALL_SIZE + progress * 10;
+ mActivityView.setLayoutParams(params);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ }
+ }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java
new file mode 100644
index 0000000..5654366
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.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 com.google.android.test.activityview;
+
+import android.app.Activity;
+import android.app.ActivityView;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+public class ActivityViewScrollActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_scroll_activity);
+
+ final ActivityView activityView = findViewById(R.id.activity_view);
+ final Button launchButton = findViewById(R.id.activity_launch_button);
+ launchButton.setOnClickListener(v -> {
+ final Intent intent = new Intent(this, ActivityViewTestActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ activityView.startActivity(intent);
+ });
+ findViewById(R.id.activity_view_host_scroll_view).setOnScrollChangeListener(
+ (View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
+ -> activityView.onLocationChanged());
+ }
+}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
new file mode 100644
index 0000000..0d62786
--- /dev/null
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
@@ -0,0 +1,115 @@
+/**
+ * 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.google.android.test.activityview;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.TextView;
+
+public class ActivityViewTestActivity extends Activity implements View.OnTouchListener {
+
+ private TextView mTextView;
+ private TextView mWidthTextView;
+ private TextView mHeightTextView;
+ private TextView mTouchStateTextView;
+ private View mTouchInterceptView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_test_activity);
+
+ mTextView = findViewById(R.id.test_activity_title);
+ mWidthTextView = findViewById(R.id.test_activity_width_text);
+ mHeightTextView = findViewById(R.id.test_activity_height_text);
+ mTouchStateTextView = findViewById(R.id.test_activity_touch_state);
+ mTouchInterceptView = findViewById(R.id.touch_intercept_view);
+ mTouchInterceptView.setOnTouchListener(this);
+ ViewTreeObserver viewTreeObserver = mTouchInterceptView.getViewTreeObserver();
+ if (viewTreeObserver.isAlive()) {
+ viewTreeObserver.addOnGlobalLayoutListener(this::updateDimensionTexts);
+ }
+ updateStateText("CREATED");
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ updateStateText("STARTED");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ updateStateText("RESUMED");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ updateStateText("PAUSED");
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ updateStateText("STOPPED");
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateDimensionTexts();
+ }
+
+ private void updateStateText(String state) {
+ mTextView.setText(state);
+ }
+
+ private void updateDimensionTexts() {
+ mWidthTextView.setText("" + mTouchInterceptView.getWidth());
+ mHeightTextView.setText("" + mTouchInterceptView.getHeight());
+ }
+
+ private void updateTouchState(MotionEvent event) {
+ switch (event.getAction()) {
+ case ACTION_DOWN:
+ case ACTION_MOVE:
+ mTouchStateTextView.setText("[" + event.getX() + "," + event.getY() + "]");
+ break;
+ case ACTION_UP:
+ case ACTION_CANCEL:
+ mTouchStateTextView.setText("");
+ break;
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ updateTouchState(event);
+ return true;
+ }
+}
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 8c1fa9a..36b5578 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -690,10 +690,6 @@
return 0;
}
- bool IsAutoNamespace() override {
- return false;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(CompileContext);
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 4b82eef..d57eaa1 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -311,10 +311,6 @@
return 0u;
}
- bool IsAutoNamespace() override {
- return false;
- }
-
bool verbose_ = false;
std::string package_;
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 7875a2b..262f4fc4e 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -65,10 +65,6 @@
return 0;
}
- bool IsAutoNamespace() override {
- return false;
- }
-
private:
std::string empty_;
StdErrDiagnostics diagnostics_;
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 717e757..8b1f672 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -292,10 +292,6 @@
return 0;
}
- bool IsAutoNamespace() override {
- return false;
- }
-
private:
StdErrDiagnostics diagnostics_;
bool verbose_ = false;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 1d508d9..c94b847 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -137,14 +137,6 @@
min_sdk_version_ = minSdk;
}
- bool IsAutoNamespace() override {
- return auto_namespace_;
- }
-
- void SetAutoNamespace(bool val) {
- auto_namespace_ = val;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(LinkContext);
@@ -156,7 +148,6 @@
SymbolTable symbols_;
bool verbose_ = false;
int min_sdk_version_ = 0;
- bool auto_namespace_ = false;
};
// A custom delegate that generates compatible pre-O IDs for use with feature splits.
@@ -2042,15 +2033,6 @@
options_.output_format = OutputFormat::kProto;
}
- if (options_.auto_namespace_static_lib) {
- if (!static_lib_) {
- context.GetDiagnostics()->Error(
- DiagMessage() << "--auto-namespace-static-lib can only be used with --static-lib");
- return 1;
- }
- context.SetAutoNamespace(true);
- }
-
if (package_id_) {
if (context.GetPackageType() != PackageType::kApp) {
context.GetDiagnostics()->Error(
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 434475e..fb8796f 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -64,7 +64,6 @@
// Static lib options.
bool no_static_lib_packages = false;
- bool auto_namespace_static_lib = false;
// AndroidManifest.xml massaging options.
ManifestFixerOptions manifest_fixer_options;
@@ -188,10 +187,6 @@
AddOptionalSwitch("--no-static-lib-packages",
"Merge all library resources under the app's package.",
&options_.no_static_lib_packages);
- AddOptionalSwitch("--auto-namespace-static-lib",
- "Automatically namespace resource references when building a static\n"
- "library.",
- &options_.auto_namespace_static_lib);
AddOptionalSwitch("--non-final-ids",
"Generates R.java without the final modifier. This is implied when\n"
"--static-lib is specified.",
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index b4cba8c..47288ec 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -104,10 +104,6 @@
return sdk_version_;
}
- bool IsAutoNamespace() override {
- return false;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(OptimizeContext);
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk
deleted file mode 100644
index 91716b9..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestAutoNamespace_LibOne
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_AAPT_NAMESPACES := true
-LOCAL_AAPT_FLAGS := --auto-namespace-static-lib
-# We need this to compile the Java sources of AaptTestStaticLib_LibTwo using javac.
-LOCAL_JAR_EXCLUDE_FILES := none
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml
deleted file mode 100644
index f585840..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest package="com.example.android.aapt2.autonamespace.staticlib.one" />
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml
deleted file mode 100644
index 3e57b0f..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/res/values/values.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <!-- An attribute from StaticLibOne -->
- <attr name="StaticLibOne_attr" format="string" />
-
- <string name="Foo">Foo</string>
-
- <declare-styleable name="Widget">
- <attr name="StaticLibOne_attr" />
- <attr name="android:text" />
- </declare-styleable>
-</resources>
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java b/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java
deleted file mode 100644
index 886d48c..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibOne/src/com/example/android/aapt2/autonamespace/staticlib/one/StaticLibOne.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * 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.example.android.aapt2.autonamespace.staticlib.one;
-
-public class StaticLibOne { public static int FooId = R.string.Foo; }
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk
deleted file mode 100644
index c85496d..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := AaptTestAutoNamespace_LibTwo
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestAutoNamespace_LibOne
-LOCAL_AAPT_NAMESPACES := true
-LOCAL_AAPT_FLAGS := --auto-namespace-static-lib
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml
deleted file mode 100644
index 8d3c506..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest package="com.example.android.aapt2.autonamespace.staticlib.two" />
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml
deleted file mode 100644
index c532387..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/values/values.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <string name="FooBar">@string/Foo</string>
-</resources>
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java b/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java
deleted file mode 100644
index 323f53a..0000000
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/src/com/example/android/aapt2/autonamespace/staticlib/two/StaticLibTwo.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.example.android.aapt2.autonamespace.staticlib.two;
-
-public class StaticLibTwo {
- // IDs from StaticLibOne
- public static int FooId = com.example.android.aapt2.autonamespace.staticlib.one.R.string.Foo;
-
- // IDs from StaticLibTwo
- public static int FooBarId = R.string.FooBar;
- public static int LayoutId = R.layout.layout_two;
-}
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 28e71cc..3a5d585 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -80,7 +80,7 @@
// Find the attribute in the symbol table and check if it is visible from this callsite.
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
- transformed_reference, callsite_, symbols_, context_->IsAutoNamespace(), &err_str);
+ transformed_reference, callsite_, symbols_, &err_str);
if (symbol) {
// Assign our style key the correct ID. The ID may not exist.
entry.key.id = symbol->id;
@@ -202,18 +202,12 @@
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
const CallSite& callsite,
- SymbolTable* symbols,
- bool auto_namespace) {
+ SymbolTable* symbols) {
if (reference.name) {
const ResourceName& name = reference.name.value();
if (name.package.empty()) {
// Use the callsite's package name if no package name was defined.
- const SymbolTable::Symbol* local_symbol =
- symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
- if (!auto_namespace || local_symbol) {
- return local_symbol;
- }
- return symbols->FindByNameInAnyPackage(name);
+ return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
}
return symbols->FindByName(name);
} else if (reference.id) {
@@ -226,9 +220,8 @@
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
- bool auto_namespace,
std::string* out_error) {
- const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols, auto_namespace);
+ const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
if (!symbol) {
if (out_error) *out_error = "not found";
return nullptr;
@@ -242,10 +235,10 @@
}
const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
- const Reference& reference, const CallSite& callsite, SymbolTable* symbols, bool auto_namespace,
+ const Reference& reference, const CallSite& callsite, SymbolTable* symbols,
std::string* out_error) {
const SymbolTable::Symbol* symbol =
- ResolveSymbolCheckVisibility(reference, callsite, symbols, auto_namespace, out_error);
+ ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error);
if (!symbol) {
return nullptr;
}
@@ -260,10 +253,9 @@
Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
- bool auto_namespace,
std::string* out_error) {
const SymbolTable::Symbol* symbol =
- ResolveAttributeCheckVisibility(reference, callsite, symbols, auto_namespace, out_error);
+ ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error);
if (!symbol) {
return {};
}
@@ -341,8 +333,8 @@
xml::ResolvePackage(decls, &transformed_reference);
std::string err_str;
- const SymbolTable::Symbol* s = ResolveSymbolCheckVisibility(
- transformed_reference, callsite, symbols, context->IsAutoNamespace(), &err_str);
+ const SymbolTable::Symbol* s =
+ ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str);
if (s) {
// The ID may not exist. This is fine because of the possibility of building
// against libraries without assigned IDs.
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 7887915..b0b4945 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -36,12 +36,10 @@
ReferenceLinker() = default;
// Performs name mangling and looks up the resource in the symbol table. Uses the callsite's
- // package if the reference has no package name defined (implicit), or if auto_namespace is
- // set try looking in all avaliable packages for a symbol of that name.
+ // package if the reference has no package name defined (implicit).
// Returns nullptr if the symbol was not found.
static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference,
- const CallSite& callsite, SymbolTable* symbols,
- bool auto_namespace);
+ const CallSite& callsite, SymbolTable* symbols);
// Performs name mangling and looks up the resource in the symbol table. If the symbol is not
// visible by the reference at the callsite, nullptr is returned.
@@ -49,7 +47,6 @@
static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
- bool auto_namespace,
std::string* out_error);
// Same as ResolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
@@ -57,14 +54,13 @@
static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,
const CallSite& callsite,
SymbolTable* symbols,
- bool auto_namespace,
std::string* out_error);
// Resolves the attribute reference and returns an xml::AaptAttribute if successful.
// If resolution fails, outError holds the error message.
static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
const CallSite& callsite,
- SymbolTable* symbols, bool auto_namespace,
+ SymbolTable* symbols,
std::string* out_error);
// Writes the resource name to the DiagMessage, using the
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 0b7b1ce..be38b96 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -267,7 +267,7 @@
std::string error;
const CallSite call_site{"com.app.test"};
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
- *test::BuildReference("com.app.test:string/foo"), call_site, &table, false, &error);
+ *test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
ASSERT_THAT(symbol, NotNull());
EXPECT_TRUE(error.empty());
}
@@ -285,13 +285,13 @@
std::string error;
const CallSite call_site{"com.app.ext"};
- EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(*test::BuildReference("com.app.test:attr/foo"),
- call_site, &table, false, &error));
+ EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
+ *test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
EXPECT_FALSE(error.empty());
error = "";
ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute(
- *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, false, &error));
+ *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, &error));
EXPECT_TRUE(error.empty());
}
@@ -303,74 +303,19 @@
.AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))
.Build());
- const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(
- *test::BuildReference("string/foo"), CallSite{"com.app.test"}, &table, false);
+ const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
+ CallSite{"com.app.test"}, &table);
ASSERT_THAT(s, NotNull());
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
- &table, false);
+ &table);
ASSERT_THAT(s, NotNull());
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
- CallSite{"com.app.bad"}, &table, false),
+ CallSite{"com.app.bad"}, &table),
IsNull());
}
-TEST(ReferenceLinkerTest, AutomaticNamespace) {
- NameMangler mangler(NameManglerPolicy{"com.example.thislib"});
- SymbolTable table(&mangler);
- table.AppendSource(
- test::StaticSymbolSourceBuilder()
- .AddSymbol("com.example.thislib:string/thislib_string", ResourceId(0x7f010006))
- .AddSymbol("com.example.thislib:string/explicit_override_string", ResourceId(0x7f010007))
- .Build());
- // Lib2 is higher priority than lib1
- table.AppendSource(
- test::StaticSymbolSourceBuilder()
- .AddSymbol("com.example.lib2:string/lib2_string", ResourceId(0x7f010003))
- .AddSymbol("com.example.lib2:string/explicit_override_string", ResourceId(0x7f010004))
- .AddSymbol("com.example.lib2:string/implicit_override_string", ResourceId(0x7f010005))
- .Build());
- table.AppendSource(
- test::StaticSymbolSourceBuilder()
- .AddSymbol("com.example.lib1:string/explicit_override_string", ResourceId(0x7f010001))
- .AddSymbol("com.example.lib1:string/implicit_override_string", ResourceId(0x7f010002))
- .Build());
-
- // Sanity test: Local references are still fine.
- const SymbolTable::Symbol* s =
- ReferenceLinker::ResolveSymbol(*test::BuildReference("string/thislib_string"),
- CallSite{"com.example.thislib"}, &table, true);
- ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010006)));
-
- // Local references are fine, even if clash with remote ones.
- s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/explicit_override_string"),
- CallSite{"com.example.thislib"}, &table, true);
- ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010007)));
-
- // An unqualified reference to lib2 is rewritten
- s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/lib2_string"),
- CallSite{"com.example.thislib"}, &table, true);
- ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010003)));
-
- // Qualified references are left alone.
- s = ReferenceLinker::ResolveSymbol(
- *test::BuildReference("com.example.lib2:string/explicit_override_string"),
- CallSite{"com.example.thislib"}, &table, true);
- ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010004)));
-
- // Implicit overrides respect priority ordering.
- s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/implicit_override_string"),
- CallSite{"com.example.thislib"}, &table, true);
- ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010005)));
-
- //
-}
} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index 420a127..160ff92 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -97,8 +97,8 @@
attr_ref.private_reference = maybe_package.value().private_namespace;
std::string err_str;
- attr.compiled_attribute = ReferenceLinker::CompileXmlAttribute(
- attr_ref, callsite_, symbols_, context_->IsAutoNamespace(), &err_str);
+ attr.compiled_attribute =
+ ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str);
if (!attr.compiled_attribute) {
DiagMessage error_msg(source);
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index d321f8f..ef99355 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -18,7 +18,6 @@
#include "test/Test.h"
-using ::testing::Eq;
using ::testing::IsNull;
using ::testing::NotNull;
@@ -71,29 +70,12 @@
.Build())
.AddPublicSymbol("com.app.test:attr/attr", ResourceId(0x7f010002),
test::AttributeBuilder().Build())
- .AddPublicSymbol("com.app.lib:string/lib_string", ResourceId(0x7f020003))
.Build())
.Build();
-
- auto_namespace_context_ =
- test::ContextBuilder()
- .SetCompilationPackage("com.app.test")
- .SetNameManglerPolicy(NameManglerPolicy{"com.app.test", {"com.android.support"}})
- .SetAutoNamespace(true)
- .AddSymbolSource(
- test::StaticSymbolSourceBuilder()
- .AddPublicSymbol("android:attr/text", ResourceId(0x01010003),
- test::AttributeBuilder()
- .SetTypeMask(android::ResTable_map::TYPE_STRING)
- .Build())
- .AddPublicSymbol("com.app.lib:string/lib_string", ResourceId(0x7f020003))
- .Build())
- .Build();
}
protected:
std::unique_ptr<IAaptContext> context_;
- std::unique_ptr<IAaptContext> auto_namespace_context_;
};
TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
@@ -213,31 +195,6 @@
EXPECT_EQ(make_value(ResourceId(0x7f020001)), ref->id);
}
-TEST_F(XmlReferenceLinkerTest, LinkAutoNamespaceResReference) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
- <View
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:text="@string/lib_string" />)");
-
- XmlReferenceLinker linker;
- // Should not link with auto-namespace support disabled.
- ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
- // Should link with auto-namespace enabled.
- ASSERT_TRUE(linker.Consume(auto_namespace_context_.get(), doc.get()));
-
- xml::Element* view_el = doc->root.get();
- ASSERT_THAT(view_el, NotNull());
-
- xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
- ASSERT_THAT(xml_attr, NotNull());
- ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010003)), xml_attr->compiled_attribute.value().id);
- Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
- ASSERT_THAT(ref, NotNull());
- ASSERT_TRUE(ref->name);
- EXPECT_EQ(make_value(ResourceId(0x7f020003)), ref->id);
-}
-
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<View xmlns:app="http://schemas.android.com/apk/res/android" app:attr="@app:id/id">
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index a931343..e92c121 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -99,10 +99,6 @@
util::make_unique<SourcePathDiagnostics>(Source{source}, context_->GetDiagnostics());
}
- bool IsAutoNamespace() override {
- return context_->IsAutoNamespace();
- }
-
private:
IAaptContext* context_;
std::unique_ptr<SourcePathDiagnostics> source_diag_;
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index a3a7719..30dad802 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -50,7 +50,6 @@
virtual NameMangler* GetNameMangler() = 0;
virtual bool IsVerbose() = 0;
virtual int GetMinSdkVersion() = 0;
- virtual bool IsAutoNamespace() = 0;
};
struct IResourceTableConsumer {
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index ef2e448..fc4c9b5 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -114,16 +114,6 @@
return shared_symbol.get();
}
-const SymbolTable::Symbol* SymbolTable::FindByNameInAnyPackage(const ResourceName& name) {
- for (auto& source : sources_) {
- std::string package = source->GetPackageForSymbol(name);
- if (!package.empty()) {
- return FindByName(ResourceName(package, name.type, name.entry));
- }
- }
- return {};
-}
-
const SymbolTable::Symbol* SymbolTable::FindById(const ResourceId& id) {
if (const std::shared_ptr<Symbol>& s = id_cache_.get(id)) {
return s.get();
@@ -221,25 +211,6 @@
return symbol;
}
-std::string ResourceTableSymbolSource::GetPackageForSymbol(const ResourceName& name) {
- for (auto& package : table_->packages) {
- ResourceTableType* type = package->FindType(name.type);
- if (type == nullptr) {
- continue;
- }
- ResourceEntry* entry = type->FindEntry(name.entry);
- if (entry == nullptr) {
- continue;
- }
- return package->name;
- }
- if (name.type == ResourceType::kAttr) {
- // Recurse and try looking up a private attribute.
- return GetPackageForSymbol(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
- }
- return {};
-}
-
bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) {
int32_t cookie = 0;
return assets_.addAssetPath(android::String8(path.data(), path.size()), &cookie);
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index c798cbb..51a2e37 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -89,13 +89,6 @@
// results are stored in a cache which may evict entries on subsequent calls.
const Symbol* FindByName(const ResourceName& name);
- // Finds the symbol from any package, for use as part of automatic conversion to namespaces.
- // This returns the symbol from the highest priority package,
- // which mimics the behavior of the resource merger and overlays.
- // NOTE: Never hold on to the result between calls to FindByXXX. The
- // results are stored in a cache which may evict entries on subsequent calls.
- const Symbol* FindByNameInAnyPackage(const ResourceName& name);
-
// NOTE: Never hold on to the result between calls to FindByXXX. The
// results are stored in a cache which may evict entries on subsequent calls.
const Symbol* FindById(const ResourceId& id);
@@ -160,11 +153,6 @@
virtual std::unique_ptr<SymbolTable::Symbol> FindByName(
const ResourceName& name) = 0;
- // Finds the name of a symbol from any package,
- // for use as part of automatic conversion to namespaces.
- // This returns the symbol from the highest priority package,
- // which mimics the behavior of the resource merger and overlays.
- virtual std::string GetPackageForSymbol(const ResourceName& name) = 0;
virtual std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) = 0;
// Default implementation tries the name if it exists, else the ID.
@@ -189,7 +177,6 @@
std::unique_ptr<SymbolTable::Symbol> FindByName(
const ResourceName& name) override;
- std::string GetPackageForSymbol(const ResourceName& name) override;
std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override {
return {};
}
@@ -210,9 +197,6 @@
std::unique_ptr<SymbolTable::Symbol> FindByName(
const ResourceName& name) override;
- std::string GetPackageForSymbol(const ResourceName& name) override {
- return {};
- }
std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override;
std::unique_ptr<SymbolTable::Symbol> FindByReference(
const Reference& ref) override;
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index df40b26..1f59d70 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -120,39 +120,4 @@
EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.other:id/foo")), IsNull());
}
-TEST(SymbolTableTest, FindByNameInAnyPackage) {
- // This represents lib3 --depends-on--> lib2 --depends-on--> lib1
-
- NameMangler mangler(NameManglerPolicy{"com.example.lib3"});
- SymbolTable symbol_table(&mangler);
- // Lib2 has higher precedence than lib1, as it is closer to the current library (lib3)
- // in the dependency graph.
-
- symbol_table.AppendSource(test::StaticSymbolSourceBuilder()
- .AddPublicSymbol("com.example.lib1:string/foo", ResourceId())
- .AddSymbol("com.example.lib1:attr/foo", ResourceId(),
- test::AttributeBuilder()
- .SetTypeMask(android::ResTable_map::TYPE_FLAGS)
- .AddItem("one", 0x01)
- .AddItem("two", 0x02)
- .Build())
- .Build());
- symbol_table.PrependSource(test::StaticSymbolSourceBuilder()
- .AddPublicSymbol("com.example.lib2:string/foo", ResourceId())
- .Build());
-
- // Sanity test
- EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("string/foo")), IsNull());
-
- // Test public symbol resolution
- const SymbolTable::Symbol* const found_string =
- symbol_table.FindByNameInAnyPackage(test::ParseNameOrDie("string/foo"));
- ASSERT_THAT(found_string, NotNull());
-
- // Test attr resolution
- const SymbolTable::Symbol* const found_attr =
- symbol_table.FindByNameInAnyPackage(test::ParseNameOrDie("attr/foo"));
- ASSERT_THAT(found_attr, NotNull());
-}
-
} // namespace aapt
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index a07d79f..0564db0 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -81,10 +81,6 @@
return min_sdk_version_;
}
- bool IsAutoNamespace() override {
- return auto_namespace_;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(Context);
@@ -97,7 +93,6 @@
NameMangler name_mangler_;
SymbolTable symbols_;
int min_sdk_version_;
- bool auto_namespace_;
};
class ContextBuilder {
@@ -132,11 +127,6 @@
return *this;
}
- ContextBuilder& SetAutoNamespace(bool auto_namespace) {
- context_->auto_namespace_ = auto_namespace;
- return *this;
- }
-
std::unique_ptr<Context> Build() { return std::move(context_); }
private:
@@ -182,15 +172,6 @@
return nullptr;
}
- std::string GetPackageForSymbol(const ResourceName& name) override {
- for (auto const& imap : name_map_) {
- if (imap.first.type == name.type && imap.first.entry == name.entry) {
- return imap.first.package;
- }
- }
- return "";
- }
-
std::unique_ptr<SymbolTable::Symbol> FindById(ResourceId id) override {
auto iter = id_map_.find(id);
if (iter != id_map_.end()) {
diff --git a/tools/hiddenapi/class2greylist/Android.bp b/tools/hiddenapi/class2greylist/Android.bp
new file mode 100644
index 0000000..7b1233b
--- /dev/null
+++ b/tools/hiddenapi/class2greylist/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+java_library_host {
+ name: "class2greylistlib",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "commons-cli-1.2",
+ "apache-bcel",
+ ],
+}
+
+java_binary_host {
+ name: "class2greylist",
+ manifest: "src/class2greylist.mf",
+ static_libs: [
+ "class2greylistlib",
+ ],
+}
+
diff --git a/tools/hiddenapi/class2greylist/src/class2greylist.mf b/tools/hiddenapi/class2greylist/src/class2greylist.mf
new file mode 100644
index 0000000..ea3a3d9
--- /dev/null
+++ b/tools/hiddenapi/class2greylist/src/class2greylist.mf
@@ -0,0 +1 @@
+Main-Class: com.android.class2greylist.Class2Greylist
diff --git a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
new file mode 100644
index 0000000..6685752
--- /dev/null
+++ b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
@@ -0,0 +1,118 @@
+/*
+ * 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.class2greylist;
+
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.AnnotationEntry;
+import org.apache.bcel.classfile.DescendingVisitor;
+import org.apache.bcel.classfile.ElementValuePair;
+import org.apache.bcel.classfile.EmptyVisitor;
+import org.apache.bcel.classfile.Field;
+import org.apache.bcel.classfile.FieldOrMethod;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+
+import java.util.Locale;
+
+/**
+ * Visits a JavaClass instance and pulls out all members annotated with a
+ * specific annotation. The signatures of such members are passed to {@link
+ * Status#greylistEntry(String)}. Any errors result in a call to {@link
+ * Status#error(String)}.
+ *
+ * If the annotation has a property "expectedSignature" the generated signature
+ * will be verified against the one specified there. If it differs, an error
+ * will be generated.
+ */
+public class AnnotationVisitor extends EmptyVisitor {
+
+ private static final String EXPECTED_SIGNATURE = "expectedSignature";
+
+ private final JavaClass mClass;
+ private final String mAnnotationType;
+ private final Status mStatus;
+ private final DescendingVisitor mDescendingVisitor;
+
+ public AnnotationVisitor(JavaClass clazz, String annotation, Status d) {
+ mClass = clazz;
+ mAnnotationType = annotation;
+ mStatus = d;
+ mDescendingVisitor = new DescendingVisitor(clazz, this);
+ }
+
+ public void visit() {
+ mStatus.debug("Visit class %s", mClass.getClassName());
+ mDescendingVisitor.visit();
+ }
+
+ private static String getClassDescriptor(JavaClass clazz) {
+ // JavaClass.getName() returns the Java-style name (with . not /), so we must fetch
+ // the original class name from the constant pool.
+ return clazz.getConstantPool().getConstantString(
+ clazz.getClassNameIndex(), Const.CONSTANT_Class);
+ }
+
+ @Override
+ public void visitMethod(Method method) {
+ visitMember(method, "L%s;->%s%s");
+ }
+
+ @Override
+ public void visitField(Field field) {
+ visitMember(field, "L%s;->%s:%s");
+ }
+
+ private void visitMember(FieldOrMethod member, String signatureFormatString) {
+ JavaClass definingClass = (JavaClass) mDescendingVisitor.predecessor();
+ mStatus.debug("Visit member %s : %s", member.getName(), member.getSignature());
+ for (AnnotationEntry a : member.getAnnotationEntries()) {
+ if (mAnnotationType.equals(a.getAnnotationType())) {
+ mStatus.debug("Method has annotation %s", mAnnotationType);
+ String signature = String.format(Locale.US, signatureFormatString,
+ getClassDescriptor(definingClass), member.getName(), member.getSignature());
+ for (ElementValuePair property : a.getElementValuePairs()) {
+ switch (property.getNameString()) {
+ case EXPECTED_SIGNATURE:
+ String expected = property.getValue().stringifyValue();
+ if (!signature.equals(expected)) {
+ error(definingClass, member,
+ "Expected signature does not match generated:\n"
+ + "Expected: %s\n"
+ + "Generated: %s", expected, signature);
+ }
+ break;
+ }
+ }
+ mStatus.greylistEntry(signature);
+ }
+ }
+ }
+
+ private void error(JavaClass clazz, FieldOrMethod member, String message, Object... args) {
+ StringBuilder error = new StringBuilder();
+ error.append(clazz.getSourceFileName())
+ .append(": ")
+ .append(clazz.getClassName())
+ .append(".")
+ .append(member.getName())
+ .append(": ")
+ .append(String.format(Locale.US, message, args));
+
+ mStatus.error(error.toString());
+ }
+
+}
diff --git a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Class2Greylist.java
new file mode 100644
index 0000000..bcccf4a
--- /dev/null
+++ b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Class2Greylist.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.class2greylist;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.PatternOptionBuilder;
+
+import java.io.IOException;
+
+/**
+ * Build time tool for extracting a list of members from jar files that have the @UsedByApps
+ * annotation, for building the greylist.
+ */
+public class Class2Greylist {
+
+ private static final String ANNOTATION_TYPE = "Landroid/annotation/UsedByApps;";
+
+ public static void main(String[] args) {
+ Options options = new Options();
+ options.addOption(OptionBuilder
+ .withLongOpt("debug")
+ .hasArgs(0)
+ .withDescription("Enable debug")
+ .create("d"));
+ options.addOption(OptionBuilder
+ .withLongOpt("help")
+ .hasArgs(0)
+ .withDescription("Show this help")
+ .create("h"));
+
+ CommandLineParser parser = new GnuParser();
+ CommandLine cmd;
+
+ try {
+ cmd = parser.parse(options, args);
+ } catch (ParseException e) {
+ System.err.println(e.getMessage());
+ help(options);
+ return;
+ }
+ if (cmd.hasOption('h')) {
+ help(options);
+ }
+
+ String[] jarFiles = cmd.getArgs();
+ if (jarFiles.length == 0) {
+ System.err.println("Error: no jar files specified.");
+ help(options);
+ }
+
+ Status status = new Status(cmd.hasOption('d'));
+
+ for (String jarFile : jarFiles) {
+ status.debug("Processing jar file %s", jarFile);
+ try {
+ JarReader reader = new JarReader(status, jarFile);
+ reader.stream().forEach(clazz -> new AnnotationVisitor(
+ clazz, ANNOTATION_TYPE, status).visit());
+ reader.close();
+ } catch (IOException e) {
+ status.error(e);
+ }
+ }
+ if (status.ok()) {
+ System.exit(0);
+ } else {
+ System.exit(1);
+ }
+
+ }
+
+ private static void help(Options options) {
+ new HelpFormatter().printHelp(
+ "class2greylist path/to/classes.jar [classes2.jar ...]",
+ "Extracts greylist entries from classes jar files given",
+ options, null, true);
+ System.exit(1);
+ }
+}
diff --git a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/JarReader.java b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/JarReader.java
new file mode 100644
index 0000000..f3a9d0b
--- /dev/null
+++ b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/JarReader.java
@@ -0,0 +1,65 @@
+/*
+ * 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.class2greylist;
+
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.JavaClass;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Reads {@link JavaClass} members from a zip/jar file, providing a stream of them for processing.
+ * Any errors are reported via {@link Status#error(Throwable)}.
+ */
+public class JarReader {
+
+ private final Status mStatus;
+ private final String mFileName;
+ private final ZipFile mZipFile;
+
+ public JarReader(Status s, String filename) throws IOException {
+ mStatus = s;
+ mFileName = filename;
+ mZipFile = new ZipFile(mFileName);
+ }
+
+ private JavaClass openZipEntry(ZipEntry e) {
+ try {
+ mStatus.debug("Reading %s from %s", e.getName(), mFileName);
+ return new ClassParser(mZipFile.getInputStream(e), e.getName()).parse();
+ } catch (IOException ioe) {
+ mStatus.error(ioe);
+ return null;
+ }
+ }
+
+
+ public Stream<JavaClass> stream() {
+ return mZipFile.stream()
+ .filter(zipEntry -> zipEntry.getName().endsWith(".class"))
+ .map(zipEntry -> openZipEntry(zipEntry))
+ .filter(Objects::nonNull);
+ }
+
+ public void close() throws IOException {
+ mZipFile.close();
+ }
+}
diff --git a/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Status.java b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Status.java
new file mode 100644
index 0000000..d707898
--- /dev/null
+++ b/tools/hiddenapi/class2greylist/src/com/android/class2greylist/Status.java
@@ -0,0 +1,58 @@
+/*
+ * 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.class2greylist;
+
+import java.util.Locale;
+
+public class Status {
+
+ // Highlight "Error:" in red.
+ private static final String ERROR = "\u001B[31mError: \u001B[0m";
+
+ private final boolean mDebug;
+ private boolean mHasErrors;
+
+ public Status(boolean debug) {
+ mDebug = debug;
+ }
+
+ public void debug(String msg, Object... args) {
+ if (mDebug) {
+ System.err.println(String.format(Locale.US, msg, args));
+ }
+ }
+
+ public void error(Throwable t) {
+ System.err.print(ERROR);
+ t.printStackTrace(System.err);
+ mHasErrors = true;
+ }
+
+ public void error(String message) {
+ System.err.print(ERROR);
+ System.err.println(message);
+ mHasErrors = true;
+ }
+
+ public void greylistEntry(String signature) {
+ System.out.println(signature);
+ }
+
+ public boolean ok() {
+ return !mHasErrors;
+ }
+}
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk b/tools/hiddenapi/class2greylist/test/Android.mk
similarity index 61%
rename from tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
rename to tools/hiddenapi/class2greylist/test/Android.mk
index 5d7a6f7..23f4156 100644
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/Android.mk
+++ b/tools/hiddenapi/class2greylist/test/Android.mk
@@ -1,4 +1,3 @@
-#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,7 +11,22 @@
# 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.
-#
LOCAL_PATH := $(call my-dir)
-include $(call all-makefiles-under,$(LOCAL_PATH))
+
+include $(CLEAR_VARS)
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := class2greylisttest
+
+LOCAL_STATIC_JAVA_LIBRARIES := class2greylistlib truth-host-prebuilt mockito-host junit-host
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := general-tests
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml b/tools/hiddenapi/class2greylist/test/AndroidTest.xml
similarity index 70%
rename from tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml
rename to tools/hiddenapi/class2greylist/test/AndroidTest.xml
index fb20220..66bb6344 100644
--- a/tools/aapt2/integration-tests/AutoNamespaceTest/LibTwo/res/layout/layout_two.xml
+++ b/tools/hiddenapi/class2greylist/test/AndroidTest.xml
@@ -13,6 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<View xmlns:custom="http://schemas.android.com/apk/res-auto"
- custom:StaticLibOne_attr="@string/FooBar" />
+<configuration description="class2greylist tests">
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="class2greylisttest.jar" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tools/hiddenapi/class2greylist/test/src/com/android/javac/AnnotationVisitorTest.java b/tools/hiddenapi/class2greylist/test/src/com/android/javac/AnnotationVisitorTest.java
new file mode 100644
index 0000000..2d97218
--- /dev/null
+++ b/tools/hiddenapi/class2greylist/test/src/com/android/javac/AnnotationVisitorTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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.javac;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.android.class2greylist.Status;
+import com.android.class2greylist.AnnotationVisitor;
+
+import com.google.common.base.Joiner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.io.IOException;
+
+public class AnnotationVisitorTest {
+
+ private static final String ANNOTATION = "Lannotation/Anno;";
+
+ private Javac mJavac;
+ @Mock
+ private Status mStatus;
+
+ @Before
+ public void setup() throws IOException {
+ initMocks(this);
+ mJavac = new Javac();
+ mJavac.addSource("annotation.Anno", Joiner.on('\n').join(
+ "package annotation;",
+ "import static java.lang.annotation.RetentionPolicy.CLASS;",
+ "import java.lang.annotation.Retention;",
+ "import java.lang.annotation.Target;",
+ "@Retention(CLASS)",
+ "public @interface Anno {",
+ " String expectedSignature() default \"\";",
+ "}"));
+ }
+
+ private void assertNoErrors() {
+ verify(mStatus, never()).error(any(Throwable.class));
+ verify(mStatus, never()).error(any(String.class));
+ }
+
+ @Test
+ public void testGreylistMethod() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno",
+ " public void method() {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
+ .visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mStatus, times(1)).greylistEntry(greylist.capture());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
+ }
+
+ @Test
+ public void testGreylistConstructor() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno",
+ " public Class() {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
+ .visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mStatus, times(1)).greylistEntry(greylist.capture());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;-><init>()V");
+ }
+
+ @Test
+ public void testGreylistField() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno",
+ " public int i;",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
+ .visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mStatus, times(1)).greylistEntry(greylist.capture());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;->i:I");
+ }
+
+ @Test
+ public void testGreylistMethodExpectedSignature() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno(expectedSignature=\"La/b/Class;->method()V\")",
+ " public void method() {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
+ .visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mStatus, times(1)).greylistEntry(greylist.capture());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
+ }
+
+ @Test
+ public void testGreylistMethodExpectedSignatureWrong() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno(expectedSignature=\"La/b/Class;->nomethod()V\")",
+ " public void method() {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
+ .visit();
+
+ verify(mStatus, times(1)).error(any(String.class));
+ }
+
+ @Test
+ public void testGreylistInnerClassMethod() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " public class Inner {",
+ " @Anno",
+ " public void method() {}",
+ " }",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class$Inner"), ANNOTATION,
+ mStatus).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mStatus, times(1)).greylistEntry(greylist.capture());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class$Inner;->method()V");
+ }
+
+ @Test
+ public void testMethodNotGreylisted() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "public class Class {",
+ " public void method() {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
+ .visit();
+
+ assertNoErrors();
+ verify(mStatus, never()).greylistEntry(any(String.class));
+ }
+
+}
diff --git a/tools/hiddenapi/class2greylist/test/src/com/android/javac/Javac.java b/tools/hiddenapi/class2greylist/test/src/com/android/javac/Javac.java
new file mode 100644
index 0000000..202f412
--- /dev/null
+++ b/tools/hiddenapi/class2greylist/test/src/com/android/javac/Javac.java
@@ -0,0 +1,103 @@
+/*
+ * 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.javac;
+
+import com.google.common.io.Files;
+
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.JavaClass;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+/**
+ * Helper class for compiling snippets of Java source and providing access to the resulting class
+ * files.
+ */
+public class Javac {
+
+ private final JavaCompiler mJavac;
+ private final StandardJavaFileManager mFileMan;
+ private final List<JavaFileObject> mCompilationUnits;
+ private final File mClassOutDir;
+
+ public Javac() throws IOException {
+ mJavac = ToolProvider.getSystemJavaCompiler();
+ mFileMan = mJavac.getStandardFileManager(null, Locale.US, null);
+ mClassOutDir = Files.createTempDir();
+ mFileMan.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(mClassOutDir));
+ mFileMan.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(mClassOutDir));
+ mCompilationUnits = new ArrayList<>();
+ }
+
+ private String classToFileName(String classname) {
+ return classname.replace('.', '/');
+ }
+
+ public Javac addSource(String classname, String contents) {
+ JavaFileObject java = new SimpleJavaFileObject(URI.create(
+ String.format("string:///%s.java", classToFileName(classname))),
+ JavaFileObject.Kind.SOURCE
+ ){
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+ return contents;
+ }
+ };
+ mCompilationUnits.add(java);
+ return this;
+ }
+
+ public boolean compile() {
+ JavaCompiler.CompilationTask task = mJavac.getTask(
+ null,
+ mFileMan,
+ null,
+ null,
+ null,
+ mCompilationUnits);
+ return task.call();
+ }
+
+ public InputStream getClassFile(String classname) throws IOException {
+ Iterable<? extends JavaFileObject> objs = mFileMan.getJavaFileObjects(
+ new File(mClassOutDir, String.format("%s.class", classToFileName(classname))));
+ if (!objs.iterator().hasNext()) {
+ return null;
+ }
+ return objs.iterator().next().openInputStream();
+ }
+
+ public JavaClass getCompiledClass(String classname) throws IOException {
+ return new ClassParser(getClassFile(classname),
+ String.format("%s.class", classToFileName(classname))).parse();
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6963ed0..922e025 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1585,13 +1585,12 @@
* Return the record of {@link WifiActivityEnergyInfo} object that
* has the activity and energy info. This can be used to ascertain what
* the controller has been up to, since the last sample.
- * @param updateType Type of info, cached vs refreshed.
*
* @return a record with {@link WifiActivityEnergyInfo} or null if
* report is unavailable or unsupported
* @hide
*/
- public WifiActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
+ public WifiActivityEnergyInfo getControllerActivityEnergyInfo() {
if (mService == null) return null;
try {
synchronized(this) {
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 928a1da..8fb7c87 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import android.Manifest;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -721,6 +722,17 @@
}
/**
+ * Enable/Disable wifi scanning.
+ *
+ * {@hide}
+ */
+ @RequiresPermission(Manifest.permission.NETWORK_STACK)
+ public void setScanningEnabled(boolean enable) {
+ validateChannel();
+ mAsyncChannel.sendMessage(enable ? CMD_ENABLE : CMD_DISABLE);
+ }
+
+ /**
* Register a listener that will receive results from all single scans
* Either the onSuccess/onFailure will be called once when the listener is registered. After
* (assuming onSuccess was called) all subsequent single scan results will be delivered to the
@@ -1164,6 +1176,10 @@
public static final int CMD_DEREGISTER_SCAN_LISTENER = BASE + 28;
/** @hide */
public static final int CMD_GET_SINGLE_SCAN_RESULTS = BASE + 29;
+ /** @hide */
+ public static final int CMD_ENABLE = BASE + 30;
+ /** @hide */
+ public static final int CMD_DISABLE = BASE + 31;
private Context mContext;
private IWifiScanner mService;