Merge "Create a util method to get icon badged."
diff --git a/Android.bp b/Android.bp
index 5796fb1..12bc906 100644
--- a/Android.bp
+++ b/Android.bp
@@ -653,6 +653,33 @@
output_extension: "srcjar",
}
+gensrcs {
+ name: "framework-cppstream-protos",
+ depfile: true,
+
+ tools: [
+ "aprotoc",
+ "protoc-gen-cppstream",
+ ],
+
+ cmd: "mkdir -p $(genDir) " +
+ "&& $(location aprotoc) " +
+ " --plugin=$(location protoc-gen-cppstream) " +
+ " --dependency_out=$(depfile) " +
+ " --cppstream_out=$(genDir) " +
+ " -Iexternal/protobuf/src " +
+ " -I . " +
+ " $(in)",
+
+ srcs: [
+ ":ipconnectivity-proto-src",
+ "core/proto/**/*.proto",
+ "libs/incident/**/*.proto",
+ ],
+
+ output_extension: "proto.h",
+}
+
filegroup {
name: "framework-annotations",
srcs: [
@@ -1010,43 +1037,6 @@
},
}
-gensrcs {
- name: "gen-platform-proto-constants",
- depfile: true,
-
- tools: [
- "aprotoc",
- "protoc-gen-cppstream",
- ],
-
- srcs: [
- "core/proto/android/os/backtrace.proto",
- "core/proto/android/os/batterytype.proto",
- "core/proto/android/os/cpufreq.proto",
- "core/proto/android/os/cpuinfo.proto",
- "core/proto/android/os/data.proto",
- "core/proto/android/os/kernelwake.proto",
- "core/proto/android/os/pagetypeinfo.proto",
- "core/proto/android/os/procrank.proto",
- "core/proto/android/os/ps.proto",
- "core/proto/android/os/system_properties.proto",
- "core/proto/android/util/event_log_tags.proto",
- "core/proto/android/util/log.proto",
- ],
-
- // Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
- cmd: "mkdir -p $(genDir) " +
- "&& $(location aprotoc) " +
- " --plugin=$(location protoc-gen-cppstream) " +
- " --dependency_out=$(depfile) " +
- " --cppstream_out=$(genDir) " +
- " -Iexternal/protobuf/src " +
- " -I . " +
- " $(in)",
-
- output_extension: "proto.h",
-}
-
subdirs = [
"cmds/*",
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 0bb07ca..088cadb 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1287,7 +1287,7 @@
* {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor
* for content changes, you need to schedule a new JobInfo observing the same URIs
* before you finish execution of the JobService handling the most recent changes.
- * Following this pattern will ensure you do not lost any content changes: while your
+ * Following this pattern will ensure you do not lose any content changes: while your
* job is running, the system will continue monitoring for content changes, and propagate
* any it sees over to the next job you schedule.</p>
*
diff --git a/apex/sdkextensions/testing/Android.bp b/apex/sdkextensions/testing/Android.bp
index e6451cc..f2f5b32 100644
--- a/apex/sdkextensions/testing/Android.bp
+++ b/apex/sdkextensions/testing/Android.bp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-apex {
+apex_test {
name: "test_com.android.sdkext",
visibility: [ "//system/apex/tests" ],
defaults: ["com.android.sdkext-defaults"],
diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl
index a2564212..253b2c1 100644
--- a/apex/statsd/aidl/android/os/IStatsd.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -222,12 +222,6 @@
const int FLAG_REQUIRE_LOW_LATENCY_MONITOR = 0x04;
/**
- * Logs an event for binary push for module updates.
- */
- oneway void sendBinaryPushStateChangedAtom(in String trainName, in long trainVersionCode,
- in int options, in int state, in long[] experimentId);
-
- /**
* Logs an event for watchdog rollbacks.
*/
oneway void sendWatchdogRollbackOccurredAtom(in int rollbackType, in String packageName,
diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java
index a1de330..411482b 100644
--- a/apex/statsd/framework/java/android/app/StatsManager.java
+++ b/apex/statsd/framework/java/android/app/StatsManager.java
@@ -476,7 +476,7 @@
/**
* Registers a callback for an atom when that atom is to be pulled. The stats service will
* invoke pullData in the callback when the stats service determines that this atom needs to be
- * pulled.
+ * pulled. This method should not be called by third-party apps.
*
* @param atomTag The tag of the atom for this puller callback.
* @param metadata Optional metadata specifying the timeout, cool down time, and
@@ -485,6 +485,7 @@
* @param executor The executor in which to run the callback.
*
*/
+ @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM)
public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata,
@NonNull @CallbackExecutor Executor executor,
@NonNull StatsPullAtomCallback callback) {
@@ -510,11 +511,12 @@
/**
* Unregisters a callback for an atom when that atom is to be pulled. Note that any ongoing
- * pulls will still occur.
+ * pulls will still occur. This method should not be called by third-party apps.
*
* @param atomTag The tag of the atom of which to unregister
*
*/
+ @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM)
public void unregisterPullAtomCallback(int atomTag) {
synchronized (sLock) {
try {
diff --git a/core/java/android/util/StatsLog.java b/apex/statsd/framework/java/android/util/StatsLog.java
similarity index 91%
rename from core/java/android/util/StatsLog.java
rename to apex/statsd/framework/java/android/util/StatsLog.java
index feeff6c..79107379 100644
--- a/core/java/android/util/StatsLog.java
+++ b/apex/statsd/framework/java/android/util/StatsLog.java
@@ -27,6 +27,7 @@
import android.os.IStatsd;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FrameworkStatsLog;
@@ -37,6 +38,7 @@
public final class StatsLog {
private static final String TAG = "StatsLog";
private static final boolean DEBUG = false;
+ private static final int EXPERIMENT_IDS_FIELD_ID = 1;
private static IStatsd sService;
@@ -152,28 +154,25 @@
public static boolean logBinaryPushStateChanged(@NonNull String trainName,
long trainVersionCode, int options, int state,
@NonNull long[] experimentIds) {
- synchronized (sLogLock) {
- try {
- IStatsd service = getIStatsdLocked();
- if (service == null) {
- if (DEBUG) {
- Slog.d(TAG, "Failed to find statsd when logging event");
- }
- return false;
- }
- service.sendBinaryPushStateChangedAtom(
- trainName, trainVersionCode, options, state, experimentIds);
- return true;
- } catch (RemoteException e) {
- sService = null;
- if (DEBUG) {
- Slog.d(TAG,
- "Failed to connect to StatsCompanionService when logging "
- + "BinaryPushStateChanged");
- }
- return false;
- }
+ ProtoOutputStream proto = new ProtoOutputStream();
+ for (long id : experimentIds) {
+ proto.write(
+ ProtoOutputStream.FIELD_TYPE_INT64
+ | ProtoOutputStream.FIELD_COUNT_REPEATED
+ | EXPERIMENT_IDS_FIELD_ID,
+ id);
}
+ FrameworkStatsLog.write(FrameworkStatsLog.BINARY_PUSH_STATE_CHANGED,
+ trainName,
+ trainVersionCode,
+ (options & IStatsd.FLAG_REQUIRE_STAGING) > 0,
+ (options & IStatsd.FLAG_ROLLBACK_ENABLED) > 0,
+ (options & IStatsd.FLAG_REQUIRE_LOW_LATENCY_MONITOR) > 0,
+ state,
+ proto.getBytes(),
+ 0,
+ 0);
+ return true;
}
/**
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
index 4383b50..4495dc9 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
@@ -38,11 +38,15 @@
private static final String TAG = "StatsCompanion";
private static final boolean DEBUG = false;
- static void enforceStatsCompanionPermission(Context context) {
+ private static final int AID_STATSD = 1066;
+
+ static void enforceStatsdCallingUid() {
if (Binder.getCallingPid() == Process.myPid()) {
return;
}
- context.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
+ if (Binder.getCallingUid() != AID_STATSD) {
+ throw new SecurityException("Not allowed to access StatsCompanion");
+ }
}
/**
@@ -114,7 +118,7 @@
@Override
public void sendDataBroadcast(long lastReportTimeNs) {
- enforceStatsCompanionPermission(mContext);
+ enforceStatsdCallingUid();
Intent intent = new Intent();
intent.putExtra(EXTRA_LAST_REPORT_TIME, lastReportTimeNs);
try {
@@ -126,7 +130,7 @@
@Override
public void sendActiveConfigsChangedBroadcast(long[] configIds) {
- enforceStatsCompanionPermission(mContext);
+ enforceStatsdCallingUid();
Intent intent = new Intent();
intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds);
try {
@@ -142,7 +146,7 @@
@Override
public void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
long subscriptionRuleId, String[] cookies, StatsDimensionsValue dimensionsValue) {
- enforceStatsCompanionPermission(mContext);
+ enforceStatsdCallingUid();
Intent intent =
new Intent()
.putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index 3e9a488..a735cb8 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -398,7 +398,7 @@
@Override // Binder call
public void setAnomalyAlarm(long timestampMs) {
- StatsCompanion.enforceStatsCompanionPermission(mContext);
+ StatsCompanion.enforceStatsdCallingUid();
if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
final long callingToken = Binder.clearCallingIdentity();
try {
@@ -414,7 +414,7 @@
@Override // Binder call
public void cancelAnomalyAlarm() {
- StatsCompanion.enforceStatsCompanionPermission(mContext);
+ StatsCompanion.enforceStatsdCallingUid();
if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
final long callingToken = Binder.clearCallingIdentity();
try {
@@ -426,7 +426,7 @@
@Override // Binder call
public void setAlarmForSubscriberTriggering(long timestampMs) {
- StatsCompanion.enforceStatsCompanionPermission(mContext);
+ StatsCompanion.enforceStatsdCallingUid();
if (DEBUG) {
Slog.d(TAG,
"Setting periodic alarm in about " + (timestampMs
@@ -445,7 +445,7 @@
@Override // Binder call
public void cancelAlarmForSubscriberTriggering() {
- StatsCompanion.enforceStatsCompanionPermission(mContext);
+ StatsCompanion.enforceStatsdCallingUid();
if (DEBUG) {
Slog.d(TAG, "Cancelling periodic alarm");
}
@@ -459,7 +459,7 @@
@Override // Binder call
public void setPullingAlarm(long nextPullTimeMs) {
- StatsCompanion.enforceStatsCompanionPermission(mContext);
+ StatsCompanion.enforceStatsdCallingUid();
if (DEBUG) {
Slog.d(TAG, "Setting pulling alarm in about "
+ (nextPullTimeMs - SystemClock.elapsedRealtime()));
@@ -477,7 +477,7 @@
@Override // Binder call
public void cancelPullingAlarm() {
- StatsCompanion.enforceStatsCompanionPermission(mContext);
+ StatsCompanion.enforceStatsdCallingUid();
if (DEBUG) {
Slog.d(TAG, "Cancelling pulling alarm");
}
@@ -491,7 +491,7 @@
@Override // Binder call
public void statsdReady() {
- StatsCompanion.enforceStatsCompanionPermission(mContext);
+ StatsCompanion.enforceStatsdCallingUid();
if (DEBUG) {
Slog.d(TAG, "learned that statsdReady");
}
@@ -503,7 +503,7 @@
@Override
public void triggerUidSnapshot() {
- StatsCompanion.enforceStatsCompanionPermission(mContext);
+ StatsCompanion.enforceStatsdCallingUid();
synchronized (sStatsdLock) {
final long token = Binder.clearCallingIdentity();
try {
@@ -518,7 +518,7 @@
@Override // Binder call
public boolean checkPermission(String permission, int pid, int uid) {
- StatsCompanion.enforceStatsCompanionPermission(mContext);
+ StatsCompanion.enforceStatsdCallingUid();
return mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED;
}
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
index 04d8b00..c1dc584 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -171,8 +171,8 @@
@Override
public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
int[] additiveFields, IPullAtomCallback pullerCallback) {
+ enforceRegisterStatsPullAtomPermission();
int callingUid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
PullerKey key = new PullerKey(callingUid, atomTag);
PullerValue val = new PullerValue(coolDownNs, timeoutNs, additiveFields, pullerCallback);
@@ -187,6 +187,7 @@
return;
}
+ final long token = Binder.clearCallingIdentity();
try {
statsd.registerPullAtomCallback(
callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback);
@@ -199,8 +200,8 @@
@Override
public void unregisterPullAtomCallback(int atomTag) {
+ enforceRegisterStatsPullAtomPermission();
int callingUid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
PullerKey key = new PullerKey(callingUid, atomTag);
// Always remove the puller from StatsManagerService even if statsd is down. When statsd
@@ -214,6 +215,7 @@
return;
}
+ final long token = Binder.clearCallingIdentity();
try {
statsd.unregisterPullAtomCallback(callingUid, atomTag);
} catch (RemoteException e) {
@@ -502,6 +504,13 @@
}
}
+ private void enforceRegisterStatsPullAtomPermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.REGISTER_STATS_PULL_ATOM,
+ "Need REGISTER_STATS_PULL_ATOM permission.");
+ }
+
+
/**
* Clients should call this if blocking until statsd to be ready is desired
*
diff --git a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
index e4ab823..22daa8e 100644
--- a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
+++ b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
@@ -46,15 +46,15 @@
}
}
-static status_pull_atom_return_t pullAtomCallback(int32_t atomTag, pulled_stats_event_list* data,
- void* /*cookie*/) {
+static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag, AStatsEventList* data,
+ void* /*cookie*/) {
sNumPulls++;
sleep_for(std::chrono::milliseconds(sLatencyMillis));
for (int i = 0; i < sAtomsPerPull; i++) {
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event, atomTag);
- stats_event_write_int64(event, (int64_t) sNumPulls);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, atomTag);
+ AStatsEvent_writeInt64(event, (int64_t) sNumPulls);
+ AStatsEvent_build(event);
}
return sPullReturnVal;
}
@@ -71,11 +71,12 @@
sLatencyMillis = latencyMillis;
sAtomsPerPull = atomsPerPull;
sNumPulls = 0;
- pull_atom_metadata metadata = {.cool_down_ns = coolDownNs,
- .timeout_ns = timeoutNs,
- .additive_fields = nullptr,
- .additive_fields_size = 0};
- register_stats_pull_atom_callback(sAtomTag, &pullAtomCallback, &metadata, nullptr);
+ AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
+ AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, coolDownNs);
+ AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, timeoutNs);
+
+ AStatsManager_registerPullAtomCallback(sAtomTag, &pullAtomCallback, metadata, nullptr);
+ AStatsManager_PullAtomMetadata_release(metadata);
}
extern "C"
@@ -83,6 +84,6 @@
Java_com_android_internal_os_statsd_libstats_LibStatsPullTests_unregisterStatsPuller(
JNIEnv* /*env*/, jobject /* this */, jint /*atomTag*/)
{
- unregister_stats_pull_atom_callback(sAtomTag);
+ AStatsManager_unregisterPullAtomCallback(sAtomTag);
}
-} // namespace
\ No newline at end of file
+} // namespace
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
index dbd636d..e119b4c 100644
--- a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
@@ -71,7 +71,6 @@
*/
@Before
public void setup() {
-// Debug.waitForDebugger();
mContext = InstrumentationRegistry.getTargetContext();
assertThat(InstrumentationRegistry.getInstrumentation()).isNotNull();
sPullReturnValue = StatsManager.PULL_SUCCESS;
diff --git a/api/current.txt b/api/current.txt
index 87a5cd7..02fac79 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3850,7 +3850,7 @@
method public void onPerformDirectAction(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.os.Bundle>);
method public void onPictureInPictureModeChanged(boolean, android.content.res.Configuration);
method @Deprecated public void onPictureInPictureModeChanged(boolean);
- method public void onPictureInPictureRequested();
+ method public boolean onPictureInPictureRequested();
method @CallSuper protected void onPostCreate(@Nullable android.os.Bundle);
method public void onPostCreate(@Nullable android.os.Bundle, @Nullable android.os.PersistableBundle);
method @CallSuper protected void onPostResume();
@@ -26799,7 +26799,6 @@
method public abstract void onSelectRoute(@NonNull String, @NonNull String);
method public abstract void onSetVolume(@NonNull String, int);
method public abstract void onTransferToRoute(@NonNull String, @NonNull String);
- method public abstract void onUpdateVolume(@NonNull String, int);
field public static final long REQUEST_ID_UNKNOWN = 0L; // 0x0L
field public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
}
@@ -28772,6 +28771,7 @@
field public static final String COLUMN_DESCRIPTION = "description";
field public static final String COLUMN_DISPLAY_NAME = "display_name";
field public static final String COLUMN_DISPLAY_NUMBER = "display_number";
+ field public static final String COLUMN_GLOBAL_CONTENT_ID = "global_content_id";
field public static final String COLUMN_INPUT_ID = "input_id";
field public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
@@ -28797,6 +28797,7 @@
field public static final String SERVICE_TYPE_AUDIO_VIDEO = "SERVICE_TYPE_AUDIO_VIDEO";
field public static final String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
field public static final String TYPE_1SEG = "TYPE_1SEG";
+ field public static final String TYPE_ATSC3_T = "TYPE_ATSC3_T";
field public static final String TYPE_ATSC_C = "TYPE_ATSC_C";
field public static final String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
field public static final String TYPE_ATSC_T = "TYPE_ATSC_T";
@@ -28890,6 +28891,7 @@
field public static final String COLUMN_SEASON_TITLE = "season_title";
field public static final String COLUMN_SERIES_ID = "series_id";
field public static final String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final String COLUMN_SPLIT_ID = "split_id";
field public static final String COLUMN_STARTING_PRICE = "starting_price";
field public static final String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
field public static final String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
@@ -28937,6 +28939,8 @@
field public static final String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
field @Deprecated public static final String COLUMN_EPISODE_NUMBER = "episode_number";
field public static final String COLUMN_EPISODE_TITLE = "episode_title";
+ field public static final String COLUMN_EVENT_ID = "event_id";
+ field public static final String COLUMN_GLOBAL_CONTENT_ID = "global_content_id";
field public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
@@ -28953,6 +28957,7 @@
field public static final String COLUMN_SEASON_TITLE = "season_title";
field public static final String COLUMN_SERIES_ID = "series_id";
field public static final String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final String COLUMN_SPLIT_ID = "split_id";
field public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
field public static final String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
field public static final String COLUMN_TITLE = "title";
@@ -29018,6 +29023,7 @@
field public static final String COLUMN_SEASON_TITLE = "season_title";
field public static final String COLUMN_SERIES_ID = "series_id";
field public static final String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final String COLUMN_SPLIT_ID = "split_id";
field public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
field public static final String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
field public static final String COLUMN_TITLE = "title";
@@ -29078,6 +29084,7 @@
field public static final String COLUMN_SEASON_TITLE = "season_title";
field public static final String COLUMN_SERIES_ID = "series_id";
field public static final String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final String COLUMN_SPLIT_ID = "split_id";
field public static final String COLUMN_STARTING_PRICE = "starting_price";
field public static final String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
field public static final String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
diff --git a/api/system-current.txt b/api/system-current.txt
index 1128556..7903fba 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -188,6 +188,7 @@
field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
+ field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM";
field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
field public static final String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
@@ -692,7 +693,7 @@
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] getRegisteredExperimentIds() throws android.app.StatsManager.StatsUnavailableException;
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException;
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException;
- method public void registerPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback);
+ method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void registerPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback);
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException;
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long);
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException;
@@ -700,7 +701,7 @@
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent);
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException;
- method public void unregisterPullAtomCallback(int);
+ method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void unregisterPullAtomCallback(int);
field public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
field public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = "android.app.extra.STATS_ACTIVE_CONFIG_KEYS";
field public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES";
@@ -1472,9 +1473,9 @@
public final class BluetoothAdapter {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice);
method public boolean disableBLE();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice);
method public boolean enableBLE();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset();
@@ -2098,6 +2099,10 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.AppUsageLimit> CREATOR;
}
+ public static class LauncherApps.ShortcutQuery {
+ method @NonNull public android.content.pm.LauncherApps.ShortcutQuery setLocusIds(@Nullable java.util.List<android.content.LocusId>);
+ }
+
public class PackageInstaller {
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2
@@ -2201,6 +2206,8 @@
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
+ field public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE = 131072; // 0x20000
+ field public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET = 262144; // 0x40000
field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000
field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000
@@ -2284,7 +2291,7 @@
method public void onPermissionsChanged(int);
}
- @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
+ @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME, android.content.pm.PackageManager.FLAG_PERMISSION_DONT_AUTO_REVOKE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
}
public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -4317,7 +4324,7 @@
}
public final class AudioRecordingConfiguration implements android.os.Parcelable {
- method public int getClientUid();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getClientUid();
}
public class HwAudioSource {
@@ -7517,9 +7524,6 @@
method @Deprecated public boolean isNoInternetAccessExpected();
method @Deprecated public void setIpConfiguration(@Nullable android.net.IpConfiguration);
method @Deprecated public void setNetworkSelectionStatus(@NonNull android.net.wifi.WifiConfiguration.NetworkSelectionStatus);
- field @Deprecated public static final int AP_BAND_2GHZ = 0; // 0x0
- field @Deprecated public static final int AP_BAND_5GHZ = 1; // 0x1
- field @Deprecated public static final int AP_BAND_ANY = -1; // 0xffffffff
field @Deprecated public static final int INVALID_NETWORK_ID = -1; // 0xffffffff
field @Deprecated public static final int METERED_OVERRIDE_METERED = 1; // 0x1
field @Deprecated public static final int METERED_OVERRIDE_NONE = 0; // 0x0
@@ -7529,7 +7533,6 @@
field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0
field @Deprecated public boolean allowAutojoin;
- field @Deprecated public int apBand;
field @Deprecated public int carrierId;
field @Deprecated public String creatorName;
field @Deprecated public int creatorUid;
@@ -8199,7 +8202,7 @@
ctor public NativeScanResult();
method public int describeContents();
method @NonNull public byte[] getBssid();
- method @NonNull public java.util.BitSet getCapabilities();
+ method @NonNull public int getCapabilities();
method public int getFrequencyMhz();
method @NonNull public byte[] getInformationElements();
method @NonNull public java.util.List<android.net.wifi.wificond.RadioChainInfo> getRadioChainInfos();
@@ -8235,12 +8238,12 @@
public final class PnoSettings implements android.os.Parcelable {
ctor public PnoSettings();
method public int describeContents();
- method public int getIntervalMillis();
+ method public long getIntervalMillis();
method public int getMin2gRssiDbm();
method public int getMin5gRssiDbm();
method public int getMin6gRssiDbm();
method @NonNull public java.util.List<android.net.wifi.wificond.PnoNetwork> getPnoNetworks();
- method public void setIntervalMillis(int);
+ method public void setIntervalMillis(long);
method public void setMin2gRssiDbm(int);
method public void setMin5gRssiDbm(int);
method public void setMin6gRssiDbm(int);
@@ -8265,10 +8268,10 @@
method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
- method public boolean initialize(@NonNull Runnable);
method @Nullable public static android.net.wifi.wificond.WifiCondManager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SoftApCallback);
method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SendMgmtFrameCallback);
+ method public void setOnServiceDeadCallback(@NonNull Runnable);
method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback);
method public boolean setupInterfaceForSoftApMode(@NonNull String);
method @Nullable public android.net.wifi.wificond.WifiCondManager.SignalPollResult signalPoll(@NonNull String);
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 2f1889c..0caee6b 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -64,10 +64,6 @@
-HeavyBitSet: android.net.wifi.wificond.NativeScanResult#getCapabilities():
-
-
-
IntentBuilderName: android.content.Context#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler):
Methods creating an Intent should be named `create<Foo>Intent()`, was `registerReceiverForAllUsers`
@@ -200,8 +196,6 @@
MutableBareField: android.net.wifi.WifiConfiguration#allowAutojoin:
-MutableBareField: android.net.wifi.WifiConfiguration#apBand:
-
MutableBareField: android.net.wifi.WifiConfiguration#carrierId:
MutableBareField: android.net.wifi.WifiConfiguration#fromWifiNetworkSpecifier:
diff --git a/api/test-current.txt b/api/test-current.txt
index 5fc35a2..d3e7ea1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -886,6 +886,7 @@
method public abstract boolean arePermissionsIndividuallyControlled();
method @Nullable public String getContentCaptureServicePackageName();
method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract String getDefaultBrowserPackageNameAsUser(int);
+ method @Nullable public String getDefaultTextClassifierPackageName();
method @Nullable public String getIncidentReportApproverPackageName();
method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
@@ -895,6 +896,7 @@
method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
+ method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String[] getTelephonyPackageNames();
method @Nullable public String getWellbeingPackageName();
method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
diff --git a/cmds/incident/Android.bp b/cmds/incident/Android.bp
index 9e9dac1..94855aa 100644
--- a/cmds/incident/Android.bp
+++ b/cmds/incident/Android.bp
@@ -26,7 +26,7 @@
"libcutils",
"liblog",
"libutils",
- "libincident",
+ "libincidentpriv",
],
static_libs: [
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index 64f4c66..f07743e 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -44,7 +44,7 @@
"src/ih_util.cpp",
],
- generated_headers: ["gen-platform-proto-constants"],
+ generated_headers: ["framework-cppstream-protos"],
shared_libs: [
"libbase",
diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp
index 25e0328..c47526a 100644
--- a/cmds/incidentd/Android.bp
+++ b/cmds/incidentd/Android.bp
@@ -43,7 +43,7 @@
],
local_include_dirs: ["src"],
- generated_headers: ["gen-platform-proto-constants"],
+ generated_headers: ["framework-cppstream-protos"],
proto: {
type: "lite",
@@ -54,7 +54,7 @@
"libbinder",
"libdebuggerd_client",
"libdumputils",
- "libincident",
+ "libincidentpriv",
"liblog",
"libprotoutil",
"libservices",
@@ -98,7 +98,7 @@
],
local_include_dirs: ["src"],
- generated_headers: ["gen-platform-proto-constants"],
+ generated_headers: ["framework-cppstream-protos"],
srcs: [
"tests/**/*.cpp",
@@ -128,7 +128,7 @@
"libbinder",
"libdebuggerd_client",
"libdumputils",
- "libincident",
+ "libincidentpriv",
"liblog",
"libprotobuf-cpp-full",
"libprotoutil",
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 2237bf2..b31fe62 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -64,7 +64,7 @@
"src/config/ConfigKey.cpp",
"src/config/ConfigListener.cpp",
"src/config/ConfigManager.cpp",
- "src/external/GpuStatsPuller.cpp",
+ "src/experiment_ids.proto",
"src/external/Perfetto.cpp",
"src/external/PullResultReceiver.cpp",
"src/external/puller_util.cpp",
@@ -110,7 +110,7 @@
],
cflags: [
- // "-DNEW_ENCODING_SCHEME",
+ "-DNEW_ENCODING_SCHEME",
],
local_include_dirs: [
@@ -121,19 +121,18 @@
"android.frameworks.stats@1.0",
"libbase",
"libcutils",
- "liblog",
"libprotoutil",
"libstatslog",
+ "libstatsmetadata",
"libstatssocket",
"libsysutils",
],
shared_libs: [
"libbinder",
- "libgraphicsenv",
"libhidlbase",
"libincident",
+ "liblog",
"libservices",
- "libstatsmetadata",
"libutils",
],
}
@@ -160,7 +159,7 @@
],
}
-cc_library_shared {
+cc_library_static {
name: "libstatsmetadata",
host_supported: true,
generated_sources: [
@@ -277,8 +276,6 @@
"tests/e2e/PartialBucket_e2e_test.cpp",
"tests/e2e/ValueMetric_pull_e2e_test.cpp",
"tests/e2e/WakelockDuration_e2e_test.cpp",
- "tests/external/GpuStatsPuller_test.cpp",
- "tests/external/IncidentReportArgs_test.cpp",
"tests/external/puller_util_test.cpp",
"tests/external/StatsCallbackPuller_test.cpp",
"tests/external/StatsPuller_test.cpp",
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
index 30dfe32..8b68743 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -23,14 +23,14 @@
namespace statsd {
static size_t createAndParseStatsEvent(uint8_t* msg) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
- stats_event_write_int32(event, 2);
- stats_event_write_float(event, 2.0);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ AStatsEvent_writeInt32(event, 2);
+ AStatsEvent_writeFloat(event, 2.0);
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
memcpy(msg, buf, size);
return size;
}
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 879b3c3..bde15a5 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -20,13 +20,17 @@
#include "StatsLogProcessor.h"
#include <android-base/file.h>
+#include <cutils/multiuser.h>
#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
+#include <frameworks/base/cmds/statsd/src/experiment_ids.pb.h>
#include "android-base/stringprintf.h"
#include "atoms_info.h"
#include "external/StatsPullerManager.h"
#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
#include "metrics/CountMetricProducer.h"
+#include "StatsService.h"
#include "state/StateManager.h"
#include "stats_log_util.h"
#include "stats_util.h"
@@ -68,6 +72,10 @@
// for ActiveConfigList
const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1;
+// for permissions checks
+constexpr const char* kPermissionDump = "android.permission.DUMP";
+constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
+
#define NS_PER_HOUR 3600 * NS_PER_SEC
#define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric"
@@ -181,6 +189,115 @@
}
}
+void StatsLogProcessor::onBinaryPushStateChangedEventLocked(LogEvent* event) {
+ pid_t pid = event->GetPid();
+ uid_t uid = event->GetUid();
+ if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
+ !checkPermissionForIds(kPermissionUsage, pid, uid)) {
+ return;
+ }
+ status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR, err4 = NO_ERROR;
+ string trainName = string(event->GetString(1 /*train name field id*/, &err));
+ int64_t trainVersionCode = event->GetLong(2 /*train version field id*/, &err2);
+ int32_t state = int32_t(event->GetLong(6 /*state field id*/, &err3));
+#ifdef NEW_ENCODING_SCHEME
+ std::vector<uint8_t> trainExperimentIdBytes =
+ event->GetStorage(7 /*experiment ids field id*/, &err4);
+#else
+ string trainExperimentIdString = event->GetString(7 /*experiment ids field id*/, &err4);
+#endif
+ if (err != NO_ERROR || err2 != NO_ERROR || err3 != NO_ERROR || err4 != NO_ERROR) {
+ ALOGE("Failed to parse fields in binary push state changed log event");
+ return;
+ }
+ ExperimentIds trainExperimentIds;
+#ifdef NEW_ENCODING_SCHEME
+ if (!trainExperimentIds.ParseFromArray(trainExperimentIdBytes.data(),
+ trainExperimentIdBytes.size())) {
+#else
+ if (!trainExperimentIds.ParseFromString(trainExperimentIdString)) {
+#endif
+ ALOGE("Failed to parse experimentids in binary push state changed.");
+ return;
+ }
+ vector<int64_t> experimentIdVector = {trainExperimentIds.experiment_id().begin(),
+ trainExperimentIds.experiment_id().end()};
+ // Update the train info on disk and get any data the logevent is missing.
+ getAndUpdateTrainInfoOnDisk(
+ state, &trainVersionCode, &trainName, &experimentIdVector);
+
+ std::vector<uint8_t> trainExperimentIdProto;
+ writeExperimentIdsToProto(experimentIdVector, &trainExperimentIdProto);
+ int32_t userId = multiuser_get_user_id(uid);
+
+ event->updateValue(1 /*train name field id*/, trainName, STRING);
+ event->updateValue(2 /*train version field id*/, trainVersionCode, LONG);
+#ifdef NEW_ENCODING_SCHEME
+ event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STORAGE);
+#else
+ event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STRING);
+#endif
+ event->updateValue(8 /*user id field id*/, userId, INT);
+}
+
+void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(int32_t state,
+ int64_t* trainVersionCode,
+ string* trainName,
+ std::vector<int64_t>* experimentIds) {
+ bool readTrainInfoSuccess = false;
+ InstallTrainInfo trainInfoOnDisk;
+ readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
+
+ bool resetExperimentIds = false;
+ if (readTrainInfoSuccess) {
+ // Keep the old train version if we received an empty version.
+ if (*trainVersionCode == -1) {
+ *trainVersionCode = trainInfoOnDisk.trainVersionCode;
+ } else if (*trainVersionCode != trainInfoOnDisk.trainVersionCode) {
+ // Reset experiment ids if we receive a new non-empty train version.
+ resetExperimentIds = true;
+ }
+
+ // Keep the old train name if we received an empty train name.
+ if (trainName->size() == 0) {
+ *trainName = trainInfoOnDisk.trainName;
+ } else if (*trainName != trainInfoOnDisk.trainName) {
+ // Reset experiment ids if we received a new valid train name.
+ resetExperimentIds = true;
+ }
+
+ // Reset if we received a different experiment id.
+ if (!experimentIds->empty() &&
+ (trainInfoOnDisk.experimentIds.empty() ||
+ experimentIds->at(0) != trainInfoOnDisk.experimentIds[0])) {
+ resetExperimentIds = true;
+ }
+ }
+
+ // Find the right experiment IDs
+ if (!resetExperimentIds && readTrainInfoSuccess) {
+ *experimentIds = trainInfoOnDisk.experimentIds;
+ }
+
+ if (!experimentIds->empty()) {
+ int64_t firstId = experimentIds->at(0);
+ switch (state) {
+ case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
+ experimentIds->push_back(firstId + 1);
+ break;
+ case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
+ experimentIds->push_back(firstId + 2);
+ break;
+ case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
+ experimentIds->push_back(firstId + 3);
+ break;
+ }
+ }
+
+ StorageManager::writeTrainInfo(*trainVersionCode, *trainName, state, *experimentIds);
+}
+
+
void StatsLogProcessor::resetConfigs() {
std::lock_guard<std::mutex> lock(mMetricsMutex);
resetConfigsLocked(getElapsedRealtimeNs());
@@ -201,6 +318,12 @@
void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
+ // Hard-coded logic to update train info on disk and fill in any information
+ // this log event may be missing.
+ if (event->GetTagId() == android::util::BINARY_PUSH_STATE_CHANGED) {
+ onBinaryPushStateChangedEventLocked(event);
+ }
+
#ifdef VERY_VERBOSE_PRINTING
if (mPrintAllLogs) {
ALOGI("%s", event->ToString().c_str());
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index c569bc1..c49f2e0 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -196,6 +196,14 @@
// Handler over the isolated uid change event.
void onIsolatedUidChangedEventLocked(const LogEvent& event);
+ // Handler over the binary push state changed event.
+ void onBinaryPushStateChangedEventLocked(LogEvent* event);
+
+ // Updates train info on disk based on binary push state changed info and
+ // write disk info into parameters.
+ void getAndUpdateTrainInfoOnDisk(int32_t state, int64_t* trainVersionCode,
+ string* trainName, std::vector<int64_t>* experimentIds);
+
// Reset all configs.
void resetConfigsLocked(const int64_t timestampNs);
// Reset the specified configs.
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 8a8c1e6..0256e36 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -70,25 +70,12 @@
return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
}
-
static bool checkPermission(const char* permission) {
- sp<IStatsCompanionService> scs = getStatsCompanionService();
- if (scs == nullptr) {
- return false;
- }
-
- bool success;
pid_t pid = IPCThreadState::self()->getCallingPid();
uid_t uid = IPCThreadState::self()->getCallingUid();
-
- binder::Status status = scs->checkPermission(String16(permission), pid, uid, &success);
- if (!status.isOk()) {
- return false;
- }
- return success;
+ return checkPermissionForIds(permission, pid, uid);
}
-
binder::Status checkUid(uid_t expectedUid) {
uid_t uid = IPCThreadState::self()->getCallingUid();
if (uid == expectedUid || uid == AID_ROOT) {
@@ -870,18 +857,8 @@
dprintf(out, "Incorrect number of argument supplied\n");
return UNKNOWN_ERROR;
}
- android::String16 trainName = android::String16(args[1].c_str());
+ string trainName = string(args[1].c_str());
int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10);
- int options = 0;
- if (args[3] == "1") {
- options = options | IStatsd::FLAG_REQUIRE_STAGING;
- }
- if (args[4] == "1") {
- options = options | IStatsd::FLAG_ROLLBACK_ENABLED;
- }
- if (args[5] == "1") {
- options = options | IStatsd::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
- }
int32_t state = atoi(args[6].c_str());
vector<int64_t> experimentIds;
if (argCount == 8) {
@@ -892,7 +869,10 @@
}
}
dprintf(out, "Logging BinaryPushStateChanged\n");
- sendBinaryPushStateChangedAtom(trainName, trainVersion, options, state, experimentIds);
+ vector<uint8_t> experimentIdBytes;
+ writeExperimentIdsToProto(experimentIds, &experimentIdBytes);
+ LogEvent event(trainName, trainVersion, args[3], args[4], args[5], state, experimentIdBytes, 0);
+ mProcessor->OnLogEvent(&event);
return NO_ERROR;
}
@@ -1313,101 +1293,6 @@
return Status::ok();
}
-Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainNameIn,
- const int64_t trainVersionCodeIn,
- const int options,
- const int32_t state,
- const std::vector<int64_t>& experimentIdsIn) {
- // Note: We skip the usage stats op check here since we do not have a package name.
- // This is ok since we are overloading the usage_stats permission.
- // This method only sends data, it does not receive it.
- pid_t pid = IPCThreadState::self()->getCallingPid();
- uid_t uid = IPCThreadState::self()->getCallingUid();
- // Root, system, and shell always have access
- if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
- // Caller must be granted these permissions
- if (!checkPermission(kPermissionDump)) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
- kPermissionDump));
- }
- if (!checkPermission(kPermissionUsage)) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
- kPermissionUsage));
- }
- }
-
- bool readTrainInfoSuccess = false;
- InstallTrainInfo trainInfoOnDisk;
- readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
-
- bool resetExperimentIds = false;
- int64_t trainVersionCode = trainVersionCodeIn;
- std::string trainNameUtf8 = std::string(String8(trainNameIn).string());
- if (readTrainInfoSuccess) {
- // Keep the old train version if we received an empty version.
- if (trainVersionCodeIn == -1) {
- trainVersionCode = trainInfoOnDisk.trainVersionCode;
- } else if (trainVersionCodeIn != trainInfoOnDisk.trainVersionCode) {
- // Reset experiment ids if we receive a new non-empty train version.
- resetExperimentIds = true;
- }
-
- // Keep the old train name if we received an empty train name.
- if (trainNameUtf8.size() == 0) {
- trainNameUtf8 = trainInfoOnDisk.trainName;
- } else if (trainNameUtf8 != trainInfoOnDisk.trainName) {
- // Reset experiment ids if we received a new valid train name.
- resetExperimentIds = true;
- }
-
- // Reset if we received a different experiment id.
- if (!experimentIdsIn.empty() &&
- (trainInfoOnDisk.experimentIds.empty() ||
- experimentIdsIn[0] != trainInfoOnDisk.experimentIds[0])) {
- resetExperimentIds = true;
- }
- }
-
- // Find the right experiment IDs
- std::vector<int64_t> experimentIds;
- if (resetExperimentIds || !readTrainInfoSuccess) {
- experimentIds = experimentIdsIn;
- } else {
- experimentIds = trainInfoOnDisk.experimentIds;
- }
-
- if (!experimentIds.empty()) {
- int64_t firstId = experimentIds[0];
- switch (state) {
- case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
- experimentIds.push_back(firstId + 1);
- break;
- case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
- experimentIds.push_back(firstId + 2);
- break;
- case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
- experimentIds.push_back(firstId + 3);
- break;
- }
- }
-
- // Flatten the experiment IDs to proto
- vector<uint8_t> experimentIdsProtoBuffer;
- writeExperimentIdsToProto(experimentIds, &experimentIdsProtoBuffer);
- StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIds);
-
- userid_t userId = multiuser_get_user_id(uid);
- bool requiresStaging = options & IStatsd::FLAG_REQUIRE_STAGING;
- bool rollbackEnabled = options & IStatsd::FLAG_ROLLBACK_ENABLED;
- bool requiresLowLatencyMonitor = options & IStatsd::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
- LogEvent event(trainNameUtf8, trainVersionCode, requiresStaging, rollbackEnabled,
- requiresLowLatencyMonitor, state, experimentIdsProtoBuffer, userId);
- mProcessor->OnLogEvent(&event);
- return Status::ok();
-}
-
Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackTypeIn,
const android::String16& packageNameIn,
const int64_t packageVersionCodeIn,
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 3bfaa98..af3016f 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -193,16 +193,6 @@
virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override;
/**
- * Binder call to log BinaryPushStateChanged atom.
- */
- virtual Status sendBinaryPushStateChangedAtom(
- const android::String16& trainNameIn,
- const int64_t trainVersionCodeIn,
- const int options,
- const int32_t state,
- const std::vector<int64_t>& experimentIdsIn) override;
-
- /**
* Binder call to log WatchdogRollbackOccurred atom.
*/
virtual Status sendWatchdogRollbackOccurredAtom(
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index fccefdc..183d741 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -202,7 +202,8 @@
DocsUIStartupMsReported docs_ui_startup_ms = 111 [(module) = "docsui"];
DocsUIUserActionReported docs_ui_user_action_reported = 112 [(module) = "docsui"];
WifiEnabledStateChanged wifi_enabled_state_changed = 113 [(module) = "framework"];
- WifiRunningStateChanged wifi_running_state_changed = 114 [(module) = "framework"];
+ WifiRunningStateChanged wifi_running_state_changed = 114
+ [(module) = "framework", deprecated = true];
AppCompacted app_compacted = 115 [(module) = "framework"];
NetworkDnsEventReported network_dns_event_reported = 116 [(module) = "resolv"];
DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported =
@@ -1258,6 +1259,8 @@
}
/**
+ * This atom is deprecated starting in R.
+ *
* Logs when an app causes Wifi to run. In this context, 'to run' means to use Wifi Client Mode.
* TODO: Include support for Hotspot, perhaps by using an extra field to denote 'mode'.
* Note that Wifi Scanning is monitored separately in WifiScanStateChanged.
@@ -3706,6 +3709,7 @@
/**
* Potential experiment ids that goes with a train install.
+ * Should be kept in sync with experiment_ids.proto.
*/
message TrainExperimentIds {
repeated int64 experiment_id = 1;
diff --git a/cmds/statsd/src/experiment_ids.proto b/cmds/statsd/src/experiment_ids.proto
new file mode 100644
index 0000000..c203631
--- /dev/null
+++ b/cmds/statsd/src/experiment_ids.proto
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd;
+
+option java_package = "com.android.internal.os";
+option java_outer_classname = "ExperimentIdsProto";
+
+// StatsLogProcessor uses the proto to parse experiment ids from
+// BinaryPushStateChanged atoms. This needs to be in sync with
+// TrainExperimentIds in atoms.proto.
+message ExperimentIds {
+ repeated int64 experiment_id = 1;
+}
diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp
deleted file mode 100644
index 3229ba8..0000000
--- a/cmds/statsd/src/external/GpuStatsPuller.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#include "GpuStatsPuller.h"
-
-#include <binder/IServiceManager.h>
-#include <graphicsenv/GpuStatsInfo.h>
-#include <graphicsenv/IGpuService.h>
-
-#include "logd/LogEvent.h"
-
-#include "stats_log_util.h"
-#include "statslog.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::util::ProtoReader;
-
-GpuStatsPuller::GpuStatsPuller(const int tagId) : StatsPuller(tagId) {
-}
-
-static sp<IGpuService> getGpuService() {
- const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
- if (!binder) {
- ALOGE("Failed to get gpu service");
- return nullptr;
- }
-
- return interface_cast<IGpuService>(binder);
-}
-
-static bool pullGpuStatsGlobalInfo(const sp<IGpuService>& gpuService,
- std::vector<std::shared_ptr<LogEvent>>* data) {
- std::vector<GpuStatsGlobalInfo> stats;
- status_t status = gpuService->getGpuStatsGlobalInfo(&stats);
- if (status != OK) {
- return false;
- }
-
- data->clear();
- data->reserve(stats.size());
- for (const auto& info : stats) {
- std::shared_ptr<LogEvent> event = make_shared<LogEvent>(
- android::util::GPU_STATS_GLOBAL_INFO, getWallClockNs(), getElapsedRealtimeNs());
- if (!event->write(info.driverPackageName)) return false;
- if (!event->write(info.driverVersionName)) return false;
- if (!event->write((int64_t)info.driverVersionCode)) return false;
- if (!event->write(info.driverBuildTime)) return false;
- if (!event->write((int64_t)info.glLoadingCount)) return false;
- if (!event->write((int64_t)info.glLoadingFailureCount)) return false;
- if (!event->write((int64_t)info.vkLoadingCount)) return false;
- if (!event->write((int64_t)info.vkLoadingFailureCount)) return false;
- if (!event->write(info.vulkanVersion)) return false;
- if (!event->write(info.cpuVulkanVersion)) return false;
- if (!event->write(info.glesVersion)) return false;
- if (!event->write((int64_t)info.angleLoadingCount)) return false;
- if (!event->write((int64_t)info.angleLoadingFailureCount)) return false;
- event->init();
- data->emplace_back(event);
- }
-
- return true;
-}
-
-static bool pullGpuStatsAppInfo(const sp<IGpuService>& gpuService,
- std::vector<std::shared_ptr<LogEvent>>* data) {
- std::vector<GpuStatsAppInfo> stats;
- status_t status = gpuService->getGpuStatsAppInfo(&stats);
- if (status != OK) {
- return false;
- }
-
- data->clear();
- data->reserve(stats.size());
- for (const auto& info : stats) {
- std::shared_ptr<LogEvent> event = make_shared<LogEvent>(
- android::util::GPU_STATS_APP_INFO, getWallClockNs(), getElapsedRealtimeNs());
- if (!event->write(info.appPackageName)) return false;
- if (!event->write((int64_t)info.driverVersionCode)) return false;
- if (!event->writeBytes(int64VectorToProtoByteString(info.glDriverLoadingTime))) {
- return false;
- }
- if (!event->writeBytes(int64VectorToProtoByteString(info.vkDriverLoadingTime))) {
- return false;
- }
- if (!event->writeBytes(int64VectorToProtoByteString(info.angleDriverLoadingTime))) {
- return false;
- }
- if (!event->write(info.cpuVulkanInUse)) return false;
- if (!event->write(info.falsePrerotation)) return false;
- if (!event->write(info.gles1InUse)) return false;
- event->init();
- data->emplace_back(event);
- }
-
- return true;
-}
-
-bool GpuStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) {
- const sp<IGpuService> gpuService = getGpuService();
- if (!gpuService) {
- return false;
- }
-
- switch (mTagId) {
- case android::util::GPU_STATS_GLOBAL_INFO:
- return pullGpuStatsGlobalInfo(gpuService, data);
- case android::util::GPU_STATS_APP_INFO:
- return pullGpuStatsAppInfo(gpuService, data);
- default:
- break;
- }
-
- return false;
-}
-
-static std::string protoOutputStreamToByteString(ProtoOutputStream& proto) {
- if (!proto.size()) return "";
-
- std::string byteString;
- sp<ProtoReader> reader = proto.data();
- while (reader->readBuffer() != nullptr) {
- const size_t toRead = reader->currentToRead();
- byteString.append((char*)reader->readBuffer(), toRead);
- reader->move(toRead);
- }
-
- if (byteString.size() != proto.size()) return "";
-
- return byteString;
-}
-
-std::string int64VectorToProtoByteString(const std::vector<int64_t>& value) {
- if (value.empty()) return "";
-
- ProtoOutputStream proto;
- for (const auto& ele : value) {
- proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
- 1 /* field id */,
- (long long)ele);
- }
-
- return protoOutputStreamToByteString(proto);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/GpuStatsPuller.h b/cmds/statsd/src/external/GpuStatsPuller.h
deleted file mode 100644
index 2da199c..0000000
--- a/cmds/statsd/src/external/GpuStatsPuller.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Pull GpuStats from GpuService.
- */
-class GpuStatsPuller : public StatsPuller {
-public:
- explicit GpuStatsPuller(const int tagId);
- bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override;
-};
-
-// convert a int64_t vector into a byte string for proto message like:
-// message RepeatedInt64Wrapper {
-// repeated int64 value = 1;
-// }
-std::string int64VectorToProtoByteString(const std::vector<int64_t>& value);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 15d7e33..3ceff75 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -32,7 +32,6 @@
#include "../logd/LogEvent.h"
#include "../stats_log_util.h"
#include "../statscompanion_util.h"
-#include "GpuStatsPuller.h"
#include "StatsCallbackPuller.h"
#include "TrainInfoPuller.h"
#include "statslog.h"
@@ -51,15 +50,6 @@
: kAllPullAtomInfo({
// TrainInfo.
{{.atomTag = android::util::TRAIN_INFO}, new TrainInfoPuller()},
-
- // GpuStatsGlobalInfo
- {{.atomTag = android::util::GPU_STATS_GLOBAL_INFO},
- new GpuStatsPuller(android::util::GPU_STATS_GLOBAL_INFO)},
-
- // GpuStatsAppInfo
- {{.atomTag = android::util::GPU_STATS_APP_INFO},
- new GpuStatsPuller(android::util::GPU_STATS_APP_INFO)},
-
}),
mNextPullTimeNs(NO_ALARM_UPDATE) {
}
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 9a0693a..e8fc603 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -35,6 +35,34 @@
using std::string;
using std::vector;
+// stats_event.h socket types. Keep in sync.
+/* ERRORS */
+#define ERROR_NO_TIMESTAMP 0x1
+#define ERROR_NO_ATOM_ID 0x2
+#define ERROR_OVERFLOW 0x4
+#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
+#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
+#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
+#define ERROR_INVALID_ANNOTATION_ID 0x40
+#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
+#define ERROR_TOO_MANY_ANNOTATIONS 0x100
+#define ERROR_TOO_MANY_FIELDS 0x200
+#define ERROR_INVALID_VALUE_TYPE 0x400
+#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
+
+/* TYPE IDS */
+#define INT32_TYPE 0x00
+#define INT64_TYPE 0x01
+#define STRING_TYPE 0x02
+#define LIST_TYPE 0x03
+#define FLOAT_TYPE 0x04
+#define BOOL_TYPE 0x05
+#define BYTE_ARRAY_TYPE 0x06
+#define OBJECT_TYPE 0x07
+#define KEY_VALUE_PAIRS_TYPE 0x08
+#define ATTRIBUTION_CHAIN_TYPE 0x09
+#define ERROR_TYPE 0x0F
+
// Msg is expected to begin at the start of the serialized atom -- it should not
// include the android_log_header_t or the StatsEventTag.
LogEvent::LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid)
@@ -809,6 +837,26 @@
return 0.0;
}
+std::vector<uint8_t> LogEvent::GetStorage(size_t key, status_t* err) const {
+ int field = getSimpleField(key);
+ for (const auto& value : mValues) {
+ if (value.mField.getField() == field) {
+ if (value.mValue.getType() == STORAGE) {
+ return value.mValue.storage_value;
+ } else {
+ *err = BAD_TYPE;
+ return vector<uint8_t>();
+ }
+ }
+ if ((size_t)value.mField.getPosAtDepth(0) > key) {
+ break;
+ }
+ }
+
+ *err = BAD_INDEX;
+ return vector<uint8_t>();
+}
+
string LogEvent::ToString() const {
string result;
result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 3db2676..e4b784e 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -144,6 +144,7 @@
const char* GetString(size_t key, status_t* err) const;
bool GetBool(size_t key, status_t* err) const;
float GetFloat(size_t key, status_t* err) const;
+ std::vector<uint8_t> GetStorage(size_t key, status_t* err) const;
/**
* Write test data to the LogEvent. This can only be used when the LogEvent is constructed
@@ -214,6 +215,22 @@
return LogEvent(*this);
}
+ template <class T>
+ status_t updateValue(size_t key, T& value, Type type) {
+ int field = getSimpleField(key);
+ for (auto& fieldValue : mValues) {
+ if (fieldValue.mField.getField() == field) {
+ if (fieldValue.mValue.getType() == type) {
+ fieldValue.mValue = Value(value);
+ return OK;
+ } else {
+ return BAD_TYPE;
+ }
+ }
+ }
+ return BAD_INDEX;
+ }
+
private:
/**
* Only use this if copy is absolutely needed.
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 8e0c628..73f640e 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -21,6 +21,8 @@
#include <set>
#include <utils/SystemClock.h>
+#include "statscompanion_util.h"
+
using android::util::AtomsInfo;
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
@@ -584,6 +586,21 @@
return millis * 1000000;
}
+bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid) {
+ sp<IStatsCompanionService> scs = getStatsCompanionService();
+ if (scs == nullptr) {
+ return false;
+ }
+
+ bool success;
+ binder::Status status = scs->checkPermission(String16(permission), pid, uid, &success);
+ if (!status.isOk()) {
+ return false;
+ }
+
+ return success;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index 5fdf6e2..aec0956 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -95,6 +95,9 @@
// Returns the truncated timestamp to the nearest 5 minutes if needed.
int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs);
+// Checks permission for given pid and uid.
+bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid);
+
inline bool isVendorPulledAtom(int atomId) {
return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag;
}
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index d86e291..30c90b1 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -21,10 +21,8 @@
#include "packages/UidMap.h"
#include "stats_log_util.h"
-#include <android/os/IIncidentManager.h>
-#include <android/os/IncidentReportArgs.h>
#include <android/util/ProtoOutputStream.h>
-#include <binder/IServiceManager.h>
+#include <incident/incident_report.h>
#include <vector>
@@ -132,7 +130,7 @@
return false;
}
- IncidentReportArgs incidentReport;
+ android::os::IncidentReportRequest incidentReport;
vector<uint8_t> protoData;
getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey,
@@ -146,30 +144,21 @@
uint8_t dest;
switch (config.dest()) {
case IncidentdDetails_Destination_AUTOMATIC:
- dest = android::os::PRIVACY_POLICY_AUTOMATIC;
+ dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC;
break;
case IncidentdDetails_Destination_EXPLICIT:
- dest = android::os::PRIVACY_POLICY_EXPLICIT;
+ dest = INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT;
break;
default:
- dest = android::os::PRIVACY_POLICY_AUTOMATIC;
+ dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC;
}
incidentReport.setPrivacyPolicy(dest);
- incidentReport.setReceiverPkg(config.receiver_pkg());
+ incidentReport.setReceiverPackage(config.receiver_pkg());
- incidentReport.setReceiverCls(config.receiver_cls());
+ incidentReport.setReceiverClass(config.receiver_cls());
- sp<IIncidentManager> service = interface_cast<IIncidentManager>(
- defaultServiceManager()->getService(android::String16("incident")));
- if (service == nullptr) {
- ALOGW("Failed to fetch incident service.");
- return false;
- }
- VLOG("Calling incidentd %p", service.get());
- binder::Status s = service->reportIncident(incidentReport);
- VLOG("Report incident status: %s", s.toString8().string());
- return s.isOk();
+ return incidentReport.takeReport() == NO_ERROR;
}
} // namespace statsd
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 35b0396..f624e12 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -46,16 +46,16 @@
}
TEST(LogEventTest, TestPrimitiveParsing) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
- stats_event_write_int32(event, 10);
- stats_event_write_int64(event, 0x123456789);
- stats_event_write_float(event, 2.0);
- stats_event_write_bool(event, true);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ AStatsEvent_writeInt32(event, 10);
+ AStatsEvent_writeInt64(event, 0x123456789);
+ AStatsEvent_writeFloat(event, 2.0);
+ AStatsEvent_writeBool(event, true);
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
@@ -90,20 +90,20 @@
EXPECT_EQ(Type::INT, boolItem.mValue.getType()); // FieldValue does not support boolean type
EXPECT_EQ(1, boolItem.mValue.int_value);
- stats_event_release(event);
+ AStatsEvent_release(event);
}
TEST(LogEventTest, TestStringAndByteArrayParsing) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
string str = "test";
- stats_event_write_string8(event, str.c_str());
- stats_event_write_byte_array(event, (uint8_t*)str.c_str(), str.length());
- stats_event_build(event);
+ AStatsEvent_writeString(event, str.c_str());
+ AStatsEvent_writeByteArray(event, (uint8_t*)str.c_str(), str.length());
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
@@ -127,18 +127,18 @@
vector<uint8_t> expectedValue = {'t', 'e', 's', 't'};
EXPECT_EQ(expectedValue, storageItem.mValue.storage_value);
- stats_event_release(event);
+ AStatsEvent_release(event);
}
TEST(LogEventTest, TestEmptyString) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
string empty = "";
- stats_event_write_string8(event, empty.c_str());
- stats_event_build(event);
+ AStatsEvent_writeString(event, empty.c_str());
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
@@ -155,18 +155,18 @@
EXPECT_EQ(Type::STRING, item.mValue.getType());
EXPECT_EQ(empty, item.mValue.str_value);
- stats_event_release(event);
+ AStatsEvent_release(event);
}
TEST(LogEventTest, TestByteArrayWithNullCharacter) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
uint8_t message[] = {'\t', 'e', '\0', 's', 't'};
- stats_event_write_byte_array(event, message, 5);
- stats_event_build(event);
+ AStatsEvent_writeByteArray(event, message, 5);
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
@@ -184,79 +184,12 @@
vector<uint8_t> expectedValue(message, message + 5);
EXPECT_EQ(expectedValue, item.mValue.storage_value);
- stats_event_release(event);
-}
-
-TEST(LogEventTest, TestKeyValuePairs) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
-
- struct key_value_pair pairs[4];
- pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = 1};
- pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789};
- pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 2.0};
- string str = "test";
- pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()};
-
- stats_event_write_key_value_pairs(event, pairs, 4);
- stats_event_build(event);
-
- size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
-
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
- EXPECT_EQ(100, logEvent.GetTagId());
- EXPECT_EQ(1000, logEvent.GetUid());
- EXPECT_EQ(1001, logEvent.GetPid());
-
- const vector<FieldValue>& values = logEvent.getValues();
- EXPECT_EQ(8, values.size()); // 2 FieldValues per key-value pair
-
- // Check the keys first
- for (int i = 0; i < values.size() / 2; i++) {
- const FieldValue& item = values[2 * i];
- int32_t depth1Pos = i + 1;
- bool depth1Last = i == (values.size() / 2 - 1);
- Field expectedField = getField(100, {1, depth1Pos, 1}, 2, {true, depth1Last, false});
-
- EXPECT_EQ(expectedField, item.mField);
- EXPECT_EQ(Type::INT, item.mValue.getType());
- EXPECT_EQ(i, item.mValue.int_value);
- }
-
- // Check the values now
- // Note: pos[2] = index of type in KeyValuePair in atoms.proto
- const FieldValue& int32Item = values[1];
- Field expectedField = getField(100, {1, 1, 2}, 2, {true, false, true});
- EXPECT_EQ(expectedField, int32Item.mField);
- EXPECT_EQ(Type::INT, int32Item.mValue.getType());
- EXPECT_EQ(1, int32Item.mValue.int_value);
-
- const FieldValue& int64Item = values[3];
- expectedField = getField(100, {1, 2, 3}, 2, {true, false, true});
- EXPECT_EQ(expectedField, int64Item.mField);
- EXPECT_EQ(Type::LONG, int64Item.mValue.getType());
- EXPECT_EQ(0x123456789, int64Item.mValue.long_value);
-
- const FieldValue& floatItem = values[5];
- expectedField = getField(100, {1, 3, 5}, 2, {true, false, true});
- EXPECT_EQ(expectedField, floatItem.mField);
- EXPECT_EQ(Type::FLOAT, floatItem.mValue.getType());
- EXPECT_EQ(2.0, floatItem.mValue.float_value);
-
- const FieldValue& stringItem = values[7];
- expectedField = getField(100, {1, 4, 4}, 2, {true, true, true});
- EXPECT_EQ(expectedField, stringItem.mField);
- EXPECT_EQ(Type::STRING, stringItem.mValue.getType());
- EXPECT_EQ(str, stringItem.mValue.str_value);
-
- stats_event_release(event);
+ AStatsEvent_release(event);
}
TEST(LogEventTest, TestAttributionChain) {
- struct stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, 100);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
string tag1 = "tag1";
string tag2 = "tag2";
@@ -264,11 +197,11 @@
uint32_t uids[] = {1001, 1002};
const char* tags[] = {tag1.c_str(), tag2.c_str()};
- stats_event_write_attribution_chain(event, uids, tags, 2);
- stats_event_build(event);
+ AStatsEvent_writeAttributionChain(event, uids, tags, 2);
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buf = stats_event_get_buffer(event, &size);
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
@@ -305,7 +238,7 @@
EXPECT_EQ(Type::STRING, tag2Item.mValue.getType());
EXPECT_EQ(tag2, tag2Item.mValue.str_value);
- stats_event_release(event);
+ AStatsEvent_release(event);
}
#else // NEW_ENCODING_SCHEME
diff --git a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp
deleted file mode 100644
index ae92705..0000000
--- a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright 2019 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "GpuStatsPuller_test"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <graphicsenv/GpuStatsInfo.h>
-#include <log/log.h>
-
-#include "src/external/GpuStatsPuller.h"
-#include "statslog.h"
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// clang-format off
-static const std::string DRIVER_PACKAGE_NAME = "TEST_DRIVER";
-static const std::string DRIVER_VERSION_NAME = "TEST_DRIVER_VERSION";
-static const std::string APP_PACKAGE_NAME = "TEST_APP";
-static const int64_t TIMESTAMP_WALLCLOCK = 111;
-static const int64_t TIMESTAMP_ELAPSED = 222;
-static const int64_t DRIVER_VERSION_CODE = 333;
-static const int64_t DRIVER_BUILD_TIME = 444;
-static const int64_t GL_LOADING_COUNT = 3;
-static const int64_t GL_LOADING_FAILURE_COUNT = 1;
-static const int64_t VK_LOADING_COUNT = 4;
-static const int64_t VK_LOADING_FAILURE_COUNT = 0;
-static const int64_t ANGLE_LOADING_COUNT = 2;
-static const int64_t ANGLE_LOADING_FAILURE_COUNT = 1;
-static const int64_t GL_DRIVER_LOADING_TIME_0 = 555;
-static const int64_t GL_DRIVER_LOADING_TIME_1 = 666;
-static const int64_t VK_DRIVER_LOADING_TIME_0 = 777;
-static const int64_t VK_DRIVER_LOADING_TIME_1 = 888;
-static const int64_t VK_DRIVER_LOADING_TIME_2 = 999;
-static const int64_t ANGLE_DRIVER_LOADING_TIME_0 = 1010;
-static const int64_t ANGLE_DRIVER_LOADING_TIME_1 = 1111;
-static const int32_t VULKAN_VERSION = 1;
-static const int32_t CPU_VULKAN_VERSION = 2;
-static const int32_t GLES_VERSION = 3;
-static const bool CPU_VULKAN_IN_USE = true;
-static const bool FALSE_PREROTATION = true;
-static const bool GLES_1_IN_USE = true;
-static const size_t NUMBER_OF_VALUES_GLOBAL = 13;
-static const size_t NUMBER_OF_VALUES_APP = 8;
-// clang-format on
-
-class MockGpuStatsPuller : public GpuStatsPuller {
-public:
- MockGpuStatsPuller(const int tagId, vector<std::shared_ptr<LogEvent>>* data)
- : GpuStatsPuller(tagId), mData(data){};
-
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override {
- *data = *mData;
- return true;
- }
-
- vector<std::shared_ptr<LogEvent>>* mData;
-};
-
-class GpuStatsPuller_test : public ::testing::Test {
-public:
- GpuStatsPuller_test() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- }
-
- ~GpuStatsPuller_test() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
- }
-};
-
-TEST_F(GpuStatsPuller_test, PullGpuStatsGlobalInfo) {
- vector<std::shared_ptr<LogEvent>> inData, outData;
- std::shared_ptr<LogEvent> event = make_shared<LogEvent>(android::util::GPU_STATS_GLOBAL_INFO,
- TIMESTAMP_WALLCLOCK, TIMESTAMP_ELAPSED);
- EXPECT_TRUE(event->write(DRIVER_PACKAGE_NAME));
- EXPECT_TRUE(event->write(DRIVER_VERSION_NAME));
- EXPECT_TRUE(event->write(DRIVER_VERSION_CODE));
- EXPECT_TRUE(event->write(DRIVER_BUILD_TIME));
- EXPECT_TRUE(event->write(GL_LOADING_COUNT));
- EXPECT_TRUE(event->write(GL_LOADING_FAILURE_COUNT));
- EXPECT_TRUE(event->write(VK_LOADING_COUNT));
- EXPECT_TRUE(event->write(VK_LOADING_FAILURE_COUNT));
- EXPECT_TRUE(event->write(VULKAN_VERSION));
- EXPECT_TRUE(event->write(CPU_VULKAN_VERSION));
- EXPECT_TRUE(event->write(GLES_VERSION));
- EXPECT_TRUE(event->write(ANGLE_LOADING_COUNT));
- EXPECT_TRUE(event->write(ANGLE_LOADING_FAILURE_COUNT));
- event->init();
- inData.emplace_back(event);
- MockGpuStatsPuller mockPuller(android::util::GPU_STATS_GLOBAL_INFO, &inData);
- mockPuller.ForceClearCache();
- mockPuller.Pull(&outData);
-
- ASSERT_EQ(1, outData.size());
- EXPECT_EQ(android::util::GPU_STATS_GLOBAL_INFO, outData[0]->GetTagId());
- ASSERT_EQ(NUMBER_OF_VALUES_GLOBAL, outData[0]->size());
- EXPECT_EQ(DRIVER_PACKAGE_NAME, outData[0]->getValues()[0].mValue.str_value);
- EXPECT_EQ(DRIVER_VERSION_NAME, outData[0]->getValues()[1].mValue.str_value);
- EXPECT_EQ(DRIVER_VERSION_CODE, outData[0]->getValues()[2].mValue.long_value);
- EXPECT_EQ(DRIVER_BUILD_TIME, outData[0]->getValues()[3].mValue.long_value);
- EXPECT_EQ(GL_LOADING_COUNT, outData[0]->getValues()[4].mValue.long_value);
- EXPECT_EQ(GL_LOADING_FAILURE_COUNT, outData[0]->getValues()[5].mValue.long_value);
- EXPECT_EQ(VK_LOADING_COUNT, outData[0]->getValues()[6].mValue.long_value);
- EXPECT_EQ(VK_LOADING_FAILURE_COUNT, outData[0]->getValues()[7].mValue.long_value);
- EXPECT_EQ(VULKAN_VERSION, outData[0]->getValues()[8].mValue.int_value);
- EXPECT_EQ(CPU_VULKAN_VERSION, outData[0]->getValues()[9].mValue.int_value);
- EXPECT_EQ(GLES_VERSION, outData[0]->getValues()[10].mValue.int_value);
- EXPECT_EQ(ANGLE_LOADING_COUNT, outData[0]->getValues()[11].mValue.long_value);
- EXPECT_EQ(ANGLE_LOADING_FAILURE_COUNT, outData[0]->getValues()[12].mValue.long_value);
-}
-
-TEST_F(GpuStatsPuller_test, PullGpuStatsAppInfo) {
- vector<std::shared_ptr<LogEvent>> inData, outData;
- std::shared_ptr<LogEvent> event = make_shared<LogEvent>(android::util::GPU_STATS_APP_INFO,
- TIMESTAMP_WALLCLOCK, TIMESTAMP_ELAPSED);
- EXPECT_TRUE(event->write(APP_PACKAGE_NAME));
- EXPECT_TRUE(event->write(DRIVER_VERSION_CODE));
- std::vector<int64_t> glDriverLoadingTime;
- glDriverLoadingTime.emplace_back(GL_DRIVER_LOADING_TIME_0);
- glDriverLoadingTime.emplace_back(GL_DRIVER_LOADING_TIME_1);
- std::vector<int64_t> vkDriverLoadingTime;
- vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_0);
- vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_1);
- vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_2);
- std::vector<int64_t> angleDriverLoadingTime;
- angleDriverLoadingTime.emplace_back(ANGLE_DRIVER_LOADING_TIME_0);
- angleDriverLoadingTime.emplace_back(ANGLE_DRIVER_LOADING_TIME_1);
- EXPECT_TRUE(event->write(int64VectorToProtoByteString(glDriverLoadingTime)));
- EXPECT_TRUE(event->write(int64VectorToProtoByteString(vkDriverLoadingTime)));
- EXPECT_TRUE(event->write(int64VectorToProtoByteString(angleDriverLoadingTime)));
- EXPECT_TRUE(event->write(CPU_VULKAN_IN_USE));
- EXPECT_TRUE(event->write(FALSE_PREROTATION));
- EXPECT_TRUE(event->write(GLES_1_IN_USE));
- event->init();
- inData.emplace_back(event);
- MockGpuStatsPuller mockPuller(android::util::GPU_STATS_APP_INFO, &inData);
- mockPuller.ForceClearCache();
- mockPuller.Pull(&outData);
-
- ASSERT_EQ(1, outData.size());
- EXPECT_EQ(android::util::GPU_STATS_APP_INFO, outData[0]->GetTagId());
- ASSERT_EQ(NUMBER_OF_VALUES_APP, outData[0]->size());
- EXPECT_EQ(APP_PACKAGE_NAME, outData[0]->getValues()[0].mValue.str_value);
- EXPECT_EQ(DRIVER_VERSION_CODE, outData[0]->getValues()[1].mValue.long_value);
- EXPECT_EQ(int64VectorToProtoByteString(glDriverLoadingTime),
- outData[0]->getValues()[2].mValue.str_value);
- EXPECT_EQ(int64VectorToProtoByteString(vkDriverLoadingTime),
- outData[0]->getValues()[3].mValue.str_value);
- EXPECT_EQ(int64VectorToProtoByteString(angleDriverLoadingTime),
- outData[0]->getValues()[4].mValue.str_value);
- EXPECT_EQ(CPU_VULKAN_IN_USE, outData[0]->getValues()[5].mValue.int_value);
- EXPECT_EQ(FALSE_PREROTATION, outData[0]->getValues()[6].mValue.int_value);
- EXPECT_EQ(GLES_1_IN_USE, outData[0]->getValues()[7].mValue.int_value);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index 2576cf5..a011692e 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -50,11 +50,11 @@
int64_t pullCoolDownNs;
std::thread pullThread;
-stats_event* createSimpleEvent(int64_t value) {
- stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, pullTagId);
- stats_event_write_int64(event, value);
- stats_event_build(event);
+AStatsEvent* createSimpleEvent(int64_t value) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, pullTagId);
+ AStatsEvent_writeInt64(event, value);
+ AStatsEvent_build(event);
return event;
}
@@ -62,16 +62,16 @@
// Convert stats_events into StatsEventParcels.
std::vector<android::util::StatsEventParcel> parcels;
for (int i = 0; i < values.size(); i++) {
- stats_event* event = createSimpleEvent(values[i]);
+ AStatsEvent* event = createSimpleEvent(values[i]);
size_t size;
- uint8_t* buffer = stats_event_get_buffer(event, &size);
+ uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
android::util::StatsEventParcel p;
// vector.assign() creates a copy, but this is inevitable unless
// stats_event.h/c uses a vector as opposed to a buffer.
p.buffer.assign(buffer, buffer + size);
parcels.push_back(std::move(p));
- stats_event_release(event);
+ AStatsEvent_release(event);
}
sleep_for(std::chrono::nanoseconds(pullDelayNs));
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 6e1890a..db09ee9 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -953,24 +953,24 @@
// Convert stats_events into StatsEventParcels.
std::vector<android::util::StatsEventParcel> parcels;
for (int i = 1; i < 3; i++) {
- stats_event* event = stats_event_obtain();
- stats_event_set_atom_id(event, atomTag);
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, atomTag);
std::string subsystemName = "subsystem_name_";
subsystemName = subsystemName + std::to_string(i);
- stats_event_write_string8(event, subsystemName.c_str());
- stats_event_write_string8(event, "subsystem_subname foo");
- stats_event_write_int64(event, /*count= */ i);
- stats_event_write_int64(event, /*time_millis= */ i * 100);
- stats_event_build(event);
+ AStatsEvent_writeString(event, subsystemName.c_str());
+ AStatsEvent_writeString(event, "subsystem_subname foo");
+ AStatsEvent_writeInt64(event, /*count= */ i);
+ AStatsEvent_writeInt64(event, /*time_millis= */ i * 100);
+ AStatsEvent_build(event);
size_t size;
- uint8_t* buffer = stats_event_get_buffer(event, &size);
+ uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
android::util::StatsEventParcel p;
// vector.assign() creates a copy, but this is inevitable unless
// stats_event.h/c uses a vector as opposed to a buffer.
p.buffer.assign(buffer, buffer + size);
parcels.push_back(std::move(p));
- stats_event_release(event);
+ AStatsEvent_write(event);
}
resultReceiver->pullFinished(atomTag, /*success=*/true, parcels);
return binder::Status::ok();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f31c614..642f51b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2880,13 +2880,14 @@
* {@link #enterPictureInPictureMode(PictureInPictureParams)} at this time. For example, the
* system will call this method when the activity is being put into the background, so the app
* developer might want to switch an activity into PIP mode instead.</p>
+ *
+ * @return {@code true} if the activity received this callback regardless of if it acts on it
+ * or not. If {@code false}, the framework will assume the app hasn't been updated to leverage
+ * this callback and will in turn send a legacy callback of {@link #onUserLeaveHint()} for the
+ * app to enter picture-in-picture mode.
*/
- public void onPictureInPictureRequested() {
- // Previous recommendation was for apps to enter picture-in-picture in onUserLeaveHint()
- // which is sent after onPause(). This new method allows the system to request the app to
- // go into picture-in-picture decoupling it from life cycle events. For backwards
- // compatibility we schedule the life cycle events if the app didn't override this method.
- mMainThread.schedulePauseAndReturnToCurrentState(mToken);
+ public boolean onPictureInPictureRequested() {
+ return false;
}
void dispatchMovedToDisplay(int displayId, Configuration config) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 206c771..db9aa18 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1472,7 +1472,7 @@
dest.writeInt(1);
dest.writeString(mLabel);
}
- if (mIcon == null) {
+ if (mIcon == null || mIcon.isRecycled()) {
dest.writeInt(0);
} else {
dest.writeInt(1);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c901d2a..1921567 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3772,7 +3772,15 @@
return;
}
- r.activity.onPictureInPictureRequested();
+ final boolean receivedByApp = r.activity.onPictureInPictureRequested();
+ if (!receivedByApp) {
+ // Previous recommendation was for apps to enter picture-in-picture in
+ // onUserLeavingHint() for cases such as the app being put into the background. For
+ // backwards compatibility with apps that are not using the newer
+ // onPictureInPictureRequested() callback, we schedule the life cycle events needed to
+ // trigger onUserLeavingHint(), then we return the activity to its previous state.
+ schedulePauseWithUserLeaveHintAndReturnToCurrentState(r);
+ }
}
/**
@@ -3780,18 +3788,7 @@
* return to its previous state. This allows activities that rely on onUserLeaveHint instead of
* onPictureInPictureRequested to enter picture-in-picture.
*/
- public void schedulePauseAndReturnToCurrentState(IBinder token) {
- final ActivityClientRecord r = mActivities.get(token);
- if (r == null) {
- Log.w(TAG, "Activity to request pause with user leaving hint to no longer exists");
- return;
- }
-
- if (r.mIsUserLeaving) {
- // The activity is about to perform user leaving, so there's no need to cycle ourselves.
- return;
- }
-
+ private void schedulePauseWithUserLeaveHintAndReturnToCurrentState(ActivityClientRecord r) {
final int prevState = r.getLifecycleState();
if (prevState != ON_RESUME && prevState != ON_PAUSE) {
return;
@@ -4544,7 +4541,6 @@
if (r != null) {
if (userLeaving) {
performUserLeavingActivity(r);
- r.mIsUserLeaving = false;
}
r.activity.mConfigChangeFlags |= configChanges;
@@ -4559,7 +4555,6 @@
}
final void performUserLeavingActivity(ActivityClientRecord r) {
- r.mIsUserLeaving = true;
mInstrumentation.callActivityOnPictureInPictureRequested(r.activity);
mInstrumentation.callActivityOnUserLeaving(r.activity);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 46f8669..861d41d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -88,10 +88,15 @@
import java.util.function.Supplier;
/**
- * API for interacting with "application operation" tracking.
+ * AppOps are mappings of [package/uid, op-name] -> [mode]. The list of existing appops is defined
+ * by the system and cannot be amended by apps. Only system apps can change appop-modes.
*
- * <p>This API is not generally intended for third party application developers; most
- * features are only available to system applications.
+ * <p>Beside a mode the system tracks when an op was {@link #noteOp noted}. The tracked data can
+ * only be read by system components.
+ *
+ * <p>Installed apps can usually only listen to changes and events on their own ops. E.g.
+ * {@link AppOpsCollector} allows to get a callback each time an app called {@link #noteOp} or
+ * {@link #startOp} for an op belonging to the app.
*/
@SystemService(Context.APP_OPS_SERVICE)
public class AppOpsManager {
@@ -6774,6 +6779,9 @@
* succeeds, the last execution time of the operation for this app will be updated to
* the current time.
*
+ * <p>If this is a check that is not preceding the protected operation, use
+ * {@link #unsafeCheckOp} instead.
+ *
* @param op The operation to note. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
@@ -6798,6 +6806,9 @@
* succeeds, the last execution time of the operation for this app will be updated to
* the current time.
*
+ * <p>If this is a check that is not preceding the protected operation, use
+ * {@link #unsafeCheckOp} instead.
+ *
* @param op The operation to note. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
@@ -7757,9 +7768,38 @@
/**
* Callback an app can choose to {@link #setNotedAppOpsCollector register} to monitor it's noted
- * appops.
+ * appops. I.e. each time any app calls {@link #noteOp} or {@link #startOp} one of the callback
+ * methods of this object is called.
*
* <p><b>Only appops related to dangerous permissions are collected.</b>
+ *
+ * <pre>
+ * setNotedAppOpsCollector(new AppOpsCollector() {
+ * ArraySet<Pair<String, String>> opsNotedForThisProcess = new ArraySet<>();
+ *
+ * private synchronized void addAccess(String op, String accessLocation) {
+ * // Ops are often noted when permission protected APIs were called.
+ * // In this case permissionToOp() allows to resolve the permission<->op
+ * opsNotedForThisProcess.add(new Pair(accessType, accessLocation));
+ * }
+ *
+ * public void onNoted(SyncNotedAppOp op) {
+ * // Accesses is currently happening, hence stack trace describes location of access
+ * addAccess(op.getOp(), Arrays.toString(Thread.currentThread().getStackTrace()));
+ * }
+ *
+ * public void onSelfNoted(SyncNotedAppOp op) {
+ * onNoted(op);
+ * }
+ *
+ * public void onAsyncNoted(AsyncNotedAppOp asyncOp) {
+ * // Stack trace is not useful for async ops as accessed happened on different thread
+ * addAccess(asyncOp.getOp(), asyncOp.getMessage());
+ * }
+ * });
+ * </pre>
+ *
+ * @see #setNotedAppOpsCollector
*/
public abstract static class AppOpsCollector {
/** Callback registered with the system. This will receive the async notes ops */
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 71cb4a4..cd05e2c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3229,18 +3229,18 @@
}
@Override
- public String getSystemTextClassifierPackageName() {
+ public String getDefaultTextClassifierPackageName() {
try {
- return mPM.getSystemTextClassifierPackageName();
+ return mPM.getDefaultTextClassifierPackageName();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public String[] getSystemTextClassifierPackages() {
+ public String getSystemTextClassifierPackageName() {
try {
- return mPM.getSystemTextClassifierPackages();
+ return mPM.getSystemTextClassifierPackageName();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2e93d43..01ccb86 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1861,15 +1861,19 @@
}
/**
- * Connects all enabled and supported bluetooth profiles between the local and remote device
+ * Connects all enabled and supported bluetooth profiles between the local and remote device.
+ * Connection is asynchronous and you should listen to each profile's broadcast intent
+ * ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful. For example,
+ * to verify a2dp is connected, you would listen for
+ * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
*
* @param device is the remote device with which to connect these profiles
- * @return true if all profiles successfully connected, false if an error occurred
+ * @return true if message sent to try to connect all profiles, false if an error occurred
*
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) {
try {
mServiceLock.readLock().lock();
@@ -1886,15 +1890,19 @@
}
/**
- * Disconnects all enabled and supported bluetooth profiles between the local and remote device
+ * Disconnects all enabled and supported bluetooth profiles between the local and remote device.
+ * Disconnection is asynchronous and you should listen to each profile's broadcast intent
+ * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example,
+ * to verify a2dp is disconnected, you would listen for
+ * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
*
* @param device is the remote device with which to disconnect these profiles
- * @return true if all profiles successfully disconnected, false if an error occurred
+ * @return true if message sent to try to disconnect all profiles, false if an error occurred
*
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) {
try {
mServiceLock.readLock().lock();
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 50bb3c7..0492359 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -20,11 +20,13 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IOnAppsChangedListener;
import android.content.pm.LauncherApps;
import android.content.pm.IPackageInstallerCallback;
+import android.content.pm.IShortcutChangeCallback;
import android.content.pm.PackageInstaller;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
@@ -66,7 +68,8 @@
in UserHandle user);
ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName,
- in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user);
+ in List shortcutIds, in List<LocusId> locusIds, in ComponentName componentName,
+ int flags, in UserHandle user);
void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
in UserHandle user);
boolean startShortcut(String callingPackage, String packageName, String id,
@@ -89,4 +92,10 @@
void registerPackageInstallerCallback(String callingPackage,
in IPackageInstallerCallback callback);
ParceledListSlice getAllSessions(String callingPackage);
+
+ void registerShortcutChangeCallback(String callingPackage, long changedSince,
+ String packageName, in List shortcutIds, in List<LocusId> locusIds,
+ in ComponentName componentName, int flags, in IShortcutChangeCallback callback,
+ int callbackId);
+ void unregisterShortcutChangeCallback(String callingPackage, int callbackId);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 93126b8..6552d1b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -679,9 +679,9 @@
boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags);
- String getSystemTextClassifierPackageName();
+ String getDefaultTextClassifierPackageName();
- String[] getSystemTextClassifierPackages();
+ String getSystemTextClassifierPackageName();
String getAttentionServicePackageName();
diff --git a/core/java/android/content/pm/IShortcutChangeCallback.aidl b/core/java/android/content/pm/IShortcutChangeCallback.aidl
new file mode 100644
index 0000000..fed4e4a
--- /dev/null
+++ b/core/java/android/content/pm/IShortcutChangeCallback.aidl
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
+
+import java.util.List;
+
+/**
+ * Interface for LauncherApps#ShortcutChangeCallbackProxy.
+ *
+ * @hide
+ */
+oneway interface IShortcutChangeCallback
+{
+ void onShortcutsAddedOrUpdated(String packageName, in List<ShortcutInfo> shortcuts,
+ in UserHandle user);
+
+ void onShortcutsRemoved(String packageName, in List<ShortcutInfo> shortcuts,
+ in UserHandle user);
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 747e929..9e85fc3 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -29,10 +29,6 @@
boolean setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
int userId);
- ParceledListSlice getDynamicShortcuts(String packageName, int userId);
-
- ParceledListSlice getManifestShortcuts(String packageName, int userId);
-
boolean addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
int userId);
@@ -40,8 +36,6 @@
void removeAllDynamicShortcuts(String packageName, int userId);
- ParceledListSlice getPinnedShortcuts(String packageName, int userId);
-
boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId);
boolean requestPinShortcut(String packageName, in ShortcutInfo shortcut,
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index cea0b6b..73c9e4d 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -34,6 +34,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.LocusId;
import android.content.pm.PackageInstaller.SessionCallback;
import android.content.pm.PackageInstaller.SessionCallbackDelegate;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -61,15 +62,21 @@
import android.os.UserManager;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.util.function.pooled.PooledLambda;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -152,6 +159,9 @@
private final List<CallbackMessageHandler> mCallbacks = new ArrayList<>();
private final List<SessionCallbackDelegate> mDelegates = new ArrayList<>();
+ private final Map<Integer, Pair<Executor, ShortcutChangeCallback>>
+ mShortcutChangeCallbacks = new HashMap<>();
+
/**
* Callbacks for package changes to this and related managed profiles.
*/
@@ -406,6 +416,9 @@
List<String> mShortcutIds;
@Nullable
+ List<LocusId> mLocusIds;
+
+ @Nullable
ComponentName mActivity;
@QueryFlags
@@ -442,6 +455,19 @@
}
/**
+ * If non-null, return only the specified shortcuts by locus ID. When setting this field,
+ * a package name must also be set with {@link #setPackage}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public ShortcutQuery setLocusIds(@Nullable List<LocusId> locusIds) {
+ mLocusIds = locusIds;
+ return this;
+ }
+
+ /**
* If non-null, returns only shortcuts associated with the activity; i.e.
* {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal
* to {@code activity}.
@@ -469,6 +495,95 @@
}
}
+ /**
+ * Callbacks for shortcut changes to this and related managed profiles.
+ *
+ * @hide
+ */
+ public interface ShortcutChangeCallback {
+ /**
+ * Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to
+ * register this callback, have been added or updated.
+ * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery)
+ *
+ * <p>Only the applications that are allowed to access the shortcut information,
+ * as defined in {@link #hasShortcutHostPermission()}, will receive it.
+ *
+ * @param packageName The name of the package that has the shortcuts.
+ * @param shortcuts Shortcuts from the package that have updated or added. Only "key"
+ * information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}.
+ * @param user The UserHandle of the profile that generated the change.
+ *
+ * @see ShortcutManager
+ */
+ default void onShortcutsAddedOrUpdated(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {}
+
+ /**
+ * Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to
+ * register this callback, have been removed.
+ * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery)
+ *
+ * <p>Only the applications that are allowed to access the shortcut information,
+ * as defined in {@link #hasShortcutHostPermission()}, will receive it.
+ *
+ * @param packageName The name of the package that has the shortcuts.
+ * @param shortcuts Shortcuts from the package that have been removed. Only "key"
+ * information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}.
+ * @param user The UserHandle of the profile that generated the change.
+ *
+ * @see ShortcutManager
+ */
+ default void onShortcutsRemoved(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {}
+ }
+
+ /**
+ * Callback proxy class for {@link ShortcutChangeCallback}
+ *
+ * @hide
+ */
+ private static class ShortcutChangeCallbackProxy extends
+ android.content.pm.IShortcutChangeCallback.Stub {
+ private final WeakReference<Pair<Executor, ShortcutChangeCallback>> mRemoteReferences;
+
+ ShortcutChangeCallbackProxy(Pair<Executor, ShortcutChangeCallback> remoteReferences) {
+ mRemoteReferences = new WeakReference<>(remoteReferences);
+ }
+
+ @Override
+ public void onShortcutsAddedOrUpdated(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+ Pair<Executor, ShortcutChangeCallback> remoteReferences = mRemoteReferences.get();
+ if (remoteReferences == null) {
+ // Binder is dead.
+ return;
+ }
+
+ final Executor executor = remoteReferences.first;
+ final ShortcutChangeCallback callback = remoteReferences.second;
+ executor.execute(
+ PooledLambda.obtainRunnable(ShortcutChangeCallback::onShortcutsAddedOrUpdated,
+ callback, packageName, shortcuts, user).recycleOnUse());
+ }
+
+ @Override
+ public void onShortcutsRemoved(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+ Pair<Executor, ShortcutChangeCallback> remoteReferences = mRemoteReferences.get();
+ if (remoteReferences == null) {
+ // Binder is dead.
+ return;
+ }
+
+ final Executor executor = remoteReferences.first;
+ final ShortcutChangeCallback callback = remoteReferences.second;
+ executor.execute(
+ PooledLambda.obtainRunnable(ShortcutChangeCallback::onShortcutsRemoved,
+ callback, packageName, shortcuts, user).recycleOnUse());
+ }
+ }
+
/** @hide */
public LauncherApps(Context context, ILauncherApps service) {
mContext = context;
@@ -924,8 +1039,8 @@
// changed callback, but that only returns shortcuts with the "key" information, so
// that won't return disabled message.
return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(),
- query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity,
- query.mQueryFlags, user)
+ query.mChangedSince, query.mPackage, query.mShortcutIds, query.mLocusIds,
+ query.mActivity, query.mQueryFlags, user)
.getList());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1560,6 +1675,63 @@
}
/**
+ * Register a callback to watch for shortcut change events in this user and managed profiles.
+ *
+ * @param callback The callback to register.
+ * @param query {@link ShortcutQuery} to match and filter the shortcut events. Only matching
+ * shortcuts will be returned by the callback.
+ * @param executor {@link Executor} to handle the callbacks. To dispatch callbacks to the main
+ * thread of your application, you can use {@link android.content.Context#getMainExecutor()}.
+ *
+ * @hide
+ */
+ public void registerShortcutChangeCallback(@NonNull ShortcutChangeCallback callback,
+ @NonNull ShortcutQuery query, @NonNull @CallbackExecutor Executor executor) {
+ Objects.requireNonNull(callback, "Callback cannot be null");
+ Objects.requireNonNull(query, "Query cannot be null");
+ Objects.requireNonNull(executor, "Executor cannot be null");
+
+ synchronized (mShortcutChangeCallbacks) {
+ final int callbackId = callback.hashCode();
+ final Pair<Executor, ShortcutChangeCallback> state = new Pair<>(executor, callback);
+ mShortcutChangeCallbacks.put(callbackId, state);
+ try {
+ mService.registerShortcutChangeCallback(mContext.getPackageName(),
+ query.mChangedSince, query.mPackage, query.mShortcutIds, query.mLocusIds,
+ query.mActivity, query.mQueryFlags, new ShortcutChangeCallbackProxy(state),
+ callbackId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters a callback that was previously registered.
+ * @see #registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery, Executor)
+ *
+ * @param callback Callback to be unregistered.
+ *
+ * @hide
+ */
+ public void unregisterShortcutChangeCallback(@NonNull ShortcutChangeCallback callback) {
+ Objects.requireNonNull(callback, "Callback cannot be null");
+
+ synchronized (mShortcutChangeCallbacks) {
+ final int callbackId = callback.hashCode();
+ if (mShortcutChangeCallbacks.containsKey(callbackId)) {
+ mShortcutChangeCallbacks.remove(callbackId);
+ try {
+ mService.unregisterShortcutChangeCallback(mContext.getPackageName(),
+ callbackId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ /**
* A helper method to extract a {@link PinItemRequest} set to
* the {@link #EXTRA_PIN_ITEM_REQUEST} extra.
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6d5e8fb..5a0bcf0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3354,6 +3354,22 @@
public static final int FLAG_PERMISSION_ONE_TIME = 1 << 16;
/**
+ * Permission flag: The permission is whitelisted to not be auto-revoked when app goes unused.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE = 1 << 17;
+
+ /**
+ * Permission flag: Whether {@link #FLAG_PERMISSION_DONT_AUTO_REVOKE} state was set by user.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET = 1 << 18;
+
+ /**
* Permission flags: Reserved for use by the permission controller.
*
* @hide
@@ -3404,7 +3420,9 @@
| FLAG_PERMISSION_APPLY_RESTRICTION
| FLAG_PERMISSION_GRANTED_BY_ROLE
| FLAG_PERMISSION_REVOKED_COMPAT
- | FLAG_PERMISSION_ONE_TIME;
+ | FLAG_PERMISSION_ONE_TIME
+ | FLAG_PERMISSION_DONT_AUTO_REVOKE
+ | FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET;
/**
* Injected activity in app that forwards user to setting activity of that app.
@@ -4227,7 +4245,8 @@
FLAG_PERMISSION_APPLY_RESTRICTION,
FLAG_PERMISSION_GRANTED_BY_ROLE,
FLAG_PERMISSION_REVOKED_COMPAT,
- FLAG_PERMISSION_ONE_TIME
+ FLAG_PERMISSION_ONE_TIME,
+ FLAG_PERMISSION_DONT_AUTO_REVOKE
})
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionFlags {}
@@ -7364,6 +7383,8 @@
case FLAG_PERMISSION_GRANTED_BY_ROLE: return "GRANTED_BY_ROLE";
case FLAG_PERMISSION_REVOKED_COMPAT: return "REVOKED_COMPAT";
case FLAG_PERMISSION_ONE_TIME: return "ONE_TIME";
+ case FLAG_PERMISSION_DONT_AUTO_REVOKE: return "DONT_AUTO_REVOKE";
+ case FLAG_PERMISSION_DONT_AUTO_REVOKE_USER_SET: return "DONT_AUTO_REVOKE_USER_SET";
default: return Integer.toString(flag);
}
}
@@ -7602,14 +7623,15 @@
}
/**
- * @return the system defined text classifier package name, or null if there's none.
+ * @return the default text classifier package name, or null if there's none.
*
* @hide
*/
@Nullable
- public String getSystemTextClassifierPackageName() {
+ @TestApi
+ public String getDefaultTextClassifierPackageName() {
throw new UnsupportedOperationException(
- "getSystemTextClassifierPackageName not implemented in subclass");
+ "getDefaultTextClassifierPackageName not implemented in subclass");
}
/**
@@ -7617,10 +7639,11 @@
*
* @hide
*/
- @NonNull
- public String[] getSystemTextClassifierPackages() {
+ @Nullable
+ @TestApi
+ public String getSystemTextClassifierPackageName() {
throw new UnsupportedOperationException(
- "getSystemTextClassifierPackages not implemented in subclass");
+ "getSystemTextClassifierPackageName not implemented in subclass");
}
/**
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index e6f682d..a11a1dd 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -23,6 +23,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.LocusId;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -45,8 +46,8 @@
getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable List<String> shortcutIds,
- @Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags,
- int userId, int callingPid, int callingUid);
+ @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
+ @ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid);
public abstract boolean
isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 140363c..b128ea7 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -676,7 +676,8 @@
}
try {
- mService.registerConnectivityDiagnosticsCallback(binder, request);
+ mService.registerConnectivityDiagnosticsCallback(
+ binder, request, mContext.getOpPackageName());
} catch (RemoteException exception) {
exception.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1089a19..0fae607 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -222,7 +222,7 @@
boolean isCallerCurrentAlwaysOnVpnLockdownApp();
void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback,
- in NetworkRequest request);
+ in NetworkRequest request, String callingPackageName);
void unregisterConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback);
IBinder startOrGetTestNetworkService();
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 4f4e27b..cf5f225 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -858,8 +858,8 @@
*
* <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator.
*
- * <p>An app is granted owner privileges over Networks that it supplies. Owner privileges
- * implicitly include administrator privileges.
+ * <p>An app is granted owner privileges over Networks that it supplies. The owner UID MUST
+ * always be included in administratorUids.
*
* @param administratorUids the UIDs to be set as administrators of this Network.
* @hide
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 368c94c..79852d3 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -31,7 +31,6 @@
import android.content.Intent;
import android.graphics.Rect;
import android.os.Build;
-import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
@@ -93,7 +92,7 @@
// Used for metrics / debug only
private ComponentName mServiceComponentName;
- private final IAugmentedAutofillService mInterface = new IAugmentedAutofillService.Stub() {
+ private final class AugmentedAutofillServiceImpl extends IAugmentedAutofillService.Stub {
@Override
public void onConnected(boolean debug, boolean verbose) {
@@ -137,7 +136,7 @@
public final IBinder onBind(Intent intent) {
mServiceComponentName = intent.getComponent();
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mInterface.asBinder();
+ return new AugmentedAutofillServiceImpl();
}
Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
return null;
@@ -352,11 +351,13 @@
static final int REPORT_EVENT_NO_RESPONSE = 1;
static final int REPORT_EVENT_UI_SHOWN = 2;
static final int REPORT_EVENT_UI_DESTROYED = 3;
+ static final int REPORT_EVENT_INLINE_RESPONSE = 4;
@IntDef(prefix = { "REPORT_EVENT_" }, value = {
REPORT_EVENT_NO_RESPONSE,
REPORT_EVENT_UI_SHOWN,
- REPORT_EVENT_UI_DESTROYED
+ REPORT_EVENT_UI_DESTROYED,
+ REPORT_EVENT_INLINE_RESPONSE
})
@Retention(RetentionPolicy.SOURCE)
@interface ReportEvent{}
@@ -365,8 +366,8 @@
private final Object mLock = new Object();
private final IAugmentedAutofillManagerClient mClient;
private final int mSessionId;
- public final int taskId;
- public final ComponentName componentName;
+ public final int mTaskId;
+ public final ComponentName mComponentName;
// Used for metrics / debug only
private String mServicePackageName;
@GuardedBy("mLock")
@@ -406,8 +407,8 @@
mSessionId = sessionId;
mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
mCallback = callback;
- this.taskId = taskId;
- this.componentName = componentName;
+ mTaskId = taskId;
+ mComponentName = componentName;
mServicePackageName = serviceComponentName.getPackageName();
mFocusedId = focusedId;
mFocusedValue = focusedValue;
@@ -514,22 +515,24 @@
}
}
- public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData,
- @Nullable Bundle clientState) {
+ void reportResult(@Nullable List<Dataset> inlineSuggestionsData) {
try {
- mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}), clientState);
+ final Dataset[] inlineSuggestions = (inlineSuggestionsData != null)
+ ? inlineSuggestionsData.toArray(new Dataset[inlineSuggestionsData.size()])
+ : null;
+ mCallback.onSuccess(inlineSuggestions);
} catch (RemoteException e) {
Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
}
}
- // Used (mostly) for metrics.
- public void report(@ReportEvent int event) {
- if (sVerbose) Log.v(TAG, "report(): " + event);
+ void logEvent(@ReportEvent int event) {
+ if (sVerbose) Log.v(TAG, "returnAndLogResult(): " + event);
long duration = -1;
int type = MetricsEvent.TYPE_UNKNOWN;
+
switch (event) {
- case REPORT_EVENT_NO_RESPONSE:
+ case REPORT_EVENT_NO_RESPONSE: {
type = MetricsEvent.TYPE_SUCCESS;
if (mFirstOnSuccessTime == 0) {
mFirstOnSuccessTime = SystemClock.elapsedRealtime();
@@ -538,40 +541,49 @@
Log.d(TAG, "Service responded nothing in " + formatDuration(duration));
}
}
- try {
- mCallback.onSuccess(/* inlineSuggestionsData= */null, /* clientState=*/
- null);
- } catch (RemoteException e) {
- Log.e(TAG, "Error reporting success: " + e);
+ } break;
+
+ case REPORT_EVENT_INLINE_RESPONSE: {
+ // TODO: Define a constant and log this event
+ // type = MetricsEvent.TYPE_SUCCESS_INLINE;
+ if (mFirstOnSuccessTime == 0) {
+ mFirstOnSuccessTime = SystemClock.elapsedRealtime();
+ duration = mFirstOnSuccessTime - mFirstRequestTime;
+ if (sDebug) {
+ Log.d(TAG, "Service responded nothing in " + formatDuration(duration));
+ }
}
- break;
- case REPORT_EVENT_UI_SHOWN:
+ } break;
+
+ case REPORT_EVENT_UI_SHOWN: {
type = MetricsEvent.TYPE_OPEN;
if (mUiFirstShownTime == 0) {
mUiFirstShownTime = SystemClock.elapsedRealtime();
duration = mUiFirstShownTime - mFirstRequestTime;
if (sDebug) Log.d(TAG, "UI shown in " + formatDuration(duration));
}
- break;
- case REPORT_EVENT_UI_DESTROYED:
+ } break;
+
+ case REPORT_EVENT_UI_DESTROYED: {
type = MetricsEvent.TYPE_CLOSE;
if (mUiFirstDestroyedTime == 0) {
mUiFirstDestroyedTime = SystemClock.elapsedRealtime();
- duration = mUiFirstDestroyedTime - mFirstRequestTime;
+ duration = mUiFirstDestroyedTime - mFirstRequestTime;
if (sDebug) Log.d(TAG, "UI destroyed in " + formatDuration(duration));
}
- break;
+ } break;
+
default:
Log.w(TAG, "invalid event reported: " + event);
}
- logResponse(type, mServicePackageName, componentName, mSessionId, duration);
+ logResponse(type, mServicePackageName, mComponentName, mSessionId, duration);
}
public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("sessionId: "); pw.println(mSessionId);
- pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
+ pw.print(prefix); pw.print("taskId: "); pw.println(mTaskId);
pw.print(prefix); pw.print("component: ");
- pw.println(componentName.flattenToShortString());
+ pw.println(mComponentName.flattenToShortString());
pw.print(prefix); pw.print("focusedId: "); pw.println(mFocusedId);
if (mFocusedValue != null) {
pw.print(prefix); pw.print("focusedValue: "); pw.println(mFocusedValue);
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index d0ffd7b..19eff57 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -54,13 +54,15 @@
if (sDebug) Log.d(TAG, "onSuccess(): " + response);
if (response == null) {
- mProxy.report(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
+ mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
+ mProxy.reportResult(null /*inlineSuggestions*/);
return;
}
List<Dataset> inlineSuggestions = response.getInlineSuggestions();
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
- mProxy.onInlineSuggestionsDataReady(inlineSuggestions, response.getClientState());
+ mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
+ mProxy.reportResult(inlineSuggestions);
return;
}
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index 63ec2d8..7d552d6 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -62,12 +62,13 @@
try {
mProxy.autofill(values);
- final FillWindow fillWindow = mProxy.getFillWindow();
- if (fillWindow != null) {
- fillWindow.destroy();
- }
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
+
+ final FillWindow fillWindow = mProxy.getFillWindow();
+ if (fillWindow != null) {
+ fillWindow.destroy();
+ }
}
}
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index ca49e7d..6927cf6 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -53,7 +53,7 @@
* Gets the task of the activity associated with this request.
*/
public int getTaskId() {
- return mProxy.taskId;
+ return mProxy.mTaskId;
}
/**
@@ -61,7 +61,7 @@
*/
@NonNull
public ComponentName getActivityComponent() {
- return mProxy.componentName;
+ return mProxy.mComponentName;
}
/**
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 5d00370..077df6c 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -21,6 +21,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.graphics.Rect;
@@ -41,6 +42,7 @@
import dalvik.system.CloseGuard;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
/**
* Handle to a window used to display the augmented autofill UI.
@@ -70,23 +72,22 @@
private final CloseGuard mCloseGuard = CloseGuard.get();
private final @NonNull Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
- private final @NonNull FillWindowPresenter mFillWindowPresenter = new FillWindowPresenter();
@GuardedBy("mLock")
- private WindowManager mWm;
+ private @NonNull WindowManager mWm;
@GuardedBy("mLock")
private View mFillView;
@GuardedBy("mLock")
private boolean mShowing;
@GuardedBy("mLock")
- private Rect mBounds;
+ private @Nullable Rect mBounds;
@GuardedBy("mLock")
private boolean mUpdateCalled;
@GuardedBy("mLock")
private boolean mDestroyed;
- private AutofillProxy mProxy;
+ private @NonNull AutofillProxy mProxy;
/**
* Updates the content of the window.
@@ -172,11 +173,11 @@
try {
mProxy.requestShowFillUi(mBounds.right - mBounds.left,
mBounds.bottom - mBounds.top,
- /*anchorBounds=*/ null, mFillWindowPresenter);
+ /*anchorBounds=*/ null, new FillWindowPresenter(this));
} catch (RemoteException e) {
Log.w(TAG, "Error requesting to show fill window", e);
}
- mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
+ mProxy.logEvent(AutofillProxy.REPORT_EVENT_UI_SHOWN);
}
}
}
@@ -244,7 +245,7 @@
if (mUpdateCalled) {
mFillView.setOnClickListener(null);
hide();
- mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
+ mProxy.logEvent(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
}
mDestroyed = true;
mCloseGuard.close();
@@ -254,9 +255,7 @@
@Override
protected void finalize() throws Throwable {
try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
+ mCloseGuard.warnIfOpen();
destroy();
} finally {
super.finalize();
@@ -289,22 +288,36 @@
/** @hide */
@Override
- public void close() throws Exception {
+ public void close() {
destroy();
}
- private final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
+ private static final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
+ private final @NonNull WeakReference<FillWindow> mFillWindowReference;
+
+ FillWindowPresenter(@NonNull FillWindow fillWindow) {
+ mFillWindowReference = new WeakReference<>(fillWindow);
+ }
+
@Override
public void show(WindowManager.LayoutParams p, Rect transitionEpicenter,
boolean fitsSystemWindows, int layoutDirection) {
if (sDebug) Log.d(TAG, "FillWindowPresenter.show()");
- mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleShow, FillWindow.this, p));
+ final FillWindow fillWindow = mFillWindowReference.get();
+ if (fillWindow != null) {
+ fillWindow.mUiThreadHandler.sendMessage(
+ obtainMessage(FillWindow::handleShow, fillWindow, p));
+ }
}
@Override
public void hide(Rect transitionEpicenter) {
if (sDebug) Log.d(TAG, "FillWindowPresenter.hide()");
- mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleHide, FillWindow.this));
+ final FillWindow fillWindow = mFillWindowReference.get();
+ if (fillWindow != null) {
+ fillWindow.mUiThreadHandler.sendMessage(
+ obtainMessage(FillWindow::handleHide, fillWindow));
+ }
}
}
}
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index 31e77f35..d983721 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -28,7 +28,7 @@
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
- void onSuccess(in @nullable Dataset[] inlineSuggestionsData, in @nullable Bundle clientState);
+ void onSuccess(in @nullable Dataset[] inlineSuggestionsData);
boolean isCompleted();
void cancel();
}
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 8dca69f..848868a 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -27,7 +27,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
@@ -42,7 +41,6 @@
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassificationConstants;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
@@ -394,19 +392,32 @@
*/
@Deprecated
public final TextClassifier getLocalTextClassifier() {
- // Deprecated: In the future, we may not guarantee that this runs in the service's process.
- return getDefaultTextClassifierImplementation(this);
+ return TextClassifier.NO_OP;
}
/**
* Returns the platform's default TextClassifier implementation.
+ *
+ * @throws RuntimeException if the TextClassifier from
+ * PackageManager#getDefaultTextClassifierPackageName() calls
+ * this method.
*/
@NonNull
public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
+ final String defaultTextClassifierPackageName =
+ context.getPackageManager().getDefaultTextClassifierPackageName();
+ if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
+ return TextClassifier.NO_OP;
+ }
+ if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
+ throw new RuntimeException(
+ "The default text classifier itself should not call the"
+ + "getDefaultTextClassifierImplementation() method.");
+ }
final TextClassificationManager tcm =
context.getSystemService(TextClassificationManager.class);
if (tcm != null) {
- return tcm.getTextClassifier(TextClassifier.LOCAL);
+ return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE);
}
return TextClassifier.NO_OP;
}
@@ -434,46 +445,20 @@
}
/**
- * Returns the component name of the system default textclassifier service if it can be found
- * on the system. Otherwise, returns null.
+ * Returns the component name of the textclassifier service from the given package.
+ * Otherwise, returns null.
*
- * @param context the text classification context
+ * @param context
+ * @param packageName the package to look for.
+ * @param resolveFlags the flags that are used by PackageManager to resolve the component name.
* @hide
*/
@Nullable
- public static ComponentName getServiceComponentName(@NonNull Context context) {
- final TextClassificationConstants settings = TextClassificationManager.getSettings(context);
- // get override TextClassifierService package name
- String packageName = settings.getTextClassifierServicePackageOverride();
-
- ComponentName serviceComponent = null;
- final boolean isOverrideService = !TextUtils.isEmpty(packageName);
- if (isOverrideService) {
- serviceComponent = getServiceComponentNameByPackage(context, packageName,
- isOverrideService);
- }
- if (serviceComponent != null) {
- return serviceComponent;
- }
- // If no TextClassifierService override or invalid override package name, read the first
- // package defined in the config
- final String[] packages = context.getPackageManager().getSystemTextClassifierPackages();
- if (packages.length == 0 || TextUtils.isEmpty(packages[0])) {
- Slog.d(LOG_TAG, "No configured system TextClassifierService");
- return null;
- }
- packageName = packages[0];
- serviceComponent = getServiceComponentNameByPackage(context, packageName,
- isOverrideService);
- return serviceComponent;
- }
-
- private static ComponentName getServiceComponentNameByPackage(Context context,
- String packageName, boolean isOverrideService) {
+ public static ComponentName getServiceComponentName(
+ Context context, String packageName, int resolveFlags) {
final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName);
- final int flags = isOverrideService ? 0 : PackageManager.MATCH_SYSTEM_ONLY;
- final ResolveInfo ri = context.getPackageManager().resolveService(intent, flags);
+ final ResolveInfo ri = context.getPackageManager().resolveService(intent, resolveFlags);
if ((ri == null) || (ri.serviceInfo == null)) {
Slog.w(LOG_TAG, String.format("Package or service not found in package %s for user %d",
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 22d6f37..54de1bb 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -190,9 +190,7 @@
onAnimationFinish();
}
});
- setStartingAnimation(true);
mAnimator.start();
- setStartingAnimation(false);
}
@Override
@@ -203,9 +201,6 @@
}
}
- protected void setStartingAnimation(boolean startingAnimation) {
- }
-
protected void onAnimationFinish() {
mController.finish(mShow);
}
@@ -239,16 +234,6 @@
final @AnimationType int type;
}
- private class DefaultAnimationControlListener extends InternalAnimationControlListener {
- DefaultAnimationControlListener(boolean show) {
- super(show);
- }
-
- @Override
- protected void setStartingAnimation(boolean startingAnimation) {
- mStartingAnimation = startingAnimation;
- }
- }
/**
* Represents a control request that we had to defer because we are waiting for the IME to
* process our show request.
@@ -822,7 +807,8 @@
return;
}
- final DefaultAnimationControlListener listener = new DefaultAnimationControlListener(show);
+ final InternalAnimationControlListener listener =
+ new InternalAnimationControlListener(show);
// Show/hide animations always need to be relative to the display frame, in order that shown
// and hidden state insets are correct.
controlAnimationUnchecked(
@@ -878,7 +864,9 @@
return true;
}
mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds);
+ mStartingAnimation = true;
listener.onReady(controller, types);
+ mStartingAnimation = false;
return true;
}
});
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 80027b1e..6246b50 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -323,6 +323,7 @@
private int mUserId = UserHandle.USER_NULL;
@NonNull
private Bundle mExtras;
+ private boolean mUseDefaultTextClassifier;
private Request(
@NonNull List<Message> conversation,
@@ -347,6 +348,8 @@
String callingPackageName = in.readString();
int userId = in.readInt();
Bundle extras = in.readBundle();
+ boolean useDefaultTextClassifier = in.readBoolean();
+
Request request = new Request(
conversation,
typeConfig,
@@ -355,6 +358,7 @@
extras);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
+ request.setUseDefaultTextClassifier(useDefaultTextClassifier);
return request;
}
@@ -367,6 +371,7 @@
parcel.writeString(mCallingPackageName);
parcel.writeInt(mUserId);
parcel.writeBundle(mExtras);
+ parcel.writeBoolean(mUseDefaultTextClassifier);
}
@Override
@@ -455,6 +460,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the extended data related to this request.
*
* <p><b>NOTE: </b>Do not modify this bundle.
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 09cb7a0..e0f29a9 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -140,6 +140,7 @@
private int mEnd;
private int mSmartStart;
private int mSmartEnd;
+ private boolean mUseDefaultTextClassifier;
SelectionEvent(
int start, int end,
@@ -175,6 +176,7 @@
mSmartStart = in.readInt();
mSmartEnd = in.readInt();
mUserId = in.readInt();
+ mUseDefaultTextClassifier = in.readBoolean();
}
@Override
@@ -204,6 +206,7 @@
dest.writeInt(mSmartStart);
dest.writeInt(mSmartEnd);
dest.writeInt(mUserId);
+ dest.writeBoolean(mUseDefaultTextClassifier);
}
@Override
@@ -428,6 +431,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the type of widget that was involved in triggering this event.
*/
@WidgetType
@@ -642,7 +665,8 @@
return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId,
mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
- mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
+ mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd,
+ mUseDefaultTextClassifier);
}
@Override
@@ -673,7 +697,8 @@
&& mStart == other.mStart
&& mEnd == other.mEnd
&& mSmartStart == other.mSmartStart
- && mSmartEnd == other.mSmartEnd;
+ && mSmartEnd == other.mSmartEnd
+ && mUseDefaultTextClassifier == other.mUseDefaultTextClassifier;
}
@Override
@@ -683,12 +708,13 @@
+ "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
+ "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
+ "durationSincePreviousEvent=%d, eventIndex=%d,"
- + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}",
+ + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d, "
+ + "mUseDefaultTextClassifier=%b}",
mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod,
mUserId, mResultId, mEventTime, mDurationSinceSessionStart,
mDurationSincePreviousEvent, mEventIndex,
- mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
+ mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mUseDefaultTextClassifier);
}
public static final @android.annotation.NonNull Creator<SelectionEvent> CREATOR = new Creator<SelectionEvent>() {
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 138d25d..fe5e8d6 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -55,17 +55,20 @@
// service will throw a remote exception.
@UserIdInt
private final int mUserId;
+ private final boolean mUseDefault;
private TextClassificationSessionId mSessionId;
- public SystemTextClassifier(Context context, TextClassificationConstants settings)
- throws ServiceManager.ServiceNotFoundException {
+ public SystemTextClassifier(
+ Context context,
+ TextClassificationConstants settings,
+ boolean useDefault) throws ServiceManager.ServiceNotFoundException {
mManagerService = ITextClassifierService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
mSettings = Objects.requireNonNull(settings);
- mFallback = context.getSystemService(TextClassificationManager.class)
- .getTextClassifier(TextClassifier.LOCAL);
+ mFallback = TextClassifier.NO_OP;
mPackageName = Objects.requireNonNull(context.getOpPackageName());
mUserId = context.getUserId();
+ mUseDefault = useDefault;
}
/**
@@ -79,6 +82,7 @@
try {
request.setCallingPackageName(mPackageName);
request.setUserId(mUserId);
+ request.setUseDefaultTextClassifier(mUseDefault);
final BlockingCallback<TextSelection> callback =
new BlockingCallback<>("textselection");
mManagerService.onSuggestSelection(mSessionId, request, callback);
@@ -103,6 +107,7 @@
try {
request.setCallingPackageName(mPackageName);
request.setUserId(mUserId);
+ request.setUseDefaultTextClassifier(mUseDefault);
final BlockingCallback<TextClassification> callback =
new BlockingCallback<>("textclassification");
mManagerService.onClassifyText(mSessionId, request, callback);
@@ -124,7 +129,9 @@
public TextLinks generateLinks(@NonNull TextLinks.Request request) {
Objects.requireNonNull(request);
Utils.checkMainThread();
-
+ if (!Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength())) {
+ return mFallback.generateLinks(request);
+ }
if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) {
return Utils.generateLegacyLinks(request);
}
@@ -132,6 +139,7 @@
try {
request.setCallingPackageName(mPackageName);
request.setUserId(mUserId);
+ request.setUseDefaultTextClassifier(mUseDefault);
final BlockingCallback<TextLinks> callback =
new BlockingCallback<>("textlinks");
mManagerService.onGenerateLinks(mSessionId, request, callback);
@@ -152,6 +160,7 @@
try {
event.setUserId(mUserId);
+ event.setUseDefaultTextClassifier(mUseDefault);
mManagerService.onSelectionEvent(mSessionId, event);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error reporting selection event.", e);
@@ -169,6 +178,7 @@
.build()
: event.getEventContext();
tcContext.setUserId(mUserId);
+ tcContext.setUseDefaultTextClassifier(mUseDefault);
event.setEventContext(tcContext);
mManagerService.onTextClassifierEvent(mSessionId, event);
} catch (RemoteException e) {
@@ -184,6 +194,7 @@
try {
request.setCallingPackageName(mPackageName);
request.setUserId(mUserId);
+ request.setUseDefaultTextClassifier(mUseDefault);
final BlockingCallback<TextLanguage> callback =
new BlockingCallback<>("textlanguage");
mManagerService.onDetectLanguage(mSessionId, request, callback);
@@ -205,6 +216,7 @@
try {
request.setCallingPackageName(mPackageName);
request.setUserId(mUserId);
+ request.setUseDefaultTextClassifier(mUseDefault);
final BlockingCallback<ConversationActions> callback =
new BlockingCallback<>("conversation-actions");
mManagerService.onSuggestConversationActions(mSessionId, request, callback);
@@ -225,7 +237,7 @@
@WorkerThread
public int getMaxGenerateLinksTextLength() {
// TODO: retrieve this from the bound service.
- return mFallback.getMaxGenerateLinksTextLength();
+ return mSettings.getGenerateLinksMaxTextLength();
}
@Override
@@ -247,6 +259,7 @@
printWriter.printPair("mPackageName", mPackageName);
printWriter.printPair("mSessionId", mSessionId);
printWriter.printPair("mUserId", mUserId);
+ printWriter.printPair("mUseDefault", mUseDefault);
printWriter.decreaseIndent();
printWriter.println();
}
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 3628d2d4..00f762b 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -555,6 +555,7 @@
@Nullable private String mCallingPackageName;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
+ private boolean mUseDefaultTextClassifier;
private Request(
CharSequence text,
@@ -654,6 +655,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Do not modify this bundle.
@@ -755,6 +776,7 @@
dest.writeString(mCallingPackageName);
dest.writeInt(mUserId);
dest.writeBundle(mExtras);
+ dest.writeBoolean(mUseDefaultTextClassifier);
}
private static Request readFromParcel(Parcel in) {
@@ -768,11 +790,13 @@
final String callingPackageName = in.readString();
final int userId = in.readInt();
final Bundle extras = in.readBundle();
+ final boolean useDefaultTextClassifier = in.readBoolean();
final Request request = new Request(text, startIndex, endIndex,
defaultLocales, referenceTime, extras);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
+ request.setUseDefaultTextClassifier(useDefaultTextClassifier);
return request;
}
diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java
index 930765b..d58d175 100644
--- a/core/java/android/view/textclassifier/TextClassificationContext.java
+++ b/core/java/android/view/textclassifier/TextClassificationContext.java
@@ -38,6 +38,7 @@
@Nullable private final String mWidgetVersion;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
+ private boolean mUseDefaultTextClassifier;
private TextClassificationContext(
String packageName,
@@ -76,6 +77,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the widget type for this classification context.
*/
@NonNull
@@ -156,6 +177,7 @@
parcel.writeString(mWidgetType);
parcel.writeString(mWidgetVersion);
parcel.writeInt(mUserId);
+ parcel.writeBoolean(mUseDefaultTextClassifier);
}
private TextClassificationContext(Parcel in) {
@@ -163,6 +185,7 @@
mWidgetType = in.readString();
mWidgetVersion = in.readString();
mUserId = in.readInt();
+ mUseDefaultTextClassifier = in.readBoolean();
}
public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR =
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index bb96d55..a6c83a1 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -25,7 +25,7 @@
import android.os.ServiceManager;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
-import android.service.textclassifier.TextClassifierService;
+import android.util.SparseArray;
import android.view.textclassifier.TextClassifier.TextClassifierType;
import com.android.internal.annotations.GuardedBy;
@@ -62,8 +62,7 @@
@Nullable
private TextClassifier mLocalTextClassifier;
@GuardedBy("mLock")
- @Nullable
- private TextClassifier mSystemTextClassifier;
+ private SparseArray<TextClassifier> mSystemTextClassifiers = new SparseArray<>();
@GuardedBy("mLock")
private TextClassificationSessionFactory mSessionFactory;
@GuardedBy("mLock")
@@ -91,8 +90,8 @@
synchronized (mLock) {
if (mCustomTextClassifier != null) {
return mCustomTextClassifier;
- } else if (isSystemTextClassifierEnabled()) {
- return getSystemTextClassifier();
+ } else if (getSettings().isSystemTextClassifierEnabled()) {
+ return getSystemTextClassifier(SystemTextClassifier.SYSTEM);
} else {
return getLocalTextClassifier();
}
@@ -116,6 +115,7 @@
*
* @see TextClassifier#LOCAL
* @see TextClassifier#SYSTEM
+ * @see TextClassifier#DEFAULT_SERVICE
* @hide
*/
@UnsupportedAppUsage
@@ -124,7 +124,7 @@
case TextClassifier.LOCAL:
return getLocalTextClassifier();
default:
- return getSystemTextClassifier();
+ return getSystemTextClassifier(type);
}
}
@@ -204,21 +204,28 @@
}
}
- private TextClassifier getSystemTextClassifier() {
+ /** @hide */
+ private TextClassifier getSystemTextClassifier(@TextClassifierType int type) {
synchronized (mLock) {
- if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
+ if (mSystemTextClassifiers.get(type) == null
+ && getSettings().isSystemTextClassifierEnabled()) {
try {
- mSystemTextClassifier = new SystemTextClassifier(mContext, getSettings());
- Log.d(LOG_TAG, "Initialized SystemTextClassifier");
+ mSystemTextClassifiers.put(
+ type,
+ new SystemTextClassifier(
+ mContext,
+ getSettings(),
+ /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE));
+ Log.d(LOG_TAG, "Initialized SystemTextClassifier, type = " + type);
} catch (ServiceManager.ServiceNotFoundException e) {
Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
}
}
+ if (mSystemTextClassifiers.get(type) != null) {
+ return mSystemTextClassifiers.get(type);
+ }
+ return TextClassifier.NO_OP;
}
- if (mSystemTextClassifier != null) {
- return mSystemTextClassifier;
- }
- return TextClassifier.NO_OP;
}
/**
@@ -240,11 +247,6 @@
}
}
- private boolean isSystemTextClassifierEnabled() {
- return getSettings().isSystemTextClassifierEnabled()
- && TextClassifierService.getServiceComponentName(mContext) != null;
- }
-
/** @hide */
@VisibleForTesting
public void invalidateForTesting() {
@@ -261,7 +263,7 @@
private void invalidateTextClassifiers() {
synchronized (mLock) {
mLocalTextClassifier = null;
- mSystemTextClassifier = null;
+ mSystemTextClassifiers.clear();
}
}
@@ -274,7 +276,8 @@
/** @hide **/
public void dump(IndentingPrintWriter pw) {
getLocalTextClassifier().dump(pw);
- getSystemTextClassifier().dump(pw);
+ getSystemTextClassifier(TextClassifier.DEFAULT_SERVICE).dump(pw);
+ getSystemTextClassifier(TextClassifier.SYSTEM).dump(pw);
getSettings().dump(pw);
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 9b33693..2cc226d 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -66,12 +66,14 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {LOCAL, SYSTEM})
+ @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SERVICE})
@interface TextClassifierType {} // TODO: Expose as system APIs.
/** Specifies a TextClassifier that runs locally in the app's process. @hide */
int LOCAL = 0;
/** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */
int SYSTEM = 1;
+ /** Specifies the default TextClassifier that runs in the system process. @hide */
+ int DEFAULT_SERVICE = 2;
/** The TextClassifier failed to run. */
String TYPE_UNKNOWN = "";
@@ -667,8 +669,10 @@
Preconditions.checkArgument(endIndex > startIndex);
}
- static void checkTextLength(CharSequence text, int maxLength) {
- Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()");
+ /** Returns if the length of the text is within the range. */
+ static boolean checkTextLength(CharSequence text, int maxLength) {
+ int textLength = text.length();
+ return textLength >= 0 && textLength <= maxLength;
}
/**
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 61bd7c7..d7149ee 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -286,8 +286,10 @@
@WorkerThread
public TextLinks generateLinks(@NonNull TextLinks.Request request) {
Objects.requireNonNull(request);
- Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength());
Utils.checkMainThread();
+ if (!Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength())) {
+ return mFallback.generateLinks(request);
+ }
if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) {
return Utils.generateLegacyLinks(request);
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index cc9109e..58024dc 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -230,6 +230,7 @@
@Nullable private String mCallingPackageName;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
+ private boolean mUseDefaultTextClassifier;
private Request(CharSequence text, Bundle bundle) {
mText = text;
@@ -283,6 +284,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns a bundle containing non-structured extra information about this request.
*
* <p><b>NOTE: </b>Do not modify this bundle.
@@ -303,6 +324,7 @@
dest.writeString(mCallingPackageName);
dest.writeInt(mUserId);
dest.writeBundle(mExtra);
+ dest.writeBoolean(mUseDefaultTextClassifier);
}
private static Request readFromParcel(Parcel in) {
@@ -310,10 +332,12 @@
final String callingPackageName = in.readString();
final int userId = in.readInt();
final Bundle extra = in.readBundle();
+ final boolean useDefaultTextClassifier = in.readBoolean();
final Request request = new Request(text, extra);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
+ request.setUseDefaultTextClassifier(useDefaultTextClassifier);
return request;
}
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index bda12b0..7430cb3 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -345,6 +345,7 @@
@Nullable private final ZonedDateTime mReferenceTime;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
+ private boolean mUseDefaultTextClassifier;
private Request(
CharSequence text,
@@ -447,6 +448,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Do not modify this bundle.
@@ -568,6 +589,7 @@
dest.writeInt(mUserId);
dest.writeBundle(mExtras);
dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
+ dest.writeBoolean(mUseDefaultTextClassifier);
}
private static Request readFromParcel(Parcel in) {
@@ -580,11 +602,13 @@
final String referenceTimeString = in.readString();
final ZonedDateTime referenceTime = referenceTimeString == null
? null : ZonedDateTime.parse(referenceTimeString);
+ final boolean useDefaultTextClassifier = in.readBoolean();
final Request request = new Request(text, defaultLocales, entityConfig,
/* legacyFallback= */ true, referenceTime, extras);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
+ request.setUseDefaultTextClassifier(useDefaultTextClassifier);
return request;
}
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 4a36cbf..575a072 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -216,6 +216,7 @@
@Nullable private String mCallingPackageName;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
+ private boolean mUseDefaultTextClassifier;
private Request(
CharSequence text,
@@ -316,6 +317,26 @@
}
/**
+ * Sets whether to use the default text classifier to handle this request.
+ * This will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+ mUseDefaultTextClassifier = useDefaultTextClassifier;
+ }
+
+ /**
+ * Returns whether to use the default text classifier to handle this request. This
+ * will be ignored if it is not the system text classifier to handle this request.
+ *
+ * @hide
+ */
+ public boolean getUseDefaultTextClassifier() {
+ return mUseDefaultTextClassifier;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Do not modify this bundle.
@@ -420,6 +441,7 @@
dest.writeString(mCallingPackageName);
dest.writeInt(mUserId);
dest.writeBundle(mExtras);
+ dest.writeBoolean(mUseDefaultTextClassifier);
}
private static Request readFromParcel(Parcel in) {
@@ -430,11 +452,13 @@
final String callingPackageName = in.readString();
final int userId = in.readInt();
final Bundle extras = in.readBundle();
+ final boolean systemTextClassifierType = in.readBoolean();
final Request request = new Request(text, startIndex, endIndex, defaultLocales,
/* darkLaunchAllowed= */ false, extras);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
+ request.setUseDefaultTextClassifier(systemTextClassifierType);
return request;
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 4752ead..9f03d95 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -756,9 +756,6 @@
*/
private ListItemAccessibilityDelegate mAccessibilityDelegate;
- private int mLastAccessibilityScrollEventFromIndex;
- private int mLastAccessibilityScrollEventToIndex;
-
/**
* Track the item count from the last time we handled a data change.
*/
@@ -1520,25 +1517,10 @@
onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
}
- /** @hide */
- @Override
- public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
- // Since this class calls onScrollChanged even if the mFirstPosition and the
- // child count have not changed we will avoid sending duplicate accessibility
- // events.
- if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
- final int firstVisiblePosition = getFirstVisiblePosition();
- final int lastVisiblePosition = getLastVisiblePosition();
- if (mLastAccessibilityScrollEventFromIndex == firstVisiblePosition
- && mLastAccessibilityScrollEventToIndex == lastVisiblePosition) {
- return;
- } else {
- mLastAccessibilityScrollEventFromIndex = firstVisiblePosition;
- mLastAccessibilityScrollEventToIndex = lastVisiblePosition;
- }
- }
- super.sendAccessibilityEventUnchecked(event);
- }
+ /**
+ * A TYPE_VIEW_SCROLLED event should be sent whenever a scroll happens, even if the
+ * mFirstPosition and the child count have not changed.
+ */
@Override
public CharSequence getAccessibilityClassName() {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 7fd4150..4e4a983 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -454,15 +454,17 @@
aspectRatio = 5.5f;
}
- final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
- final float sourceHeight = fontMetrics.descent - fontMetrics.ascent;
+ final Layout layout = mTextView.getLayout();
+ final int line = layout.getLineForOffset(mTextView.getSelectionStart());
+ final int sourceHeight =
+ layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line);
// Slightly increase the height to avoid tooLargeTextForMagnifier() returns true.
int height = (int)(sourceHeight * zoom) + 2;
int width = (int)(aspectRatio * height);
params.setFishEyeStyle()
.setSize(width, height)
- .setSourceSize(width, Math.round(sourceHeight))
+ .setSourceSize(width, sourceHeight)
.setElevation(0)
.setInitialZoom(zoom)
.setClippingEnabled(false);
@@ -5041,7 +5043,7 @@
// Vertically snap to middle of current line.
showPosInView.y = ((mTextView.getLayout().getLineTop(lineNumber)
- + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f
+ + mTextView.getLayout().getLineBottomWithoutSpacing(lineNumber)) / 2.0f
+ mTextView.getTotalPaddingTop() - mTextView.getScrollY()) * mTextViewScaleY;
return true;
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 79ec680..3c3daa3 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3255,6 +3255,9 @@
*/
@UnsupportedAppUsage
private void scrollListItemsBy(int amount) {
+ int oldX = mScrollX;
+ int oldY = mScrollY;
+
offsetChildrenTopAndBottom(amount);
final int listBottom = getHeight() - mListPadding.bottom;
@@ -3327,6 +3330,7 @@
recycleBin.fullyDetachScrapViews();
removeUnusedFixedViews(mHeaderViewInfos);
removeUnusedFixedViews(mFooterViewInfos);
+ onScrollChanged(mScrollX, mScrollY, oldX, oldY);
}
private View addViewAbove(View theView, int position) {
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 78d4e61..db714c2 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -266,6 +266,7 @@
* targeting API level {@link Build.VERSION_CODES#R} or higher that are in the background
* will not have custom toast views displayed.
*/
+ @Deprecated
public View getView() {
return mNextView;
}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index f5a19fe..6d2d735 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -52,6 +52,7 @@
public static final String DIALOGS_PACKAGE = "com.android.vpndialogs";
+ // TODO: Rename this to something that encompasses Settings-based Platform VPNs as well.
public static final String LEGACY_VPN = "[Legacy VPN]";
public static Intent getIntentForConfirmation() {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index cec68df..9758673 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -35,8 +35,13 @@
"android_animation_PropertyValuesHolder.cpp",
"android_os_SystemClock.cpp",
"android_os_SystemProperties.cpp",
+ "android_os_Trace.cpp",
+ "android_text_AndroidCharacter.cpp",
"android_util_EventLog.cpp",
"android_util_Log.cpp",
+ "android_util_StringBlock.cpp",
+ "android_util_XmlBlock.cpp",
+ "android_view_RenderNodeAnimator.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
"com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
],
@@ -114,14 +119,12 @@
"android_view_KeyEvent.cpp",
"android_view_MotionEvent.cpp",
"android_view_PointerIcon.cpp",
- "android_view_RenderNodeAnimator.cpp",
"android_view_Surface.cpp",
"android_view_SurfaceControl.cpp",
"android_graphics_BLASTBufferQueue.cpp",
"android_view_SurfaceSession.cpp",
"android_view_TextureView.cpp",
"android_view_VelocityTracker.cpp",
- "android_text_AndroidCharacter.cpp",
"android_text_Hyphenator.cpp",
"android_os_Debug.cpp",
"android_os_GraphicsEnvironment.cpp",
@@ -138,7 +141,6 @@
"android_os_SELinux.cpp",
"android_os_SharedMemory.cpp",
"android_os_storage_StorageManager.cpp",
- "android_os_Trace.cpp",
"android_os_UEventObserver.cpp",
"android_os_VintfObject.cpp",
"android_os_VintfRuntimeInfo.cpp",
@@ -150,8 +152,6 @@
"android_util_Binder.cpp",
"android_util_MemoryIntArray.cpp",
"android_util_Process.cpp",
- "android_util_StringBlock.cpp",
- "android_util_XmlBlock.cpp",
"android_util_jar_StrictJarFile.cpp",
"android_media_AudioDeviceAddress.cpp",
"android_media_AudioEffectDescriptor.cpp",
@@ -311,11 +311,8 @@
srcs: [
"android_content_res_ApkAssets.cpp",
"android_os_MessageQueue.cpp",
- "android_os_Trace.cpp",
"android_util_AssetManager.cpp",
"android_util_FileObserver.cpp",
- "android_util_StringBlock.cpp",
- "android_util_XmlBlock.cpp",
],
},
},
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 481be24..657336e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -634,8 +634,6 @@
char cachePruneBuf[sizeof("-Xzygote-max-boot-retry=")-1 + PROPERTY_VALUE_MAX];
char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
- char dex2oatXmsFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
- char dex2oatXmxFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
char dex2oatCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
char dex2oatImageCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
char dex2oatThreadsBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
@@ -885,88 +883,45 @@
bool skip_compilation = ((strcmp(voldDecryptBuf, "trigger_restart_min_framework") == 0) ||
(strcmp(voldDecryptBuf, "1") == 0));
- // Extra options for boot.art/boot.oat image generation.
- parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf,
- "-Xms", "-Ximage-compiler-option");
- parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf,
- "-Xmx", "-Ximage-compiler-option");
- if (skip_compilation) {
- addOption("-Ximage-compiler-option");
- addOption("--compiler-filter=assume-verified");
- } else {
- parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf,
- "--compiler-filter=", "-Ximage-compiler-option");
- }
-
- // If there is a boot profile, it takes precedence over the image and preloaded classes.
- if (hasFile("/system/etc/boot-image.prof")) {
- addOption("-Ximage-compiler-option");
- addOption("--profile-file=/system/etc/boot-image.prof");
- addOption("-Ximage-compiler-option");
- addOption("--compiler-filter=speed-profile");
- } else {
- ALOGE("Missing boot-image.prof file, /system/etc/boot-image.prof not found: %s\n",
- strerror(errno));
- return -1;
- }
-
-
- // If there is a dirty-image-objects file, push it.
- if (hasFile("/system/etc/dirty-image-objects")) {
- addOption("-Ximage-compiler-option");
- addOption("--dirty-image-objects=/system/etc/dirty-image-objects");
- }
-
- property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");
- parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option");
-
- // Extra options for DexClassLoader.
- parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xms", dex2oatXmsFlagsBuf,
- "-Xms", "-Xcompiler-option");
- parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xmx", dex2oatXmxFlagsBuf,
- "-Xmx", "-Xcompiler-option");
+ // Extra options for JIT.
if (skip_compilation) {
addOption("-Xcompiler-option");
addOption("--compiler-filter=assume-verified");
-
- // We skip compilation when a minimal runtime is brought up for decryption. In that case
- // /data is temporarily backed by a tmpfs, which is usually small.
- // If the system image contains prebuilts, they will be relocated into the tmpfs. In this
- // specific situation it is acceptable to *not* relocate and run out of the prebuilts
- // directly instead.
- addOption("--runtime-arg");
- addOption("-Xnorelocate");
} else {
parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,
"--compiler-filter=", "-Xcompiler-option");
}
parseCompilerOption("dalvik.vm.dex2oat-threads", dex2oatThreadsBuf, "-j", "-Xcompiler-option");
- parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
- "-Ximage-compiler-option");
parseCompilerOption("dalvik.vm.dex2oat-cpu-set", dex2oatCpuSetBuf, "--cpu-set=",
"-Xcompiler-option");
- parseCompilerOption("dalvik.vm.image-dex2oat-cpu-set", dex2oatCpuSetImageBuf, "--cpu-set=",
- "-Ximage-compiler-option");
-
- // The runtime will compile a boot image, when necessary, not using installd. Thus, we need to
- // pass the instruction-set-features/variant as an image-compiler-option.
- // Note: it is OK to reuse the buffer, as the values are exactly the same between
- // * compiler-option, used for runtime compilation (DexClassLoader)
- // * image-compiler-option, used for boot-image compilation on device
// Copy the variant.
sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", ABI_STRING);
parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
- "--instruction-set-variant=", "-Ximage-compiler-option");
- parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
"--instruction-set-variant=", "-Xcompiler-option");
// Copy the features.
sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", ABI_STRING);
parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
- "--instruction-set-features=", "-Ximage-compiler-option");
- parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
"--instruction-set-features=", "-Xcompiler-option");
+ /*
+ * When running with debug.generate-debug-info, add --generate-debug-info to
+ * the compiler options so that both JITted code and the boot image extension,
+ * if it is compiled on device, will include native debugging information.
+ */
+ property_get("debug.generate-debug-info", propBuf, "");
+ bool generate_debug_info = (strcmp(propBuf, "true") == 0);
+ if (generate_debug_info) {
+ addOption("-Xcompiler-option");
+ addOption("--generate-debug-info");
+ }
+
+ // The mini-debug-info makes it possible to backtrace through compiled code.
+ bool generate_mini_debug_info = property_get_bool("dalvik.vm.minidebuginfo", 0);
+ if (generate_mini_debug_info) {
+ addOption("-Xcompiler-option");
+ addOption("--generate-mini-debug-info");
+ }
property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");
parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");
@@ -975,6 +930,53 @@
property_get("dalvik.vm.extra-opts", extraOptsBuf, "");
parseExtraOpts(extraOptsBuf, NULL);
+ // Extra options for boot image extension generation.
+ if (skip_compilation) {
+ addOption("-Xnoimage-dex2oat");
+ } else {
+ parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf,
+ "-Xms", "-Ximage-compiler-option");
+ parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf,
+ "-Xmx", "-Ximage-compiler-option");
+
+ parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf,
+ "--compiler-filter=", "-Ximage-compiler-option");
+
+ // If there is a dirty-image-objects file, push it.
+ if (hasFile("/system/etc/dirty-image-objects")) {
+ addOption("-Ximage-compiler-option");
+ addOption("--dirty-image-objects=/system/etc/dirty-image-objects");
+ }
+
+ parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
+ "-Ximage-compiler-option");
+ parseCompilerOption("dalvik.vm.image-dex2oat-cpu-set", dex2oatCpuSetImageBuf, "--cpu-set=",
+ "-Ximage-compiler-option");
+
+ // The runtime may compile a boot image extension, when necessary, not using installd.
+ // Thus, we need to pass the instruction-set-features/variant as an image-compiler-option.
+ // Note: it is OK to reuse the buffer, as the values are exactly the same between
+ // * compiler-option, used for runtime compilation (DexClassLoader)
+ // * image-compiler-option, used for boot-image compilation on device
+ parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
+ "--instruction-set-variant=", "-Ximage-compiler-option");
+ parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
+ "--instruction-set-features=", "-Ximage-compiler-option");
+
+ if (generate_debug_info) {
+ addOption("-Ximage-compiler-option");
+ addOption("--generate-debug-info");
+ }
+
+ if (generate_mini_debug_info) {
+ addOption("-Ximage-compiler-option");
+ addOption("--generate-mini-debug-info");
+ }
+
+ property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");
+ parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option");
+ }
+
/* Set the properties for locale */
{
strcpy(localeOption, "-Duser.locale=");
@@ -1032,25 +1034,6 @@
parseRuntimeOption("dalvik.vm.zygote.max-boot-retry", cachePruneBuf,
"-Xzygote-max-boot-retry=");
- /*
- * When running with debug.generate-debug-info, add --generate-debug-info to
- * the compiler options so that the boot image, if it is compiled on device,
- * will include native debugging information.
- */
- property_get("debug.generate-debug-info", propBuf, "");
- if (strcmp(propBuf, "true") == 0) {
- addOption("-Xcompiler-option");
- addOption("--generate-debug-info");
- addOption("-Ximage-compiler-option");
- addOption("--generate-debug-info");
- }
-
- // The mini-debug-info makes it possible to backtrace through JIT code.
- if (property_get_bool("dalvik.vm.minidebuginfo", 0)) {
- addOption("-Xcompiler-option");
- addOption("--generate-mini-debug-info");
- }
-
// If set, the property below can be used to enable core platform API violation reporting.
property_get("persist.debug.dalvik.vm.core_platform_api_policy", propBuf, "");
if (propBuf[0] != '\0') {
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 6c0680f..571a3387 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -75,10 +75,12 @@
extern int register_android_os_SystemClock(JNIEnv* env);
extern int register_android_os_SystemProperties(JNIEnv* env);
extern int register_android_os_Trace(JNIEnv* env);
+extern int register_android_text_AndroidCharacter(JNIEnv* env);
extern int register_android_util_EventLog(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_view_RenderNode(JNIEnv* env);
+extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
@@ -90,58 +92,66 @@
// Map of all possible class names to register to their corresponding JNI registration function pointer
// The actual list of registered classes will be determined at runtime via the 'native_classes' System property
-static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
- {"android.animation.PropertyValuesHolder", REG_JNI(register_android_animation_PropertyValuesHolder)},
+static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
+ {"android.animation.PropertyValuesHolder",
+ REG_JNI(register_android_animation_PropertyValuesHolder)},
#ifdef __linux__
- {"android.content.AssetManager", REG_JNI(register_android_content_AssetManager)},
- {"android.content.StringBlock", REG_JNI(register_android_content_StringBlock)},
- {"android.content.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
- {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
+ {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
+ {"android.content.res.AssetManager", REG_JNI(register_android_content_AssetManager)},
#endif
- {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)},
- {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
- {"android.graphics.ByteBufferStreamAdaptor", REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
- {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
- {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
- {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
- {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
- {"android.graphics.CreateJavaOutputStreamAdaptor", REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)},
- {"android.graphics.DrawFilter", REG_JNI(register_android_graphics_DrawFilter)},
- {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)},
- {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)},
- {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)},
- {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)},
- {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)},
- {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)},
- {"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)},
- {"android.graphics.Path", REG_JNI(register_android_graphics_Path)},
- {"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)},
- {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)},
- {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)},
- {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
- {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
- {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
- {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
- {"android.graphics.drawable.AnimatedVectorDrawable", REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)},
- {"android.graphics.drawable.VectorDrawable", REG_JNI(register_android_graphics_drawable_VectorDrawable)},
- {"android.graphics.fonts.Font", REG_JNI(register_android_graphics_fonts_Font)},
- {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)},
- {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
- {"android.graphics.text.MeasuredText", REG_JNI(register_android_graphics_text_MeasuredText)},
+ {"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)},
+ {"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
+ {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)},
+ {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
+ {"android.graphics.ByteBufferStreamAdaptor",
+ REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
+ {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
+ {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
+ {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
+ {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
+ {"android.graphics.CreateJavaOutputStreamAdaptor",
+ REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)},
+ {"android.graphics.DrawFilter", REG_JNI(register_android_graphics_DrawFilter)},
+ {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)},
+ {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)},
+ {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)},
+ {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)},
+ {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)},
+ {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)},
+ {"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)},
+ {"android.graphics.Path", REG_JNI(register_android_graphics_Path)},
+ {"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)},
+ {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)},
+ {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)},
+ {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
+ {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
+ {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
+ {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
+ {"android.graphics.drawable.AnimatedVectorDrawable",
+ REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)},
+ {"android.graphics.drawable.VectorDrawable",
+ REG_JNI(register_android_graphics_drawable_VectorDrawable)},
+ {"android.graphics.fonts.Font", REG_JNI(register_android_graphics_fonts_Font)},
+ {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)},
+ {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
+ {"android.graphics.text.MeasuredText",
+ REG_JNI(register_android_graphics_text_MeasuredText)},
#ifdef __linux__
- {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
- {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
+ {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
+ {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
#endif
- {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
- {"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
-#ifdef __linux__
- {"android.os.Trace", REG_JNI(register_android_os_Trace)},
-#endif
- {"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
- {"android.util.Log", REG_JNI(register_android_util_Log)},
- {"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
- {"com.android.internal.util.VirtualRefBasePtr", REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
- {"com.android.internal.view.animation.NativeInterpolatorFactoryHelper", REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper)},
+ {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
+ {"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
+ {"android.os.Trace", REG_JNI(register_android_os_Trace)},
+ {"android.text.AndroidCharacter", REG_JNI(register_android_text_AndroidCharacter)},
+ {"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
+ {"android.util.Log", REG_JNI(register_android_util_Log)},
+ {"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
+ {"android.view.RenderNodeAnimator", REG_JNI(register_android_view_RenderNodeAnimator)},
+ {"com.android.internal.util.VirtualRefBasePtr",
+ REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
+ {"com.android.internal.view.animation.NativeInterpolatorFactoryHelper",
+ REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper)},
};
// Vector to store the names of classes that need delegates of their native methods
static vector<string> classesToDelegate;
@@ -159,7 +169,6 @@
int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods) {
-
string classNameString = string(className);
if (find(classesToDelegate.begin(), classesToDelegate.end(), classNameString)
!= classesToDelegate.end()) {
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index a6e08d2..fc0a2ef 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2562,7 +2562,7 @@
// CATEGORY: SETTINGS
// OS: R
OPEN_SUPPORTED_LINKS = 1824;
-
+
// OPEN: Settings > Display > Dark theme > Set start time dialog
DIALOG_DARK_THEME_SET_START_TIME = 1825;
@@ -2573,4 +2573,9 @@
// CATEGORY: SETTINGS
// OS: R
VIBRATE_FOR_CALLS = 1827;
+
+ // OPEN: Settings > Connected devices > Connection preferences > NFC
+ // CATEGORY: SETTINGS
+ // OS: R
+ CONNECTION_DEVICE_ADVANCED_NFC = 1828;
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 8adcc9e..bf4cdee 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -52,6 +52,7 @@
import "frameworks/base/core/proto/android/service/print.proto";
import "frameworks/base/core/proto/android/service/procstats.proto";
import "frameworks/base/core/proto/android/service/restricted_image.proto";
+import "frameworks/base/core/proto/android/service/sensor_service.proto";
import "frameworks/base/core/proto/android/service/usb.proto";
import "frameworks/base/core/proto/android/util/event_log_tags.proto";
import "frameworks/base/core/proto/android/util/log.proto";
@@ -492,6 +493,11 @@
(section).args = "contexthub --proto"
];
+ optional android.service.SensorServiceProto sensor_service = 3053 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "sensorservice --proto"
+ ];
+
// Reserved for OEMs.
extensions 50000 to 100000;
}
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 0a2fd70..2d2ead4 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -27,7 +27,6 @@
import "frameworks/base/core/proto/android/content/configuration.proto";
import "frameworks/base/core/proto/android/content/intent.proto";
import "frameworks/base/core/proto/android/content/package_item_info.proto";
-import "frameworks/base/core/proto/android/graphics/rect.proto";
import "frameworks/base/core/proto/android/internal/processstats.proto";
import "frameworks/base/core/proto/android/os/bundle.proto";
import "frameworks/base/core/proto/android/os/looper.proto";
diff --git a/core/proto/android/server/notificationhistory.proto b/core/proto/android/server/notificationhistory.proto
index 1e6ee3f..6749719 100644
--- a/core/proto/android/server/notificationhistory.proto
+++ b/core/proto/android/server/notificationhistory.proto
@@ -17,8 +17,6 @@
syntax = "proto2";
package com.android.server.notification;
-import "frameworks/base/core/proto/android/server/enums.proto";
-
option java_multiple_files = true;
// On disk data store for historical notifications
diff --git a/core/proto/android/service/sensor_service.proto b/core/proto/android/service/sensor_service.proto
new file mode 100644
index 0000000..8598f86
--- /dev/null
+++ b/core/proto/android/service/sensor_service.proto
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.service;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+/*
+ * Notes:
+ * 1. When using ProtoOutputStream to write this proto message, must call
+ * token = ProtoOutputStream#start(fieldId) before and ProtoOutputStream#end(token) after
+ * writing a nested message.
+ * 2. Never reuse a proto field number. When removing a field, mark it as reserved.
+ */
+
+// Proto dump of android::SensorService. dumpsys sensorservice --proto
+message SensorServiceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ enum OperatingModeEnum {
+ OP_MODE_UNKNOWN = 0;
+ OP_MODE_NORMAL = 1;
+ OP_MODE_RESTRICTED = 2;
+ OP_MODE_DATA_INJECTION = 3;
+ }
+
+ optional int64 current_time_ms = 1;
+ optional SensorDeviceProto sensor_device = 2;
+ optional SensorListProto sensors = 3;
+ optional SensorFusionProto fusion_state = 4;
+ optional SensorEventsProto sensor_events = 5;
+ repeated ActiveSensorProto active_sensors = 6;
+ optional int32 socket_buffer_size = 7;
+ optional int32 socket_buffer_size_in_events = 8;
+ optional bool wake_lock_acquired = 9;
+ optional OperatingModeEnum operating_mode = 10;
+ // Non-empty only if operating_mode is RESTRICTED or DATA_INJECTION.
+ optional string whitelisted_package = 11;
+ optional bool sensor_privacy = 12;
+ repeated SensorEventConnectionProto active_connections = 13;
+ repeated SensorDirectConnectionProto direct_connections = 14;
+ repeated SensorRegistrationInfoProto previous_registrations = 15;
+}
+
+// Proto dump of android::SensorDevice
+message SensorDeviceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional bool initialized = 1;
+ optional int32 total_sensors = 2;
+ optional int32 active_sensors = 3;
+
+ message SensorProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 handle = 1;
+ optional int32 active_count = 2;
+ repeated float sampling_period_ms = 3;
+ optional float sampling_period_selected = 4;
+ repeated float batching_period_ms = 5;
+ optional float batching_period_selected = 6;
+ }
+ repeated SensorProto sensors = 4;
+}
+
+// Proto dump of android::SensorServiceUtil::SensorList
+message SensorListProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ enum ReportingModeEnum {
+ RM_UNKNOWN = 0;
+ RM_CONTINUOUS = 1;
+ RM_ON_CHANGE = 2;
+ RM_ONE_SHOT = 3;
+ RM_SPECIAL_TRIGGER = 4;
+ }
+
+ message SensorProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 handle = 1;
+ optional string name = 2;
+ optional string vendor = 3;
+ optional int32 version = 4;
+ optional string string_type = 5;
+ optional int32 type = 6;
+ optional string required_permission = 7;
+ optional int32 flags = 8;
+ optional ReportingModeEnum reporting_mode = 9;
+ optional int32 max_delay_us = 10;
+ optional int32 min_delay_us = 11;
+ optional int32 fifo_max_event_count = 12;
+ optional int32 fifo_reserved_event_count = 13;
+ optional bool is_wakeup = 14;
+ optional bool data_injection_supported = 15;
+ optional bool is_dynamic = 16;
+ optional bool has_additional_info = 17;
+ optional int32 highest_rate_level = 18;
+ optional bool ashmem = 19;
+ optional bool gralloc = 20;
+ optional float min_value = 21;
+ optional float max_value = 22;
+ optional float resolution = 23;
+ optional float power_usage = 24;
+ }
+ repeated SensorProto sensors = 1;
+}
+
+
+// Proto dump of android::SensorFusion
+message SensorFusionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ message FusionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional bool enabled = 1;
+ optional int32 num_clients = 2;
+ optional float estimated_gyro_rate = 3;
+ optional float attitude_x = 4;
+ optional float attitude_y = 5;
+ optional float attitude_z = 6;
+ optional float attitude_w = 7;
+ optional float attitude_length = 8;
+ optional float bias_x = 9;
+ optional float bias_y = 10;
+ optional float bias_z = 11;
+ }
+ optional FusionProto fusion_9axis = 1;
+ optional FusionProto fusion_nomag = 2;
+ optional FusionProto fusion_nogyro = 3;
+}
+
+// Proto dump of android::SensorServiceUtil::RecentEventLogger
+message SensorEventsProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ message Event {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional float timestamp_sec = 1;
+ optional int64 wall_timestamp_ms = 2;
+ optional bool masked = 3;
+ optional int64 int64_data = 4;
+ repeated float float_array = 5;
+ }
+
+ message RecentEventsLog {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string name = 1;
+ optional int32 recent_events_count = 2;
+ repeated Event events = 3;
+ }
+ repeated RecentEventsLog recent_events_logs = 1;
+}
+
+message ActiveSensorProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string name = 1;
+ optional int32 handle = 2;
+ optional int32 num_connections = 3;
+}
+
+// Proto dump of android::SensorService::SensorDirectConnection
+message SensorDirectConnectionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ message SensorProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 sensor = 1;
+ optional int32 rate = 2;
+ }
+
+ optional string package_name = 1;
+ optional int32 hal_channel_handle = 2;
+ optional int32 num_sensor_activated = 3;
+ repeated SensorProto sensors = 4;
+}
+
+// Proto dump of android::SensorService::SensorEventConnection
+message SensorEventConnectionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ message FlushInfoProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional string sensor_name = 1;
+ optional int32 sensor_handle = 2;
+ optional bool first_flush_pending = 3;
+ optional int32 pending_flush_events_to_send = 4;
+ }
+
+ enum OperatingModeEnum {
+ OP_MODE_UNKNOWN = 0;
+ OP_MODE_NORMAL = 1;
+ OP_MODE_RESTRICTED = 2;
+ OP_MODE_DATA_INJECTION = 3;
+ }
+
+ optional OperatingModeEnum operating_mode = 1;
+ optional string package_name = 2;
+ optional int32 wake_lock_ref_count = 3;
+ optional int32 uid = 4;
+ optional int32 cache_size = 5;
+ optional int32 max_cache_size = 6;
+ repeated FlushInfoProto flush_infos = 7;
+ optional int32 events_received = 8;
+ optional int32 events_sent = 9;
+ optional int32 events_cache = 10;
+ optional int32 events_dropped = 11;
+ optional int32 total_acks_needed = 12;
+ optional int32 total_acks_received = 13;
+}
+
+// Proto dump of android::SensorService::SensorRegistrationInfo
+message SensorRegistrationInfoProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int64 timestamp_sec = 1;
+ optional int32 sensor_handle = 2;
+ optional string package_name = 3;
+ optional int32 pid = 4;
+ optional int32 uid = 5;
+ optional int64 sampling_rate_us = 6;
+ optional int64 max_report_latency_us = 7;
+ optional bool activated = 8;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7a30256..efa7d59 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3448,6 +3448,14 @@
<permission android:name="android.permission.TUNER_RESOURCE_ACCESS"
android:protectionLevel="signature|privileged" />
+ <!-- This permission is required by Media Resource Manager Service when
+ accessing its overridePid Api.
+ <p>Protection level: signature|privileged
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"
+ android:protectionLevel="signature|privileged" />
+
<!-- Must be required by a {@link android.media.routing.MediaRouteService}
to ensure that only the system can interact with it.
@hide -->
@@ -4057,6 +4065,11 @@
<permission android:name="android.permission.STATSCOMPANION"
android:protectionLevel="signature" />
+ <!--@SystemApi @hide Allows an application to register stats pull atom callbacks.
+ <p>Not for use by third-party applications.-->
+ <permission android:name="android.permission.REGISTER_STATS_PULL_ATOM"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows an application to control the backup and restore process.
<p>Not for use by third-party applications.
@hide pending API council -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3d7b1e1..b2d08a9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3560,25 +3560,16 @@
-->
<string name="config_defaultAutofillService" translatable="false"></string>
- <!-- The package name for the default system textclassifier service.
+ <!-- The package name for the OEM custom system textclassifier service.
This service must be trusted, as it can be activated without explicit consent of the user.
Example: "com.android.textclassifier"
- If no textclassifier service with the specified name exists on the device (or if this is
- set to empty string), a default textclassifier will be loaded in the calling app's process.
+ If this is empty, the default textclassifier service (i.e. config_servicesExtensionPackage)
+ will be used.
+
See android.view.textclassifier.TextClassificationManager.
-->
- <!-- TODO(b/144896755) remove the config -->
<string name="config_defaultTextClassifierPackage" translatable="false"></string>
- <!-- A list of supported system textClassifier service package names. Only one of the packages
- will be activated. The first package in the list is the default system textClassifier
- service. OS only tries to bind and grant permissions to the first trusted service and the
- others can be selected via device config. These services must be trusted, as they can be
- activated without explicit consent of the user. Example: "com.android.textclassifier"
- -->
- <string-array name="config_defaultTextClassifierPackages" translatable="false">
- <item>android.ext.services</item>
- </string-array>
<!-- The package name for the system companion device manager service.
This service must be trusted, as it can be activated without explicit consent of the user.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e839550..c59d25f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3393,7 +3393,6 @@
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
- <java-symbol type="array" name="config_defaultTextClassifierPackages" />
<java-symbol type="string" name="config_defaultWellbeingPackage" />
<java-symbol type="string" name="config_telephonyPackages" />
<java-symbol type="string" name="config_defaultContentCaptureService" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
index 6c5d548..02be557 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -24,6 +24,9 @@
import android.content.Context;
import android.content.pm.ConfigurationInfo;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.test.AndroidTestCase;
import androidx.test.filters.SmallTest;
@@ -137,21 +140,7 @@
// Must overwrite all the fields
td2.copyFrom(td1);
- assertEquals(td1.getLabel(), td2.getLabel());
- assertEquals(td1.getInMemoryIcon(), td2.getInMemoryIcon());
- assertEquals(td1.getIconFilename(), td2.getIconFilename());
- assertEquals(td1.getIconResource(), td2.getIconResource());
- assertEquals(td1.getPrimaryColor(), td2.getPrimaryColor());
- assertEquals(td1.getBackgroundColor(), td2.getBackgroundColor());
- assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor());
- assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor());
- assertEquals(td1.getEnsureStatusBarContrastWhenTransparent(),
- td2.getEnsureStatusBarContrastWhenTransparent());
- assertEquals(td1.getEnsureNavigationBarContrastWhenTransparent(),
- td2.getEnsureNavigationBarContrastWhenTransparent());
- assertEquals(td1.getResizeMode(), td2.getResizeMode());
- assertEquals(td1.getMinWidth(), td2.getMinWidth());
- assertEquals(td1.getMinHeight(), td2.getMinHeight());
+ assertTaskDescriptionEqual(td1, td2, true, true);
}
@SmallTest
@@ -191,44 +180,101 @@
// Must overwrite all public and hidden fields, since other has all fields set.
td2.copyFromPreserveHiddenFields(td1);
- assertEquals(td1.getLabel(), td2.getLabel());
- assertEquals(td1.getInMemoryIcon(), td2.getInMemoryIcon());
- assertEquals(td1.getIconFilename(), td2.getIconFilename());
- assertEquals(td1.getIconResource(), td2.getIconResource());
- assertEquals(td1.getPrimaryColor(), td2.getPrimaryColor());
- assertEquals(td1.getBackgroundColor(), td2.getBackgroundColor());
- assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor());
- assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor());
- assertEquals(td1.getEnsureStatusBarContrastWhenTransparent(),
- td2.getEnsureStatusBarContrastWhenTransparent());
- assertEquals(td1.getEnsureNavigationBarContrastWhenTransparent(),
- td2.getEnsureNavigationBarContrastWhenTransparent());
- assertEquals(td1.getResizeMode(), td2.getResizeMode());
- assertEquals(td1.getMinWidth(), td2.getMinWidth());
- assertEquals(td1.getMinHeight(), td2.getMinHeight());
+ assertTaskDescriptionEqual(td1, td2, true, true);
TaskDescription td3 = new TaskDescription();
// Must overwrite only public fields, and preserve hidden fields.
td2.copyFromPreserveHiddenFields(td3);
- // Overwritten fields
- assertEquals(td3.getLabel(), td2.getLabel());
- assertEquals(td3.getInMemoryIcon(), td2.getInMemoryIcon());
- assertEquals(td3.getIconFilename(), td2.getIconFilename());
- assertEquals(td3.getIconResource(), td2.getIconResource());
- assertEquals(td3.getPrimaryColor(), td2.getPrimaryColor());
- assertEquals(td3.getEnsureStatusBarContrastWhenTransparent(),
- td2.getEnsureStatusBarContrastWhenTransparent());
- assertEquals(td3.getEnsureNavigationBarContrastWhenTransparent(),
- td2.getEnsureNavigationBarContrastWhenTransparent());
+ assertTaskDescriptionEqual(td3, td2, true, false);
+ assertTaskDescriptionEqual(td1, td2, false, true);
+ }
- // Preserved fields
- assertEquals(td1.getBackgroundColor(), td2.getBackgroundColor());
- assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor());
- assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor());
- assertEquals(td1.getResizeMode(), td2.getResizeMode());
- assertEquals(td1.getMinWidth(), td2.getMinWidth());
- assertEquals(td1.getMinHeight(), td2.getMinHeight());
+ @SmallTest
+ public void testTaskDescriptionParceling() throws Exception {
+ TaskDescription tdBitmapNull = new TaskDescription(
+ "test label", // label
+ null, // bitmap
+ 21, // iconRes
+ "dummy file", // iconFilename
+ 0x111111, // colorPrimary
+ 0x222222, // colorBackground
+ 0x333333, // statusBarColor
+ 0x444444, // navigationBarColor
+ false, // ensureStatusBarContrastWhenTransparent
+ false, // ensureNavigationBarContrastWhenTransparent
+ RESIZE_MODE_UNRESIZEABLE, // resizeMode
+ 10, // minWidth
+ 20 // minHeight
+ );
+
+ // Normal parceling should keep everything the same.
+ TaskDescription tdParcelled = new TaskDescription(parcelingRoundTrip(tdBitmapNull));
+ assertTaskDescriptionEqual(tdBitmapNull, tdParcelled, true, true);
+
+ Bitmap recycledBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
+ recycledBitmap.recycle();
+ assertTrue(recycledBitmap.isRecycled());
+ TaskDescription tdBitmapRecycled = new TaskDescription(
+ "test label", // label
+ recycledBitmap, // bitmap
+ 21, // iconRes
+ "dummy file", // iconFilename
+ 0x111111, // colorPrimary
+ 0x222222, // colorBackground
+ 0x333333, // statusBarColor
+ 0x444444, // navigationBarColor
+ false, // ensureStatusBarContrastWhenTransparent
+ false, // ensureNavigationBarContrastWhenTransparent
+ RESIZE_MODE_UNRESIZEABLE, // resizeMode
+ 10, // minWidth
+ 20 // minHeight
+ );
+ // Recycled bitmap will be ignored while parceling.
+ tdParcelled = new TaskDescription(parcelingRoundTrip(tdBitmapRecycled));
+ assertTaskDescriptionEqual(tdBitmapNull, tdParcelled, true, true);
+
+ }
+
+ private void assertTaskDescriptionEqual(TaskDescription td1, TaskDescription td2,
+ boolean checkOverwrittenFields, boolean checkPreservedFields) {
+ if (checkOverwrittenFields) {
+ assertEquals(td1.getLabel(), td2.getLabel());
+ assertEquals(td1.getInMemoryIcon(), td2.getInMemoryIcon());
+ assertEquals(td1.getIconFilename(), td2.getIconFilename());
+ assertEquals(td1.getIconResource(), td2.getIconResource());
+ assertEquals(td1.getPrimaryColor(), td2.getPrimaryColor());
+ assertEquals(td1.getEnsureStatusBarContrastWhenTransparent(),
+ td2.getEnsureStatusBarContrastWhenTransparent());
+ assertEquals(td1.getEnsureNavigationBarContrastWhenTransparent(),
+ td2.getEnsureNavigationBarContrastWhenTransparent());
+ }
+ if (checkPreservedFields) {
+ assertEquals(td1.getBackgroundColor(), td2.getBackgroundColor());
+ assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor());
+ assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor());
+ assertEquals(td1.getResizeMode(), td2.getResizeMode());
+ assertEquals(td1.getMinWidth(), td2.getMinWidth());
+ assertEquals(td1.getMinHeight(), td2.getMinHeight());
+ }
+ }
+
+ private <T extends Parcelable> T parcelingRoundTrip(final T in) throws Exception {
+ final Parcel p = Parcel.obtain();
+ in.writeToParcel(p, /* flags */ 0);
+ p.setDataPosition(0);
+ final byte[] marshalledData = p.marshall();
+ p.recycle();
+
+ final Parcel q = Parcel.obtain();
+ q.unmarshall(marshalledData, 0, marshalledData.length);
+ q.setDataPosition(0);
+
+ final Parcelable.Creator<T> creator = (Parcelable.Creator<T>)
+ in.getClass().getField("CREATOR").get(null); // static object, so null receiver
+ final T unmarshalled = (T) creator.createFromParcel(q);
+ q.recycle();
+ return unmarshalled;
}
// If any entries in appear in the list, sanity check them against all running applications
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index c986db8..c328d72 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -504,15 +504,17 @@
}
@Override
- public void onPictureInPictureRequested() {
+ public boolean onPictureInPictureRequested() {
mPipRequested = true;
if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_ENTER, false)) {
enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
mPipEntered = true;
+ return true;
} else if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_SKIP, false)) {
mPipEnterSkipped = true;
+ return false;
}
- super.onPictureInPictureRequested();
+ return super.onPictureInPictureRequested();
}
boolean pipRequested() {
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 84c42db..d649b94 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -376,6 +376,24 @@
}
@Test
+ public void resetToDefault_makeDefault() {
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, true);
+ assertThat(DeviceConfig.getProperty(NAMESPACE, KEY)).isEqualTo(VALUE);
+
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, NAMESPACE);
+ assertThat(DeviceConfig.getProperty(NAMESPACE, KEY)).isEqualTo(VALUE);
+ }
+
+ @Test
+ public void resetToDefault_doNotMakeDefault() {
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+ assertThat(DeviceConfig.getProperty(NAMESPACE, KEY)).isEqualTo(VALUE);
+
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, NAMESPACE);
+ assertThat(DeviceConfig.getProperty(NAMESPACE, KEY)).isNull();
+ }
+
+ @Test
public void getProperties_fullNamespace() {
Properties properties = DeviceConfig.getProperties(NAMESPACE);
assertThat(properties.getKeyset()).isEmpty();
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 8faf790..1ca4649 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -25,7 +25,6 @@
import android.content.Context;
import android.content.Intent;
import android.os.LocaleList;
-import android.service.textclassifier.TextClassifierService;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -64,9 +63,7 @@
@Test
public void testGetSystemTextClassifier() {
- assertTrue(
- TextClassifierService.getServiceComponentName(mContext) == null
- || mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
+ assertTrue(mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
}
@Test
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 2304ba6..372a478 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -27,6 +27,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.LocaleList;
+import android.service.textclassifier.TextClassifierService;
import android.text.Spannable;
import android.text.SpannableString;
@@ -58,11 +59,12 @@
public class TextClassifierTest {
private static final String LOCAL = "local";
private static final String SESSION = "session";
+ private static final String DEFAULT = "default";
// TODO: Add SYSTEM, which tests TextClassifier.SYSTEM.
@Parameterized.Parameters(name = "{0}")
public static Iterable<Object> textClassifierTypes() {
- return Arrays.asList(LOCAL, SESSION);
+ return Arrays.asList(LOCAL, SESSION, DEFAULT);
}
@Parameterized.Parameter
@@ -84,13 +86,15 @@
if (mTextClassifierType.equals(LOCAL)) {
mClassifier = mTcm.getTextClassifier(TextClassifier.LOCAL);
- } else {
+ } else if (mTextClassifierType.equals(SESSION)) {
mClassifier = mTcm.createTextClassificationSession(
new TextClassificationContext.Builder(
"android",
TextClassifier.WIDGET_TYPE_NOTIFICATION)
.build(),
mTcm.getTextClassifier(TextClassifier.LOCAL));
+ } else {
+ mClassifier = TextClassifierService.getDefaultTextClassifierImplementation(mContext);
}
}
@@ -369,16 +373,14 @@
mClassifier.generateLinks(request).apply(url, 0, null));
}
-
- @Test(expected = IllegalArgumentException.class)
+ @Test
public void testGenerateLinks_tooLong() {
- if (isTextClassifierDisabled()) {
- throw new IllegalArgumentException("pass if disabled");
- }
+ if (isTextClassifierDisabled()) return;
char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
Arrays.fill(manySpaces, ' ');
TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
- mClassifier.generateLinks(request);
+ TextLinks links = mClassifier.generateLinks(request);
+ assertTrue(links.getLinks().isEmpty());
}
@Test
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index da50550..6929d0d 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -72,6 +72,11 @@
<group gid="net_admin" />
</permission>
+ <permission name="android.permission.MAINLINE_NETWORK_STACK" >
+ <group gid="net_admin" />
+ <group gid="net_raw" />
+ </permission>
+
<!-- The group that /cache belongs to, linked to the permission
set on the applications that can access /cache -->
<permission name="android.permission.ACCESS_CACHE_FILESYSTEM" >
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index dcc6b95..1290633 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -38,6 +38,10 @@
ICredentialStoreFactory storeFactory =
ICredentialStoreFactory.Stub.asInterface(
ServiceManager.getService("android.security.identity"));
+ if (storeFactory == null) {
+ // This can happen if credstore is not running or not installed.
+ return null;
+ }
ICredentialStore credStore = null;
try {
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 3681c69..a3d552f 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -187,7 +187,9 @@
EGLSyncKHR fence = mUploadThread->queue().runSync([&]() -> EGLSyncKHR {
AutoSkiaGlTexture glTexture;
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
- GL_CHECKPOINT(MODERATE);
+ if (GLUtils::dumpGLErrors()) {
+ return EGL_NO_SYNC_KHR;
+ }
// glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we
// provide.
@@ -195,19 +197,26 @@
// when we first use it in drawing
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
format.format, format.type, bitmap.getPixels());
- GL_CHECKPOINT(MODERATE);
+ if (GLUtils::dumpGLErrors()) {
+ return EGL_NO_SYNC_KHR;
+ }
EGLSyncKHR uploadFence =
eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
- LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR,
- "Could not create sync fence %#x", eglGetError());
+ if (uploadFence == EGL_NO_SYNC_KHR) {
+ ALOGW("Could not create sync fence %#x", eglGetError());
+ };
glFlush();
+ GLUtils::dumpGLErrors();
return uploadFence;
});
+ if (fence == EGL_NO_SYNC_KHR) {
+ return false;
+ }
EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
- LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
- "Failed to wait for the fence %#x", eglGetError());
+ ALOGE_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
+ "Failed to wait for the fence %#x", eglGetError());
eglDestroySyncKHR(display, fence);
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c1435d1e..0b5e005 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -429,7 +429,8 @@
if (renderAhead) {
presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
- (frameIntervalNanos * (renderAhead + 1));
+ (frameIntervalNanos * (renderAhead + 1)) - DeviceInfo::get()->getAppOffset() +
+ (frameIntervalNanos / 2);
}
native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime);
}
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
index 150f6dc..512b8c4 100644
--- a/libs/incident/Android.bp
+++ b/libs/incident/Android.bp
@@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-cc_library_shared {
- name: "libincident",
+
+cc_defaults {
+ name: "libincidentpriv_defaults",
cflags: [
"-Wall",
@@ -50,6 +51,70 @@
":libincident_aidl",
"src/IncidentReportArgs.cpp",
],
+}
+
+cc_library_shared {
+ name: "libincidentpriv",
+ defaults: ["libincidentpriv_defaults"],
+ export_include_dirs: ["include_priv"],
+}
+
+cc_library_shared {
+ name: "libincident",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ "libincidentpriv",
+ ],
+
+ srcs: [
+ "src/incident_report.cpp",
+ ],
export_include_dirs: ["include"],
+
+ stubs: {
+ symbol_file: "libincident.map.txt",
+ versions: [
+ "30",
+ ],
+ },
}
+
+cc_test {
+ name: "libincident_test",
+ defaults: ["libincidentpriv_defaults"],
+ test_suites: ["device-tests"],
+
+ include_dirs: [
+ "frameworks/base/libs/incident/include",
+ "frameworks/base/libs/incident/include_priv",
+ ],
+
+ srcs: [
+ "tests/IncidentReportArgs_test.cpp",
+ "tests/IncidentReportRequest_test.cpp",
+ "tests/c_api_compile_test.c",
+ ],
+
+ shared_libs: [
+ "libincident",
+ ],
+
+ static_libs: [
+ "libgmock",
+ ],
+}
+
+
+
diff --git a/libs/incident/include/incident/incident_report.h b/libs/incident/include/incident/incident_report.h
new file mode 100644
index 0000000..49fe5b9
--- /dev/null
+++ b/libs/incident/include/incident/incident_report.h
@@ -0,0 +1,192 @@
+/**
+ * Copyright (c) 2020, 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.
+ */
+
+/**
+ * @file incident_report.h
+ */
+
+#ifndef ANDROID_INCIDENT_INCIDENT_REPORT_H
+#define ANDROID_INCIDENT_INCIDENT_REPORT_H
+
+#include <stdbool.h>
+
+#if __cplusplus
+#include <set>
+#include <string>
+#include <vector>
+
+extern "C" {
+#endif // __cplusplus
+
+struct AIncidentReportArgs;
+/**
+ * Opaque class to represent the arguments to an incident report request.
+ * Incident reports contain debugging data about the device at runtime.
+ * For more information see the android.os.IncidentManager java class.
+ */
+typedef struct AIncidentReportArgs AIncidentReportArgs;
+
+// Privacy policy enum value, sync with frameworks/base/core/proto/android/privacy.proto,
+// IncidentReportArgs.h and IncidentReportArgs.java.
+enum {
+ /**
+ * Flag marking fields and incident reports than can be taken
+ * off the device only via adb.
+ */
+ INCIDENT_REPORT_PRIVACY_POLICY_LOCAL = 0,
+
+ /**
+ * Flag marking fields and incident reports than can be taken
+ * off the device with contemporary consent.
+ */
+ INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT = 100,
+
+ /**
+ * Flag marking fields and incident reports than can be taken
+ * off the device with prior consent.
+ */
+ INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC = 200,
+
+ /**
+ * Flag to indicate that a given field has not been marked
+ * with a privacy policy.
+ */
+ INCIDENT_REPORT_PRIVACY_POLICY_UNSET = 255
+};
+
+/**
+ * Allocate and initialize an AIncidentReportArgs object.
+ */
+AIncidentReportArgs* AIncidentReportArgs_init();
+
+/**
+ * Duplicate an existing AIncidentReportArgs object.
+ */
+AIncidentReportArgs* AIncidentReportArgs_clone(AIncidentReportArgs* that);
+
+/**
+ * Clean up and delete an AIncidentReportArgs object.
+ */
+void AIncidentReportArgs_delete(AIncidentReportArgs* args);
+
+/**
+ * Set this incident report to include all sections.
+ */
+void AIncidentReportArgs_setAll(AIncidentReportArgs* args, bool all);
+
+/**
+ * Set this incident report privacy policy spec.
+ */
+void AIncidentReportArgs_setPrivacyPolicy(AIncidentReportArgs* args, int privacyPolicy);
+
+/**
+ * Add this section to the incident report. The section IDs are the field numbers
+ * from the android.os.IncidentProto protobuf message.
+ */
+void AIncidentReportArgs_addSection(AIncidentReportArgs* args, int section);
+
+/**
+ * Set the apk package name that will be sent a broadcast when the incident
+ * report completes. Must be called in conjunction with AIncidentReportArgs_setReceiverClass.
+ */
+void AIncidentReportArgs_setReceiverPackage(AIncidentReportArgs* args, char const* pkg);
+
+/**
+ * Set the fully qualified class name of the java BroadcastReceiver class that will be
+ * sent a broadcast when the report completes. Must be called in conjunction with
+ * AIncidentReportArgs_setReceiverPackage.
+ */
+void AIncidentReportArgs_setReceiverClass(AIncidentReportArgs* args, char const* cls);
+
+/**
+ * Add protobuf data as a header to the incident report. The buffer should be a serialized
+ * android.os.IncidentHeaderProto object.
+ */
+void AIncidentReportArgs_addHeader(AIncidentReportArgs* args, uint8_t const* buf, size_t size);
+
+/**
+ * Initiate taking the report described in the args object. Returns 0 on success,
+ * and non-zero otherwise.
+ */
+int AIncidentReportArgs_takeReport(AIncidentReportArgs* args);
+
+#if __cplusplus
+} // extern "C"
+
+namespace android {
+namespace os {
+
+class IncidentReportRequest {
+public:
+ inline IncidentReportRequest() {
+ mImpl = AIncidentReportArgs_init();
+ }
+
+ inline IncidentReportRequest(const IncidentReportRequest& that) {
+ mImpl = AIncidentReportArgs_clone(that.mImpl);
+ }
+
+ inline ~IncidentReportRequest() {
+ AIncidentReportArgs_delete(mImpl);
+ }
+
+ inline AIncidentReportArgs* getImpl() {
+ return mImpl;
+ }
+
+ inline void setAll(bool all) {
+ AIncidentReportArgs_setAll(mImpl, all);
+ }
+
+ inline void setPrivacyPolicy(int privacyPolicy) {
+ AIncidentReportArgs_setPrivacyPolicy(mImpl, privacyPolicy);
+ }
+
+ inline void addSection(int section) {
+ AIncidentReportArgs_addSection(mImpl, section);
+ }
+
+ inline void setReceiverPackage(const std::string& pkg) {
+ AIncidentReportArgs_setReceiverPackage(mImpl, pkg.c_str());
+ };
+
+ inline void setReceiverClass(const std::string& cls) {
+ AIncidentReportArgs_setReceiverClass(mImpl, cls.c_str());
+ };
+
+ inline void addHeader(const std::vector<uint8_t>& headerProto) {
+ AIncidentReportArgs_addHeader(mImpl, headerProto.data(), headerProto.size());
+ };
+
+ inline void addHeader(const uint8_t* buf, size_t size) {
+ AIncidentReportArgs_addHeader(mImpl, buf, size);
+ };
+
+ // returns a status_t
+ inline int takeReport() {
+ return AIncidentReportArgs_takeReport(mImpl);
+ }
+
+private:
+ AIncidentReportArgs* mImpl;
+};
+
+} // namespace os
+} // namespace android
+
+#endif // __cplusplus
+
+#endif // ANDROID_INCIDENT_INCIDENT_REPORT_H
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include_priv/android/os/IncidentReportArgs.h
similarity index 89%
rename from libs/incident/include/android/os/IncidentReportArgs.h
rename to libs/incident/include_priv/android/os/IncidentReportArgs.h
index 94b4ad6..0e61590 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include_priv/android/os/IncidentReportArgs.h
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#ifndef ANDROID_OS_DUMPSTATE_ARGS_H_
-#define ANDROID_OS_DUMPSTATE_ARGS_H_
+#ifndef ANDROID_OS_INCIDENT_REPORT_ARGS_H
+#define ANDROID_OS_INCIDENT_REPORT_ARGS_H
+#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <utils/String16.h>
@@ -29,7 +30,8 @@
using namespace std;
-// DESTINATION enum value, sync with frameworks/base/core/proto/android/privacy.proto
+// DESTINATION enum value, sync with frameworks/base/core/proto/android/privacy.proto,
+// incident/incident_report.h and IncidentReportArgs.java
const uint8_t PRIVACY_POLICY_LOCAL = 0;
const uint8_t PRIVACY_POLICY_EXPLICIT = 100;
const uint8_t PRIVACY_POLICY_AUTOMATIC = 200;
@@ -74,4 +76,4 @@
}
}
-#endif // ANDROID_OS_DUMPSTATE_ARGS_H_
+#endif // ANDROID_OS_INCIDENT_REPORT_ARGS_H
diff --git a/libs/incident/libincident.map.txt b/libs/incident/libincident.map.txt
new file mode 100644
index 0000000..f157763
--- /dev/null
+++ b/libs/incident/libincident.map.txt
@@ -0,0 +1,15 @@
+LIBINCIDENT {
+ global:
+ AIncidentReportArgs_init; # apex # introduced=30
+ AIncidentReportArgs_clone; # apex # introduced=30
+ AIncidentReportArgs_delete; # apex # introduced=30
+ AIncidentReportArgs_setAll; # apex # introduced=30
+ AIncidentReportArgs_setPrivacyPolicy; # apex # introduced=30
+ AIncidentReportArgs_addSection; # apex # introduced=30
+ AIncidentReportArgs_setReceiverPackage; # apex # introduced=30
+ AIncidentReportArgs_setReceiverClass; # apex # introduced=30
+ AIncidentReportArgs_addHeader; # apex # introduced=30
+ AIncidentReportArgs_takeReport; # apex # introduced=30
+ local:
+ *;
+};
diff --git a/libs/incident/src/incident_report.cpp b/libs/incident/src/incident_report.cpp
new file mode 100644
index 0000000..7897ddf
--- /dev/null
+++ b/libs/incident/src/incident_report.cpp
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libincident"
+
+#include <incident/incident_report.h>
+
+#include <android/os/IIncidentManager.h>
+#include <android/os/IncidentReportArgs.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <log/log.h>
+
+using android::sp;
+using android::binder::Status;
+using android::os::IncidentReportArgs;
+using android::os::IIncidentManager;
+using std::string;
+using std::vector;
+
+AIncidentReportArgs* AIncidentReportArgs_init() {
+ return reinterpret_cast<AIncidentReportArgs*>(new IncidentReportArgs());
+}
+
+AIncidentReportArgs* AIncidentReportArgs_clone(AIncidentReportArgs* that) {
+ return reinterpret_cast<AIncidentReportArgs*>(
+ new IncidentReportArgs(*reinterpret_cast<IncidentReportArgs*>(that)));
+}
+
+void AIncidentReportArgs_delete(AIncidentReportArgs* args) {
+ delete reinterpret_cast<IncidentReportArgs*>(args);
+}
+
+void AIncidentReportArgs_setAll(AIncidentReportArgs* args, bool all) {
+ reinterpret_cast<IncidentReportArgs*>(args)->setAll(all);
+}
+
+void AIncidentReportArgs_setPrivacyPolicy(AIncidentReportArgs* args, int privacyPolicy) {
+ reinterpret_cast<IncidentReportArgs*>(args)->setPrivacyPolicy(privacyPolicy);
+}
+
+void AIncidentReportArgs_addSection(AIncidentReportArgs* args, int section) {
+ reinterpret_cast<IncidentReportArgs*>(args)->addSection(section);
+}
+
+void AIncidentReportArgs_setReceiverPackage(AIncidentReportArgs* args, char const* pkg) {
+ reinterpret_cast<IncidentReportArgs*>(args)->setReceiverPkg(string(pkg));
+}
+
+void AIncidentReportArgs_setReceiverClass(AIncidentReportArgs* args, char const* cls) {
+ reinterpret_cast<IncidentReportArgs*>(args)->setReceiverCls(string(cls));
+}
+
+void AIncidentReportArgs_addHeader(AIncidentReportArgs* args, uint8_t const* buf, size_t size) {
+ vector<uint8_t> vec(buf, buf+size);
+ reinterpret_cast<IncidentReportArgs*>(args)->addHeader(vec);
+}
+
+int AIncidentReportArgs_takeReport(AIncidentReportArgs* argp) {
+ IncidentReportArgs* args = reinterpret_cast<IncidentReportArgs*>(argp);
+
+ sp<IIncidentManager> service = android::interface_cast<IIncidentManager>(
+ android::defaultServiceManager()->getService(android::String16("incident")));
+ if (service == nullptr) {
+ ALOGW("Failed to fetch incident service.");
+ return false;
+ }
+ Status s = service->reportIncident(*args);
+ return s.transactionError();
+}
diff --git a/cmds/statsd/tests/external/IncidentReportArgs_test.cpp b/libs/incident/tests/IncidentReportArgs_test.cpp
similarity index 93%
rename from cmds/statsd/tests/external/IncidentReportArgs_test.cpp
rename to libs/incident/tests/IncidentReportArgs_test.cpp
index 38bc194..224b343 100644
--- a/cmds/statsd/tests/external/IncidentReportArgs_test.cpp
+++ b/libs/incident/tests/IncidentReportArgs_test.cpp
@@ -20,6 +20,8 @@
namespace os {
namespace statsd {
+// Checks that all of the inline methods on IncidentReportRequest and the real C functions
+// result in a working IncidentReportArgs.
TEST(IncidentReportArgsTest, testSerialization) {
IncidentReportArgs args;
args.setAll(0);
diff --git a/libs/incident/tests/IncidentReportRequest_test.cpp b/libs/incident/tests/IncidentReportRequest_test.cpp
new file mode 100644
index 0000000..6d218b6
--- /dev/null
+++ b/libs/incident/tests/IncidentReportRequest_test.cpp
@@ -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.
+
+#include <android/os/IncidentReportArgs.h>
+#include <incident/incident_report.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(IncidentReportRequestTest, testWrite) {
+ IncidentReportRequest request;
+ request.setAll(0);
+ request.addSection(1000);
+ request.addSection(1001);
+
+ vector<uint8_t> header1;
+ header1.push_back(0x1);
+ header1.push_back(0x2);
+ vector<uint8_t> header2;
+ header1.push_back(0x22);
+ header1.push_back(0x33);
+
+ request.addHeader(header1);
+ request.addHeader(header2);
+
+ request.setPrivacyPolicy(1);
+
+ request.setReceiverPackage("com.android.os");
+ request.setReceiverClass("com.android.os.Receiver");
+
+ IncidentReportArgs* args = reinterpret_cast<IncidentReportArgs*>(request.getImpl());
+
+ EXPECT_EQ(0, args->all());
+ set<int> sections;
+ sections.insert(1000);
+ sections.insert(1001);
+ EXPECT_EQ(sections, args->sections());
+ EXPECT_EQ(1, args->getPrivacyPolicy());
+
+ EXPECT_EQ(string("com.android.os"), args->receiverPkg());
+ EXPECT_EQ(string("com.android.os.Receiver"), args->receiverCls());
+
+ vector<vector<uint8_t>> headers;
+ headers.push_back(header1);
+ headers.push_back(header2);
+ EXPECT_EQ(headers, args->headers());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/libs/incident/tests/c_api_compile_test.c b/libs/incident/tests/c_api_compile_test.c
new file mode 100644
index 0000000..e1620df
--- /dev/null
+++ b/libs/incident/tests/c_api_compile_test.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <incident/incident_report.h>
+
+/*
+ * This file ensures that incident/incident_report.h actually compiles with C,
+ * since there is no other place in the tree that actually uses it from C.
+ */
+int not_called() {
+ return 0;
+}
+
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index f3613d3..42841d1 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -229,13 +230,19 @@
* <p>This information is only available if the caller has the
* {@link android.Manifest.permission.MODIFY_AUDIO_ROUTING}
* permission.
- * <br>The result is -1 without the permission.
* @return the user id
+ * @throws SecurityException Thrown if the caller is missing the MODIFY_AUDIO_ROUTING permission
*
* @hide
*/
@SystemApi
- public int getClientUid() { return mClientUid; }
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public int getClientUid() {
+ if (mClientUid == -1) {
+ throw new SecurityException("MODIFY_AUDIO_ROUTING permission is missing");
+ }
+ return mClientUid;
+ }
/**
* Returns information about the audio input device used for this recording.
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index 194669c..9131f3b 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -37,5 +37,4 @@
void notifyControlRequestSent(String id, in Intent request);
void requestSetVolume(String id, int volume);
- void requestUpdateVolume(String id, int delta);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index dac0fba..6fef468 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -52,7 +52,6 @@
void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route,
in Intent request);
void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
- void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId,
in @nullable Bundle sessionHints);
@@ -70,8 +69,6 @@
void requestSetVolume2Manager(IMediaRouter2Manager manager,
in MediaRoute2Info route, int volume);
- void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
- in MediaRoute2Info route, int direction);
List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
void selectClientRoute(IMediaRouter2Manager manager,
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index d0b5d48..20a59bba5 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -46,13 +46,20 @@
/**
* Base class for media route provider services.
* <p>
+ * Media route provider services are used to publish {@link MediaRoute2Info media routes} such as
+ * speakers, TVs, etc. The routes are published by calling {@link #notifyRoutes(Collection)}.
+ * Media apps which use {@link MediaRouter2} can request to play their media on the routes.
+ * </p><p>
+ * When {@link MediaRouter2 media router} wants to play media on a route,
+ * {@link #onCreateSession(String, String, long, Bundle)} will be called to handle the request.
+ * A session can be considered as a group of currently selected routes for each connection.
+ * Create and manage the sessions by yourself, and notify the {@link RoutingSessionInfo
+ * session infos} when there are any changes.
+ * </p><p>
* The system media router service will bind to media route provider services when a
* {@link RouteDiscoveryPreference discovery preference} is registered via
- * a {@link MediaRouter2 media router} by an application.
- * </p><p>
- * To implement your own media route provider service, extend this class and
- * override {@link #onDiscoveryPreferenceChanged(RouteDiscoveryPreference)} to publish
- * {@link MediaRoute2Info routes}.
+ * a {@link MediaRouter2 media router} by an application. See
+ * {@link #onDiscoveryPreferenceChanged(RouteDiscoveryPreference)} for the details.
* </p>
*/
public abstract class MediaRoute2ProviderService extends Service {
@@ -118,22 +125,15 @@
public abstract void onControlRequest(@NonNull String routeId, @NonNull Intent request);
/**
- * Called when requestSetVolume is called on a route of the provider
+ * Called when requestSetVolume is called on a route of the provider.
*
* @param routeId the id of the route
* @param volume the target volume
+ * @see MediaRoute2Info#getVolumeMax()
*/
public abstract void onSetVolume(@NonNull String routeId, int volume);
/**
- * Called when requestUpdateVolume is called on a route of the provider
- *
- * @param routeId id of the route
- * @param delta the delta to add to the current volume
- */
- public abstract void onUpdateVolume(@NonNull String routeId, int delta);
-
- /**
* Gets information of the session with the given id.
*
* @param sessionId id of the session
@@ -520,14 +520,5 @@
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume,
MediaRoute2ProviderService.this, routeId, volume));
}
-
- @Override
- public void requestUpdateVolume(String routeId, int delta) {
- if (!checkCallerisSystem()) {
- return;
- }
- mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUpdateVolume,
- MediaRoute2ProviderService.this, routeId, delta));
- }
}
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 515cabe..d7b74df 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -462,31 +462,6 @@
}
}
- /**
- * Requests an incremental volume update for the route asynchronously.
- * <p>
- * It may have no effect if the route is currently not selected.
- * </p>
- *
- * @param delta The delta to add to the current volume.
- * @hide
- */
- public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
- Objects.requireNonNull(route, "route must not be null");
-
- Client2 client;
- synchronized (sRouterLock) {
- client = mClient;
- }
- if (client != null) {
- try {
- mMediaRouterService.requestUpdateVolume2(client, route, delta);
- } catch (RemoteException ex) {
- Log.e(TAG, "Unable to send control request.", ex);
- }
- }
- }
-
void addRoutesOnHandler(List<MediaRoute2Info> routes) {
// TODO: When onRoutesAdded is first called,
// 1) clear mRoutes before adding the routes
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 662eeb1..b1f930d 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -294,30 +294,6 @@
}
}
- /**
- * Requests an incremental volume update for the route asynchronously.
- * <p>
- * It may have no effect if the route is currently not selected.
- * </p>
- *
- * @param delta The delta to add to the current volume.
- */
- public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
- Objects.requireNonNull(route, "route must not be null");
-
- Client client;
- synchronized (sLock) {
- client = mClient;
- }
- if (client != null) {
- try {
- mMediaRouterService.requestUpdateVolume2Manager(client, route, delta);
- } catch (RemoteException ex) {
- Log.e(TAG, "Unable to send control request.", ex);
- }
- }
- }
-
void addRoutesOnHandler(List<MediaRoute2Info> routes) {
synchronized (mRoutesLock) {
for (MediaRoute2Info route : routes) {
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 09b7559..433c622 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -1109,6 +1109,24 @@
* <p>Type: TEXT
*/
String COLUMN_SERIES_ID = "series_id";
+
+ /**
+ * The split ID of this TV program for multi-part content, as a URI.
+ *
+ * <p>A content may consist of multiple programs within the same channel or over several
+ * channels. For example, a film might be divided into two parts interrupted by a news in
+ * the middle or a longer sport event might be split into several parts over several
+ * channels. The split ID is used to identify all the programs in the same multi-part
+ * content. Suitable URIs include
+ * <ul>
+ * <li>{@code crid://<CRIDauthority>/<data>#<IMI>} from ETSI TS 102 323
+ * </ul>
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ String COLUMN_SPLIT_ID = "split_id";
}
/**
@@ -1677,6 +1695,7 @@
TYPE_ATSC_T,
TYPE_ATSC_C,
TYPE_ATSC_M_H,
+ TYPE_ATSC3_T,
TYPE_ISDB_T,
TYPE_ISDB_TB,
TYPE_ISDB_S,
@@ -1801,6 +1820,13 @@
public static final String TYPE_ATSC_M_H = "TYPE_ATSC_M_H";
/**
+ * The channel type for ATSC3.0 (terrestrial).
+ *
+ * @see #COLUMN_TYPE
+ */
+ public static final String TYPE_ATSC3_T = "TYPE_ATSC3_T";
+
+ /**
* The channel type for ISDB-T (terrestrial).
*
* @see #COLUMN_TYPE
@@ -2022,6 +2048,7 @@
* {@link #TYPE_ATSC_C},
* {@link #TYPE_ATSC_M_H},
* {@link #TYPE_ATSC_T},
+ * {@link #TYPE_ATSC3_T},
* {@link #TYPE_CMMB},
* {@link #TYPE_DTMB},
* {@link #TYPE_DVB_C},
@@ -2407,6 +2434,22 @@
*/
public static final String COLUMN_TRANSIENT = "transient";
+ /**
+ * The global content ID of this TV channel, as a URI.
+ *
+ * <p>A globally unique URI that identifies this TV channel, if applicable. Suitable URIs
+ * include
+ * <ul>
+ * <li>{@code globalServiceId} from ATSC A/331. ex {@code https://doi.org/10.5239/7E4E-B472}
+ * <li>Other broadcast ID provider. ex {@code http://example.com/tv_channel/1234}
+ * </ul>
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_GLOBAL_CONTENT_ID = "global_content_id";
+
private Channels() {}
/**
@@ -2562,6 +2605,37 @@
*/
public static final String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+ /**
+ * The event ID of this TV program.
+ *
+ * <p>It is used to identify the current TV program in the same channel, if applicable.
+ * Use the same coding for {@code event_id} in the underlying broadcast standard if it
+ * is defined there (e.g. ATSC A/65, ETSI EN 300 468 and ARIB STD-B10).
+ *
+ * <p>This is a required field only if the underlying broadcast standard defines the same
+ * name field. Otherwise, leave empty.
+ *
+ * <p>Type: INTEGER
+ */
+ public static final String COLUMN_EVENT_ID = "event_id";
+
+ /**
+ * The global content ID of this TV program, as a URI.
+ *
+ * <p>A globally unique ID that identifies this TV program, if applicable. Suitable URIs
+ * include
+ * <ul>
+ * <li>{@code crid://<CRIDauthority>/<data>} from ETSI TS 102 323
+ * <li>{@code globalContentId} from ATSC A/332
+ * <li>Other broadcast ID provider. ex {@code http://example.com/tv_program/1234}
+ * </ul>
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_GLOBAL_CONTENT_ID = "global_content_id";
+
private Programs() {}
/** Canonical genres for TV programs. */
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 4316e42..f10e5eb 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -289,12 +289,13 @@
MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
int originalVolume = volRoute.getVolume();
- int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
+ int targetVolume = originalVolume == volRoute.getVolumeMax()
+ ? originalVolume - 1 : originalVolume + 1;
awaitOnRouteChangedManager(
- () -> mManager.requestUpdateVolume(volRoute, deltaVolume),
+ () -> mManager.requestSetVolume(volRoute, targetVolume),
ROUTE_ID_VARIABLE_VOLUME,
- (route -> route.getVolume() == originalVolume + deltaVolume));
+ (route -> route.getVolume() == targetVolume));
awaitOnRouteChangedManager(
() -> mManager.requestSetVolume(volRoute, originalVolume),
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index f1a08f2..1a866ca 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -154,20 +154,6 @@
}
@Override
- public void onUpdateVolume(String routeId, int delta) {
- MediaRoute2Info route = mRoutes.get(routeId);
- if (route == null) {
- return;
- }
- int volume = route.getVolume() + delta;
- volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
- mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
- .setVolume(volume)
- .build());
- publishRoutes();
- }
-
- @Override
public void onCreateSession(String packageName, String routeId, long requestId,
@Nullable Bundle sessionHints) {
MediaRoute2Info route = mRoutes.get(routeId);
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 392c9f6..ba793e8 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -294,7 +294,7 @@
auto& aSurfaceControlStats = aSurfaceTransactionStats.aSurfaceControlStats;
- for (const auto& [surfaceControl, acquireTime, previousReleaseFence, transformHint] : surfaceControlStats) {
+ for (const auto& [surfaceControl, latchTime, acquireTime, presentFence, previousReleaseFence, transformHint, frameEvents] : surfaceControlStats) {
ASurfaceControl* aSurfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
aSurfaceControlStats[aSurfaceControl].acquireTime = acquireTime;
aSurfaceControlStats[aSurfaceControl].previousReleaseFence = previousReleaseFence;
diff --git a/packages/DynamicSystemInstallationService/res/values/strings.xml b/packages/DynamicSystemInstallationService/res/values/strings.xml
index 9bd5be7..7595d2b 100644
--- a/packages/DynamicSystemInstallationService/res/values/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values/strings.xml
@@ -35,4 +35,7 @@
<!-- Toast when we fail to launch into Dynamic System [CHAR LIMIT=64] -->
<string name="toast_failed_to_reboot_to_dynsystem">Can\u2019t restart or load dynamic system</string>
+ <!-- URL of Dynamic System Key Revocation List [DO NOT TRANSLATE] -->
+ <string name="key_revocation_list_url" translatable="false">https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json</string>
+
</resources>
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 9ccb837..9bae223 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -46,6 +46,7 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.net.http.HttpResponseCache;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -60,6 +61,8 @@
import android.util.Log;
import android.widget.Toast;
+import java.io.File;
+import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -146,10 +149,26 @@
prepareNotification();
mDynSystem = (DynamicSystemManager) getSystemService(Context.DYNAMIC_SYSTEM_SERVICE);
+
+ // Install an HttpResponseCache in the application cache directory so we can cache
+ // gsi key revocation list. The http(s) protocol handler uses this cache transparently.
+ // The cache size is chosen heuristically. Since we don't have too much traffic right now,
+ // a moderate size of 1MiB should be enough.
+ try {
+ File httpCacheDir = new File(getCacheDir(), "httpCache");
+ long httpCacheSize = 1 * 1024 * 1024; // 1 MiB
+ HttpResponseCache.install(httpCacheDir, httpCacheSize);
+ } catch (IOException e) {
+ Log.d(TAG, "HttpResponseCache.install() failed: " + e);
+ }
}
@Override
public void onDestroy() {
+ HttpResponseCache cache = HttpResponseCache.getInstalled();
+ if (cache != null) {
+ cache.flush();
+ }
// Cancel the persistent notification.
mNM.cancel(NOTIFICATION_ID);
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 9aea0e7..438c435 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -25,6 +25,8 @@
import android.util.Log;
import android.webkit.URLUtil;
+import org.json.JSONException;
+
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
@@ -100,7 +102,9 @@
private final Context mContext;
private final DynamicSystemManager mDynSystem;
private final ProgressListener mListener;
+ private final boolean mIsNetworkUrl;
private DynamicSystemManager.Session mInstallationSession;
+ private KeyRevocationList mKeyRevocationList;
private boolean mIsZip;
private boolean mIsCompleted;
@@ -123,6 +127,7 @@
mContext = context;
mDynSystem = dynSystem;
mListener = listener;
+ mIsNetworkUrl = URLUtil.isNetworkUrl(mUrl);
}
@Override
@@ -152,9 +157,11 @@
return null;
}
+ // TODO(yochiang): do post-install public key check (revocation list / boot-ramdisk)
+
mDynSystem.finishInstallation();
} catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, e.toString(), e);
mDynSystem.remove();
return e;
} finally {
@@ -220,7 +227,7 @@
String.format(Locale.US, "Unsupported file format: %s", mUrl));
}
- if (URLUtil.isNetworkUrl(mUrl)) {
+ if (mIsNetworkUrl) {
mStream = new URL(mUrl).openStream();
} else if (URLUtil.isFileUrl(mUrl)) {
if (mIsZip) {
@@ -234,6 +241,25 @@
throw new UnsupportedUrlException(
String.format(Locale.US, "Unsupported URL: %s", mUrl));
}
+
+ // TODO(yochiang): Bypass this check if device is unlocked
+ try {
+ String listUrl = mContext.getString(R.string.key_revocation_list_url);
+ mKeyRevocationList = KeyRevocationList.fromUrl(new URL(listUrl));
+ } catch (IOException | JSONException e) {
+ Log.d(TAG, "Failed to fetch Dynamic System Key Revocation List");
+ mKeyRevocationList = new KeyRevocationList();
+ keyRevocationThrowOrWarning(e);
+ }
+ }
+
+ private void keyRevocationThrowOrWarning(Exception e) throws Exception {
+ if (mIsNetworkUrl) {
+ throw e;
+ } else {
+ // If DSU is being installed from a local file URI, then be permissive
+ Log.w(TAG, e.toString());
+ }
}
private void installUserdata() throws Exception {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/KeyRevocationList.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/KeyRevocationList.java
new file mode 100644
index 0000000..522bc54
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/KeyRevocationList.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2019 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.dynsystem;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+
+class KeyRevocationList {
+
+ private static final String TAG = "KeyRevocationList";
+
+ private static final String JSON_ENTRIES = "entries";
+ private static final String JSON_PUBLIC_KEY = "public_key";
+ private static final String JSON_STATUS = "status";
+ private static final String JSON_REASON = "reason";
+
+ private static final String STATUS_REVOKED = "REVOKED";
+
+ @VisibleForTesting
+ HashMap<String, RevocationStatus> mEntries;
+
+ static class RevocationStatus {
+ final String mStatus;
+ final String mReason;
+
+ RevocationStatus(String status, String reason) {
+ mStatus = status;
+ mReason = reason;
+ }
+ }
+
+ KeyRevocationList() {
+ mEntries = new HashMap<String, RevocationStatus>();
+ }
+
+ /**
+ * Returns the revocation status of a public key.
+ *
+ * @return a RevocationStatus for |publicKey|, null if |publicKey| doesn't exist.
+ */
+ RevocationStatus getRevocationStatusForKey(String publicKey) {
+ return mEntries.get(publicKey);
+ }
+
+ /** Test if a public key is revoked or not. */
+ boolean isRevoked(String publicKey) {
+ RevocationStatus entry = getRevocationStatusForKey(publicKey);
+ return entry != null && TextUtils.equals(entry.mStatus, STATUS_REVOKED);
+ }
+
+ @VisibleForTesting
+ void addEntry(String publicKey, String status, String reason) {
+ mEntries.put(publicKey, new RevocationStatus(status, reason));
+ }
+
+ /**
+ * Creates a KeyRevocationList from a JSON String.
+ *
+ * @param jsonString the revocation list, for example:
+ * <pre>{@code
+ * {
+ * "entries": [
+ * {
+ * "public_key": "00fa2c6637c399afa893fe83d85f3569998707d5",
+ * "status": "REVOKED",
+ * "reason": "Revocation Reason"
+ * }
+ * ]
+ * }
+ * }</pre>
+ *
+ * @throws JSONException if |jsonString| is malformed.
+ */
+ static KeyRevocationList fromJsonString(String jsonString) throws JSONException {
+ JSONObject jsonObject = new JSONObject(jsonString);
+ KeyRevocationList list = new KeyRevocationList();
+ Log.d(TAG, "Begin of revocation list");
+ if (jsonObject.has(JSON_ENTRIES)) {
+ JSONArray entries = jsonObject.getJSONArray(JSON_ENTRIES);
+ for (int i = 0; i < entries.length(); ++i) {
+ JSONObject entry = entries.getJSONObject(i);
+ String publicKey = entry.getString(JSON_PUBLIC_KEY);
+ String status = entry.getString(JSON_STATUS);
+ String reason = entry.has(JSON_REASON) ? entry.getString(JSON_REASON) : "";
+ list.addEntry(publicKey, status, reason);
+ Log.d(TAG, "Revocation entry: " + entry.toString());
+ }
+ }
+ Log.d(TAG, "End of revocation list");
+ return list;
+ }
+
+ /**
+ * Creates a KeyRevocationList from a URL.
+ *
+ * @throws IOException if |url| is inaccessible.
+ * @throws JSONException if fetched content is malformed.
+ */
+ static KeyRevocationList fromUrl(URL url) throws IOException, JSONException {
+ Log.d(TAG, "Fetch from URL: " + url.toString());
+ // Force "conditional GET"
+ // Force validate the cached result with server each time, and use the cached result
+ // only if it is validated by server, else fetch new data from server.
+ // Ref: https://developer.android.com/reference/android/net/http/HttpResponseCache#force-a-network-response
+ URLConnection connection = url.openConnection();
+ connection.setUseCaches(true);
+ connection.addRequestProperty("Cache-Control", "max-age=0");
+ try (InputStream stream = connection.getInputStream()) {
+ return fromJsonString(readFully(stream));
+ }
+ }
+
+ private static String readFully(InputStream in) throws IOException {
+ int n;
+ byte[] buffer = new byte[4096];
+ StringBuilder builder = new StringBuilder();
+ while ((n = in.read(buffer, 0, 4096)) > -1) {
+ builder.append(new String(buffer, 0, n));
+ }
+ return builder.toString();
+ }
+}
diff --git a/packages/DynamicSystemInstallationService/tests/Android.bp b/packages/DynamicSystemInstallationService/tests/Android.bp
new file mode 100644
index 0000000..3bdf829
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/tests/Android.bp
@@ -0,0 +1,15 @@
+android_test {
+ name: "DynamicSystemInstallationServiceTests",
+
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ ],
+
+ resource_dirs: ["res"],
+ platform_apis: true,
+ instrumentation_for: "DynamicSystemInstallationService",
+ certificate: "platform",
+}
diff --git a/packages/DynamicSystemInstallationService/tests/AndroidManifest.xml b/packages/DynamicSystemInstallationService/tests/AndroidManifest.xml
new file mode 100644
index 0000000..f5f0ae6
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dynsystem.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.dynsystem"
+ android:label="Tests for DynamicSystemInstallationService" />
+
+</manifest>
diff --git a/packages/DynamicSystemInstallationService/tests/res/values/strings.xml b/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
new file mode 100644
index 0000000..fdb620b
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- testFromJsonString -->
+ <string name="blacklist_json_string" translatable="false">
+ {
+ \"entries\":[
+ {
+ \"public_key\":\"00fa2c6637c399afa893fe83d85f3569998707d5\",
+ \"status\":\"REVOKED\",
+ \"reason\":\"Key revocation test key\"
+ },
+ {
+ \"public_key\":\"key2\",
+ \"status\":\"REVOKED\"
+ }
+ ]
+ }
+ </string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java b/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
new file mode 100644
index 0000000..82ce542
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2019 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.dynsystem;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.json.JSONException;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+/**
+ * A test for KeyRevocationList.java
+ */
+@RunWith(AndroidJUnit4.class)
+public class KeyRevocationListTest {
+
+ private static final String TAG = "KeyRevocationListTest";
+
+ private static Context sContext;
+
+ private static String sBlacklistJsonString;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ sContext = InstrumentationRegistry.getInstrumentation().getContext();
+ sBlacklistJsonString =
+ sContext.getString(com.android.dynsystem.tests.R.string.blacklist_json_string);
+ }
+
+ @Test
+ @SmallTest
+ public void testFromJsonString() throws JSONException {
+ KeyRevocationList blacklist;
+ blacklist = KeyRevocationList.fromJsonString(sBlacklistJsonString);
+ Assert.assertNotNull(blacklist);
+ Assert.assertFalse(blacklist.mEntries.isEmpty());
+ blacklist = KeyRevocationList.fromJsonString("{}");
+ Assert.assertNotNull(blacklist);
+ Assert.assertTrue(blacklist.mEntries.isEmpty());
+ }
+
+ @Test
+ @SmallTest
+ public void testFromUrl() throws IOException, JSONException {
+ URLConnection mockConnection = mock(URLConnection.class);
+ doReturn(new ByteArrayInputStream(sBlacklistJsonString.getBytes()))
+ .when(mockConnection).getInputStream();
+ URL mockUrl = new URL(
+ "http", // protocol
+ "foo.bar", // host
+ 80, // port
+ "baz", // file
+ new URLStreamHandler() {
+ @Override
+ protected URLConnection openConnection(URL url) {
+ return mockConnection;
+ }
+ });
+ URL mockBadUrl = new URL(
+ "http", // protocol
+ "foo.bar", // host
+ 80, // port
+ "baz", // file
+ new URLStreamHandler() {
+ @Override
+ protected URLConnection openConnection(URL url) throws IOException {
+ throw new IOException();
+ }
+ });
+
+ KeyRevocationList blacklist = KeyRevocationList.fromUrl(mockUrl);
+ Assert.assertNotNull(blacklist);
+ Assert.assertFalse(blacklist.mEntries.isEmpty());
+
+ blacklist = null;
+ try {
+ blacklist = KeyRevocationList.fromUrl(mockBadUrl);
+ // Up should throw, down should be unreachable
+ Assert.fail("Expected IOException not thrown");
+ } catch (IOException e) {
+ // This is expected, do nothing
+ }
+ Assert.assertNull(blacklist);
+ }
+
+ @Test
+ @SmallTest
+ public void testIsRevoked() {
+ KeyRevocationList blacklist = new KeyRevocationList();
+ blacklist.addEntry("key1", "REVOKED", "reason for key1");
+
+ KeyRevocationList.RevocationStatus revocationStatus =
+ blacklist.getRevocationStatusForKey("key1");
+ Assert.assertNotNull(revocationStatus);
+ Assert.assertEquals(revocationStatus.mReason, "reason for key1");
+
+ revocationStatus = blacklist.getRevocationStatusForKey("key2");
+ Assert.assertNull(revocationStatus);
+
+ Assert.assertTrue(blacklist.isRevoked("key1"));
+ Assert.assertFalse(blacklist.isRevoked("key2"));
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index ddb7341..1ebe917 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -153,21 +153,6 @@
return mService.getDevicesMatchingConnectionStates(states);
}
- public boolean connect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
- }
-
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
-
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -187,31 +172,37 @@
return mService.getActiveDevice();
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
if (mService == null) {
return false;
}
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
if (mService == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
if (mService == null) {
- return;
+ return false;
}
- if (preferred) {
+ if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
boolean isA2dpPlaying() {
if (mService == null) return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 8ca5a74..c7a5bd8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -115,21 +115,6 @@
BluetoothProfile.STATE_DISCONNECTING});
}
- public boolean connect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
- }
-
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
-
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -137,31 +122,37 @@
return mService.getConnectionState(device);
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
if (mService == null) {
return false;
}
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
if (mService == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
if (mService == null) {
- return;
+ return false;
}
- if (preferred) {
+ if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
boolean isAudioPlaying() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 50d3a5d..3aa35cb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -195,7 +195,7 @@
if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
if (profile instanceof MapProfile) {
- profile.setPreferred(mDevice, true);
+ profile.setEnabled(mDevice, true);
}
if (!mProfiles.contains(profile)) {
mRemovedProfiles.remove(profile);
@@ -208,7 +208,7 @@
}
} else if (profile instanceof MapProfile
&& newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
- profile.setPreferred(mDevice, false);
+ profile.setEnabled(mDevice, false);
} else if (mLocalNapRoleConnected && profile instanceof PanProfile
&& ((PanProfile) profile).isLocalRoleNap(mDevice)
&& newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
@@ -250,12 +250,12 @@
PbapServerProfile PbapProfile = mProfileManager.getPbapProfile();
if (PbapProfile != null && isConnectedProfile(PbapProfile))
{
- PbapProfile.disconnect(mDevice);
+ PbapProfile.setEnabled(mDevice, false);
}
}
public void disconnect(LocalBluetoothProfile profile) {
- if (profile.disconnect(mDevice)) {
+ if (profile.setEnabled(mDevice, false)) {
if (BluetoothUtils.D) {
Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile));
}
@@ -342,7 +342,7 @@
if (!ensurePaired()) {
return;
}
- if (profile.connect(mDevice)) {
+ if (profile.setEnabled(mDevice, true)) {
if (BluetoothUtils.D) {
Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 218d0b2..9dfc4d9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -114,21 +114,6 @@
return true;
}
- public boolean connect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
- }
-
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
-
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -164,31 +149,37 @@
return mService.getAudioState(device);
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
if (mService == null) {
return false;
}
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
if (mService == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
if (mService == null) {
- return;
+ return false;
}
- if (preferred) {
+ if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index b82fb37..a3b68b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -151,21 +151,6 @@
return mService.getDevicesMatchingConnectionStates(states);
}
- public boolean connect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
- }
-
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
-
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -185,31 +170,37 @@
return mService.getActiveDevices();
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
if (mService == null) {
return false;
}
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
if (mService == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
if (mService == null) {
- return;
+ return false;
}
- if (preferred) {
+ if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
public void setVolume(int volume) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 678f2e3..66225a2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -125,23 +125,6 @@
}
@Override
- public boolean connect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
-
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
- @Override
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -150,7 +133,7 @@
}
@Override
- public boolean isPreferred(BluetoothDevice device) {
+ public boolean isEnabled(BluetoothDevice device) {
if (mService == null) {
return false;
}
@@ -158,7 +141,7 @@
}
@Override
- public int getPreferred(BluetoothDevice device) {
+ public int getConnectionPolicy(BluetoothDevice device) {
if (mService == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
@@ -166,17 +149,20 @@
}
@Override
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
if (mService == null) {
- return;
+ return false;
}
- if (preferred) {
+ if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 35600b5..8a2c4f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -16,6 +16,8 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -102,20 +104,6 @@
}
@Override
- public boolean connect(BluetoothDevice device) {
- // Don't invoke method in service because settings is not allowed to connect this profile.
- return false;
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.disconnect(device);
- }
-
- @Override
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -124,21 +112,24 @@
}
@Override
- public boolean isPreferred(BluetoothDevice device) {
+ public boolean isEnabled(BluetoothDevice device) {
return getConnectionStatus(device) != BluetoothProfile.STATE_DISCONNECTED;
}
@Override
- public int getPreferred(BluetoothDevice device) {
+ public int getConnectionPolicy(BluetoothDevice device) {
return PREFERRED_VALUE;
}
@Override
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
// if set preferred to false, then disconnect to the current device
- if (!preferred) {
- mService.disconnect(device);
+ if (!enabled) {
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 588083e..3c24b4a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -101,20 +101,6 @@
return true;
}
- public boolean connect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
- }
-
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -122,29 +108,37 @@
return mService.getConnectionState(device);
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
if (mService == null) {
return false;
}
return mService.getConnectionPolicy(device) != CONNECTION_POLICY_FORBIDDEN;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
if (mService == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
- if (mService == null) return;
- if (preferred) {
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
+ if (mService == null) {
+ return false;
+ }
+ if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
index 4b0ca74..f609e43 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
@@ -35,17 +35,26 @@
*/
boolean isAutoConnectable();
- boolean connect(BluetoothDevice device);
-
- boolean disconnect(BluetoothDevice device);
-
int getConnectionStatus(BluetoothDevice device);
- boolean isPreferred(BluetoothDevice device);
+ /**
+ * Return {@code true} if the profile is enabled, otherwise return {@code false}.
+ * @param device the device to query for enable status
+ */
+ boolean isEnabled(BluetoothDevice device);
- int getPreferred(BluetoothDevice device);
+ /**
+ * Get the connection policy of the profile.
+ * @param device the device to query for enable status
+ */
+ int getConnectionPolicy(BluetoothDevice device);
- void setPreferred(BluetoothDevice device, boolean preferred);
+ /**
+ * Enable the profile if {@code enabled} is {@code true}, otherwise disable profile.
+ * @param device the device to set profile status
+ * @param enabled {@code true} for enable profile, otherwise disable profile.
+ */
+ boolean setEnabled(BluetoothDevice device, boolean enabled);
boolean isProfileReady();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index ae2acbe..c72efb7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -528,14 +528,14 @@
(mMapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
profiles.add(mMapProfile);
removedProfiles.remove(mMapProfile);
- mMapProfile.setPreferred(device, true);
+ mMapProfile.setEnabled(device, true);
}
if ((mPbapProfile != null) &&
(mPbapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
profiles.add(mPbapProfile);
removedProfiles.remove(mPbapProfile);
- mPbapProfile.setPreferred(device, true);
+ mPbapProfile.setEnabled(device, true);
}
if (mMapClientProfile != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 7d121aa..19cb2f5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -114,21 +114,6 @@
return true;
}
- public boolean connect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
- }
-
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
-
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -136,31 +121,37 @@
return mService.getConnectionState(device);
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
if (mService == null) {
return false;
}
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
if (mService == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
if (mService == null) {
- return;
+ return false;
}
- if (preferred) {
+ if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index a96a4e7..75c1926 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -16,6 +16,7 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import android.bluetooth.BluetoothAdapter;
@@ -112,19 +113,6 @@
return true;
}
- public boolean connect(BluetoothDevice device) {
- Log.d(TAG, "connect() - should not get called");
- return false; // MAP never connects out
- }
-
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
-
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -132,31 +120,37 @@
return mService.getConnectionState(device);
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
if (mService == null) {
return false;
}
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
if (mService == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
if (mService == null) {
- return;
+ return false;
}
- if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (enabled) {
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
index 8e3f3ed..5a6e6e8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
@@ -40,27 +40,23 @@
return false;
}
- public boolean connect(BluetoothDevice device) {
- return false;
- }
-
- public boolean disconnect(BluetoothDevice device) {
- return false;
- }
-
public int getConnectionStatus(BluetoothDevice device) {
return BluetoothProfile.STATE_DISCONNECTED; // Settings app doesn't handle OPP
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
return false;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; // Settings app doesn't handle OPP
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ return false;
}
public boolean isProfileReady() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 6638592..767df35 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -83,22 +86,6 @@
return false;
}
- public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- List<BluetoothDevice> sinks = mService.getConnectedDevices();
- if (sinks != null) {
- for (BluetoothDevice sink : sinks) {
- mService.disconnect(sink);
- }
- }
- return mService.connect(device);
- }
-
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.disconnect(device);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -106,16 +93,36 @@
return mService.getConnectionState(device);
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
return true;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
return -1;
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
- // ignore: isPreferred is always true for PAN
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
+ if (mService == null) {
+ return false;
+ }
+
+ if (enabled) {
+ final List<BluetoothDevice> sinks = mService.getConnectedDevices();
+ if (sinks != null) {
+ for (BluetoothDevice sink : sinks) {
+ mService.setConnectionPolicy(sink, CONNECTION_POLICY_FORBIDDEN);
+ }
+ }
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ } else {
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ }
+
+ return isEnabled;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 56267fc..0d11293 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -126,23 +126,6 @@
BluetoothProfile.STATE_DISCONNECTING});
}
- public boolean connect(BluetoothDevice device) {
- Log.d(TAG,"PBAPClientProfile got connect request");
- if (mService == null) {
- return false;
- }
- Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
- }
-
- public boolean disconnect(BluetoothDevice device) {
- Log.d(TAG,"PBAPClientProfile got disconnect request");
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -150,31 +133,37 @@
return mService.getConnectionState(device);
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
if (mService == null) {
return false;
}
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
if (mService == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
if (mService == null) {
- return;
+ return false;
}
- if (preferred) {
+ if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index f7c0bf5..9e2e4a1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -91,34 +91,33 @@
return false;
}
- public boolean connect(BluetoothDevice device) {
- /*Can't connect from server */
- return false;
-
- }
-
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
return mService.getConnectionState(device);
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
return false;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
return -1;
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
- // ignore: isPreferred is always true for PBAP
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
+ if (mService == null) {
+ return false;
+ }
+
+ if (!enabled) {
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ }
+
+ return isEnabled;
}
public String toString() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 3022c5b..104f1d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -111,21 +111,6 @@
return true;
}
- public boolean connect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
- }
-
- public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
-
- return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
- }
-
public int getConnectionStatus(BluetoothDevice device) {
if (mService == null) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -133,31 +118,37 @@
return mService.getConnectionState(device);
}
- public boolean isPreferred(BluetoothDevice device) {
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
if (mService == null) {
return false;
}
return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
- public int getPreferred(BluetoothDevice device) {
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
if (mService == null) {
return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
- public void setPreferred(BluetoothDevice device, boolean preferred) {
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
if (mService == null) {
- return;
+ return false;
}
- if (preferred) {
+ if (enabled) {
if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
+
+ return isEnabled;
}
public List<BluetoothDevice> getConnectedDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
index 12d054e..3a807c9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
@@ -108,9 +108,9 @@
Log.d(TAG, "addConnectableA2dpDevices() device : " + cachedDevice.getName()
+ ", is connected : " + cachedDevice.isConnected()
- + ", is preferred : " + a2dpProfile.isPreferred(device));
+ + ", is enabled : " + a2dpProfile.isEnabled(device));
- if (a2dpProfile.isPreferred(device)
+ if (a2dpProfile.isEnabled(device)
&& BluetoothDevice.BOND_BONDED == cachedDevice.getBondState()) {
addMediaDevice(cachedDevice);
}
@@ -143,9 +143,9 @@
Log.d(TAG, "addConnectableHearingAidDevices() device : " + cachedDevice.getName()
+ ", is connected : " + cachedDevice.isConnected()
- + ", is preferred : " + hapProfile.isPreferred(device));
+ + ", is enabled : " + hapProfile.isEnabled(device));
- if (hapProfile.isPreferred(device)
+ if (hapProfile.isEnabled(device)
&& BluetoothDevice.BOND_BONDED == cachedDevice.getBondState()) {
addMediaDevice(cachedDevice);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
index ccb6646..9bb2f22d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
@@ -16,12 +16,8 @@
package com.android.settingslib.bluetooth;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothA2dpSink;
@@ -68,18 +64,6 @@
}
@Test
- public void connect_shouldConnectBluetoothA2dpSink() {
- mProfile.connect(mBluetoothDevice);
- verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
- }
-
- @Test
- public void disconnect_shouldDisconnectBluetoothA2dpSink() {
- mProfile.disconnect(mBluetoothDevice);
- verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
- }
-
- @Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
index 9180760..d121e0b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
@@ -16,12 +16,8 @@
package com.android.settingslib.bluetooth;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
@@ -68,18 +64,6 @@
}
@Test
- public void connect_shouldConnectBluetoothHeadsetClient() {
- mProfile.connect(mBluetoothDevice);
- verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
- }
-
- @Test
- public void disconnect_shouldDisconnectBluetoothHeadsetClient() {
- mProfile.disconnect(mBluetoothDevice);
- verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
- }
-
- @Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
index f38af70..3665d9c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
@@ -18,7 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
@@ -65,17 +64,6 @@
}
@Test
- public void connect_shouldReturnFalse() {
- assertThat(mProfile.connect(mBluetoothDevice)).isFalse();
- }
-
- @Test
- public void disconnect_shouldDisconnectBluetoothHidDevice() {
- mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
- }
-
- @Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
index 1425c38..25031a6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -16,12 +16,8 @@
package com.android.settingslib.bluetooth;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
@@ -68,18 +64,6 @@
}
@Test
- public void connect_shouldConnectBluetoothMapClient() {
- mProfile.connect(mBluetoothDevice);
- verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
- }
-
- @Test
- public void disconnect_shouldDisconnectBluetoothMapClient() {
- mProfile.disconnect(mBluetoothDevice);
- verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
- }
-
- @Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
index 15f560b..4305a3b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -16,12 +16,8 @@
package com.android.settingslib.bluetooth;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
@@ -68,18 +64,6 @@
}
@Test
- public void connect_shouldConnectBluetoothPbapClient() {
- mProfile.connect(mBluetoothDevice);
- verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
- }
-
- @Test
- public void disconnect_shouldDisconnectBluetoothPbapClient() {
- mProfile.disconnect(mBluetoothDevice);
- verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
- }
-
- @Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
index 4f978a8..e460eaf 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
@@ -16,12 +16,8 @@
package com.android.settingslib.bluetooth;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
-import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
@@ -67,18 +63,6 @@
}
@Test
- public void connect_shouldConnectBluetoothSap() {
- mProfile.connect(mBluetoothDevice);
- verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
- }
-
- @Test
- public void disconnect_shouldDisconnectBluetoothSap() {
- mProfile.disconnect(mBluetoothDevice);
- verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
- }
-
- @Test
public void getConnectionStatus_shouldReturnConnectionState() {
when(mService.getConnectionState(mBluetoothDevice)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java
index f27cef9..0ee5ea8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java
@@ -96,7 +96,7 @@
when(mA2dpProfile.getConnectableDevices()).thenReturn(devices);
when(mCachedDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice);
when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
- when(mA2dpProfile.isPreferred(bluetoothDevice)).thenReturn(true);
+ when(mA2dpProfile.isEnabled(bluetoothDevice)).thenReturn(true);
assertThat(mMediaManager.mMediaDevices).isEmpty();
mMediaManager.startScan();
@@ -113,7 +113,7 @@
when(mA2dpProfile.getConnectableDevices()).thenReturn(devices);
when(mCachedDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice);
when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
- when(mA2dpProfile.isPreferred(bluetoothDevice)).thenReturn(true);
+ when(mA2dpProfile.isEnabled(bluetoothDevice)).thenReturn(true);
assertThat(mMediaManager.mMediaDevices).isEmpty();
mMediaManager.startScan();
@@ -141,7 +141,7 @@
when(mHapProfile.getConnectableDevices()).thenReturn(devices);
when(mCachedDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice);
when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
- when(mHapProfile.isPreferred(bluetoothDevice)).thenReturn(true);
+ when(mHapProfile.isEnabled(bluetoothDevice)).thenReturn(true);
assertThat(mMediaManager.mMediaDevices).isEmpty();
mMediaManager.startScan();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 9934e59..cd62420 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -425,7 +425,8 @@
}
newState = oldState;
} else {
- newState = new Setting(name, value, makeDefault, packageName, tag);
+ newState = new Setting(name, value, makeDefault, packageName, tag,
+ forceNonSystemPackage);
mSettings.put(name, newState);
}
@@ -1173,11 +1174,15 @@
public Setting(String name, String value, boolean makeDefault, String packageName,
String tag) {
+ this(name, value, makeDefault, packageName, tag, false);
+ }
+
+ Setting(String name, String value, boolean makeDefault, String packageName,
+ String tag, boolean forceNonSystemPackage) {
this.name = name;
// overrideableByRestore = true as the first initialization isn't considered a
// modification.
- update(value, makeDefault, packageName, tag, false,
- /* overrideableByRestore */ true);
+ update(value, makeDefault, packageName, tag, forceNonSystemPackage, true);
}
public Setting(String name, String value, String defaultValue,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index d67a9bc..8ff595b 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -33,7 +33,6 @@
import android.provider.Settings;
import android.util.Log;
-import org.junit.Ignore;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -692,125 +691,4 @@
cursor.close();
}
}
-
- @Test
- @Ignore("b/140250974")
- public void testLocationModeChanges_viaFrontEndApi() throws Exception {
- setStringViaFrontEndApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_MODE,
- String.valueOf(Settings.Secure.LOCATION_MODE_OFF),
- UserHandle.USER_SYSTEM);
- assertEquals(
- "Wrong location providers",
- "",
- getStringViaFrontEndApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- UserHandle.USER_SYSTEM));
-
- setStringViaFrontEndApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_MODE,
- String.valueOf(Settings.Secure.LOCATION_MODE_BATTERY_SAVING),
- UserHandle.USER_SYSTEM);
- assertEquals(
- "Wrong location providers",
- "network",
- getStringViaFrontEndApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- UserHandle.USER_SYSTEM));
-
- setStringViaFrontEndApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_MODE,
- String.valueOf(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY),
- UserHandle.USER_SYSTEM);
- assertEquals(
- "Wrong location providers",
- "gps,network",
- getStringViaFrontEndApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- UserHandle.USER_SYSTEM));
- }
-
- @Test
- @Ignore("b/140250974")
- public void testLocationProvidersAllowed_disableProviders() throws Exception {
- setStringViaFrontEndApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_MODE,
- String.valueOf(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY),
- UserHandle.USER_SYSTEM);
-
- // Disable providers that were enabled
- updateStringViaProviderApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- "-gps,-network");
- assertEquals(
- "Wrong location providers",
- "",
- queryStringViaProviderApi(
- SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
-
- // Disable a provider that was not enabled
- updateStringViaProviderApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- "-test");
- assertEquals(
- "Wrong location providers",
- "",
- queryStringViaProviderApi(
- SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
- }
-
- @Test
- @Ignore("b/140250974")
- public void testLocationProvidersAllowed_enableAndDisable() throws Exception {
- setStringViaFrontEndApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_MODE,
- String.valueOf(Settings.Secure.LOCATION_MODE_OFF),
- UserHandle.USER_SYSTEM);
-
- updateStringViaProviderApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- "+gps,+network,+test");
- updateStringViaProviderApiSetting(
- SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test");
-
- assertEquals(
- "Wrong location providers",
- "gps,network",
- queryStringViaProviderApi(
- SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
- }
-
- @Test
- @Ignore("b/140250974")
- public void testLocationProvidersAllowedLocked_invalidInput() throws Exception {
- setStringViaFrontEndApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_MODE,
- String.valueOf(Settings.Secure.LOCATION_MODE_OFF),
- UserHandle.USER_SYSTEM);
-
- // update providers with a invalid string
- updateStringViaProviderApiSetting(
- SETTING_TYPE_SECURE,
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- "+gps, invalid-string");
-
- // Verifies providers list does not change
- assertEquals(
- "Wrong location providers",
- "",
- queryStringViaProviderApi(
- SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
- }
}
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index a1c593f..b14bc7d 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -73,6 +73,7 @@
android:layout_width="208dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
+ android:minHeight="48dp"
android:gravity="center"
android:inputType="textPassword"
android:maxLength="500"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9c997e8..5a1d1e2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1090,7 +1090,7 @@
<!-- Blur radius on status bar window and power menu -->
<dimen name="min_window_blur_radius">1px</dimen>
- <dimen name="max_window_blur_radius">100px</dimen>
+ <dimen name="max_window_blur_radius">250px</dimen>
<!-- How much into a DisplayCutout's bounds we can go, on each side -->
<dimen name="display_cutout_margin_consumption">0px</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index b8d32ae..5c65977 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -583,11 +583,13 @@
* @return
*/
public static WindowManager.LayoutParams getLayoutParams(IBinder windowToken) {
+ final int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+ | WindowManager.LayoutParams.FLAG_SECURE;
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ windowFlags,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("BiometricPrompt");
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 6062a3d..20d19ec 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -533,10 +533,6 @@
mBubbleContainer.addView(mOverflowBtn, 0,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- mOverflowBtn.setOnClickListener(v -> {
- setSelectedBubble(null);
- });
-
TypedArray ta = mContext.obtainStyledAttributes(
new int[]{android.R.attr.colorBackgroundFloating});
int bgColor = ta.getColor(0, Color.WHITE /* default */);
@@ -856,6 +852,10 @@
updateBubbleZOrdersAndDotPosition(false /* animate */);
}
+ void showOverflow() {
+ setSelectedBubble(null);
+ }
+
/**
* Changes the currently selected bubble. If the stack is already expanded, the newly selected
* bubble will be shown immediately. This does not change the expanded state or change the
@@ -950,6 +950,10 @@
}
if (mIsExpanded) {
if (isIntersecting(mBubbleContainer, x, y)) {
+ if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
+ && isIntersecting(mOverflowBtn, x, y)) {
+ return mOverflowBtn;
+ }
// Could be tapping or dragging a bubble while expanded
for (int i = 0; i < getBubbleCount(); i++) {
BadgedImageView view = (BadgedImageView) mBubbleContainer.getChildAt(i);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index fdeaf1f..5a9d44b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -24,6 +24,7 @@
import android.view.ViewConfiguration;
import com.android.systemui.Dependency;
+import com.android.systemui.R;
/**
* Handles interpreting touches on a {@link BubbleStackView}. This includes expanding, collapsing,
@@ -109,6 +110,10 @@
if (!(mTouchedView instanceof BadgedImageView)
&& !(mTouchedView instanceof BubbleStackView)
&& !(mTouchedView instanceof BubbleFlyoutView)) {
+
+ if (mTouchedView.getId() == R.id.bubble_overflow_button) {
+ mStack.showOverflow();
+ }
// Not touching anything touchable, but we shouldn't collapse (e.g. touching edge
// of expanded view).
mStack.hideBubbleMenu();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InflationTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/InflationTask.java
index 22fd37c..eb580c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InflationTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InflationTask.java
@@ -22,11 +22,4 @@
*/
public interface InflationTask {
void abort();
-
- /**
- * Supersedes an existing task. i.e another task was superceeded by this.
- *
- * @param task the task that was previously running
- */
- default void supersedeTask(InflationTask task) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 81833a4..d0e238a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -28,7 +28,6 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import javax.inject.Inject;
@@ -64,8 +63,8 @@
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
- public void onEntryInflated(NotificationEntry entry, int inflatedFlags) {
- showAlertingView(entry, inflatedFlags);
+ public void onEntryInflated(NotificationEntry entry) {
+ showAlertingView(entry);
}
@Override
@@ -90,12 +89,11 @@
/**
* Adds the entry to the respective alerting manager if the content view was inflated and
* the entry should still alert.
- *
- * @param entry entry to add
- * @param inflatedFlags flags representing content views that were inflated
*/
- private void showAlertingView(NotificationEntry entry, @InflationFlag int inflatedFlags) {
- if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
+ private void showAlertingView(NotificationEntry entry) {
+ // TODO: Instead of this back and forth, we should listen to changes in heads up and
+ // cancel on-going heads up view inflation using the bind pipeline.
+ if (entry.getRow().getPrivateLayout().getHeadsUpChild() != null) {
// Possible for shouldHeadsUp to change between the inflation starting and ending.
// If it does and we no longer need to heads up, we should free the view.
if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index f6b5583..25253a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -24,7 +24,6 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
/**
* Listener interface for changes sent by NotificationEntryManager.
@@ -62,7 +61,7 @@
/**
* Called when a notification's views are inflated for the first time.
*/
- default void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) {
+ default void onEntryInflated(NotificationEntry entry) {
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 6bb377e8..61915ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -18,6 +18,7 @@
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_ERROR;
+import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import android.annotation.NonNull;
@@ -44,10 +45,11 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.logging.NotifEvent;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -96,6 +98,7 @@
*/
@Singleton
public class NotificationEntryManager implements
+ CommonNotifCollection,
Dumpable,
InflationCallback,
VisualStabilityManager.Callback {
@@ -130,6 +133,7 @@
private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
private final LeakDetector mLeakDetector;
+ private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
private final KeyguardEnvironment mKeyguardEnvironment;
private final NotificationGroupManager mGroupManager;
@@ -318,8 +322,7 @@
}
@Override
- public void onAsyncInflationFinished(NotificationEntry entry,
- @InflationFlag int inflatedFlags) {
+ public void onAsyncInflationFinished(NotificationEntry entry) {
mPendingNotifications.remove(entry.getKey());
// If there was an async task started after the removal, we don't want to add it back to
// the list, otherwise we might get leaks.
@@ -328,7 +331,7 @@
if (isNew) {
for (NotificationEntryListener listener : mNotificationEntryListeners) {
mNotifLog.log(NotifEvent.INFLATED, entry);
- listener.onEntryInflated(entry, inflatedFlags);
+ listener.onEntryInflated(entry);
}
addActiveNotification(entry);
updateNotifications("onAsyncInflationFinished");
@@ -488,6 +491,13 @@
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onEntryRemoved(entry, visibility, removedByUser);
}
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ // NEM doesn't have a good knowledge of reasons so defaulting to unknown.
+ listener.onEntryRemoved(entry, REASON_UNKNOWN);
+ }
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryCleanUp(entry);
+ }
}
}
}
@@ -553,6 +563,10 @@
mLeakDetector.trackInstance(entry);
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryInit(entry);
+ }
+
// Construct the expanded view.
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
mNotificationRowBinderLazy.get()
@@ -566,6 +580,9 @@
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPendingEntryAdded(entry);
}
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryAdded(entry);
+ }
}
public void addNotification(StatusBarNotification notification, RankingMap ranking) {
@@ -600,6 +617,9 @@
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPreEntryUpdated(entry);
}
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryUpdated(entry);
+ }
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
mNotificationRowBinderLazy.get()
@@ -674,6 +694,9 @@
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onNotificationRankingUpdated(rankingMap);
}
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onRankingUpdate(rankingMap);
+ }
}
private void updateRankingOfPendingNotifications(@Nullable RankingMap rankingMap) {
@@ -862,6 +885,11 @@
return mReadOnlyNotifications.size() != 0;
}
+ @Override
+ public void addCollectionListener(NotifCollectionListener listener) {
+ mNotifCollectionListeners.add(listener);
+ }
+
/*
* End annexation
* -----
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index 7a6d4f1..9272e51b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -149,9 +149,7 @@
}
@Override
- public void onAsyncInflationFinished(
- NotificationEntry entry,
- int inflatedFlags) {
+ public void onAsyncInflationFinished(NotificationEntry entry) {
if (mExternalInflationCallback != null) {
mExternalInflationCallback.onInflationFinished(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
index 9142388..5767ad9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
@@ -66,7 +67,7 @@
* 9. The list is handed off to the view layer to be rendered
*/
@Singleton
-public class NotifPipeline {
+public class NotifPipeline implements CommonNotifCollection {
private final NotifCollection mNotifCollection;
private final ShadeListBuilder mShadeListBuilder;
@@ -89,10 +90,7 @@
return mNotifCollection.getActiveNotifs();
}
- /**
- * Registers a listener to be informed when there is a notification entry event such as an add,
- * update, or remove.
- */
+ @Override
public void addCollectionListener(NotifCollectionListener listener) {
mNotifCollection.addCollectionListener(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index df65dac..5dbf47e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -275,7 +275,12 @@
return mHasInflationError;
}
- void setHasInflationError(boolean hasError) {
+ /**
+ * Set whether the notification has an error while inflating.
+ *
+ * TODO: Move this into an inflation error manager class.
+ */
+ public void setHasInflationError(boolean hasError) {
mHasInflationError = hasError;
}
@@ -595,12 +600,8 @@
public void setInflationTask(InflationTask abortableTask) {
// abort any existing inflation
- InflationTask existing = mRunningTask;
abortTask();
mRunningTask = abortableTask;
- if (existing != null && mRunningTask != null) {
- mRunningTask.supersedeTask(existing);
- }
}
public void onInflationTaskFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 2a7683a..59d82a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -42,8 +42,11 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
+import com.android.systemui.statusbar.notification.row.RowContentBindParams;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -67,8 +70,10 @@
private final NotificationGroupManager mGroupManager;
private final NotificationGutsManager mGutsManager;
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+
private final Context mContext;
- private final NotificationRowContentBinder mRowContentBinder;
+ private final NotifBindPipeline mNotifBindPipeline;
+ private final RowContentBindStage mRowContentBindStage;
private final NotificationMessagingUtil mMessagingUtil;
private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
this::logNotificationExpansion;
@@ -93,7 +98,8 @@
Context context,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationLockscreenUserManager notificationLockscreenUserManager,
- NotificationRowContentBinder rowContentBinder,
+ NotifBindPipeline notifBindPipeline,
+ RowContentBindStage rowContentBindStage,
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
KeyguardBypassController keyguardBypassController,
StatusBarStateController statusBarStateController,
@@ -103,7 +109,8 @@
Provider<RowInflaterTask> rowInflaterTaskProvider,
NotificationLogger logger) {
mContext = context;
- mRowContentBinder = rowContentBinder;
+ mNotifBindPipeline = notifBindPipeline;
+ mRowContentBindStage = rowContentBindStage;
mMessagingUtil = new NotificationMessagingUtil(context);
mNotificationRemoteInputManager = notificationRemoteInputManager;
mNotificationLockscreenUserManager = notificationLockscreenUserManager;
@@ -167,6 +174,7 @@
}
}
+ //TODO: This method associates a row with an entry, but eventually needs to not do that
private void bindRow(NotificationEntry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row,
Runnable onDismissRunnable) {
@@ -195,12 +203,11 @@
mKeyguardBypassController,
mGroupManager,
mHeadsUpManager,
- mRowContentBinder,
+ mRowContentBindStage,
mPresenter);
// TODO: Either move these into ExpandableNotificationRow#initialize or out of row entirely
row.setStatusBarStateController(mStatusBarStateController);
- row.setInflationCallback(mInflationCallback);
row.setAppOpsOnClickListener(mOnAppOpsClickListener);
if (mAllowLongPress) {
row.setLongPressListener(mGutsManager::openGuts);
@@ -214,6 +221,10 @@
row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
}
+ entry.setRow(row);
+ row.setEntry(entry);
+ mNotifBindPipeline.manageRow(entry, row);
+
mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
}
@@ -247,13 +258,11 @@
}
}
- //TODO: This method associates a row with an entry, but eventually needs to not do that
private void updateNotification(
NotificationEntry entry,
PackageManager pmUser,
StatusBarNotification sbn,
ExpandableNotificationRow row) {
- row.setIsLowPriority(entry.isAmbient());
// Extract target SDK version.
try {
@@ -268,22 +277,30 @@
// TODO: should updates to the entry be happening somewhere else?
entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- entry.setRow(row);
row.setOnActivatedListener(mPresenter);
- boolean useIncreasedCollapsedHeight =
+ final boolean useIncreasedCollapsedHeight =
mMessagingUtil.isImportantMessaging(sbn, entry.getImportance());
- boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
+ final boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight
&& !mPresenter.isPresenterFullyCollapsed();
- row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
- row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
- row.setEntry(entry);
+ final boolean isLowPriority = entry.isAmbient();
+
+ RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
+ params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
+ params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
+ params.setUseLowPriority(entry.isAmbient());
if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
- row.setInflationFlags(FLAG_CONTENT_VIEW_HEADS_UP);
+ params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
}
+ //TODO: Replace this API with RowContentBindParams directly
row.setNeedsRedaction(mNotificationLockscreenUserManager.needsRedaction(entry));
- row.inflateViews();
+ mRowContentBindStage.requestRebind(entry, en -> {
+ row.setUsesIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
+ row.setUsesIncreasedHeadsUpHeight(useIncreasedHeadsUp);
+ row.setIsLowPriority(isLowPriority);
+ mInflationCallback.onAsyncInflationFinished(en);
+ });
// bind the click event to the content area
Objects.requireNonNull(mNotificationClicker).register(row, sbn);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
new file mode 100644
index 0000000..171816f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.notifcollection;
+
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * A notification collection that manages the list of {@link NotificationEntry}s that will be
+ * rendered.
+ *
+ * TODO: (b/145659174) Once we fully switch off {@link NotificationEntryManager} to
+ * {@link NotifPipeline}, we probably won't need this, but having it for now makes it easy to
+ * switch between the two.
+ */
+public interface CommonNotifCollection {
+ /**
+ * Registers a listener to be informed when notifications are created, added, updated, removed,
+ * or deleted.
+ */
+ void addCollectionListener(NotifCollectionListener listener);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index c7666e4..39f4dfa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -19,6 +19,10 @@
import android.content.Context;
import com.android.systemui.R;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
@@ -45,4 +49,16 @@
return stubController.get();
}
}
+
+ /**
+ * Provide the active notification collection managing the notifications to render.
+ */
+ @Provides
+ @Singleton
+ public CommonNotifCollection provideCommonNotifCollection(
+ FeatureFlags featureFlags,
+ Lazy<NotifPipeline> pipeline,
+ NotificationEntryManager entryManager) {
+ return featureFlags.isNewNotifPipelineRenderingEnabled() ? pipeline.get() : entryManager;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 61e3192..254b64f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -28,6 +28,7 @@
import com.android.systemui.statusbar.notification.NotificationListController
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
+import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper
import com.android.systemui.statusbar.phone.NotificationGroupManager
@@ -55,6 +56,7 @@
private val notificationListener: NotificationListener,
private val entryManager: NotificationEntryManager,
private val newNotifPipeline: Lazy<NotifPipelineInitializer>,
+ private val notifBindPipelineInitializer: NotifBindPipelineInitializer,
private val deviceProvisionedController: DeviceProvisionedController,
private val notificationRowBinder: NotificationRowBinderImpl,
private val remoteInputUriController: RemoteInputUriController,
@@ -98,6 +100,7 @@
if (featureFlags.isNewNotifPipelineRenderingEnabled) {
// TODO
} else {
+ notifBindPipelineInitializer.initialize()
notificationRowBinder.setInflationCallback(entryManager)
remoteInputUriController.attach(entryManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
index 88b4147..fc221d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -93,8 +93,7 @@
private val peopleHubManagerForUser = SparseArray<PeopleHubManager>()
private val notificationEntryListener = object : NotificationEntryListener {
- override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) =
- addVisibleEntry(entry)
+ override fun onEntryInflated(entry: NotificationEntry) = addVisibleEntry(entry)
override fun onEntryReinflated(entry: NotificationEntry) = addVisibleEntry(entry)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindRequester.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindRequester.java
new file mode 100644
index 0000000..1cf6b4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindRequester.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.os.CancellationSignal;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
+
+/**
+ * A {@link BindRequester} is a general superclass for something that notifies
+ * {@link NotifBindPipeline} when it needs it to kick off a bind run.
+ */
+public abstract class BindRequester {
+ private @Nullable BindRequestListener mBindRequestListener;
+
+ /**
+ * Notifies the listener that some parameters/state has changed for some notification and that
+ * content needs to be bound again.
+ *
+ * The caller can also specify a callback for when the entire bind pipeline completes, i.e.
+ * when the change is fully propagated to the final view. The caller can cancel this
+ * callback with the returned cancellation signal.
+ *
+ * @param callback callback after bind completely finishes
+ * @return cancellation signal to cancel callback
+ */
+ public final CancellationSignal requestRebind(
+ @NonNull NotificationEntry entry,
+ @Nullable BindCallback callback) {
+ CancellationSignal signal = new CancellationSignal();
+ if (mBindRequestListener != null) {
+ mBindRequestListener.onBindRequest(entry, signal, callback);
+ }
+ return signal;
+ }
+
+ final void setBindRequestListener(BindRequestListener listener) {
+ mBindRequestListener = listener;
+ }
+
+ /**
+ * Listener interface for when content needs to be bound again.
+ */
+ public interface BindRequestListener {
+
+ /**
+ * Called when {@link #requestRebind} is called.
+ *
+ * @param entry notification that has outdated content
+ * @param signal cancellation signal to cancel callback
+ * @param callback callback after content is fully updated
+ */
+ void onBindRequest(
+ @NonNull NotificationEntry entry,
+ @NonNull CancellationSignal signal,
+ @Nullable BindCallback callback);
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java
new file mode 100644
index 0000000..29447ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BindStage.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.annotation.MainThread;
+import android.util.ArrayMap;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.Map;
+
+/**
+ * A {@link BindStage} is an abstraction for a unit of work in inflating/binding/unbinding
+ * views to a notification. Used by {@link NotifBindPipeline}.
+ *
+ * Clients may also use {@link #getStageParams} to provide parameters for this stage for a given
+ * notification and request a rebind.
+ *
+ * @param <Params> params to do this stage
+ */
+@MainThread
+public abstract class BindStage<Params> extends BindRequester {
+
+ private Map<NotificationEntry, Params> mContentParams = new ArrayMap<>();
+
+ /**
+ * Execute the stage asynchronously.
+ *
+ * @param row notification top-level view to bind views to
+ * @param callback callback after stage finishes
+ */
+ protected abstract void executeStage(
+ @NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row,
+ @NonNull StageCallback callback);
+
+ /**
+ * Abort the stage if in progress.
+ *
+ * @param row notification top-level view to bind views to
+ */
+ protected abstract void abortStage(
+ @NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row);
+
+ /**
+ * Get the stage parameters for the entry. Clients should use this to modify how the stage
+ * handles the notification content.
+ */
+ public final Params getStageParams(@NonNull NotificationEntry entry) {
+ Params params = mContentParams.get(entry);
+ if (params == null) {
+ throw new IllegalStateException(
+ String.format("Entry does not have any stage parameters. key: %s",
+ entry.getKey()));
+ }
+ return params;
+ }
+
+ /**
+ * Create a params entry for the notification for this stage.
+ */
+ final void createStageParams(@NonNull NotificationEntry entry) {
+ mContentParams.put(entry, newStageParams());
+ }
+
+ /**
+ * Delete params entry for notification.
+ */
+ final void deleteStageParams(@NonNull NotificationEntry entry) {
+ mContentParams.remove(entry);
+ }
+
+ /**
+ * Create a new, empty stage params object.
+ */
+ protected abstract Params newStageParams();
+
+ /**
+ * Interface for callback.
+ */
+ interface StageCallback {
+ /**
+ * Callback for when the stage is complete.
+ */
+ void onStageFinished(NotificationEntry entry);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 253be2fc..c34bba7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -19,8 +19,6 @@
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -88,8 +86,6 @@
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -127,14 +123,6 @@
public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
- /**
- * Content views that must be inflated at all times.
- */
- @InflationFlag
- static final int REQUIRED_INFLATION_FLAGS =
- FLAG_CONTENT_VIEW_CONTRACTED
- | FLAG_CONTENT_VIEW_EXPANDED;
-
private boolean mUpdateBackgroundOnUpdate;
private boolean mNotificationTranslationFinished = false;
@@ -149,7 +137,7 @@
private StatusBarStateController mStatusbarStateController;
private KeyguardBypassController mBypassController;
private LayoutListener mLayoutListener;
- private NotificationRowContentBinder mNotificationContentBinder;
+ private RowContentBindStage mRowContentBindStage;
private int mIconTransformContentShift;
private int mIconTransformContentShiftNoIcon;
private int mMaxHeadsUpHeightBeforeN;
@@ -244,10 +232,7 @@
private ExpandableNotificationRow mNotificationParent;
private OnExpandClickListener mOnExpandClickListener;
private View.OnClickListener mOnAppOpsClickListener;
- private InflationCallback mInflationCallback;
private boolean mIsChildInGroup;
- private @InflationFlag int mInflationFlags = REQUIRED_INFLATION_FLAGS;
- private final BindParams mBindParams = new BindParams();
// Listener will be called when receiving a long click event.
// Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -460,24 +445,25 @@
}
/**
- * Inflate views based off the inflation flags set. Inflation happens asynchronously.
- */
- public void inflateViews() {
- mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams,
- false /* forceInflate */, mInflationCallback);
- }
-
- /**
* Marks a content view as freeable, setting it so that future inflations do not reinflate
* and ensuring that the view is freed when it is safe to remove.
*
+ * TODO: This should be moved to the respective coordinator and call
+ * {@link RowContentBindParams#freeContentViews} directly after disappear animation
+ * finishes instead of depending on binding API to know when it's "safe".
+ *
* @param inflationFlag flag corresponding to the content view to be freed
*/
public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
// View should not be reinflated in the future
- clearInflationFlags(inflationFlag);
- Runnable freeViewRunnable =
- () -> mNotificationContentBinder.unbindContent(mEntry, this, inflationFlag);
+ Runnable freeViewRunnable = () -> {
+ // Possible for notification to be removed after free request.
+ if (!isRemoved()) {
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.freeContentViews(inflationFlag);
+ mRowContentBindStage.requestRebind(mEntry, null /* callback */);
+ }
+ };
switch (inflationFlag) {
case FLAG_CONTENT_VIEW_HEADS_UP:
getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
@@ -492,35 +478,6 @@
}
/**
- * Set flags for content views that should be inflated
- *
- * @param flags flags to inflate
- */
- public void setInflationFlags(@InflationFlag int flags) {
- mInflationFlags |= flags;
- }
-
- /**
- * Clear flags for content views that should not be inflated
- *
- * @param flags flags that should not be inflated
- */
- public void clearInflationFlags(@InflationFlag int flags) {
- mInflationFlags &= ~flags;
- mInflationFlags |= REQUIRED_INFLATION_FLAGS;
- }
-
- /**
- * Whether or not a content view should be inflated.
- *
- * @param flag the flag corresponding to the content view
- * @return true if the flag is set, false otherwise
- */
- public boolean isInflationFlagSet(@InflationFlag int flag) {
- return ((mInflationFlags & flag) != 0);
- }
-
- /**
* Caches whether or not this row contains a system notification. Note, this is only cached
* once per notification as the packageInfo can't technically change for a notification row.
*/
@@ -838,13 +795,13 @@
}
mNotificationParent = isChildInGroup ? parent : null;
mPrivateLayout.setIsChildInGroup(isChildInGroup);
- mBindParams.isChildInGroup = isChildInGroup;
+ // TODO: Move inflation logic out of this call
if (mIsChildInGroup != isChildInGroup) {
mIsChildInGroup = isChildInGroup;
if (mIsLowPriority) {
- int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
- mNotificationContentBinder.bindContent(mEntry, this, flags, mBindParams,
- false /* forceInflate */, mInflationCallback);
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.setUseLowPriority(mIsLowPriority);
+ mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
}
resetBackgroundAlpha();
@@ -1243,8 +1200,10 @@
l.reInflateViews();
}
mEntry.getSbn().clearPackageContext();
- mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams,
- true /* forceInflate */, mInflationCallback);
+ // TODO: Move content inflation logic out of this call
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.setNeedsReinflation(true);
+ mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
@Override
@@ -1598,7 +1557,6 @@
public void setIsLowPriority(boolean isLowPriority) {
mIsLowPriority = isLowPriority;
mPrivateLayout.setIsLowPriority(isLowPriority);
- mBindParams.isLowPriority = mIsLowPriority;
if (mChildrenContainer != null) {
mChildrenContainer.setIsLowPriority(isLowPriority);
}
@@ -1608,36 +1566,25 @@
return mIsLowPriority;
}
- public void setUseIncreasedCollapsedHeight(boolean use) {
+ public void setUsesIncreasedCollapsedHeight(boolean use) {
mUseIncreasedCollapsedHeight = use;
- mBindParams.usesIncreasedHeight = use;
}
- public void setUseIncreasedHeadsUpHeight(boolean use) {
+ public void setUsesIncreasedHeadsUpHeight(boolean use) {
mUseIncreasedHeadsUpHeight = use;
- mBindParams.usesIncreasedHeadsUpHeight = use;
- }
-
- /**
- * Set callback for notification content inflation
- *
- * @param callback inflation callback
- */
- public void setInflationCallback(InflationCallback callback) {
- mInflationCallback = callback;
}
public void setNeedsRedaction(boolean needsRedaction) {
+ // TODO: Move inflation logic out of this call and remove this method
if (mNeedsRedaction != needsRedaction) {
mNeedsRedaction = needsRedaction;
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
if (needsRedaction) {
- setInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
- mNotificationContentBinder.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC,
- mBindParams, false /* forceInflate */, mInflationCallback);
+ params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
} else {
- clearInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
- freeContentViewWhenSafe(FLAG_CONTENT_VIEW_PUBLIC);
+ params.freeContentViews(FLAG_CONTENT_VIEW_PUBLIC);
}
+ mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
}
@@ -1664,7 +1611,7 @@
KeyguardBypassController bypassController,
NotificationGroupManager groupManager,
HeadsUpManager headsUpManager,
- NotificationRowContentBinder rowContentBinder,
+ RowContentBindStage rowContentBindStage,
OnExpandClickListener onExpandClickListener) {
mAppName = appName;
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
@@ -1676,7 +1623,7 @@
mGroupManager = groupManager;
mPrivateLayout.setGroupManager(groupManager);
mHeadsUpManager = headsUpManager;
- mNotificationContentBinder = rowContentBinder;
+ mRowContentBindStage = rowContentBindStage;
mOnExpandClickListener = onExpandClickListener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
new file mode 100644
index 0000000..af2d084
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.widget.FrameLayout;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.os.CancellationSignal;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * {@link NotifBindPipeline} is responsible for converting notifications from their data form to
+ * their actual inflated views. It is essentially a control class that composes notification view
+ * binding logic (i.e. {@link BindStage}) in response to explicit bind requests. At the end of the
+ * pipeline, the notification's bound views are guaranteed to be correct and up-to-date, and any
+ * registered callbacks will be called.
+ *
+ * The pipeline ensures that a notification's top-level view and its content views are bound.
+ * Currently, a notification's top-level view, the {@link ExpandableNotificationRow} is essentially
+ * just a {@link FrameLayout} for various different content views that are switched in and out as
+ * appropriate. These include a contracted view, expanded view, heads up view, and sensitive view on
+ * keyguard. See {@link InflationFlag}. These content views themselves can have child views added
+ * on depending on different factors. For example, notification actions and smart replies are views
+ * that are dynamically added to these content views after they're inflated. Finally, aside from
+ * the app provided content views, System UI itself also provides some content views that are shown
+ * occasionally (e.g. {@link NotificationGuts}). Many of these are business logic specific views
+ * and the requirements surrounding them may change over time, so the pipeline must handle
+ * composing the logic as necessary.
+ *
+ * Note that bind requests do not only occur from add/updates from updates from the app. For
+ * example, the user may make changes to device settings (e.g. sensitive notifications on lock
+ * screen) or we may want to make certain optimizations for the sake of memory or performance (e.g
+ * freeing views when not visible). Oftentimes, we also need to wait for these changes to complete
+ * before doing something else (e.g. moving a notification to the top of the screen to heads up).
+ * The pipeline thus handles bind requests from across the system and provides a way for
+ * requesters to know when the change is propagated to the view.
+ *
+ * Right now, we only support one attached {@link BindStage} which just does all the binding but we
+ * should eventually support multiple stages once content inflation is made more modular.
+ * In particular, row inflation/binding, which is handled by {@link NotificationRowBinder} should
+ * probably be moved here in the future as a stage. Right now, the pipeline just manages content
+ * views and assumes that a row is given to it when it's inflated.
+ */
+@MainThread
+@Singleton
+public final class NotifBindPipeline {
+ private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>();
+ private BindStage mStage;
+
+ @Inject
+ NotifBindPipeline(NotificationEntryManager entryManager) {
+ entryManager.addNotificationEntryListener(mEntryListener);
+ }
+
+ /**
+ * Set the bind stage for binding notification row content.
+ */
+ public void setStage(
+ BindStage stage) {
+ mStage = stage;
+ mStage.setBindRequestListener(this::onBindRequested);
+ }
+
+ /**
+ * Start managing the row's content for a given notification.
+ */
+ public void manageRow(
+ @NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row) {
+ final BindEntry bindEntry = getBindEntry(entry);
+ bindEntry.row = row;
+ if (bindEntry.invalidated) {
+ startPipeline(entry);
+ }
+ }
+
+ private void onBindRequested(
+ @NonNull NotificationEntry entry,
+ @NonNull CancellationSignal signal,
+ @Nullable BindCallback callback) {
+ final BindEntry bindEntry = getBindEntry(entry);
+ if (bindEntry == null) {
+ // Invalidating views for a notification that is not active.
+ return;
+ }
+
+ bindEntry.invalidated = true;
+
+ // Put in new callback.
+ if (callback != null) {
+ final Set<BindCallback> callbacks = bindEntry.callbacks;
+ callbacks.add(callback);
+ signal.setOnCancelListener(() -> callbacks.remove(callback));
+ }
+
+ startPipeline(entry);
+ }
+
+ /**
+ * Run the pipeline for the notification, ensuring all views are bound when finished. Call all
+ * callbacks when the run finishes. If a run is already in progress, it is restarted.
+ */
+ private void startPipeline(NotificationEntry entry) {
+ if (mStage == null) {
+ throw new IllegalStateException("No stage was ever set on the pipeline");
+ }
+
+ final BindEntry bindEntry = mBindEntries.get(entry);
+ final ExpandableNotificationRow row = bindEntry.row;
+ if (row == null) {
+ // Row is not managed yet but may be soon. Stop for now.
+ return;
+ }
+
+ mStage.abortStage(entry, row);
+ mStage.executeStage(entry, row, (en) -> onPipelineComplete(en));
+ }
+
+ private void onPipelineComplete(NotificationEntry entry) {
+ final BindEntry bindEntry = getBindEntry(entry);
+
+ bindEntry.invalidated = false;
+
+ final Set<BindCallback> callbacks = bindEntry.callbacks;
+ for (BindCallback cb : callbacks) {
+ cb.onBindFinished(entry);
+ }
+ callbacks.clear();
+ }
+
+ //TODO: Move this to onManageEntry hook when we split that from add/remove
+ private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
+ @Override
+ public void onPendingEntryAdded(NotificationEntry entry) {
+ mBindEntries.put(entry, new BindEntry());
+ mStage.createStageParams(entry);
+ }
+
+ @Override
+ public void onEntryRemoved(NotificationEntry entry,
+ @Nullable NotificationVisibility visibility,
+ boolean removedByUser) {
+ BindEntry bindEntry = mBindEntries.remove(entry);
+ ExpandableNotificationRow row = bindEntry.row;
+ if (row != null) {
+ mStage.abortStage(entry, row);
+ }
+ mStage.deleteStageParams(entry);
+ }
+ };
+
+ private @NonNull BindEntry getBindEntry(NotificationEntry entry) {
+ final BindEntry bindEntry = mBindEntries.get(entry);
+ if (bindEntry == null) {
+ throw new IllegalStateException(
+ String.format("Attempting bind on an inactive notification. key: %s",
+ entry.getKey()));
+ }
+ return bindEntry;
+ }
+
+ /**
+ * Interface for bind callback.
+ */
+ public interface BindCallback {
+ /**
+ * Called when all views are fully bound on the notification.
+ */
+ void onBindFinished(NotificationEntry entry);
+ }
+
+ private class BindEntry {
+ public ExpandableNotificationRow row;
+ public final Set<BindCallback> callbacks = new ArraySet<>();
+ public boolean invalidated;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineInitializer.java
new file mode 100644
index 0000000..7754991
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineInitializer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import javax.inject.Inject;
+
+/**
+ * Initialize {@link NotifBindPipeline} with all its mandatory stages and dynamically added stages.
+ *
+ * In the future, coordinators should be able to register their own {@link BindStage} to the
+ * {@link NotifBindPipeline}.
+ */
+public class NotifBindPipelineInitializer {
+ NotifBindPipeline mNotifBindPipeline;
+ RowContentBindStage mRowContentBindStage;
+
+ @Inject
+ NotifBindPipelineInitializer(
+ NotifBindPipeline pipeline,
+ RowContentBindStage stage) {
+ mNotifBindPipeline = pipeline;
+ mRowContentBindStage = stage;
+ // TODO: Inject coordinators and allow them to add BindStages in initialize
+ }
+
+ /**
+ * Hooks up stages to the pipeline.
+ */
+ public void initialize() {
+ // Mandatory bind stages
+ mNotifBindPipeline.setStage(mRowContentBindStage);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java
index a6e5c2b..b62dfa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java
@@ -22,10 +22,9 @@
import androidx.annotation.Nullable;
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import java.util.Map;
@@ -40,8 +39,8 @@
new ArrayMap<>();
@Inject
- NotifRemoteViewCacheImpl(NotificationEntryManager entryManager) {
- entryManager.addNotificationEntryListener(mEntryListener);
+ NotifRemoteViewCacheImpl(CommonNotifCollection collection) {
+ collection.addCollectionListener(mCollectionListener);
}
@Override
@@ -93,17 +92,14 @@
contentViews.clear();
}
- private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
+ private final NotifCollectionListener mCollectionListener = new NotifCollectionListener() {
@Override
- public void onPendingEntryAdded(NotificationEntry entry) {
+ public void onEntryInit(NotificationEntry entry) {
mNotifCachedContentViews.put(entry, new SparseArray<>());
}
@Override
- public void onEntryRemoved(
- NotificationEntry entry,
- @Nullable NotificationVisibility visibility,
- boolean removedByUser) {
+ public void onEntryCleanUp(NotificationEntry entry) {
mNotifCachedContentViews.remove(entry);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index e1a6747..566da65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -68,7 +68,7 @@
private final NotifRemoteViewCache mRemoteViewCache;
@Inject
- public NotificationContentInflater(
+ NotificationContentInflater(
NotifRemoteViewCache remoteViewCache,
NotificationRemoteInputManager remoteInputManager) {
mRemoteViewCache = remoteViewCache;
@@ -575,7 +575,7 @@
entry.headsUpStatusBarText = result.headsUpStatusBarText;
entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
if (endListener != null) {
- endListener.onAsyncInflationFinished(entry, reInflateFlags);
+ endListener.onAsyncInflationFinished(entry);
}
return true;
}
@@ -641,7 +641,7 @@
private final boolean mUsesIncreasedHeight;
private final InflationCallback mCallback;
private final boolean mUsesIncreasedHeadsUpHeight;
- private @InflationFlag int mReInflateFlags;
+ private final @InflationFlag int mReInflateFlags;
private final NotifRemoteViewCache mRemoteViewCache;
private ExpandableNotificationRow mRow;
private Exception mError;
@@ -739,25 +739,16 @@
}
@Override
- public void supersedeTask(InflationTask task) {
- if (task instanceof AsyncInflationTask) {
- // We want to inflate all flags of the previous task as well
- mReInflateFlags |= ((AsyncInflationTask) task).mReInflateFlags;
- }
- }
-
- @Override
public void handleInflationException(NotificationEntry entry, Exception e) {
handleError(e);
}
@Override
- public void onAsyncInflationFinished(NotificationEntry entry,
- @InflationFlag int inflatedFlags) {
+ public void onAsyncInflationFinished(NotificationEntry entry) {
mEntry.onInflationTaskFinished();
mRow.onNotificationUpdated();
if (mCallback != null) {
- mCallback.onAsyncInflationFinished(mEntry, inflatedFlags);
+ mCallback.onAsyncInflationFinished(mEntry);
}
// Notify the resolver that the inflation task has finished,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
index 9b95bff..9bd8d47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
@@ -101,7 +101,7 @@
*/
int FLAG_CONTENT_VIEW_PUBLIC = 1 << 3;
- int FLAG_CONTENT_VIEW_ALL = ~0;
+ int FLAG_CONTENT_VIEW_ALL = (1 << 4) - 1;
/**
* Parameters for content view binding
@@ -146,9 +146,7 @@
* Callback for after the content views finish inflating.
*
* @param entry the entry with the content views set
- * @param inflatedFlags the flags associated with the content views that were inflated
*/
- void onAsyncInflationFinished(NotificationEntry entry,
- @InflationFlag int inflatedFlags);
+ void onAsyncInflationFinished(NotificationEntry entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
new file mode 100644
index 0000000..8280a63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
+
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+
+/**
+ * Parameters for {@link RowContentBindStage}.
+ */
+public final class RowContentBindParams {
+ private boolean mUseLowPriority;
+ private boolean mUseChildInGroup;
+ private boolean mUseIncreasedHeight;
+ private boolean mUseIncreasedHeadsUpHeight;
+ private boolean mViewsNeedReinflation;
+ private @InflationFlag int mContentViews = DEFAULT_INFLATION_FLAGS;
+
+ /**
+ * Content views that are out of date and need to be rebound.
+ *
+ * TODO: This should go away once {@link NotificationContentInflater} is broken down into
+ * smaller stages as then the stage itself would be invalidated.
+ */
+ private @InflationFlag int mDirtyContentViews = mContentViews;
+
+ /**
+ * Set whether content should use a low priority version of its content views.
+ */
+ public void setUseLowPriority(boolean useLowPriority) {
+ if (mUseLowPriority != useLowPriority) {
+ mDirtyContentViews |= (FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED);
+ }
+ mUseLowPriority = useLowPriority;
+ }
+
+ public boolean useLowPriority() {
+ return mUseLowPriority;
+ }
+
+ /**
+ * Set whether content should use group child version of its content views.
+ */
+ public void setUseChildInGroup(boolean useChildInGroup) {
+ if (mUseChildInGroup != useChildInGroup) {
+ mDirtyContentViews |= (FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED);
+ }
+ mUseChildInGroup = useChildInGroup;
+ }
+
+ public boolean useChildInGroup() {
+ return mUseChildInGroup;
+ }
+
+ /**
+ * Set whether content should use an increased height version of its contracted view.
+ */
+ public void setUseIncreasedCollapsedHeight(boolean useIncreasedHeight) {
+ if (mUseIncreasedHeight != useIncreasedHeight) {
+ mDirtyContentViews |= FLAG_CONTENT_VIEW_CONTRACTED;
+ }
+ mUseIncreasedHeight = useIncreasedHeight;
+ }
+
+ public boolean useIncreasedHeight() {
+ return mUseIncreasedHeight;
+ }
+
+ /**
+ * Set whether content should use an increased height version of its heads up view.
+ */
+ public void setUseIncreasedHeadsUpHeight(boolean useIncreasedHeadsUpHeight) {
+ if (mUseIncreasedHeadsUpHeight != useIncreasedHeadsUpHeight) {
+ mDirtyContentViews |= FLAG_CONTENT_VIEW_HEADS_UP;
+ }
+ mUseIncreasedHeadsUpHeight = useIncreasedHeadsUpHeight;
+ }
+
+ public boolean useIncreasedHeadsUpHeight() {
+ return mUseIncreasedHeadsUpHeight;
+ }
+
+ /**
+ * Require the specified content views to be bound after the rebind request.
+ *
+ * @see InflationFlag
+ */
+ public void requireContentViews(@InflationFlag int contentViews) {
+ @InflationFlag int newContentViews = contentViews &= ~mContentViews;
+ mContentViews |= contentViews;
+ mDirtyContentViews |= newContentViews;
+ }
+
+ /**
+ * Free the content view so that it will no longer be bound after the rebind request.
+ *
+ * @see InflationFlag
+ */
+ public void freeContentViews(@InflationFlag int contentViews) {
+ mContentViews &= ~contentViews;
+ mDirtyContentViews &= ~contentViews;
+ }
+
+ public @InflationFlag int getContentViews() {
+ return mContentViews;
+ }
+
+ /**
+ * Clears all dirty content views so that they no longer need to be rebound.
+ */
+ void clearDirtyContentViews() {
+ mDirtyContentViews = 0;
+ }
+
+ public @InflationFlag int getDirtyContentViews() {
+ return mDirtyContentViews;
+ }
+
+ /**
+ * Set whether all content views need to be reinflated even if cached.
+ *
+ * TODO: This should probably be a more global config on {@link NotifBindPipeline} since this
+ * generally corresponds to a Context/Configuration change that all stages should know about.
+ */
+ public void setNeedsReinflation(boolean needsReinflation) {
+ mViewsNeedReinflation = needsReinflation;
+ @InflationFlag int currentContentViews = mContentViews;
+ mDirtyContentViews |= currentContentViews;
+ }
+
+ public boolean needsReinflation() {
+ return mViewsNeedReinflation;
+ }
+
+ /**
+ * Content views that should be inflated by default for all notifications.
+ */
+ @InflationFlag private static final int DEFAULT_INFLATION_FLAGS =
+ FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
new file mode 100644
index 0000000..f124179
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
+
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * A stage that binds all content views for an already inflated {@link ExpandableNotificationRow}.
+ *
+ * In the farther future, the binder logic and consequently this stage should be broken into
+ * smaller stages.
+ */
+@Singleton
+public class RowContentBindStage extends BindStage<RowContentBindParams> {
+ private final NotificationRowContentBinder mBinder;
+ private final IStatusBarService mStatusBarService;
+
+ @Inject
+ RowContentBindStage(
+ NotificationRowContentBinder binder,
+ IStatusBarService statusBarService) {
+ mBinder = binder;
+ mStatusBarService = statusBarService;
+ }
+
+ @Override
+ protected void executeStage(
+ @NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row,
+ @NonNull StageCallback callback) {
+ RowContentBindParams params = getStageParams(entry);
+
+ // Resolve content to bind/unbind.
+ @InflationFlag int inflationFlags = params.getContentViews();
+ @InflationFlag int invalidatedFlags = params.getDirtyContentViews();
+
+ @InflationFlag int contentToBind = invalidatedFlags & inflationFlags;
+ @InflationFlag int contentToUnbind = inflationFlags ^ FLAG_CONTENT_VIEW_ALL;
+
+ // Bind/unbind with parameters
+ mBinder.unbindContent(entry, row, contentToUnbind);
+
+ BindParams bindParams = new BindParams();
+ bindParams.isLowPriority = params.useLowPriority();
+ bindParams.isChildInGroup = params.useChildInGroup();
+ bindParams.usesIncreasedHeight = params.useIncreasedHeight();
+ bindParams.usesIncreasedHeadsUpHeight = params.useIncreasedHeadsUpHeight();
+ boolean forceInflate = params.needsReinflation();
+
+ InflationCallback inflationCallback = new InflationCallback() {
+ @Override
+ public void handleInflationException(NotificationEntry entry, Exception e) {
+ entry.setHasInflationError(true);
+ try {
+ final StatusBarNotification sbn = entry.getSbn();
+ mStatusBarService.onNotificationError(
+ sbn.getPackageName(),
+ sbn.getTag(),
+ sbn.getId(),
+ sbn.getUid(),
+ sbn.getInitialPid(),
+ e.getMessage(),
+ sbn.getUserId());
+ } catch (RemoteException ex) {
+ }
+ }
+
+ @Override
+ public void onAsyncInflationFinished(NotificationEntry entry) {
+ entry.setHasInflationError(false);
+ getStageParams(entry).clearDirtyContentViews();
+ callback.onStageFinished(entry);
+ }
+ };
+ mBinder.cancelBind(entry, row);
+ mBinder.bindContent(entry, row, contentToBind, bindParams, forceInflate, inflationCallback);
+ }
+
+ @Override
+ protected void abortStage(
+ @NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row) {
+ mBinder.cancelBind(entry, row);
+ }
+
+ @Override
+ protected RowContentBindParams newStageParams() {
+ return new RowContentBindParams();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 896b6e5..bdca9a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -28,11 +28,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.AlertingNotificationManager;
-import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.row.RowContentBindParams;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -67,6 +68,7 @@
private final ArrayMap<String, PendingAlertInfo> mPendingAlerts = new ArrayMap<>();
private HeadsUpManager mHeadsUpManager;
+ private final RowContentBindStage mRowContentBindStage;
private final NotificationGroupManager mGroupManager =
Dependency.get(NotificationGroupManager.class);
@@ -75,8 +77,9 @@
private boolean mIsDozing;
@Inject
- public NotificationGroupAlertTransferHelper() {
+ public NotificationGroupAlertTransferHelper(RowContentBindStage bindStage) {
Dependency.get(StatusBarStateController.class).addCallback(this);
+ mRowContentBindStage = bindStage;
}
/** Causes the TransferHelper to register itself as a listener to the appropriate classes. */
@@ -190,21 +193,6 @@
}
}
- // Called when the entry's reinflation has finished. If there is an alert pending, we
- // then show the alert.
- @Override
- public void onEntryReinflated(NotificationEntry entry) {
- PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.getKey());
- if (alertInfo != null) {
- if (alertInfo.isStillValid()) {
- alertNotificationWhenPossible(entry, mHeadsUpManager);
- } else {
- // The transfer is no longer valid. Free the content.
- entry.getRow().freeContentViewWhenSafe(mHeadsUpManager.getContentFlag());
- }
- }
- }
-
@Override
public void onEntryRemoved(
@Nullable NotificationEntry entry,
@@ -392,10 +380,21 @@
private void alertNotificationWhenPossible(@NonNull NotificationEntry entry,
@NonNull AlertingNotificationManager alertManager) {
@InflationFlag int contentFlag = alertManager.getContentFlag();
- if (!entry.getRow().isInflationFlagSet(contentFlag)) {
+ final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
+ if ((params.getContentViews() & contentFlag) == 0) {
mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
- entry.getRow().setInflationFlags(contentFlag);
- entry.getRow().inflateViews();
+ params.requireContentViews(contentFlag);
+ mRowContentBindStage.requestRebind(entry, en -> {
+ PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.getKey());
+ if (alertInfo != null) {
+ if (alertInfo.isStillValid()) {
+ alertNotificationWhenPossible(entry, mHeadsUpManager);
+ } else {
+ // The transfer is no longer valid. Free the content.
+ entry.getRow().freeContentViewWhenSafe(mHeadsUpManager.getContentFlag());
+ }
+ }
+ });
return;
}
if (alertManager.isAlerting(entry.getKey())) {
@@ -426,9 +425,9 @@
/**
* The notification is still pending inflation but we've decided that we no longer need
* the content view (e.g. suppression might have changed and we decided we need to transfer
- * back). However, there is no way to abort just this inflation if other inflation requests
- * have started (see {@link InflationTask#supersedeTask(InflationTask)}). So instead
- * we just flag it as aborted and free when it's inflated.
+ * back).
+ *
+ * TODO: Replace this entire structure with {@link RowContentBindStage#requestRebind)}.
*/
boolean mAbortOnInflation;
diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
new file mode 100644
index 0000000..70bcc214
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
@@ -0,0 +1,320 @@
+package com.android.systemui.util
+
+import android.graphics.Rect
+import android.util.Log
+import com.android.systemui.util.FloatingContentCoordinator.FloatingContent
+import java.util.HashMap
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tag for debug logging. */
+private const val TAG = "FloatingCoordinator"
+
+/**
+ * Coordinates the positions and movement of floating content, such as PIP and Bubbles, to ensure
+ * that they don't overlap. If content does overlap due to content appearing or moving, the
+ * coordinator will ask content to move to resolve the conflict.
+ *
+ * After implementing [FloatingContent], content should call [onContentAdded] to begin coordination.
+ * Subsequently, call [onContentMoved] whenever the content moves, and the coordinator will move
+ * other content out of the way. [onContentRemoved] should be called when the content is removed or
+ * no longer visible.
+ */
+@Singleton
+class FloatingContentCoordinator @Inject constructor() {
+
+ /**
+ * Represents a piece of floating content, such as PIP or the Bubbles stack. Provides methods
+ * that allow the [FloatingContentCoordinator] to determine the current location of the content,
+ * as well as the ability to ask it to move out of the way of other content.
+ *
+ * The default implementation of [calculateNewBoundsOnOverlap] moves the content up or down,
+ * depending on the position of the conflicting content. You can override this method if you
+ * want your own custom conflict resolution logic.
+ */
+ interface FloatingContent {
+
+ /**
+ * Return the bounds claimed by this content. This should include the bounds occupied by the
+ * content itself, as well as any padding, if desired. The coordinator will ensure that no
+ * other content is located within these bounds.
+ *
+ * If the content is animating, this method should return the bounds to which the content is
+ * animating. If that animation is cancelled, or updated, be sure that your implementation
+ * of this method returns the appropriate bounds, and call [onContentMoved] so that the
+ * coordinator moves other content out of the way.
+ */
+ fun getFloatingBoundsOnScreen(): Rect
+
+ /**
+ * Return the area within which this floating content is allowed to move. When resolving
+ * conflicts, the coordinator will never ask your content to move to a position where any
+ * part of the content would be out of these bounds.
+ */
+ fun getAllowedFloatingBoundsRegion(): Rect
+
+ /**
+ * Called when the coordinator needs this content to move to the given bounds. It's up to
+ * you how to do that.
+ *
+ * Note that if you start an animation to these bounds, [getFloatingBoundsOnScreen] should
+ * return the destination bounds, not the in-progress animated bounds. This is so the
+ * coordinator knows where floating content is going to be and can resolve conflicts
+ * accordingly.
+ */
+ fun moveToBounds(bounds: Rect)
+
+ /**
+ * Called by the coordinator when it needs to find a new home for this floating content,
+ * because a new or moving piece of content is now overlapping with it.
+ *
+ * [findAreaForContentVertically] and [findAreaForContentAboveOrBelow] are helpful utility
+ * functions that will find new bounds for your content automatically. Unless you require
+ * specific conflict resolution logic, these should be sufficient. By default, this method
+ * delegates to [findAreaForContentVertically].
+ *
+ * @param overlappingContentBounds The bounds of the other piece of content, which
+ * necessitated this content's relocation. Your new position must not overlap with these
+ * bounds.
+ * @param otherContentBounds The bounds of any other pieces of floating content. Your new
+ * position must not overlap with any of these either. These bounds are guaranteed to be
+ * non-overlapping.
+ * @return The new bounds for this content.
+ */
+ @JvmDefault
+ fun calculateNewBoundsOnOverlap(
+ overlappingContentBounds: Rect,
+ otherContentBounds: List<Rect>
+ ): Rect {
+ return findAreaForContentVertically(
+ getFloatingBoundsOnScreen(),
+ overlappingContentBounds,
+ otherContentBounds,
+ getAllowedFloatingBoundsRegion())
+ }
+ }
+
+ /** The bounds of all pieces of floating content added to the coordinator. */
+ private val allContentBounds: MutableMap<FloatingContent, Rect> = HashMap()
+
+ /**
+ * Makes the coordinator aware of a new piece of floating content, and moves any existing
+ * content out of the way, if necessary.
+ *
+ * If you don't want your new content to move existing content, use [getOccupiedBounds] to find
+ * an unoccupied area, and move the content there before calling this method.
+ */
+ fun onContentAdded(newContent: FloatingContent) {
+ updateContentBounds()
+ allContentBounds[newContent] = newContent.getFloatingBoundsOnScreen()
+ maybeMoveConflictingContent(newContent)
+ }
+
+ /**
+ * Called to notify the coordinator that a piece of floating content has moved (or is animating)
+ * to a new position, and that any conflicting floating content should be moved out of the way.
+ *
+ * The coordinator will call [FloatingContent.getFloatingBoundsOnScreen] to find the new bounds
+ * for the moving content. If you're animating the content, be sure that your implementation of
+ * getFloatingBoundsOnScreen returns the bounds to which it's animating, not the content's
+ * current bounds.
+ *
+ * If the animation moving this content is cancelled or updated, you'll need to call this method
+ * again, to ensure that content is moved out of the way of the latest bounds.
+ *
+ * @param content The content that has moved.
+ */
+ @JvmOverloads
+ fun onContentMoved(content: FloatingContent) {
+ if (!allContentBounds.containsKey(content)) {
+ Log.wtf(TAG, "Received onContentMoved call before onContentAdded! " +
+ "This should never happen.")
+ return
+ }
+
+ updateContentBounds()
+ maybeMoveConflictingContent(content)
+ }
+
+ /**
+ * Called to notify the coordinator that a piece of floating content has been removed or is no
+ * longer visible.
+ */
+ fun onContentRemoved(removedContent: FloatingContent) {
+ allContentBounds.remove(removedContent)
+ }
+
+ /**
+ * Returns a set of Rects that represent the bounds of all of the floating content on the
+ * screen.
+ *
+ * [onContentAdded] will move existing content out of the way if the added content intersects
+ * existing content. That's fine - but if your specific starting position is not important, you
+ * can use this function to find unoccupied space for your content before calling
+ * [onContentAdded], so that moving existing content isn't necessary.
+ */
+ fun getOccupiedBounds(): Collection<Rect> {
+ return allContentBounds.values
+ }
+
+ /**
+ * Identifies any pieces of content that are now overlapping with the given content, and asks
+ * them to move out of the way.
+ */
+ private fun maybeMoveConflictingContent(fromContent: FloatingContent) {
+ val conflictingNewBounds = allContentBounds[fromContent]!!
+ allContentBounds
+ // Filter to content that intersects with the new bounds. That's content that needs
+ // to move.
+ .filter { (content, bounds) ->
+ content != fromContent && Rect.intersects(conflictingNewBounds, bounds) }
+ // Tell that content to get out of the way, and save the bounds it says it's moving
+ // (or animating) to.
+ .forEach { (content, bounds) ->
+ content.moveToBounds(
+ content.calculateNewBoundsOnOverlap(
+ conflictingNewBounds,
+ // Pass all of the content bounds except the bounds of the
+ // content we're asking to move, and the conflicting new bounds
+ // (since those are passed separately).
+ otherContentBounds = allContentBounds.values
+ .minus(bounds)
+ .minus(conflictingNewBounds)))
+ allContentBounds[content] = content.getFloatingBoundsOnScreen()
+ }
+ }
+
+ /**
+ * Update [allContentBounds] by calling [FloatingContent.getFloatingBoundsOnScreen] for all
+ * content and saving the result.
+ */
+ private fun updateContentBounds() {
+ allContentBounds.keys.forEach { allContentBounds[it] = it.getFloatingBoundsOnScreen() }
+ }
+
+ companion object {
+ /**
+ * Finds new bounds for the given content, either above or below its current position. The
+ * new bounds won't intersect with the newly overlapping rect or the exclusion rects, and
+ * will be within the allowed bounds unless no possible position exists.
+ *
+ * You can use this method to help find a new position for your content when the coordinator
+ * calls [FloatingContent.moveToAreaExcluding].
+ *
+ * @param contentRect The bounds of the content for which we're finding a new home.
+ * @param newlyOverlappingRect The bounds of the content that forced this relocation by
+ * intersecting with the content we now need to move. If the overlapping content is
+ * overlapping the top half of this content, we'll try to move this content downward if
+ * possible (since the other content is 'pushing' it down), and vice versa.
+ * @param exclusionRects Any other areas that we need to avoid when finding a new home for
+ * the content. These areas must be non-overlapping with each other.
+ * @param allowedBounds The area within which we're allowed to find new bounds for the
+ * content.
+ * @return New bounds for the content that don't intersect the exclusion rects or the
+ * newly overlapping rect, and that is within bounds unless no possible in-bounds position
+ * exists.
+ */
+ @JvmStatic
+ fun findAreaForContentVertically(
+ contentRect: Rect,
+ newlyOverlappingRect: Rect,
+ exclusionRects: Collection<Rect>,
+ allowedBounds: Rect
+ ): Rect {
+ // If the newly overlapping Rect's center is above the content's center, we'll prefer to
+ // find a space for this content that is below the overlapping content, since it's
+ // 'pushing' it down. This may not be possible due to to screen bounds, in which case
+ // we'll find space in the other direction.
+ val overlappingContentPushingDown =
+ newlyOverlappingRect.centerY() < contentRect.centerY()
+
+ // Filter to exclusion rects that are above or below the content that we're finding a
+ // place for. Then, split into two lists - rects above the content, and rects below it.
+ var (rectsToAvoidAbove, rectsToAvoidBelow) = exclusionRects
+ .filter { rectToAvoid -> rectsIntersectVertically(rectToAvoid, contentRect) }
+ .partition { rectToAvoid -> rectToAvoid.top < contentRect.top }
+
+ // Lazily calculate the closest possible new tops for the content, above and below its
+ // current location.
+ val newContentBoundsAbove by lazy { findAreaForContentAboveOrBelow(
+ contentRect,
+ exclusionRects = rectsToAvoidAbove.plus(newlyOverlappingRect),
+ findAbove = true) }
+ val newContentBoundsBelow by lazy { findAreaForContentAboveOrBelow(
+ contentRect,
+ exclusionRects = rectsToAvoidBelow.plus(newlyOverlappingRect),
+ findAbove = false) }
+
+ val positionAboveInBounds by lazy { allowedBounds.contains(newContentBoundsAbove) }
+ val positionBelowInBounds by lazy { allowedBounds.contains(newContentBoundsBelow) }
+
+ // Use the 'below' position if the content is being overlapped from the top, unless it's
+ // out of bounds. Also use it if the content is being overlapped from the bottom, but
+ // the 'above' position is out of bounds. Otherwise, use the 'above' position.
+ val usePositionBelow =
+ overlappingContentPushingDown && positionBelowInBounds ||
+ !overlappingContentPushingDown && !positionAboveInBounds
+
+ // Return the content rect, but offset to reflect the new position.
+ return if (usePositionBelow) newContentBoundsBelow else newContentBoundsAbove
+ }
+
+ /**
+ * Finds a new position for the given content, either above or below its current position
+ * depending on whether [findAbove] is true or false, respectively. This new position will
+ * not intersect with any of the [exclusionRects].
+ *
+ * This method is useful as a helper method for implementing your own conflict resolution
+ * logic. Otherwise, you'd want to use [findAreaForContentVertically], which takes screen
+ * bounds and conflicting bounds' location into account when deciding whether to move to new
+ * bounds above or below the current bounds.
+ *
+ * @param contentRect The content we're finding an area for.
+ * @param exclusionRects The areas we need to avoid when finding a new area for the content.
+ * These areas must be non-overlapping with each other.
+ * @param findAbove Whether we are finding an area above the content's current position,
+ * rather than an area below it.
+ */
+ fun findAreaForContentAboveOrBelow(
+ contentRect: Rect,
+ exclusionRects: Collection<Rect>,
+ findAbove: Boolean
+ ): Rect {
+ // Sort the rects, since we want to move the content as little as possible. We'll
+ // start with the rects closest to the content and move outward. If we're finding an
+ // area above the content, that means we sort in reverse order to search the rects
+ // from highest to lowest y-value.
+ val sortedExclusionRects =
+ exclusionRects.sortedBy { if (findAbove) -it.top else it.top }
+
+ val proposedNewBounds = Rect(contentRect)
+ for (exclusionRect in sortedExclusionRects) {
+ // If the proposed new bounds don't intersect with this exclusion rect, that
+ // means there's room for the content here. We know this because the rects are
+ // sorted and non-overlapping, so any subsequent exclusion rects would be higher
+ // (or lower) than this one and can't possibly intersect if this one doesn't.
+ if (!Rect.intersects(proposedNewBounds, exclusionRect)) {
+ break
+ } else {
+ // Otherwise, we need to keep searching for new bounds. If we're finding an
+ // area above, propose new bounds that place the content just above the
+ // exclusion rect. If we're finding an area below, propose new bounds that
+ // place the content just below the exclusion rect.
+ val verticalOffset =
+ if (findAbove) -contentRect.height() else exclusionRect.height()
+ proposedNewBounds.offsetTo(
+ proposedNewBounds.left,
+ exclusionRect.top + verticalOffset)
+ }
+ }
+
+ return proposedNewBounds
+ }
+
+ /** Returns whether or not the two Rects share any of the same space on the X axis. */
+ private fun rectsIntersectVertically(r1: Rect, r2: Rect): Boolean {
+ return (r1.left >= r2.left && r1.left <= r2.right) ||
+ (r1.right <= r2.right && r1.right >= r2.left)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index 364ee66..ffe8c28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -31,8 +31,8 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.util.Assert;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index f264259..486aac8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -36,6 +36,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.UserManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -43,6 +44,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ScrollView;
@@ -175,6 +177,14 @@
assertEquals(Utils.CREDENTIAL_PATTERN, mAuthContainer.mCredentialView.mCredentialType);
}
+ @Test
+ public void testLayoutParams_hasSecureWindowFlag() {
+ final IBinder windowToken = mock(IBinder.class);
+ final WindowManager.LayoutParams layoutParams =
+ AuthContainerView.getLayoutParams(windowToken);
+ assertTrue((layoutParams.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0);
+ }
+
private void initializeContainer(int authenticators) {
AuthContainerView.Config config = new AuthContainerView.Config();
config.mContext = mContext;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index dcaf4ec..d7f0f50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -62,7 +62,6 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -72,6 +71,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index c9f5b40..f40fc94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -39,10 +39,10 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleData.TimeSource;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.google.common.collect.ImmutableList;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 63c911b5..60163f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -49,6 +49,7 @@
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
index 4103ede..9d667a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
@@ -26,8 +26,8 @@
import android.widget.FrameLayout;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import org.junit.Assert;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 20c67fa..b51581f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -84,9 +84,10 @@
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
+import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.RowContentBindParams;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -99,6 +100,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -206,20 +208,20 @@
mEntry.expandedIcon = mock(StatusBarIconView.class);
- NotificationContentInflater contentBinder = new NotificationContentInflater(
- mock(NotifRemoteViewCache.class),
- mRemoteInputManager);
- contentBinder.setInflateSynchronously(true);
-
when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
.thenReturn(mNotificationRowComponentBuilder);
when(mNotificationRowComponentBuilder.build()).thenReturn(
() -> mActivatableNotificationViewController);
+
+ RowContentBindStage bindStage = mock(RowContentBindStage.class);
+ when(bindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
+
NotificationRowBinderImpl notificationRowBinder =
new NotificationRowBinderImpl(mContext,
mRemoteInputManager,
mLockscreenUserManager,
- contentBinder,
+ mock(NotifBindPipeline.class),
+ bindStage,
true, /* allowLongPress */
mock(KeyguardBypassController.class),
mock(StatusBarStateController.class),
@@ -269,7 +271,10 @@
mEntry.abortTask();
}
+ // TODO: These tests are closer to functional tests and we should move them to their own file.
+ // and also strip some of the verifies that make the test too complex
@Test
+ @Ignore
public void testAddNotification() throws Exception {
TestableLooper.get(this).processAllMessages();
@@ -306,6 +311,7 @@
}
@Test
+ @Ignore
public void testUpdateNotification() throws Exception {
TestableLooper.get(this).processAllMessages();
@@ -331,6 +337,7 @@
}
@Test
+ @Ignore
public void testUpdateNotification_prePostEntryOrder() throws Exception {
TestableLooper.get(this).processAllMessages();
@@ -399,7 +406,6 @@
setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
- verify(mRow).setEntry(eq(mEntry));
assertEquals(1, mEntry.getSmartActions().size());
assertEquals("action", mEntry.getSmartActions().get(0).title);
verify(mEntryListener).onNotificationRankingUpdated(mRankingMap);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index 5aed61b..1116a33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -42,11 +42,11 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
index 7431459..a9f9db6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -48,8 +48,8 @@
public var countDownLatch: CountDownLatch = CountDownLatch(1)
- override fun onAsyncInflationFinished(entry: NotificationEntry?, inflatedFlags: Int) {
- super.onAsyncInflationFinished(entry, inflatedFlags)
+ override fun onAsyncInflationFinished(entry: NotificationEntry) {
+ super.onAsyncInflationFinished(entry)
countDownLatch.countDown()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 3d79ce1..d8cf6ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -21,7 +21,6 @@
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -51,7 +50,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
@@ -147,15 +145,6 @@
}
@Test
- public void setNeedsRedactionSetsInflationFlag() throws Exception {
- ExpandableNotificationRow row = mNotificationTestHelper.createRow();
-
- row.setNeedsRedaction(true);
-
- assertTrue(row.isInflationFlagSet(FLAG_CONTENT_VIEW_PUBLIC));
- }
-
- @Test
public void setNeedsRedactionFreesViewWhenFalse() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_CONTENT_VIEW_ALL);
row.setNeedsRedaction(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
new file mode 100644
index 0000000..8f9f65d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.annotation.NonNull;
+import androidx.core.os.CancellationSignal;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotifBindPipelineTest extends SysuiTestCase {
+
+ private NotifBindPipeline mBindPipeline;
+ private TestBindStage mStage = new TestBindStage();
+
+ @Mock private NotificationEntry mEntry;
+ @Mock private ExpandableNotificationRow mRow;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ NotificationEntryManager entryManager = mock(NotificationEntryManager.class);
+
+ mBindPipeline = new NotifBindPipeline(entryManager);
+ mBindPipeline.setStage(mStage);
+
+ ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
+ ArgumentCaptor.forClass(NotificationEntryListener.class);
+ verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture());
+ NotificationEntryListener entryListener = entryListenerCaptor.getValue();
+
+ entryListener.onPendingEntryAdded(mEntry);
+ }
+
+ @Test
+ public void testCallbackCalled() {
+ // GIVEN a bound row
+ mBindPipeline.manageRow(mEntry, mRow);
+
+ // WHEN content is invalidated
+ BindCallback callback = mock(BindCallback.class);
+ mStage.requestRebind(mEntry, callback);
+
+ // WHEN stage finishes its work
+ mStage.doWorkSynchronously();
+
+ // THEN the callback is called when bind finishes
+ verify(callback).onBindFinished(mEntry);
+ }
+
+ @Test
+ public void testCallbackCancelled() {
+ // GIVEN a bound row
+ mBindPipeline.manageRow(mEntry, mRow);
+
+ // GIVEN an in-progress pipeline run
+ BindCallback callback = mock(BindCallback.class);
+ CancellationSignal signal = mStage.requestRebind(mEntry, callback);
+
+ // WHEN the callback is cancelled.
+ signal.cancel();
+
+ // WHEN the stage finishes all its work
+ mStage.doWorkSynchronously();
+
+ // THEN the callback is not called when bind finishes
+ verify(callback, never()).onBindFinished(mEntry);
+ }
+
+ @Test
+ public void testMultipleCallbacks() {
+ // GIVEN a bound row
+ mBindPipeline.manageRow(mEntry, mRow);
+
+ // WHEN the pipeline is invalidated.
+ BindCallback callback = mock(BindCallback.class);
+ mStage.requestRebind(mEntry, callback);
+
+ // WHEN the pipeline is invalidated again before the work completes.
+ BindCallback callback2 = mock(BindCallback.class);
+ mStage.requestRebind(mEntry, callback2);
+
+ // WHEN the stage finishes all work.
+ mStage.doWorkSynchronously();
+
+ // THEN both callbacks are called when the bind finishes
+ verify(callback).onBindFinished(mEntry);
+ verify(callback2).onBindFinished(mEntry);
+ }
+
+ /**
+ * Bind stage for testing where asynchronous work can be synchronously controlled.
+ */
+ private static class TestBindStage extends BindStage {
+ private List<Runnable> mExecutionRequests = new ArrayList<>();
+
+ @Override
+ protected void executeStage(@NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row, @NonNull StageCallback callback) {
+ mExecutionRequests.add(() -> callback.onStageFinished(entry));
+ }
+
+ @Override
+ protected void abortStage(@NonNull NotificationEntry entry,
+ @NonNull ExpandableNotificationRow row) {
+
+ }
+
+ @Override
+ protected Object newStageParams() {
+ return null;
+ }
+
+ public void doWorkSynchronously() {
+ for (Runnable work: mExecutionRequests) {
+ work.run();
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
index d7214f3..20cc01a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
@@ -32,10 +32,10 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import org.junit.Before;
import org.junit.Test;
@@ -50,7 +50,7 @@
private NotifRemoteViewCacheImpl mNotifRemoteViewCache;
private NotificationEntry mEntry;
- private NotificationEntryListener mEntryListener;
+ private NotifCollectionListener mEntryListener;
@Mock private RemoteViews mRemoteViews;
@Before
@@ -58,19 +58,17 @@
MockitoAnnotations.initMocks(this);
mEntry = new NotificationEntryBuilder().build();
- NotificationEntryManager entryManager = mock(NotificationEntryManager.class);
- mNotifRemoteViewCache = new NotifRemoteViewCacheImpl(entryManager);
- ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
- ArgumentCaptor.forClass(NotificationEntryListener.class);
- verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture());
+ CommonNotifCollection collection = mock(CommonNotifCollection.class);
+ mNotifRemoteViewCache = new NotifRemoteViewCacheImpl(collection);
+ ArgumentCaptor<NotifCollectionListener> entryListenerCaptor =
+ ArgumentCaptor.forClass(NotifCollectionListener.class);
+ verify(collection).addCollectionListener(entryListenerCaptor.capture());
mEntryListener = entryListenerCaptor.getValue();
+ mEntryListener.onEntryInit(mEntry);
}
@Test
public void testPutCachedView() {
- // GIVEN an initialized cache for an entry.
- mEntryListener.onPendingEntryAdded(mEntry);
-
// WHEN a notification's cached remote views is put in.
mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
@@ -85,7 +83,6 @@
@Test
public void testRemoveCachedView() {
// GIVEN a cache with a cached view.
- mEntryListener.onPendingEntryAdded(mEntry);
mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
// WHEN we remove the cached view.
@@ -98,7 +95,6 @@
@Test
public void testClearCache() {
// GIVEN a non-empty cache.
- mEntryListener.onPendingEntryAdded(mEntry);
mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED, mRemoteViews);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 444a6e5..1dfe7bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -46,7 +46,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.util.Assert;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index cb9da6a..8a42e5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -49,9 +49,7 @@
import androidx.test.filters.Suppress;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
@@ -200,8 +198,7 @@
}
@Override
- public void onAsyncInflationFinished(NotificationEntry entry,
- @InflationFlag int inflatedFlags) {
+ public void onAsyncInflationFinished(NotificationEntry entry) {
countDownLatch.countDown();
}
}, mRow.getPrivateLayout(), null, null, new HashMap<>(),
@@ -219,34 +216,6 @@
assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
}
- /* Cancelling requires us to be on the UI thread otherwise we might have a race */
- @Test
- public void testSupersedesExistingTask() {
- mNotificationInflater.bindContent(
- mRow.getEntry(),
- mRow,
- FLAG_CONTENT_VIEW_ALL,
- new BindParams(),
- false /* forceInflate */,
- null /* callback */);
-
- // Trigger inflation of contracted only.
- mNotificationInflater.bindContent(
- mRow.getEntry(),
- mRow,
- FLAG_CONTENT_VIEW_CONTRACTED,
- new BindParams(),
- false /* forceInflate */,
- null /* callback */);
-
- InflationTask runningTask = mRow.getEntry().getRunningTask();
- NotificationContentInflater.AsyncInflationTask asyncInflationTask =
- (NotificationContentInflater.AsyncInflationTask) runningTask;
- assertEquals("Successive inflations don't inherit the previous flags!",
- FLAG_CONTENT_VIEW_ALL, asyncInflationTask.getReInflateFlags());
- runningTask.abort();
- }
-
@Test
public void doesntReapplyDisallowedRemoteView() throws Exception {
mBuilder.setStyle(new Notification.MediaStyle());
@@ -349,8 +318,7 @@
}
@Override
- public void onAsyncInflationFinished(NotificationEntry entry,
- @InflationFlag int inflatedFlags) {
+ public void onAsyncInflationFinished(NotificationEntry entry) {
if (expectingException) {
exceptionHolder.setException(new RuntimeException(
"Inflation finished even though there should be an error"));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 4e27770..bbb6723 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -66,7 +66,6 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
similarity index 88%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 457bbe23..3d9832d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -40,17 +41,20 @@
import android.view.LayoutInflater;
import android.widget.RemoteViews;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.TestableDependency;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
-import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -58,6 +62,8 @@
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.tests.R;
+import org.mockito.ArgumentCaptor;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -82,6 +88,9 @@
private final NotificationGroupManager mGroupManager;
private ExpandableNotificationRow mRow;
private HeadsUpManagerPhone mHeadsUpManager;
+ private final NotifBindPipeline mBindPipeline;
+ private final NotificationEntryListener mBindPipelineEntryListener;
+ private final RowContentBindStage mBindStage;
public NotificationTestHelper(Context context, TestableDependency dependency) {
mContext = context;
@@ -95,6 +104,23 @@
mock(KeyguardBypassController.class));
mHeadsUpManager.setUp(null, mGroupManager, null, null);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
+
+
+ NotificationContentInflater contentBinder = new NotificationContentInflater(
+ mock(NotifRemoteViewCache.class),
+ mock(NotificationRemoteInputManager.class));
+ contentBinder.setInflateSynchronously(true);
+ mBindStage = new RowContentBindStage(contentBinder, mock(IStatusBarService.class));
+
+ NotificationEntryManager entryManager = mock(NotificationEntryManager.class);
+
+ mBindPipeline = new NotifBindPipeline(entryManager);
+ mBindPipeline.setStage(mBindStage);
+
+ ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
+ ArgumentCaptor.forClass(NotificationEntryListener.class);
+ verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture());
+ mBindPipelineEntryListener = entryListenerCaptor.getValue();
}
/**
@@ -331,10 +357,8 @@
entry.createIcons(mContext, entry.getSbn());
row.setEntry(entry);
- NotificationContentInflater contentBinder = new NotificationContentInflater(
- mock(NotifRemoteViewCache.class),
- mock(NotificationRemoteInputManager.class));
- contentBinder.setInflateSynchronously(true);
+ mBindPipelineEntryListener.onPendingEntryAdded(entry);
+ mBindPipeline.manageRow(entry, row);
row.initialize(
APP_NAME,
@@ -343,12 +367,11 @@
mock(KeyguardBypassController.class),
mGroupManager,
mHeadsUpManager,
- contentBinder,
+ mBindStage,
mock(OnExpandClickListener.class));
row.setAboveShelfChangedListener(aboveShelf -> { });
-
- row.setInflationFlags(extraInflationFlags);
- inflateAndWait(row);
+ mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
+ inflateAndWait(entry, mBindStage);
// This would be done as part of onAsyncInflationFinished, but we skip large amounts of
// the callback chain, so we need to make up for not adding it to the group manager
@@ -357,24 +380,10 @@
return row;
}
- private static void inflateAndWait(ExpandableNotificationRow row) throws Exception {
+ private static void inflateAndWait(NotificationEntry entry, RowContentBindStage stage)
+ throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
- NotificationContentInflater.InflationCallback callback =
- new NotificationContentInflater.InflationCallback() {
- @Override
- public void handleInflationException(NotificationEntry entry,
- Exception e) {
- countDownLatch.countDown();
- }
-
- @Override
- public void onAsyncInflationFinished(NotificationEntry entry,
- int inflatedFlags) {
- countDownLatch.countDown();
- }
- };
- row.setInflationCallback(callback);
- row.inflateViews();
+ stage.requestRebind(entry, en -> countDownLatch.countDown());
assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
new file mode 100644
index 0000000..66aa5e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class RowContentBindStageTest extends SysuiTestCase {
+
+ private RowContentBindStage mRowContentBindStage;
+
+ @Mock private NotificationRowContentBinder mBinder;
+ @Mock private NotificationEntry mEntry;
+ @Mock private ExpandableNotificationRow mRow;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mRowContentBindStage = new RowContentBindStage(mBinder,
+ mock(IStatusBarService.class));
+ mRowContentBindStage.createStageParams(mEntry);
+ }
+
+ @Test
+ public void testSetShouldContentViewsBeBound_bindsContent() {
+ // WHEN inflation flags are set and pipeline is invalidated.
+ final int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.requireContentViews(flags);
+ mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+ // THEN binder binds inflation flags.
+ verify(mBinder).bindContent(
+ eq(mEntry),
+ any(),
+ eq(flags),
+ any(),
+ anyBoolean(),
+ any());
+ }
+
+ @Test
+ public void testSetShouldContentViewsBeBound_unbindsContent() {
+ // GIVEN a view with all content bound.
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.requireContentViews(FLAG_CONTENT_VIEW_ALL);
+
+ // WHEN inflation flags are cleared and stage executed.
+ final int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
+ params.freeContentViews(flags);
+ mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+ // THEN binder unbinds flags.
+ verify(mBinder).unbindContent(eq(mEntry), any(), eq(flags));
+ }
+
+ @Test
+ public void testSetUseLowPriority() {
+ // GIVEN a view with all content bound.
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.requireContentViews(FLAG_CONTENT_VIEW_ALL);
+ params.clearDirtyContentViews();
+
+ // WHEN low priority is set and stage executed.
+ params.setUseLowPriority(true);
+ mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+ // THEN binder is called with use low priority and contracted/expanded are called to bind.
+ ArgumentCaptor<BindParams> bindParamsCaptor = ArgumentCaptor.forClass(BindParams.class);
+ verify(mBinder).bindContent(
+ eq(mEntry),
+ any(),
+ eq(FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED),
+ bindParamsCaptor.capture(),
+ anyBoolean(),
+ any());
+ BindParams usedParams = bindParamsCaptor.getValue();
+ assertTrue(usedParams.isLowPriority);
+ }
+
+ @Test
+ public void testSetUseGroupInChild() {
+ // GIVEN a view with all content bound.
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.requireContentViews(FLAG_CONTENT_VIEW_ALL);
+ params.clearDirtyContentViews();
+
+ // WHEN use group is set and stage executed.
+ params.setUseChildInGroup(true);
+ mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+ // THEN binder is called with use group view and contracted/expanded are called to bind.
+ ArgumentCaptor<BindParams> bindParamsCaptor = ArgumentCaptor.forClass(BindParams.class);
+ verify(mBinder).bindContent(
+ eq(mEntry),
+ any(),
+ eq(FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED),
+ bindParamsCaptor.capture(),
+ anyBoolean(),
+ any());
+ BindParams usedParams = bindParamsCaptor.getValue();
+ assertTrue(usedParams.isChildInGroup);
+ }
+
+ @Test
+ public void testSetUseIncreasedHeight() {
+ // GIVEN a view with all content bound.
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.requireContentViews(FLAG_CONTENT_VIEW_ALL);
+ params.clearDirtyContentViews();
+
+ // WHEN use increased height is set and stage executed.
+ params.setUseIncreasedCollapsedHeight(true);
+ mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+ // THEN binder is called with group view and contracted is bound.
+ ArgumentCaptor<BindParams> bindParamsCaptor = ArgumentCaptor.forClass(BindParams.class);
+ verify(mBinder).bindContent(
+ eq(mEntry),
+ any(),
+ eq(FLAG_CONTENT_VIEW_CONTRACTED),
+ bindParamsCaptor.capture(),
+ anyBoolean(),
+ any());
+ BindParams usedParams = bindParamsCaptor.getValue();
+ assertTrue(usedParams.usesIncreasedHeight);
+ }
+
+ @Test
+ public void testSetUseIncreasedHeadsUpHeight() {
+ // GIVEN a view with all content bound.
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.requireContentViews(FLAG_CONTENT_VIEW_ALL);
+ params.clearDirtyContentViews();
+
+ // WHEN use increased heads up height is set and stage executed.
+ params.setUseIncreasedHeadsUpHeight(true);
+ mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+ // THEN binder is called with use group view and heads up is bound.
+ ArgumentCaptor<BindParams> bindParamsCaptor = ArgumentCaptor.forClass(BindParams.class);
+ verify(mBinder).bindContent(
+ eq(mEntry),
+ any(),
+ eq(FLAG_CONTENT_VIEW_HEADS_UP),
+ bindParamsCaptor.capture(),
+ anyBoolean(),
+ any());
+ BindParams usedParams = bindParamsCaptor.getValue();
+ assertTrue(usedParams.usesIncreasedHeadsUpHeight);
+ }
+
+ @Test
+ public void testSetNeedsReinflation() {
+ // GIVEN a view with all content bound.
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ params.requireContentViews(FLAG_CONTENT_VIEW_ALL);
+ params.clearDirtyContentViews();
+
+ // WHEN needs reinflation is set.
+ params.setNeedsReinflation(true);
+ mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+ // THEN binder is called with forceInflate and all views are requested to bind.
+ verify(mBinder).bindContent(
+ eq(mEntry),
+ any(),
+ eq(FLAG_CONTENT_VIEW_ALL),
+ any(),
+ eq(true),
+ any());
+ }
+
+ @Test
+ public void testSupersedesPreviousContentViews() {
+ // GIVEN a view with content view bind already in progress.
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ int defaultFlags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
+ params.requireContentViews(defaultFlags);
+ mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+ // WHEN we bind with another content view before the first finishes.
+ params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
+ mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+ // THEN binder is called with BOTH content views.
+ verify(mBinder).bindContent(
+ eq(mEntry),
+ any(),
+ eq(defaultFlags),
+ any(),
+ anyBoolean(),
+ any());
+ verify(mBinder).bindContent(
+ eq(mEntry),
+ any(),
+ eq(defaultFlags | FLAG_CONTENT_VIEW_HEADS_UP),
+ any(),
+ anyBoolean(),
+ any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index d280f18..0790cb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -25,8 +25,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.tests.R;
import org.junit.Assert;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
index 4f45f68..038eff7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
@@ -38,8 +38,8 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index 14e2fde..9567f33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -29,8 +29,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.util.Assert;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index ddd2884e..1773175 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -25,8 +25,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import org.junit.Assert;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 34a309f..e84f14a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -31,11 +31,11 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.util.DeviceConfigProxy;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 7448dbd..f71d0fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -35,9 +35,9 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.KeyguardStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 5b54fba..e171a28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -16,8 +16,12 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -38,6 +42,9 @@
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
+import com.android.systemui.statusbar.notification.row.RowContentBindParams;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
@@ -47,6 +54,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -62,8 +70,8 @@
private NotificationGroupManager mGroupManager;
private HeadsUpManager mHeadsUpManager;
@Mock private NotificationEntryManager mNotificationEntryManager;
- @Captor
- private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
+ @Mock private RowContentBindStage mBindStage;
+ @Captor private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
private NotificationEntryListener mNotificationEntryListener;
private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>();
private final NotificationGroupTestHelper mGroupTestHelper =
@@ -72,6 +80,7 @@
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(BubbleController.class);
mHeadsUpManager = new HeadsUpManager(mContext) {};
@@ -82,7 +91,9 @@
mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
- mGroupAlertTransferHelper = new NotificationGroupAlertTransferHelper();
+ when(mBindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
+
+ mGroupAlertTransferHelper = new NotificationGroupAlertTransferHelper(mBindStage);
mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
mGroupAlertTransferHelper.bind(mNotificationEntryManager, mGroupManager);
@@ -97,6 +108,10 @@
mHeadsUpManager.showNotification(summaryEntry);
NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+ RowContentBindParams params = new RowContentBindParams();
+ params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
+ when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+
// Summary will be suppressed because there is only one child.
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -160,8 +175,8 @@
NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
- .thenReturn(false);
+ RowContentBindParams params = new RowContentBindParams();
+ when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -178,15 +193,16 @@
NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
- .thenReturn(false);
+ RowContentBindParams params = new RowContentBindParams();
+ when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
- .thenReturn(true);
- mNotificationEntryListener.onEntryReinflated(childEntry);
+ // Child entry finishes its inflation.
+ ArgumentCaptor<BindCallback> callbackCaptor = ArgumentCaptor.forClass(BindCallback.class);
+ verify(mBindStage).requestRebind(eq(childEntry), callbackCaptor.capture());
+ callbackCaptor.getValue().onBindFinished(childEntry);
// Alert is immediately removed from summary, and we show child as its content is inflated.
assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
@@ -199,8 +215,9 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
- .thenReturn(false);
+ RowContentBindParams params = new RowContentBindParams();
+ when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+
NotificationEntry childEntry2 =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
mHeadsUpManager.showNotification(summaryEntry);
@@ -214,9 +231,9 @@
mGroupManager.onEntryAdded(childEntry2);
// Child entry finishes its inflation.
- when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
- .thenReturn(true);
- mNotificationEntryListener.onEntryReinflated(childEntry);
+ ArgumentCaptor<BindCallback> callbackCaptor = ArgumentCaptor.forClass(BindCallback.class);
+ verify(mBindStage).requestRebind(eq(childEntry), callbackCaptor.capture());
+ callbackCaptor.getValue().onBindFinished(childEntry);
verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
.getContentFlag());
@@ -229,8 +246,9 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
- .thenReturn(false);
+ RowContentBindParams params = new RowContentBindParams();
+ when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -247,8 +265,9 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
- .thenReturn(false);
+ RowContentBindParams params = new RowContentBindParams();
+ when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -270,8 +289,9 @@
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY, 47);
- when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
- .thenReturn(false);
+ RowContentBindParams params = new RowContentBindParams();
+ when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
mGroupManager.onEntryAdded(summaryEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 54dc728..d405fea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -87,7 +86,6 @@
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
entry.setRow(row);
when(row.getEntry()).thenReturn(entry);
- when(row.isInflationFlagSet(anyInt())).thenReturn(true);
return entry;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index fea4b8b..5027610 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -61,7 +61,6 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -70,6 +69,7 @@
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 390e812..df62254 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -39,9 +39,9 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.util.Assert;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
new file mode 100644
index 0000000..8eecde1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
@@ -0,0 +1,218 @@
+package com.android.systemui.util
+
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FloatingContentCoordinatorTest : SysuiTestCase() {
+
+ private val screenBounds = Rect(0, 0, 1000, 1000)
+
+ private val rect100px = Rect()
+ private val rect100pxFloating = FloatingRect(rect100px)
+
+ private val rect200px = Rect()
+ private val rect200pxFloating = FloatingRect(rect200px)
+
+ private val rect300px = Rect()
+ private val rect300pxFloating = FloatingRect(rect300px)
+
+ private val floatingCoordinator = FloatingContentCoordinator()
+
+ @Before
+ fun setup() {
+ rect100px.set(0, 0, 100, 100)
+ rect200px.set(0, 0, 200, 200)
+ rect300px.set(0, 0, 300, 300)
+ }
+
+ @After
+ fun tearDown() {
+ // We need to remove this stuff since it's a singleton object and it'll be there for the
+ // next test.
+ floatingCoordinator.onContentRemoved(rect100pxFloating)
+ floatingCoordinator.onContentRemoved(rect200pxFloating)
+ floatingCoordinator.onContentRemoved(rect300pxFloating)
+ }
+
+ @Test
+ fun testOnContentAdded() {
+ // Add rect1, and verify that the coordinator didn't move it.
+ floatingCoordinator.onContentAdded(rect100pxFloating)
+ assertEquals(rect100px.top, 0)
+
+ // Add rect2, which intersects rect1. Verify that rect2 was not moved, since newly added
+ // content is allowed to remain where it is. rect1 should have been moved below rect2
+ // since it was in the way.
+ floatingCoordinator.onContentAdded(rect200pxFloating)
+ assertEquals(rect200px.top, 0)
+ assertEquals(rect100px.top, 200)
+
+ verifyRectSizes()
+ }
+
+ @Test
+ fun testOnContentRemoved() {
+ // Add rect1, and remove it. Then add rect2. Since rect1 was removed before that, it should
+ // no longer be considered in the way, so it shouldn't move when rect2 is added.
+ floatingCoordinator.onContentAdded(rect100pxFloating)
+ floatingCoordinator.onContentRemoved(rect100pxFloating)
+ floatingCoordinator.onContentAdded(rect200pxFloating)
+
+ assertEquals(rect100px.top, 0)
+ assertEquals(rect200px.top, 0)
+
+ verifyRectSizes()
+ }
+
+ @Test
+ fun testOnContentMoved_twoRects() {
+ // Add rect1, which is at y = 0.
+ floatingCoordinator.onContentAdded(rect100pxFloating)
+
+ // Move rect2 down to 500px, where it won't conflict with rect1.
+ rect200px.offsetTo(0, 500)
+ floatingCoordinator.onContentAdded(rect200pxFloating)
+
+ // Then, move it to 0px where it will absolutely conflict with rect1.
+ rect200px.offsetTo(0, 0)
+ floatingCoordinator.onContentMoved(rect200pxFloating)
+
+ // The coordinator should have left rect2 alone, and moved rect1 below it. rect1 should now
+ // be at y = 200.
+ assertEquals(rect200px.top, 0)
+ assertEquals(rect100px.top, 200)
+
+ verifyRectSizes()
+
+ // Move rect2 to y = 275px. Since this puts it at the bottom half of rect1, it should push
+ // rect1 upward and leave rect2 alone.
+ rect200px.offsetTo(0, 275)
+ floatingCoordinator.onContentMoved(rect200pxFloating)
+
+ assertEquals(rect200px.top, 275)
+ assertEquals(rect100px.top, 175)
+
+ verifyRectSizes()
+
+ // Move rect2 to y = 110px. This makes it intersect rect1 again, but above its center of
+ // mass. That means rect1 should be pushed downward.
+ rect200px.offsetTo(0, 110)
+ floatingCoordinator.onContentMoved(rect200pxFloating)
+
+ assertEquals(rect200px.top, 110)
+ assertEquals(rect100px.top, 310)
+
+ verifyRectSizes()
+ }
+
+ @Test
+ fun testOnContentMoved_threeRects() {
+ floatingCoordinator.onContentAdded(rect100pxFloating)
+
+ // Add rect2, which should displace rect1 to y = 200
+ floatingCoordinator.onContentAdded(rect200pxFloating)
+ assertEquals(rect200px.top, 0)
+ assertEquals(rect100px.top, 200)
+
+ // Add rect3, which should completely cover both rect1 and rect2. That should cause them to
+ // move away. The order in which they do so is non-deterministic, so just make sure none of
+ // the three Rects intersect.
+ floatingCoordinator.onContentAdded(rect300pxFloating)
+
+ assertFalse(Rect.intersects(rect100px, rect200px))
+ assertFalse(Rect.intersects(rect100px, rect300px))
+ assertFalse(Rect.intersects(rect200px, rect300px))
+
+ // Move rect2 to intersect both rect1 and rect3.
+ rect200px.offsetTo(0, 150)
+ floatingCoordinator.onContentMoved(rect200pxFloating)
+
+ assertFalse(Rect.intersects(rect100px, rect200px))
+ assertFalse(Rect.intersects(rect100px, rect300px))
+ assertFalse(Rect.intersects(rect200px, rect300px))
+ }
+
+ @Test
+ fun testOnContentMoved_respectsUpperBounds() {
+ // Add rect1, which is at y = 0.
+ floatingCoordinator.onContentAdded(rect100pxFloating)
+
+ // Move rect2 down to 500px, where it won't conflict with rect1.
+ rect200px.offsetTo(0, 500)
+ floatingCoordinator.onContentAdded(rect200pxFloating)
+
+ // Then, move it to 90px where it will conflict with rect1, but with a center of mass below
+ // that of rect1's. This would normally mean that rect1 moves upward. However, since it's at
+ // the top of the screen, it should go downward instead.
+ rect200px.offsetTo(0, 90)
+ floatingCoordinator.onContentMoved(rect200pxFloating)
+
+ // rect2 should have been left alone, rect1 is now below rect2 at y = 290px even though it
+ // was intersected from below.
+ assertEquals(rect200px.top, 90)
+ assertEquals(rect100px.top, 290)
+ }
+
+ @Test
+ fun testOnContentMoved_respectsLowerBounds() {
+ // Put rect1 at the bottom of the screen and add it.
+ rect100px.offsetTo(0, screenBounds.bottom - 100)
+ floatingCoordinator.onContentAdded(rect100pxFloating)
+
+ // Put rect2 at the bottom as well. Since its center of mass is above rect1's, rect1 would
+ // normally move downward. Since it's at the bottom of the screen, it should go upward
+ // instead.
+ rect200px.offsetTo(0, 800)
+ floatingCoordinator.onContentAdded(rect200pxFloating)
+
+ assertEquals(rect200px.top, 800)
+ assertEquals(rect100px.top, 700)
+ }
+
+ /**
+ * Tests that the rect sizes didn't change when the coordinator manipulated them. This allows us
+ * to assert only the value of rect.top in tests, since if top, width, and height are correct,
+ * that means top/left/right/bottom are all correct.
+ */
+ private fun verifyRectSizes() {
+ assertEquals(100, rect100px.width())
+ assertEquals(200, rect200px.width())
+ assertEquals(300, rect300px.width())
+
+ assertEquals(100, rect100px.height())
+ assertEquals(200, rect200px.height())
+ assertEquals(300, rect300px.height())
+ }
+
+ /**
+ * Helper class that uses [floatingCoordinator.findAreaForContentVertically] to move a
+ * Rect when needed.
+ */
+ inner class FloatingRect(
+ private val underlyingRect: Rect
+ ) : FloatingContentCoordinator.FloatingContent {
+ override fun moveToBounds(bounds: Rect) {
+ underlyingRect.set(bounds)
+ }
+
+ override fun getAllowedFloatingBoundsRegion(): Rect {
+ return screenBounds
+ }
+
+ override fun getFloatingBoundsOnScreen(): Rect {
+ return underlyingRect
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1a4fc32..1cb9313 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -818,27 +818,26 @@
}
}
- void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
- @Nullable Bundle clientState) {
+ void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId) {
synchronized (mLock) {
if (mAugmentedAutofillEventHistory == null
|| mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
return;
}
mAugmentedAutofillEventHistory.addEvent(
- new Event(Event.TYPE_DATASET_SELECTED, suggestionId, clientState, null, null,
+ new Event(Event.TYPE_DATASET_SELECTED, suggestionId, null, null, null,
null, null, null, null, null, null));
}
}
- void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState) {
+ void logAugmentedAutofillShown(int sessionId) {
synchronized (mLock) {
if (mAugmentedAutofillEventHistory == null
|| mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
return;
}
mAugmentedAutofillEventHistory.addEvent(
- new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
+ new Event(Event.TYPE_DATASETS_SHOWN, null, null, null, null, null,
null, null, null, null, null));
}
@@ -1227,16 +1226,15 @@
}
@Override
- public void logAugmentedAutofillShown(int sessionId, Bundle clientState) {
- AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId,
- clientState);
+ public void logAugmentedAutofillShown(int sessionId) {
+ AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId);
}
@Override
- public void logAugmentedAutofillSelected(int sessionId, String suggestionId,
- Bundle clientState) {
+ public void logAugmentedAutofillSelected(int sessionId,
+ String suggestionId) {
AutofillManagerServiceImpl.this.logAugmentedAutofillSelected(sessionId,
- suggestionId, clientState);
+ suggestionId);
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 5e6f6fea..880c401 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -55,6 +55,7 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
+import com.android.server.autofill.ui.InlineSuggestionFactory;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
@@ -144,7 +145,8 @@
int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
@Nullable AutofillValue focusedValue,
@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
- @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback) {
+ @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
+ @NonNull Runnable onErrorCallback) {
long requestTime = SystemClock.elapsedRealtime();
AtomicReference<ICancellationSignal> cancellationRef = new AtomicReference<>();
@@ -161,12 +163,12 @@
focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
new IFillCallback.Stub() {
@Override
- public void onSuccess(@Nullable Dataset[] inlineSuggestionsData,
- @Nullable Bundle clientState) {
+ public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
mCallbacks.resetLastResponse();
maybeRequestShowInlineSuggestions(sessionId,
inlineSuggestionsData, focusedId,
- inlineSuggestionsCallback, client, clientState);
+ inlineSuggestionsCallback, client,
+ onErrorCallback);
requestAutofill.complete(null);
}
@@ -231,29 +233,31 @@
private void maybeRequestShowInlineSuggestions(int sessionId,
@Nullable Dataset[] inlineSuggestionsData, @NonNull AutofillId focusedId,
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
- @NonNull IAutoFillManagerClient client, @Nullable Bundle clientState) {
+ @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback) {
if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null) {
return;
}
mCallbacks.setLastResponse(sessionId);
+
try {
inlineSuggestionsCallback.onInlineSuggestionsResponse(
InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
inlineSuggestionsData, focusedId, mContext,
dataset -> {
mCallbacks.logAugmentedAutofillSelected(sessionId,
- dataset.getId(), clientState);
+ dataset.getId());
try {
client.autofill(sessionId, dataset.getFieldIds(),
dataset.getFieldValues());
} catch (RemoteException e) {
Slog.w(TAG, "Encounter exception autofilling the values");
}
- }));
+ }, onErrorCallback));
} catch (RemoteException e) {
Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
}
- mCallbacks.logAugmentedAutofillShown(sessionId, clientState);
+
+ mCallbacks.logAugmentedAutofillShown(sessionId);
}
@Override
@@ -275,9 +279,8 @@
void setLastResponse(int sessionId);
- void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState);
+ void logAugmentedAutofillShown(int sessionId);
- void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
- @Nullable Bundle clientState);
+ void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 415ecd8..7e5123c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -103,6 +103,7 @@
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.autofill.ui.InlineSuggestionFactory;
import com.android.server.autofill.ui.PendingUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -2681,7 +2682,6 @@
}
}
-
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
serviceLabel, serviceIcon, this, id, mCompatMode);
@@ -2733,7 +2733,11 @@
InlineSuggestionsResponse inlineSuggestionsResponse =
InlineSuggestionFactory.createInlineSuggestionsResponse(response.getRequestId(),
- datasets.toArray(new Dataset[]{}), mCurrentViewId, mContext, this);
+ datasets.toArray(new Dataset[]{}), mCurrentViewId, mContext, this, () -> {
+ synchronized (mLock) {
+ requestHideFillUi(mCurrentViewId);
+ }
+ });
try {
inlineContentCallback.onInlineSuggestionsResponse(inlineSuggestionsResponse);
} catch (RemoteException e) {
@@ -3024,7 +3028,11 @@
mInlineSuggestionsRequestCallback != null
? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
- currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback);
+ currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback, () -> {
+ synchronized (mLock) {
+ cancelAugmentedAutofillLocked();
+ }
+ });
if (mAugmentedAutofillDestroyer == null) {
mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
similarity index 80%
rename from services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
rename to services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index cb6c8f5..38a5b5b 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.autofill;
+package com.android.server.autofill.ui;
import static com.android.server.autofill.Helper.sDebug;
@@ -32,18 +32,13 @@
import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsResponse;
+import com.android.internal.util.function.QuadFunction;
import com.android.internal.view.inline.IInlineContentCallback;
import com.android.internal.view.inline.IInlineContentProvider;
import com.android.server.UiThread;
-import com.android.server.autofill.ui.AutoFillUI;
-import com.android.server.autofill.ui.InlineSuggestionUi;
import java.util.ArrayList;
-
-/**
- * @hide
- */
public final class InlineSuggestionFactory {
private static final String TAG = "InlineSuggestionFactory";
@@ -65,28 +60,12 @@
@NonNull Dataset[] datasets,
@NonNull AutofillId autofillId,
@NonNull Context context,
- @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
- if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
-
- final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
- final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context);
- for (Dataset dataset : datasets) {
- final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
- if (fieldIndex < 0) {
- Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
- return null;
- }
- final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(
- fieldIndex);
- if (inlinePresentation == null) {
- Slog.w(TAG, "InlinePresentation not found in dataset");
- return null;
- }
- InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(dataset,
- inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback);
- inlineSuggestions.add(inlineSuggestion);
- }
- return new InlineSuggestionsResponse(inlineSuggestions);
+ @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
+ @NonNull Runnable onErrorCallback) {
+ return createInlineSuggestionsResponseInternal(datasets, autofillId,
+ context, onErrorCallback, (dataset, inlinePresentation, inlineSuggestionUi,
+ filedIndex) -> createAugmentedInlineSuggestion(dataset,
+ inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback));
}
/**
@@ -97,11 +76,26 @@
@NonNull Dataset[] datasets,
@NonNull AutofillId autofillId,
@NonNull Context context,
- @NonNull AutoFillUI.AutoFillUiCallback client) {
- if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
+ @NonNull AutoFillUI.AutoFillUiCallback client,
+ @NonNull Runnable onErrorCallback) {
+ return createInlineSuggestionsResponseInternal(datasets, autofillId,
+ context, onErrorCallback, (dataset, inlinePresentation, inlineSuggestionUi,
+ filedIndex) -> createInlineSuggestion(requestId, dataset, filedIndex,
+ inlinePresentation, inlineSuggestionUi, client));
+ }
+
+ private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
+ @NonNull Dataset[] datasets,
+ @NonNull AutofillId autofillId,
+ @NonNull Context context,
+ @NonNull Runnable onErrorCallback,
+ @NonNull QuadFunction<Dataset, InlinePresentation, InlineSuggestionUi,
+ Integer, InlineSuggestion> suggestionFactory) {
+ if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
- final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context);
+ final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context,
+ onErrorCallback);
for (Dataset dataset : datasets) {
final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
if (fieldIndex < 0) {
@@ -114,9 +108,8 @@
Slog.w(TAG, "InlinePresentation not found in dataset");
return null;
}
- InlineSuggestion inlineSuggestion = createInlineSuggestion(requestId, dataset,
- fieldIndex,
- inlinePresentation, inlineSuggestionUi, client);
+ InlineSuggestion inlineSuggestion = suggestionFactory.apply(dataset,
+ inlinePresentation, inlineSuggestionUi, fieldIndex);
inlineSuggestions.add(inlineSuggestion);
}
return new InlineSuggestionsResponse(inlineSuggestions);
@@ -131,9 +124,8 @@
inlinePresentation.getInlinePresentationSpec(),
InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""},
InlineSuggestionInfo.TYPE_SUGGESTION);
- final View.OnClickListener onClickListener = v -> {
+ final View.OnClickListener onClickListener = v ->
inlineSuggestionUiCallback.autofill(dataset);
- };
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
onClickListener));
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
new file mode 100644
index 0000000..8d476d7
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill.ui;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.widget.FrameLayout;
+
+import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerInternal;
+
+/**
+ * This class is the root view for an inline suggestion. It is responsible for
+ * detecting the click on the item and to also transfer input focus to the IME
+ * window if we detect the user is scrolling.
+ */
+ // TODO(b/146453086) Move to ExtServices and add @SystemApi to transfer touch focus
+@SuppressLint("ViewConstructor")
+class InlineSuggestionRoot extends FrameLayout {
+ private static final String LOG_TAG = InlineSuggestionRoot.class.getSimpleName();
+
+ private final @NonNull Runnable mOnErrorCallback;
+ private final int mTouchSlop;
+
+ private float mDownX;
+ private float mDownY;
+
+ InlineSuggestionRoot(@NonNull Context context, @NonNull Runnable onErrorCallback) {
+ super(context);
+ mOnErrorCallback = onErrorCallback;
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ }
+
+ @Override
+ @SuppressLint("ClickableViewAccessibility")
+ public boolean onTouchEvent(@NonNull MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ mDownX = event.getX();
+ mDownY = event.getY();
+ } break;
+
+ case MotionEvent.ACTION_MOVE: {
+ final float distance = MathUtils.dist(mDownX, mDownY,
+ event.getX(), event.getY());
+ if (distance > mTouchSlop) {
+ transferTouchFocusToImeWindow();
+ }
+ } break;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ private void transferTouchFocusToImeWindow() {
+ final WindowManagerInternal windowManagerInternal = LocalServices.getService(
+ WindowManagerInternal.class);
+ if (!windowManagerInternal.transferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
+ getContext().getDisplayId())) {
+ Log.e(LOG_TAG, "Cannot transfer touch focus from suggestion to IME");
+ mOnErrorCallback.run();
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
index 2adefea..bf148a6 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
@@ -67,10 +67,12 @@
// (int)}. This name is a single string of the form "package:type/entry".
private static final Pattern RESOURCE_NAME_PATTERN = Pattern.compile("([^:]+):([^/]+)/(\\S+)");
- private final Context mContext;
+ private final @NonNull Context mContext;
+ private final @NonNull Runnable mOnErrorCallback;
- public InlineSuggestionUi(Context context) {
+ InlineSuggestionUi(@NonNull Context context, @NonNull Runnable onErrorCallback) {
this.mContext = context;
+ mOnErrorCallback = onErrorCallback;
}
/**
@@ -94,15 +96,17 @@
}
final View suggestionView = renderSlice(inlinePresentation.getSlice(),
contextThemeWrapper);
- if (onClickListener != null) {
- suggestionView.setOnClickListener(onClickListener);
- }
+
+ final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(
+ mContext, mOnErrorCallback);
+ suggestionRoot.addView(suggestionView);
+ suggestionRoot.setOnClickListener(onClickListener);
WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(width, height,
WindowManager.LayoutParams.TYPE_APPLICATION, 0,
PixelFormat.TRANSPARENT);
- wvr.addView(suggestionView, lp);
+ wvr.addView(suggestionRoot, lp);
return sc;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ce5e241..6726170 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -48,8 +48,11 @@
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
+import static java.util.Map.Entry;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -62,6 +65,8 @@
import android.database.ContentObserver;
import android.net.CaptivePortal;
import android.net.ConnectionInfo;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
import android.net.ICaptivePortal;
import android.net.IConnectivityDiagnosticsCallback;
@@ -130,6 +135,7 @@
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -170,6 +176,7 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
@@ -492,9 +499,9 @@
/**
* Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
* been tested.
- * obj = String representing URL that Internet probe was redirect to, if it was redirected.
- * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
- * arg2 = NetID.
+ * obj = {@link NetworkTestedResults} representing information sent from NetworkMonitor.
+ * data = PersistableBundle of extras passed from NetworkMonitor. If {@link
+ * NetworkMonitorCallbacks#notifyNetworkTested} is called, this will be null.
*/
private static final int EVENT_NETWORK_TESTED = 41;
@@ -596,6 +603,9 @@
private Set<String> mWolSupportedInterfaces;
private TelephonyManager mTelephonyManager;
+ private final AppOpsManager mAppOpsManager;
+
+ private final LocationPermissionChecker mLocationPermissionChecker;
private KeepaliveTracker mKeepaliveTracker;
private NetworkNotificationManager mNotifier;
@@ -992,6 +1002,8 @@
mNetd = netd;
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mLocationPermissionChecker = new LocationPermissionChecker(mContext);
// To ensure uid rules are synchronized with Network Policy, register for
// NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
@@ -2101,6 +2113,12 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
+ private boolean checkNetworkStackPermission(int pid, int uid) {
+ return checkAnyPermissionOf(pid, uid,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
private boolean checkNetworkSignalStrengthWakeupPermission(int pid, int uid) {
return checkAnyPermissionOf(pid, uid,
android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP,
@@ -2747,88 +2765,21 @@
break;
}
case EVENT_NETWORK_TESTED: {
- final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+ final NetworkTestedResults results = (NetworkTestedResults) msg.obj;
+
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(results.mNetId);
if (nai == null) break;
- final boolean wasPartial = nai.partialConnectivity;
- nai.partialConnectivity = ((msg.arg1 & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
- final boolean partialConnectivityChanged =
- (wasPartial != nai.partialConnectivity);
+ handleNetworkTested(nai, results.mTestResult,
+ (results.mRedirectUrl == null) ? "" : results.mRedirectUrl);
- final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0);
- final boolean wasValidated = nai.lastValidated;
- final boolean wasDefault = isDefaultNetwork(nai);
- // Only show a connected notification if the network is pending validation
- // after the captive portal app was open, and it has now validated.
- if (nai.captivePortalValidationPending && valid) {
- // User is now logged in, network validated.
- nai.captivePortalValidationPending = false;
- showNetworkNotification(nai, NotificationType.LOGGED_IN);
- }
-
- final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
-
- if (DBG) {
- final String logMsg = !TextUtils.isEmpty(redirectUrl)
- ? " with redirect to " + redirectUrl
- : "";
- log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
- }
- if (valid != nai.lastValidated) {
- if (wasDefault) {
- mDeps.getMetricsLogger()
- .defaultNetworkMetrics().logDefaultNetworkValidity(
- SystemClock.elapsedRealtime(), valid);
- }
- final int oldScore = nai.getCurrentScore();
- nai.lastValidated = valid;
- nai.everValidated |= valid;
- updateCapabilities(oldScore, nai, nai.networkCapabilities);
- // If score has changed, rebroadcast to NetworkProviders. b/17726566
- if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
- if (valid) {
- handleFreshlyValidatedNetwork(nai);
- // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
- // LOST_INTERNET notifications if network becomes valid.
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.NO_INTERNET);
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.LOST_INTERNET);
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.PARTIAL_CONNECTIVITY);
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.PRIVATE_DNS_BROKEN);
- // If network becomes valid, the hasShownBroken should be reset for
- // that network so that the notification will be fired when the private
- // DNS is broken again.
- nai.networkAgentConfig.hasShownBroken = false;
- }
- } else if (partialConnectivityChanged) {
- updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
- }
- updateInetCondition(nai);
- // Let the NetworkAgent know the state of its network
- Bundle redirectUrlBundle = new Bundle();
- redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
- // TODO: Evaluate to update partial connectivity to status to NetworkAgent.
- nai.asyncChannel.sendMessage(
- NetworkAgent.CMD_REPORT_NETWORK_STATUS,
- (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
- 0, redirectUrlBundle);
-
- // If NetworkMonitor detects partial connectivity before
- // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
- // immediately. Re-notify partial connectivity silently if no internet
- // notification already there.
- if (!wasPartial && nai.partialConnectivity) {
- // Remove delayed message if there is a pending message.
- mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
- handlePromptUnvalidated(nai.network);
- }
-
- if (wasValidated && !nai.lastValidated) {
- handleNetworkUnvalidated(nai);
- }
+ // Invoke ConnectivityReport generation for this Network test event.
+ final Message m =
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED,
+ new ConnectivityReportEvent(results.mTimestampMillis, nai));
+ m.setData(msg.getData());
+ mConnectivityDiagnosticsHandler.sendMessage(m);
break;
}
case EVENT_PROVISIONING_NOTIFICATION: {
@@ -2879,6 +2830,87 @@
return true;
}
+ private void handleNetworkTested(
+ @NonNull NetworkAgentInfo nai, int testResult, @NonNull String redirectUrl) {
+ final boolean wasPartial = nai.partialConnectivity;
+ nai.partialConnectivity = ((testResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
+ final boolean partialConnectivityChanged =
+ (wasPartial != nai.partialConnectivity);
+
+ final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
+ final boolean wasValidated = nai.lastValidated;
+ final boolean wasDefault = isDefaultNetwork(nai);
+ // Only show a connected notification if the network is pending validation
+ // after the captive portal app was open, and it has now validated.
+ if (nai.captivePortalValidationPending && valid) {
+ // User is now logged in, network validated.
+ nai.captivePortalValidationPending = false;
+ showNetworkNotification(nai, NotificationType.LOGGED_IN);
+ }
+
+ if (DBG) {
+ final String logMsg = !TextUtils.isEmpty(redirectUrl)
+ ? " with redirect to " + redirectUrl
+ : "";
+ log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
+ }
+ if (valid != nai.lastValidated) {
+ if (wasDefault) {
+ mDeps.getMetricsLogger()
+ .defaultNetworkMetrics().logDefaultNetworkValidity(
+ SystemClock.elapsedRealtime(), valid);
+ }
+ final int oldScore = nai.getCurrentScore();
+ nai.lastValidated = valid;
+ nai.everValidated |= valid;
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ // If score has changed, rebroadcast to NetworkProviders. b/17726566
+ if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+ if (valid) {
+ handleFreshlyValidatedNetwork(nai);
+ // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
+ // LOST_INTERNET notifications if network becomes valid.
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.NO_INTERNET);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.LOST_INTERNET);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.PARTIAL_CONNECTIVITY);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.PRIVATE_DNS_BROKEN);
+ // If network becomes valid, the hasShownBroken should be reset for
+ // that network so that the notification will be fired when the private
+ // DNS is broken again.
+ nai.networkAgentConfig.hasShownBroken = false;
+ }
+ } else if (partialConnectivityChanged) {
+ updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
+ }
+ updateInetCondition(nai);
+ // Let the NetworkAgent know the state of its network
+ Bundle redirectUrlBundle = new Bundle();
+ redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
+ // TODO: Evaluate to update partial connectivity to status to NetworkAgent.
+ nai.asyncChannel.sendMessage(
+ NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+ (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
+ 0, redirectUrlBundle);
+
+ // If NetworkMonitor detects partial connectivity before
+ // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
+ // immediately. Re-notify partial connectivity silently if no internet
+ // notification already there.
+ if (!wasPartial && nai.partialConnectivity) {
+ // Remove delayed message if there is a pending message.
+ mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
+ handlePromptUnvalidated(nai.network);
+ }
+
+ if (wasValidated && !nai.lastValidated) {
+ handleNetworkUnvalidated(nai);
+ }
+ }
+
private int getCaptivePortalMode() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_MODE,
@@ -2927,8 +2959,23 @@
@Override
public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) {
- mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED,
- testResult, mNetId, redirectUrl));
+ notifyNetworkTestedWithExtras(testResult, redirectUrl, SystemClock.elapsedRealtime(),
+ PersistableBundle.EMPTY);
+ }
+
+ @Override
+ public void notifyNetworkTestedWithExtras(
+ int testResult,
+ @Nullable String redirectUrl,
+ long timestampMillis,
+ @NonNull PersistableBundle extras) {
+ final Message msg =
+ mTrackerHandler.obtainMessage(
+ EVENT_NETWORK_TESTED,
+ new NetworkTestedResults(
+ mNetId, testResult, timestampMillis, redirectUrl));
+ msg.setData(new Bundle(extras));
+ mTrackerHandler.sendMessage(msg);
}
@Override
@@ -2970,6 +3017,21 @@
}
@Override
+ public void notifyDataStallSuspected(
+ long timestampMillis, int detectionMethod, PersistableBundle extras) {
+ final Message msg =
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED,
+ detectionMethod, mNetId, timestampMillis);
+ msg.setData(new Bundle(extras));
+
+ // NetworkStateTrackerHandler currently doesn't take any actions based on data
+ // stalls so send the message directly to ConnectivityDiagnosticsHandler and avoid
+ // the cost of going through two handlers.
+ mConnectivityDiagnosticsHandler.sendMessage(msg);
+ }
+
+ @Override
public int getInterfaceVersion() {
return this.VERSION;
}
@@ -4143,6 +4205,19 @@
final int connectivityInfo = encodeBool(hasConnectivity);
mHandler.sendMessage(
mHandler.obtainMessage(EVENT_REVALIDATE_NETWORK, uid, connectivityInfo, network));
+
+ final NetworkAgentInfo nai;
+ if (network == null) {
+ nai = getDefaultNetwork();
+ } else {
+ nai = getNetworkAgentInfoForNetwork(network);
+ }
+ if (nai != null) {
+ mConnectivityDiagnosticsHandler.sendMessage(
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
+ connectivityInfo, 0, nai));
+ }
}
private void handleReportNetworkConnectivity(
@@ -6506,6 +6581,7 @@
}
private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork(
+ @NonNull final NetworkReassignment changes,
@NonNull final NetworkAgentInfo newNetwork) {
final int score = newNetwork.getCurrentScore();
final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests = new ArrayMap<>();
@@ -6516,7 +6592,10 @@
// requests or not, and doesn't affect the network's score.
if (nri.request.isListen()) continue;
- final NetworkAgentInfo currentNetwork = nri.mSatisfier;
+ // The reassignment has been seeded with the initial assignment, therefore
+ // getReassignment can't be null and mNewNetwork is only null if there was no
+ // satisfier in the first place or there was an explicit reassignment to null.
+ final NetworkAgentInfo currentNetwork = changes.getReassignment(nri).mNewNetwork;
final boolean satisfies = newNetwork.satisfies(nri.request);
if (newNetwork == currentNetwork && satisfies) continue;
@@ -6566,7 +6645,7 @@
if (VDBG || DDBG) log("rematching " + newNetwork.name());
final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests =
- computeRequestReassignmentForNetwork(newNetwork);
+ computeRequestReassignmentForNetwork(changes, newNetwork);
// Find and migrate to this Network any NetworkRequests for
// which this network is now the best.
@@ -7373,7 +7452,11 @@
@GuardedBy("mVpns")
private Vpn getVpnIfOwner() {
- final int uid = Binder.getCallingUid();
+ return getVpnIfOwner(Binder.getCallingUid());
+ }
+
+ @GuardedBy("mVpns")
+ private Vpn getVpnIfOwner(int uid) {
final int user = UserHandle.getUserId(uid);
final Vpn vpn = mVpns.get(user);
@@ -7469,6 +7552,8 @@
*/
@VisibleForTesting
class ConnectivityDiagnosticsHandler extends Handler {
+ private final String mTag = ConnectivityDiagnosticsHandler.class.getSimpleName();
+
/**
* Used to handle ConnectivityDiagnosticsCallback registration events from {@link
* android.net.ConnectivityDiagnosticsManager}.
@@ -7485,6 +7570,37 @@
*/
private static final int EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 2;
+ /**
+ * Event for {@link NetworkStateTrackerHandler} to trigger ConnectivityReport callbacks
+ * after processing {@link #EVENT_NETWORK_TESTED} events.
+ * obj = {@link ConnectivityReportEvent} representing ConnectivityReport info reported from
+ * NetworkMonitor.
+ * data = PersistableBundle of extras passed from NetworkMonitor.
+ *
+ * <p>See {@link ConnectivityService#EVENT_NETWORK_TESTED}.
+ */
+ private static final int EVENT_NETWORK_TESTED = ConnectivityService.EVENT_NETWORK_TESTED;
+
+ /**
+ * Event for NetworkMonitor to inform ConnectivityService that a potential data stall has
+ * been detected on the network.
+ * obj = Long the timestamp (in millis) for when the suspected data stall was detected.
+ * arg1 = {@link DataStallReport#DetectionMethod} indicating the detection method.
+ * arg2 = NetID.
+ * data = PersistableBundle of extras passed from NetworkMonitor.
+ */
+ private static final int EVENT_DATA_STALL_SUSPECTED = 4;
+
+ /**
+ * Event for ConnectivityDiagnosticsHandler to handle network connectivity being reported to
+ * the platform. This event will invoke {@link
+ * IConnectivityDiagnosticsCallback#onNetworkConnectivityReported} for permissioned
+ * callbacks.
+ * obj = Network that was reported on
+ * arg1 = boolint for the quality reported
+ */
+ private static final int EVENT_NETWORK_CONNECTIVITY_REPORTED = 5;
+
private ConnectivityDiagnosticsHandler(Looper looper) {
super(looper);
}
@@ -7502,6 +7618,37 @@
(IConnectivityDiagnosticsCallback) msg.obj, msg.arg1);
break;
}
+ case EVENT_NETWORK_TESTED: {
+ final ConnectivityReportEvent reportEvent =
+ (ConnectivityReportEvent) msg.obj;
+
+ // This is safe because {@link
+ // NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} receives a
+ // PersistableBundle and converts it to the Bundle in the incoming Message. If
+ // {@link NetworkMonitorCallbacks#notifyNetworkTested} is called, msg.data will
+ // not be set. This is also safe, as msg.getData() will return an empty Bundle.
+ final PersistableBundle extras = new PersistableBundle(msg.getData());
+ handleNetworkTestedWithExtras(reportEvent, extras);
+ break;
+ }
+ case EVENT_DATA_STALL_SUSPECTED: {
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+ if (nai == null) break;
+
+ // This is safe because NetworkMonitorCallbacks#notifyDataStallSuspected
+ // receives a PersistableBundle and converts it to the Bundle in the incoming
+ // Message.
+ final PersistableBundle extras = new PersistableBundle(msg.getData());
+ handleDataStallSuspected(nai, (long) msg.obj, msg.arg1, extras);
+ break;
+ }
+ case EVENT_NETWORK_CONNECTIVITY_REPORTED: {
+ handleNetworkConnectivityReported((NetworkAgentInfo) msg.obj, toBool(msg.arg1));
+ break;
+ }
+ default: {
+ Log.e(mTag, "Unrecognized event in ConnectivityDiagnostics: " + msg.what);
+ }
}
}
}
@@ -7511,12 +7658,16 @@
class ConnectivityDiagnosticsCallbackInfo implements Binder.DeathRecipient {
@NonNull private final IConnectivityDiagnosticsCallback mCb;
@NonNull private final NetworkRequestInfo mRequestInfo;
+ @NonNull private final String mCallingPackageName;
@VisibleForTesting
ConnectivityDiagnosticsCallbackInfo(
- @NonNull IConnectivityDiagnosticsCallback cb, @NonNull NetworkRequestInfo nri) {
+ @NonNull IConnectivityDiagnosticsCallback cb,
+ @NonNull NetworkRequestInfo nri,
+ @NonNull String callingPackageName) {
mCb = cb;
mRequestInfo = nri;
+ mCallingPackageName = callingPackageName;
}
@Override
@@ -7526,6 +7677,39 @@
}
}
+ /**
+ * Class used for sending information from {@link
+ * NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} to the handler for processing it.
+ */
+ private static class NetworkTestedResults {
+ private final int mNetId;
+ private final int mTestResult;
+ private final long mTimestampMillis;
+ @Nullable private final String mRedirectUrl;
+
+ private NetworkTestedResults(
+ int netId, int testResult, long timestampMillis, @Nullable String redirectUrl) {
+ mNetId = netId;
+ mTestResult = testResult;
+ mTimestampMillis = timestampMillis;
+ mRedirectUrl = redirectUrl;
+ }
+ }
+
+ /**
+ * Class used for sending information from {@link NetworkStateTrackerHandler} to {@link
+ * ConnectivityDiagnosticsHandler}.
+ */
+ private static class ConnectivityReportEvent {
+ private final long mTimestampMillis;
+ @NonNull private final NetworkAgentInfo mNai;
+
+ private ConnectivityReportEvent(long timestampMillis, @NonNull NetworkAgentInfo nai) {
+ mTimestampMillis = timestampMillis;
+ mNai = nai;
+ }
+ }
+
private void handleRegisterConnectivityDiagnosticsCallback(
@NonNull ConnectivityDiagnosticsCallbackInfo cbInfo) {
ensureRunningOnConnectivityServiceThread();
@@ -7573,13 +7757,109 @@
cb.asBinder().unlinkToDeath(mConnectivityDiagnosticsCallbacks.remove(cb), 0);
}
+ private void handleNetworkTestedWithExtras(
+ @NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) {
+ final NetworkAgentInfo nai = reportEvent.mNai;
+ final ConnectivityReport report =
+ new ConnectivityReport(
+ reportEvent.mNai.network,
+ reportEvent.mTimestampMillis,
+ nai.linkProperties,
+ nai.networkCapabilities,
+ extras);
+ final List<IConnectivityDiagnosticsCallback> results =
+ getMatchingPermissionedCallbacks(nai);
+ for (final IConnectivityDiagnosticsCallback cb : results) {
+ try {
+ cb.onConnectivityReport(report);
+ } catch (RemoteException ex) {
+ loge("Error invoking onConnectivityReport", ex);
+ }
+ }
+ }
+
+ private void handleDataStallSuspected(
+ @NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
+ @NonNull PersistableBundle extras) {
+ final DataStallReport report =
+ new DataStallReport(nai.network, timestampMillis, detectionMethod, extras);
+ final List<IConnectivityDiagnosticsCallback> results =
+ getMatchingPermissionedCallbacks(nai);
+ for (final IConnectivityDiagnosticsCallback cb : results) {
+ try {
+ cb.onDataStallSuspected(report);
+ } catch (RemoteException ex) {
+ loge("Error invoking onDataStallSuspected", ex);
+ }
+ }
+ }
+
+ private void handleNetworkConnectivityReported(
+ @NonNull NetworkAgentInfo nai, boolean connectivity) {
+ final List<IConnectivityDiagnosticsCallback> results =
+ getMatchingPermissionedCallbacks(nai);
+ for (final IConnectivityDiagnosticsCallback cb : results) {
+ try {
+ cb.onNetworkConnectivityReported(nai.network, connectivity);
+ } catch (RemoteException ex) {
+ loge("Error invoking onNetworkConnectivityReported", ex);
+ }
+ }
+ }
+
+ private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
+ @NonNull NetworkAgentInfo nai) {
+ final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
+ for (Entry<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo> entry :
+ mConnectivityDiagnosticsCallbacks.entrySet()) {
+ final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
+ final NetworkRequestInfo nri = cbInfo.mRequestInfo;
+ if (nai.satisfies(nri.request)) {
+ if (checkConnectivityDiagnosticsPermissions(
+ nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
+ results.add(entry.getKey());
+ }
+ }
+ }
+ return results;
+ }
+
+ @VisibleForTesting
+ boolean checkConnectivityDiagnosticsPermissions(
+ int callbackPid, int callbackUid, NetworkAgentInfo nai, String callbackPackageName) {
+ if (checkNetworkStackPermission(callbackPid, callbackUid)) {
+ return true;
+ }
+
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+ return false;
+ }
+
+ synchronized (mVpns) {
+ if (getVpnIfOwner(callbackUid) != null) {
+ return true;
+ }
+ }
+
+ // Administrator UIDs also contains the Owner UID
+ if (nai.networkCapabilities.getAdministratorUids().contains(callbackUid)) {
+ return true;
+ }
+
+ return false;
+ }
+
@Override
public void registerConnectivityDiagnosticsCallback(
- @NonNull IConnectivityDiagnosticsCallback callback, @NonNull NetworkRequest request) {
+ @NonNull IConnectivityDiagnosticsCallback callback,
+ @NonNull NetworkRequest request,
+ @NonNull String callingPackageName) {
if (request.legacyType != TYPE_NONE) {
throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
+ " Please use NetworkCapabilities instead.");
}
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
// This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
// and administrator uids to be safe.
@@ -7597,7 +7877,7 @@
// callback's binder death.
final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId);
final ConnectivityDiagnosticsCallbackInfo cbInfo =
- new ConnectivityDiagnosticsCallbackInfo(callback, nri);
+ new ConnectivityDiagnosticsCallbackInfo(callback, nri, callingPackageName);
mConnectivityDiagnosticsHandler.sendMessage(
mConnectivityDiagnosticsHandler.obtainMessage(
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 50843b0..0ab8af6a 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -305,11 +305,7 @@
public void onOpChanged(int op, String packageName) {
// onOpChanged invoked on ui thread, move to our thread to reduce risk
// of blocking ui thread
- mHandler.post(() -> {
- synchronized (mLock) {
- onAppOpChangedLocked();
- }
- });
+ mHandler.post(() -> onAppOpChanged(packageName));
}
});
mPackageManager.addOnPermissionsChangeListener(
@@ -392,13 +388,26 @@
}
}
- @GuardedBy("mLock")
- private void onAppOpChangedLocked() {
- for (Receiver receiver : mReceivers.values()) {
- receiver.updateMonitoring(true);
- }
- for (LocationProviderManager manager : mProviderManagers) {
- applyRequirementsLocked(manager);
+ private void onAppOpChanged(String packageName) {
+ synchronized (mLock) {
+ for (Receiver receiver : mReceivers.values()) {
+ if (receiver.mCallerIdentity.mPackageName.equals(packageName)) {
+ receiver.updateMonitoring(true);
+ }
+ }
+
+ HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
+ for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
+ String provider = entry.getKey();
+ for (UpdateRecord record : entry.getValue()) {
+ if (record.mReceiver.mCallerIdentity.mPackageName.equals(packageName)) {
+ affectedProviders.add(provider);
+ }
+ }
+ }
+ for (String provider : affectedProviders) {
+ applyRequirementsLocked(provider);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 4a65a96..fabe92db 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -287,7 +287,7 @@
// When the restriction is enabled, foreground service started from background will not have
// while-in-use permissions like location, camera and microphone. (The foreground service can be
// started, the restriction is on while-in-use permissions.)
- volatile boolean mFlagBackgroundFgsStartRestrictionEnabled;
+ volatile boolean mFlagBackgroundFgsStartRestrictionEnabled = true;
private final ActivityManagerService mService;
private ContentResolver mResolver;
@@ -304,18 +304,19 @@
// we have no limit on the number of service, visible, foreground, or other such
// processes and the number of those processes does not count against the cached
// process limit.
- public int CUR_MAX_CACHED_PROCESSES;
+ public int CUR_MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
// The maximum number of empty app processes we will let sit around.
- public int CUR_MAX_EMPTY_PROCESSES;
+ public int CUR_MAX_EMPTY_PROCESSES = computeEmptyProcessLimit(CUR_MAX_CACHED_PROCESSES);
// The number of empty apps at which we don't consider it necessary to do
// memory trimming.
- public int CUR_TRIM_EMPTY_PROCESSES;
+ public int CUR_TRIM_EMPTY_PROCESSES = computeEmptyProcessLimit(MAX_CACHED_PROCESSES) / 2;
// The number of cached at which we don't consider it necessary to do
// memory trimming.
- public int CUR_TRIM_CACHED_PROCESSES;
+ public int CUR_TRIM_CACHED_PROCESSES =
+ (MAX_CACHED_PROCESSES - computeEmptyProcessLimit(MAX_CACHED_PROCESSES)) / 3;
/**
* Packages that can't be killed even if it's requested to be killed on imperceptible.
@@ -419,6 +420,8 @@
context.getResources().getIntArray(
com.android.internal.R.array.config_defaultImperceptibleKillingExemptionProcStates))
.boxed().collect(Collectors.toList());
+ IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
+ IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.addAll(mDefaultImperceptibleKillExemptProcStates);
}
public void start(ContentResolver resolver) {
@@ -550,8 +553,6 @@
// For new flags that are intended for server-side experiments, please use the new
// DeviceConfig package.
-
- updateMaxCachedProcesses();
}
}
@@ -580,6 +581,9 @@
}
private void updateOomAdjUpdatePolicy() {
+
+
+
OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_OOMADJ_UPDATE_POLICY,
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index b19a37e..f872c6b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -50,7 +50,7 @@
static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
static final boolean DEBUG_BROADCAST_DEFERRAL = DEBUG_BROADCAST || false;
static final boolean DEBUG_COMPACTION = DEBUG_ALL || false;
- static final boolean DEBUG_FREEZER = DEBUG_ALL || false;
+ static final boolean DEBUG_FREEZER = DEBUG_ALL || true;
static final boolean DEBUG_LRU = DEBUG_ALL || false;
static final boolean DEBUG_MU = DEBUG_ALL || false;
static final boolean DEBUG_NETWORK = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 148f7de..12b1cbf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6843,7 +6843,8 @@
cpi = cpr.info;
if (isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
- && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
+ && isValidSingletonCall(r == null ? callingUid : r.uid,
+ cpi.applicationInfo.uid)) {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else {
@@ -6931,7 +6932,8 @@
conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
stable);
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
- if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+ if (cpr.proc != null
+ && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
// If this is a perceptible app accessing the provider,
// make sure to count it as being accessed and thus
// back up on the LRU list. This is good because
@@ -7003,7 +7005,8 @@
// Then allow connecting to the singleton provider
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
- && isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
+ && isValidSingletonCall(r == null ? callingUid : r.uid,
+ cpi.applicationInfo.uid);
if (singleton) {
userId = UserHandle.USER_SYSTEM;
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 313c185..d047a3c 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -442,7 +442,7 @@
*/
@GuardedBy("mPhenotypeFlagLock")
private void updateUseFreezer() {
- if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
mUseFreezer = isFreezerSupported();
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index b107626..a651d9d 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -330,6 +330,7 @@
// If this proc state is changed, need to update its uid record here
if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
&& (uidRec.setProcState != uidRec.getCurProcState()
+ || uidRec.setCapability != uidRec.curCapability
|| uidRec.setWhitelist != uidRec.curWhitelist)) {
ActiveUids uids = mTmpUidRecords;
uids.clear();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 71486d3..dcada89 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3271,6 +3271,9 @@
}
final ProcessRecord getLRURecordForAppLocked(IApplicationThread thread) {
+ if (thread == null) {
+ return null;
+ }
final IBinder threadBinder = thread.asBinder();
// Find the application record.
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 784ce4a..a0589c5 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2126,24 +2126,27 @@
UserHandle user = UserHandle.getUserHandleForUid(uid);
boolean isRevokedCompat;
if (permissionInfo.backgroundPermission != null) {
- boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
+ if (packageManager.checkPermission(permissionInfo.backgroundPermission, packageName)
+ == PackageManager.PERMISSION_GRANTED) {
+ boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
- if (isBackgroundRevokedCompat && supportsRuntimePermissions) {
- Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
- + " permission state, this is discouraged and you should revoke the"
- + " runtime permission instead: uid=" + uid + ", switchCode="
- + switchCode + ", mode=" + mode + ", permission="
- + permissionInfo.backgroundPermission);
- }
+ if (isBackgroundRevokedCompat && supportsRuntimePermissions) {
+ Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
+ + " permission state, this is discouraged and you should revoke the"
+ + " runtime permission instead: uid=" + uid + ", switchCode="
+ + switchCode + ", mode=" + mode + ", permission="
+ + permissionInfo.backgroundPermission);
+ }
- long identity = Binder.clearCallingIdentity();
- try {
- packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
- packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
- isBackgroundRevokedCompat
- ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
+ packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+ isBackgroundRevokedCompat
+ ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b2548af..82a2f01 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3406,13 +3406,11 @@
}
public void binderDied() {
- int oldModeOwnerPid = 0;
+ int oldModeOwnerPid;
int newModeOwnerPid = 0;
synchronized (mDeviceBroker.mSetModeLock) {
Log.w(TAG, "setMode() client died");
- if (!mSetModeDeathHandlers.isEmpty()) {
- oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
- }
+ oldModeOwnerPid = getModeOwnerPid();
int index = mSetModeDeathHandlers.indexOf(this);
if (index < 0) {
Log.w(TAG, "unregistered setMode() client died");
@@ -3446,17 +3444,19 @@
/** @see AudioManager#setMode(int) */
public void setMode(int mode, IBinder cb, String callingPackage) {
- if (DEBUG_MODE) { Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); }
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
+ }
if (!checkAudioSettingsPermission("setMode()")) {
return;
}
-
- if ( (mode == AudioSystem.MODE_IN_CALL) &&
- (mContext.checkCallingOrSelfPermission(
+ final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
- != PackageManager.PERMISSION_GRANTED)) {
+ == PackageManager.PERMISSION_GRANTED;
+ final int callingPid = Binder.getCallingPid();
+ if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ + callingPid + ", uid=" + Binder.getCallingUid());
return;
}
@@ -3470,16 +3470,25 @@
return;
}
- int oldModeOwnerPid = 0;
- int newModeOwnerPid = 0;
+ int oldModeOwnerPid;
+ int newModeOwnerPid;
synchronized (mDeviceBroker.mSetModeLock) {
- if (!mSetModeDeathHandlers.isEmpty()) {
- oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
- }
if (mode == AudioSystem.MODE_CURRENT) {
mode = mMode;
}
- newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);
+ oldModeOwnerPid = getModeOwnerPid();
+ // Do not allow changing mode if a call is active and the requester
+ // does not have permission to modify phone state or is not the mode owner.
+ if (((mMode == AudioSystem.MODE_IN_CALL)
+ || (mMode == AudioSystem.MODE_IN_COMMUNICATION))
+ && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) {
+ Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid
+ + ", uid=" + Binder.getCallingUid()
+ + ", cannot change mode from " + mMode
+ + " without permission or being mode owner");
+ return;
+ }
+ newModeOwnerPid = setModeInt(mode, cb, callingPid, callingPackage);
}
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
// SCO connections not started by the application changing the mode when pid changes
@@ -3569,10 +3578,9 @@
if (status == AudioSystem.AUDIO_STATUS_OK) {
if (actualMode != AudioSystem.MODE_NORMAL) {
- if (mSetModeDeathHandlers.isEmpty()) {
+ newModeOwnerPid = getModeOwnerPid();
+ if (newModeOwnerPid == 0) {
Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
- } else {
- newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
}
}
// Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 89af5b5..cb88c4e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -49,6 +49,7 @@
import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.IpPrefix;
+import android.net.IpSecManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LocalSocket;
@@ -180,7 +181,10 @@
private boolean mIsPackageTargetingAtLeastQ;
private String mInterface;
private Connection mConnection;
- private LegacyVpnRunner mLegacyVpnRunner;
+
+ /** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */
+ private VpnRunner mVpnRunner;
+
private PendingIntent mStatusIntent;
private volatile boolean mEnableTeardown = true;
private final INetworkManagementService mNetd;
@@ -762,7 +766,7 @@
mNetworkCapabilities.setUids(null);
}
- // Revoke the connection or stop LegacyVpnRunner.
+ // Revoke the connection or stop the VpnRunner.
if (mConnection != null) {
try {
mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
@@ -772,9 +776,9 @@
}
mContext.unbindService(mConnection);
mConnection = null;
- } else if (mLegacyVpnRunner != null) {
- mLegacyVpnRunner.exit();
- mLegacyVpnRunner = null;
+ } else if (mVpnRunner != null) {
+ mVpnRunner.exit();
+ mVpnRunner = null;
}
try {
@@ -1510,8 +1514,8 @@
@Override
public void interfaceStatusChanged(String interfaze, boolean up) {
synchronized (Vpn.this) {
- if (!up && mLegacyVpnRunner != null) {
- mLegacyVpnRunner.check(interfaze);
+ if (!up && mVpnRunner != null && mVpnRunner instanceof LegacyVpnRunner) {
+ ((LegacyVpnRunner) mVpnRunner).exitIfOuterInterfaceIs(interfaze);
}
}
}
@@ -1528,9 +1532,10 @@
mContext.unbindService(mConnection);
mConnection = null;
agentDisconnect();
- } else if (mLegacyVpnRunner != null) {
- mLegacyVpnRunner.exit();
- mLegacyVpnRunner = null;
+ } else if (mVpnRunner != null) {
+ // agentDisconnect must be called from mVpnRunner.exit()
+ mVpnRunner.exit();
+ mVpnRunner = null;
}
}
}
@@ -1913,23 +1918,40 @@
private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd,
VpnProfile profile) {
- stopLegacyVpnPrivileged();
+ stopVpnRunnerPrivileged();
// Prepare for the new request.
prepareInternal(VpnConfig.LEGACY_VPN);
updateState(DetailedState.CONNECTING, "startLegacyVpn");
// Start a new LegacyVpnRunner and we are done!
- mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile);
- mLegacyVpnRunner.start();
+ mVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile);
+ mVpnRunner.start();
}
- /** Stop legacy VPN. Permissions must be checked by callers. */
- public synchronized void stopLegacyVpnPrivileged() {
- if (mLegacyVpnRunner != null) {
- mLegacyVpnRunner.exit();
- mLegacyVpnRunner = null;
+ /**
+ * Checks if this the currently running VPN (if any) was started by the Settings app
+ *
+ * <p>This includes both Legacy VPNs and Platform VPNs.
+ */
+ private boolean isSettingsVpnLocked() {
+ return mVpnRunner != null && VpnConfig.LEGACY_VPN.equals(mPackage);
+ }
+ /** Stop VPN runner. Permissions must be checked by callers. */
+ public synchronized void stopVpnRunnerPrivileged() {
+ if (!isSettingsVpnLocked()) {
+ return;
+ }
+
+ final boolean isLegacyVpn = mVpnRunner instanceof LegacyVpnRunner;
+
+ mVpnRunner.exit();
+ mVpnRunner = null;
+
+ // LegacyVpn uses daemons that must be shut down before new ones are brought up.
+ // The same limitation does not apply to Platform VPNs.
+ if (isLegacyVpn) {
synchronized (LegacyVpnRunner.TAG) {
// wait for old thread to completely finish before spinning up
// new instance, otherwise state updates can be out of order.
@@ -1951,7 +1973,7 @@
* Callers are responsible for checking permissions if needed.
*/
private synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
- if (mLegacyVpnRunner == null) return null;
+ if (!isSettingsVpnLocked()) return null;
final LegacyVpnInfo info = new LegacyVpnInfo();
info.key = mConfig.user;
@@ -1962,14 +1984,53 @@
return info;
}
- public VpnConfig getLegacyVpnConfig() {
- if (mLegacyVpnRunner != null) {
+ public synchronized VpnConfig getLegacyVpnConfig() {
+ if (isSettingsVpnLocked()) {
return mConfig;
} else {
return null;
}
}
+ /** This class represents the common interface for all VPN runners. */
+ private abstract class VpnRunner extends Thread {
+
+ protected VpnRunner(String name) {
+ super(name);
+ }
+
+ public abstract void run();
+
+ protected abstract void exit();
+ }
+
+ private class IkeV2VpnRunner extends VpnRunner {
+ private static final String TAG = "IkeV2VpnRunner";
+
+ private final IpSecManager mIpSecManager;
+ private final VpnProfile mProfile;
+
+ IkeV2VpnRunner(VpnProfile profile) {
+ super(TAG);
+ mProfile = profile;
+
+ // TODO: move this to startVpnRunnerPrivileged()
+ mConfig = new VpnConfig();
+ mIpSecManager = mContext.getSystemService(IpSecManager.class);
+ }
+
+ @Override
+ public void run() {
+ // TODO: Build IKE config, start IKE session
+ }
+
+ @Override
+ public void exit() {
+ // TODO: Teardown IKE session & any resources.
+ agentDisconnect();
+ }
+ }
+
/**
* Bringing up a VPN connection takes time, and that is all this thread
* does. Here we have plenty of time. The only thing we need to take
@@ -1977,7 +2038,7 @@
* requests will pile up. This could be done in a Handler as a state
* machine, but it is much easier to read in the current form.
*/
- private class LegacyVpnRunner extends Thread {
+ private class LegacyVpnRunner extends VpnRunner {
private static final String TAG = "LegacyVpnRunner";
private final String[] mDaemons;
@@ -2047,13 +2108,21 @@
mContext.registerReceiver(mBroadcastReceiver, filter);
}
- public void check(String interfaze) {
+ /**
+ * Checks if the parameter matches the underlying interface
+ *
+ * <p>If the underlying interface is torn down, the LegacyVpnRunner also should be. It has
+ * no ability to migrate between interfaces (or Networks).
+ */
+ public void exitIfOuterInterfaceIs(String interfaze) {
if (interfaze.equals(mOuterInterface)) {
Log.i(TAG, "Legacy VPN is going down with " + interfaze);
exit();
}
}
+ /** Tears down this LegacyVpn connection */
+ @Override
public void exit() {
// We assume that everything is reset after stopping the daemons.
interrupt();
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 6ba25a5..838dc84 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -109,24 +109,14 @@
*/
protected final void sendDisplayDeviceEventLocked(
final DisplayDevice device, final int event) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onDisplayDeviceEvent(device, event);
- }
- });
+ mHandler.post(() -> mListener.onDisplayDeviceEvent(device, event));
}
/**
* Sends a request to perform traversals.
*/
protected final void sendTraversalRequestLocked() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onTraversalRequested();
- }
- });
+ mHandler.post(() -> mListener.onTraversalRequested());
}
public static Display.Mode createMode(int width, int height, float refreshRate) {
@@ -135,7 +125,7 @@
}
public interface Listener {
- public void onDisplayDeviceEvent(DisplayDevice device, int event);
- public void onTraversalRequested();
+ void onDisplayDeviceEvent(DisplayDevice device, int event);
+ void onTraversalRequested();
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 704cbff4..fc9542a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -65,8 +65,9 @@
private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
- private final LongSparseArray<LocalDisplayDevice> mDevices =
- new LongSparseArray<LocalDisplayDevice>();
+ private static final int NO_DISPLAY_MODE_ID = 0;
+
+ private final LongSparseArray<LocalDisplayDevice> mDevices = new LongSparseArray<>();
@SuppressWarnings("unused") // Becomes active at instantiation time.
private PhysicalDisplayEventReceiver mPhysicalDisplayEventReceiver;
@@ -133,14 +134,9 @@
hdrCapabilities, isInternal);
mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
- } else {
- boolean changed = device.updateDisplayConfigsLocked(configs, activeConfig,
- configSpecs);
- changed |= device.updateColorModesLocked(colorModes, activeColorMode);
- changed |= device.updateHdrCapabilitiesLocked(hdrCapabilities);
- if (changed) {
- sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
- }
+ } else if (device.updateDisplayProperties(configs, activeConfig,
+ configSpecs, colorModes, activeColorMode, hdrCapabilities)) {
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
} else {
// The display is no longer available. Ignore the attempt to add it.
@@ -215,10 +211,8 @@
mPhysicalDisplayId = physicalDisplayId;
mIsInternal = isInternal;
mDisplayInfo = info;
-
- updateDisplayConfigsLocked(configs, activeConfigId, configSpecs);
- updateColorModesLocked(colorModes, activeColorMode);
- updateHdrCapabilitiesLocked(hdrCapabilities);
+ updateDisplayProperties(configs, activeConfigId, configSpecs, colorModes,
+ activeColorMode, hdrCapabilities);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
if (mIsInternal) {
LightsManager lights = LocalServices.getService(LightsManager.class);
@@ -240,13 +234,25 @@
return true;
}
+ /**
+ * Returns true if there is a change.
+ **/
+ public boolean updateDisplayProperties(SurfaceControl.DisplayConfig[] configs,
+ int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
+ int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) {
+ boolean changed = updateDisplayConfigsLocked(configs, activeConfigId, configSpecs);
+ changed |= updateColorModesLocked(colorModes, activeColorMode);
+ changed |= updateHdrCapabilitiesLocked(hdrCapabilities);
+ return changed;
+ }
+
public boolean updateDisplayConfigsLocked(
SurfaceControl.DisplayConfig[] configs, int activeConfigId,
SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
mDisplayConfigs = Arrays.copyOf(configs, configs.length);
mActiveConfigId = activeConfigId;
// Build an updated list of all existing modes.
- ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
+ ArrayList<DisplayModeRecord> records = new ArrayList<>();
boolean modesAdded = false;
for (int i = 0; i < configs.length; i++) {
SurfaceControl.DisplayConfig config = configs[i];
@@ -286,7 +292,7 @@
// Check whether surface flinger spontaneously changed modes out from under us.
// Schedule traversals to ensure that the correct state is reapplied if necessary.
- if (mActiveModeId != 0
+ if (mActiveModeId != NO_DISPLAY_MODE_ID
&& mActiveModeId != activeRecord.mMode.getModeId()) {
mActiveModeInvalid = true;
sendTraversalRequestLocked();
@@ -294,12 +300,12 @@
// Check whether surface flinger spontaneously changed display config specs out from
// under us. If so, schedule a traversal to reapply our display config specs.
- if (mDisplayModeSpecs.baseModeId != 0) {
+ if (mDisplayModeSpecs.baseModeId != NO_DISPLAY_MODE_ID) {
int activeBaseMode = findMatchingModeIdLocked(configSpecs.defaultConfig);
// If we can't map the defaultConfig index to a mode, then the physical display
// configs must have changed, and the code below for handling changes to the
// list of available modes will take care of updating display config specs.
- if (activeBaseMode != 0) {
+ if (activeBaseMode != NO_DISPLAY_MODE_ID) {
if (mDisplayModeSpecs.baseModeId != activeBaseMode
|| mDisplayModeSpecs.refreshRateRange.min != configSpecs.minRefreshRate
|| mDisplayModeSpecs.refreshRateRange.max
@@ -323,18 +329,23 @@
mSupportedModes.put(record.mMode.getModeId(), record);
}
- // Update the default mode, if needed.
- if (findDisplayConfigIdLocked(mDefaultModeId) < 0) {
- if (mDefaultModeId != 0) {
- Slog.w(TAG, "Default display mode no longer available, using currently"
- + " active mode as default.");
- }
+ // For a new display, we need to initialize the default mode ID.
+ if (mDefaultModeId == NO_DISPLAY_MODE_ID) {
+ mDefaultModeId = activeRecord.mMode.getModeId();
+ } else if (modesAdded && mActiveModeId != activeRecord.mMode.getModeId()) {
+ Slog.d(TAG, "New display modes are added and the active mode has changed, "
+ + "use active mode as default mode.");
+ mActiveModeId = activeRecord.mMode.getModeId();
+ mDefaultModeId = activeRecord.mMode.getModeId();
+ } else if (findDisplayConfigIdLocked(mDefaultModeId) < 0) {
+ Slog.w(TAG, "Default display mode no longer available, using currently"
+ + " active mode as default.");
mDefaultModeId = activeRecord.mMode.getModeId();
}
// Determine whether the display mode specs' base mode is still there.
if (mSupportedModes.indexOfKey(mDisplayModeSpecs.baseModeId) < 0) {
- if (mDisplayModeSpecs.baseModeId != 0) {
+ if (mDisplayModeSpecs.baseModeId != NO_DISPLAY_MODE_ID) {
Slog.w(TAG,
"DisplayModeSpecs base mode no longer available, using currently"
+ " active mode.");
@@ -345,7 +356,7 @@
// Determine whether the active mode is still there.
if (mSupportedModes.indexOfKey(mActiveModeId) < 0) {
- if (mActiveModeId != 0) {
+ if (mActiveModeId != NO_DISPLAY_MODE_ID) {
Slog.w(TAG, "Active display mode no longer available, reverting to default"
+ " mode.");
}
@@ -797,7 +808,7 @@
}
mActiveConfigId = activeConfigId;
mActiveModeId = findMatchingModeIdLocked(activeConfigId);
- mActiveModeInvalid = mActiveModeId == 0;
+ mActiveModeInvalid = mActiveModeId == NO_DISPLAY_MODE_ID;
if (mActiveModeInvalid) {
Slog.w(TAG, "In unknown mode after setting allowed configs"
+ ", activeConfigId=" + mActiveConfigId);
@@ -922,7 +933,7 @@
return record.mMode.getModeId();
}
}
- return 0;
+ return NO_DISPLAY_MODE_ID;
}
private void updateDeviceInfoLocked() {
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
index 5161a77..b0e2e64 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
@@ -113,11 +113,27 @@
return ERROR_COMMAND_EXECUTION;
}
- final Map<String, ParcelFileDescriptor> dataLoaderDynamicArgs = getDataLoaderDynamicArgs();
- if (dataLoaderDynamicArgs == null) {
+ final Map<String, ParcelFileDescriptor> fds = getShellFileDescriptors();
+ if (fds == null) {
pw.println("File names and sizes don't match.");
return ERROR_DATA_LOADER_INIT;
}
+ // dup FDs before closing them
+ final Map<String, ParcelFileDescriptor> dataLoaderDynamicArgs = new HashMap<>();
+ for (Map.Entry<String, ParcelFileDescriptor> nfd : fds.entrySet()) {
+ try {
+ dataLoaderDynamicArgs.put(nfd.getKey(), nfd.getValue().dup());
+ } catch (IOException ignored) {
+ pw.println("Failed to dup shell file descriptor");
+ return ERROR_DATA_LOADER_INIT;
+ } finally {
+ try {
+ nfd.getValue().close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
final DataLoaderParams params = DataLoaderParams.forIncremental(
new ComponentName(LOADER_PACKAGE_NAME, LOADER_CLASS_NAME), "",
dataLoaderDynamicArgs);
@@ -131,17 +147,9 @@
try {
int sessionId = packageInstaller.createSession(sessionParams);
pw.println("Successfully opened session: sessionId = " + sessionId);
- } catch (Exception ex) {
+ } catch (IOException ex) {
pw.println("Failed to create session.");
return ERROR_COMMAND_EXECUTION;
- } finally {
- try {
- for (Map.Entry<String, ParcelFileDescriptor> nfd
- : dataLoaderDynamicArgs.entrySet()) {
- nfd.getValue().close();
- }
- } catch (IOException ignored) {
- }
}
return 0;
}
@@ -177,7 +185,8 @@
InstallationFile file = installationFiles.get(i);
final int location = file.getFileType() == FILE_TYPE_OBB ? LOCATION_MEDIA_OBB
: LOCATION_DATA_APP;
- session.addFile(location, file.getName(), file.getSize(), file.getMetadata(), null);
+ session.addFile(location, file.getName(), file.getSize(), file.getMetadata(),
+ null);
}
session.commit(localReceiver.getIntentSender());
final Intent result = localReceiver.getResult();
@@ -212,7 +221,8 @@
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
- public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ public void send(int code, Intent intent, String resolvedType,
+ IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission,
Bundle options) {
try {
@@ -237,14 +247,14 @@
}
/** Helpers. */
- private Map<String, ParcelFileDescriptor> getDataLoaderDynamicArgs() {
- Map<String, ParcelFileDescriptor> dataLoaderDynamicArgs = new HashMap<>();
+ private Map<String, ParcelFileDescriptor> getShellFileDescriptors() {
+ Map<String, ParcelFileDescriptor> fds = new HashMap<>();
final FileDescriptor outFd = getOutFileDescriptor();
final FileDescriptor inFd = getInFileDescriptor();
try {
- dataLoaderDynamicArgs.put("inFd", ParcelFileDescriptor.dup(inFd));
- dataLoaderDynamicArgs.put("outFd", ParcelFileDescriptor.dup(outFd));
- return dataLoaderDynamicArgs;
+ fds.put("inFd", ParcelFileDescriptor.dup(inFd));
+ fds.put("outFd", ParcelFileDescriptor.dup(outFd));
+ return fds;
} catch (Exception ex) {
Slog.e(TAG, "Failed to dup FDs");
return null;
@@ -292,7 +302,8 @@
pw.println("Invalid file index in: " + fileArgs);
return null;
}
- final byte[] metadata = String.valueOf(index).getBytes(StandardCharsets.UTF_8);
+ final byte[] metadata = String.valueOf(index).getBytes(
+ StandardCharsets.UTF_8);
fileList.add(new InstallationFile(name, size, metadata));
break;
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 90358ca..a8f706c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -223,7 +223,7 @@
int displayId, InputApplicationHandle application);
private static native void nativeSetFocusedDisplay(long ptr, int displayId);
private static native boolean nativeTransferTouchFocus(long ptr,
- InputChannel fromChannel, InputChannel toChannel);
+ IBinder fromChannelToken, IBinder toChannelToken);
private static native void nativeSetPointerSpeed(long ptr, int speed);
private static native void nativeSetShowTouches(long ptr, boolean enabled);
private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -1554,14 +1554,29 @@
* @return True if the transfer was successful. False if the window with the
* specified channel did not actually have touch focus at the time of the request.
*/
- public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
- if (fromChannel == null) {
- throw new IllegalArgumentException("fromChannel must not be null.");
- }
- if (toChannel == null) {
- throw new IllegalArgumentException("toChannel must not be null.");
- }
- return nativeTransferTouchFocus(mPtr, fromChannel, toChannel);
+ public boolean transferTouchFocus(@NonNull InputChannel fromChannel,
+ @NonNull InputChannel toChannel) {
+ return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken());
+ }
+
+ /**
+ * Atomically transfers touch focus from one window to another as identified by
+ * their input channels. It is possible for multiple windows to have
+ * touch focus if they support split touch dispatch
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
+ * method only transfers touch focus of the specified window without affecting
+ * other windows that may also have touch focus at the same time.
+ * @param fromChannelToken The channel token of a window that currently has touch focus.
+ * @param toChannelToken The channel token of the window that should receive touch focus in
+ * place of the first.
+ * @return True if the transfer was successful. False if the window with the
+ * specified channel did not actually have touch focus at the time of the request.
+ */
+ public boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
+ @NonNull IBinder toChannelToken) {
+ Objects.nonNull(fromChannelToken);
+ Objects.nonNull(toChannelToken);
+ return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 7b17b4e..8358884 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -63,7 +63,6 @@
public abstract void sendControlRequest(String routeId, Intent request);
public abstract void requestSetVolume(String routeId, int volume);
- public abstract void requestUpdateVolume(String routeId, int delta);
@NonNull
public String getUniqueId() {
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index e22ea46..d08fb71 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -138,14 +138,6 @@
}
}
- @Override
- public void requestUpdateVolume(String routeId, int delta) {
- if (mConnectionReady) {
- mActiveConnection.requestUpdateVolume(routeId, delta);
- updateBinding();
- }
- }
-
public boolean hasComponentName(String packageName, String className) {
return mComponentName.getPackageName().equals(packageName)
&& mComponentName.getClassName().equals(className);
@@ -518,14 +510,6 @@
}
}
- public void requestUpdateVolume(String routeId, int delta) {
- try {
- mProvider.requestUpdateVolume(routeId, delta);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Failed to deliver request to request update volume.", ex);
- }
- }
-
@Override
public void binderDied() {
mHandler.post(() -> onConnectionDied(Connection.this));
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 358cc29..b113322 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -334,20 +334,6 @@
}
}
- public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) {
- Objects.requireNonNull(client, "client must not be null");
- Objects.requireNonNull(route, "route must not be null");
-
- final long token = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- requestUpdateVolumeLocked(client, route, delta);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
public void requestCreateClientSession(IMediaRouter2Manager manager, String packageName,
MediaRoute2Info route, int requestId) {
final long token = Binder.clearCallingIdentity();
@@ -375,21 +361,6 @@
}
}
- public void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
- MediaRoute2Info route, int delta) {
- Objects.requireNonNull(manager, "manager must not be null");
- Objects.requireNonNull(route, "route must not be null");
-
- final long token = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- requestUpdateVolumeLocked(manager, route, delta);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
@NonNull
public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
final long token = Binder.clearCallingIdentity();
@@ -628,18 +599,6 @@
}
}
- private void requestUpdateVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
- int delta) {
- final IBinder binder = client.asBinder();
- Client2Record clientRecord = mAllClientRecords.get(binder);
-
- if (clientRecord != null) {
- clientRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::requestUpdateVolume,
- clientRecord.mUserRecord.mHandler, route, delta));
- }
- }
-
private void registerManagerLocked(IMediaRouter2Manager manager,
int uid, int pid, String packageName, int userId, boolean trusted) {
final IBinder binder = manager.asBinder();
@@ -713,18 +672,6 @@
}
}
- private void requestUpdateVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
- int delta) {
- final IBinder binder = manager.asBinder();
- ManagerRecord managerRecord = mAllManagerRecords.get(binder);
-
- if (managerRecord != null) {
- managerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::requestUpdateVolume,
- managerRecord.mUserRecord.mHandler, route, delta));
- }
- }
-
private List<RoutingSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -1459,13 +1406,6 @@
}
}
- private void requestUpdateVolume(MediaRoute2Info route, int delta) {
- final MediaRoute2Provider provider = findProvider(route.getProviderId());
- if (provider != null) {
- provider.requestUpdateVolume(route.getOriginalId(), delta);
- }
- }
-
private List<IMediaRouter2Client> getClients() {
final List<IMediaRouter2Client> clients = new ArrayList<>();
MediaRouter2ServiceImpl service = mServiceRef.get();
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index e1d3803..57f0328 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -539,12 +539,6 @@
// Binder call
@Override
- public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) {
- mService2.requestUpdateVolume2(client, route, delta);
- }
-
- // Binder call
- @Override
public void requestSetVolume2Manager(IMediaRouter2Manager manager,
MediaRoute2Info route, int volume) {
mService2.requestSetVolume2Manager(manager, route, volume);
@@ -552,13 +546,6 @@
// Binder call
@Override
- public void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
- MediaRoute2Info route, int delta) {
- mService2.requestUpdateVolume2Manager(manager, route, delta);
- }
-
- // Binder call
- @Override
public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
return mService2.getActiveSessions(manager);
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 798a781..888f7ce 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -153,11 +153,6 @@
public void requestSetVolume(String routeId, int volume) {
}
- //TODO: implement method
- @Override
- public void requestUpdateVolume(String routeId, int delta) {
- }
-
private void initializeDefaultRoute() {
mDefaultRoute = new MediaRoute2Info.Builder(
DEFAULT_ROUTE_ID,
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index ef8f647..3cafaff 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -138,7 +138,7 @@
if (egressDisconnected || egressChanged) {
mAcceptedEgressIface = null;
- mVpn.stopLegacyVpnPrivileged();
+ mVpn.stopVpnRunnerPrivileged();
}
if (egressDisconnected) {
hideNotification();
@@ -218,7 +218,7 @@
mAcceptedEgressIface = null;
mErrorCount = 0;
- mVpn.stopLegacyVpnPrivileged();
+ mVpn.stopVpnRunnerPrivileged();
mVpn.setLockdown(false);
hideNotification();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f6276fb..b52289e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5387,8 +5387,8 @@
try {
fixNotification(notification, pkg, tag, id, userId);
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "Cannot create a context for sending app", e);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot fix notification", e);
return;
}
@@ -7594,7 +7594,7 @@
for (int i = 0; i < newUris.size(); i++) {
final Uri uri = newUris.valueAt(i);
if (oldUris == null || !oldUris.contains(uri)) {
- if (DBG) Slog.d(TAG, key + ": granting " + uri);
+ Slog.d(TAG, key + ": granting " + uri);
grantUriPermission(permissionOwner, uri, newRecord.getUid(), targetPkg,
targetUserId);
}
@@ -7631,6 +7631,8 @@
targetUserId);
} catch (RemoteException ignored) {
// Ignored because we're in same process
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Cannot grant uri access; " + sourceUid + " does not own " + uri);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9010,59 +9012,64 @@
@GuardedBy("mNotificationLock")
private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
boolean notifyAllListeners) {
- // Lazily initialized snapshots of the notification.
- StatusBarNotification sbn = r.getSbn();
- StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
- TrimCache trimCache = new TrimCache(sbn);
+ try {
+ // Lazily initialized snapshots of the notification.
+ StatusBarNotification sbn = r.getSbn();
+ StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
+ TrimCache trimCache = new TrimCache(sbn);
- for (final ManagedServiceInfo info : getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, info);
- boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
- // This notification hasn't been and still isn't visible -> ignore.
- if (!oldSbnVisible && !sbnVisible) {
- continue;
- }
- // If the notification is hidden, don't notifyPosted listeners targeting < P.
- // Instead, those listeners will receive notifyPosted when the notification is
- // unhidden.
- if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
- continue;
- }
+ for (final ManagedServiceInfo info : getServices()) {
+ boolean sbnVisible = isVisibleToListener(sbn, info);
+ boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info)
+ : false;
+ // This notification hasn't been and still isn't visible -> ignore.
+ if (!oldSbnVisible && !sbnVisible) {
+ continue;
+ }
+ // If the notification is hidden, don't notifyPosted listeners targeting < P.
+ // Instead, those listeners will receive notifyPosted when the notification is
+ // unhidden.
+ if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
+ continue;
+ }
- // If we shouldn't notify all listeners, this means the hidden state of
- // a notification was changed. Don't notifyPosted listeners targeting >= P.
- // Instead, those listeners will receive notifyRankingUpdate.
- if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
- continue;
- }
+ // If we shouldn't notify all listeners, this means the hidden state of
+ // a notification was changed. Don't notifyPosted listeners targeting >= P.
+ // Instead, those listeners will receive notifyRankingUpdate.
+ if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
+ continue;
+ }
- final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
+ final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
- // This notification became invisible -> remove the old one.
- if (oldSbnVisible && !sbnVisible) {
- final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
+ // This notification became invisible -> remove the old one.
+ if (oldSbnVisible && !sbnVisible) {
+ final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyRemoved(
+ info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
+ }
+ });
+ continue;
+ }
+
+ // Grant access before listener is notified
+ final int targetUserId = (info.userid == UserHandle.USER_ALL)
+ ? UserHandle.USER_SYSTEM : info.userid;
+ updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
+
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemoved(
- info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
+ notifyPosted(info, sbnToPost, update);
}
});
- continue;
}
-
- // Grant access before listener is notified
- final int targetUserId = (info.userid == UserHandle.USER_ALL)
- ? UserHandle.USER_SYSTEM : info.userid;
- updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
-
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- notifyPosted(info, sbnToPost, update);
- }
- });
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not notify listeners for " + r.getKey(), e);
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 7bb782b..3e64e98 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -29,12 +29,14 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ILauncherApps;
import android.content.pm.IOnAppsChangedListener;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageManager;
+import android.content.pm.IShortcutChangeCallback;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
@@ -661,8 +663,8 @@
@Override
public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
- String packageName, List shortcutIds, ComponentName componentName, int flags,
- UserHandle targetUser) {
+ String packageName, List shortcutIds, List<LocusId> locusIds,
+ ComponentName componentName, int flags, UserHandle targetUser) {
ensureShortcutPermission(callingPackage);
if (!canAccessProfile(targetUser.getIdentifier(), "Cannot get shortcuts")) {
return new ParceledListSlice<>(Collections.EMPTY_LIST);
@@ -671,16 +673,31 @@
throw new IllegalArgumentException(
"To query by shortcut ID, package name must also be set");
}
+ if (locusIds != null && packageName == null) {
+ throw new IllegalArgumentException(
+ "To query by locus ID, package name must also be set");
+ }
// TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below.
return new ParceledListSlice<>((List<ShortcutInfo>)
mShortcutServiceInternal.getShortcuts(getCallingUserId(),
- callingPackage, changedSince, packageName, shortcutIds,
+ callingPackage, changedSince, packageName, shortcutIds, locusIds,
componentName, flags, targetUser.getIdentifier(),
injectBinderCallingPid(), injectBinderCallingUid()));
}
@Override
+ public void registerShortcutChangeCallback(String callingPackage, long changedSince,
+ String packageName, List shortcutIds, List<LocusId> locusIds,
+ ComponentName componentName, int flags, IShortcutChangeCallback callback,
+ int callbackId) {
+ }
+
+ @Override
+ public void unregisterShortcutChangeCallback(String callingPackage, int callbackId) {
+ }
+
+ @Override
public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
UserHandle targetUser) {
ensureShortcutPermission(callingPackage);
@@ -1137,7 +1154,7 @@
mShortcutServiceInternal.getShortcuts(launcherUserId,
cookie.packageName,
/* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
- /* component= */ null,
+ /* locusIds=*/ null, /* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
| ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED
, userId, cookie.callingPid, cookie.callingUid);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 79cf23f..c14b42d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1537,15 +1537,16 @@
final @NonNull String mRequiredPermissionControllerPackage;
final @Nullable String mSetupWizardPackage;
final @Nullable String mStorageManagerPackage;
- final @Nullable String mSystemTextClassifierPackage;
+ final @Nullable String mDefaultTextClassifierPackage;
+ final @Nullable String mSystemTextClassifierPackageName;
final @Nullable String mWellbeingPackage;
final @Nullable String mDocumenterPackage;
final @Nullable String mConfiguratorPackage;
final @Nullable String mAppPredictionServicePackage;
final @Nullable String mIncidentReportApproverPackage;
final @Nullable String[] mTelephonyPackages;
- final @NonNull String mServicesExtensionPackageName;
- final @NonNull String mSharedSystemSharedLibraryPackageName;
+ final @Nullable String mServicesExtensionPackageName;
+ final @Nullable String mSharedSystemSharedLibraryPackageName;
final @Nullable String mRetailDemoPackage;
private final PackageUsage mPackageUsage = new PackageUsage();
@@ -3155,8 +3156,8 @@
mSetupWizardPackage = getSetupWizardPackageNameImpl();
mComponentResolver.fixProtectedFilterPriorities();
- mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
-
+ mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
+ mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
mWellbeingPackage = getWellbeingPackageName();
mDocumenterPackage = getDocumenterPackageName();
mConfiguratorPackage = getDeviceConfiguratorPackageName();
@@ -3351,6 +3352,7 @@
mServicesExtensionPackageName = null;
mSharedSystemSharedLibraryPackageName = null;
}
+
// PermissionController hosts default permission granting and role management, so it's a
// critical part of the core system.
mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
@@ -19626,15 +19628,15 @@
}
@Override
- public String getSystemTextClassifierPackageName() {
- return ensureSystemPackageName(mContext.getString(
- R.string.config_defaultTextClassifierPackage));
+ public String getDefaultTextClassifierPackageName() {
+ return ensureSystemPackageName(
+ mContext.getString(R.string.config_servicesExtensionPackage));
}
@Override
- public String[] getSystemTextClassifierPackages() {
- return ensureSystemPackageNames(mContext.getResources().getStringArray(
- R.array.config_defaultTextClassifierPackages));
+ public String getSystemTextClassifierPackageName() {
+ return ensureSystemPackageName(
+ mContext.getString(R.string.config_defaultTextClassifierPackage));
}
@Override
@@ -22755,6 +22757,11 @@
private class PackageManagerNative extends IPackageManagerNative.Stub {
@Override
+ public String[] getAllPackages() {
+ return PackageManagerService.this.getAllPackages().toArray(new String[0]);
+ }
+
+ @Override
public String[] getNamesForUids(int[] uids) throws RemoteException {
final String[] results = PackageManagerService.this.getNamesForUids(uids);
// massage results so they can be parsed by the native binder
@@ -23063,7 +23070,7 @@
}
private String[] getKnownPackageNamesInternal(int knownPackage, int userId) {
- switch(knownPackage) {
+ switch (knownPackage) {
case PackageManagerInternal.PACKAGE_BROWSER:
return new String[]{mPermissionManager.getDefaultBrowser(userId)};
case PackageManagerInternal.PACKAGE_INSTALLER:
@@ -23075,7 +23082,8 @@
case PackageManagerInternal.PACKAGE_VERIFIER:
return filterOnlySystemPackages(mRequiredVerifierPackage);
case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
- return filterOnlySystemPackages(mSystemTextClassifierPackage);
+ return filterOnlySystemPackages(
+ mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
return filterOnlySystemPackages(mRequiredPermissionControllerPackage);
case PackageManagerInternal.PACKAGE_WELLBEING:
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index f7889ea..d16c074 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -33,6 +33,7 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
+import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -1985,7 +1986,6 @@
// Verify if caller is the shortcut owner, only if caller doesn't have ACCESS_SHORTCUTS.
verifyShortcutInfoPackage(callingPackage, shortcut);
}
- final String shortcutPackage = shortcut.getPackage();
final boolean ret;
synchronized (mLock) {
@@ -1999,6 +1999,7 @@
// someone already), then we just replace the existing one with this new one,
// and then proceed the rest of the process.
if (shortcut != null) {
+ final String shortcutPackage = shortcut.getPackage();
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(
shortcutPackage, userId);
final String id = shortcut.getId();
@@ -2155,48 +2156,6 @@
}
@Override
- public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
- @UserIdInt int userId) {
- verifyCaller(packageName, userId);
-
- synchronized (mLock) {
- throwIfUserLockedL(userId);
-
- return getShortcutsWithQueryLocked(
- packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
- ShortcutInfo::isDynamicVisible);
- }
- }
-
- @Override
- public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
- @UserIdInt int userId) {
- verifyCaller(packageName, userId);
-
- synchronized (mLock) {
- throwIfUserLockedL(userId);
-
- return getShortcutsWithQueryLocked(
- packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
- ShortcutInfo::isManifestVisible);
- }
- }
-
- @Override
- public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
- @UserIdInt int userId) {
- verifyCaller(packageName, userId);
-
- synchronized (mLock) {
- throwIfUserLockedL(userId);
-
- return getShortcutsWithQueryLocked(
- packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
- ShortcutInfo::isPinnedVisible);
- }
- }
-
- @Override
public ParceledListSlice<ShortcutInfo> getShortcuts(String packageName,
@ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId) {
verifyCaller(packageName, userId);
@@ -2631,7 +2590,7 @@
public List<ShortcutInfo> getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable List<String> shortcutIds,
- @Nullable ComponentName componentName,
+ @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
int queryFlags, int userId, int callingPid, int callingUid) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
@@ -2652,15 +2611,16 @@
if (packageName != null) {
getShortcutsInnerLocked(launcherUserId,
- callingPackage, packageName, shortcutIds, changedSince,
+ callingPackage, packageName, shortcutIds, locusIds, changedSince,
componentName, queryFlags, userId, ret, cloneFlag,
callingPid, callingUid);
} else {
final List<String> shortcutIdsF = shortcutIds;
+ final List<LocusId> locusIdsF = locusIds;
getUserShortcutsLocked(userId).forAllPackages(p -> {
getShortcutsInnerLocked(launcherUserId,
- callingPackage, p.getPackageName(), shortcutIdsF, changedSince,
- componentName, queryFlags, userId, ret, cloneFlag,
+ callingPackage, p.getPackageName(), shortcutIdsF, locusIdsF,
+ changedSince, componentName, queryFlags, userId, ret, cloneFlag,
callingPid, callingUid);
});
}
@@ -2670,12 +2630,15 @@
@GuardedBy("ShortcutService.this.mLock")
private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
- @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
+ @Nullable String packageName, @Nullable List<String> shortcutIds,
+ @Nullable List<LocusId> locusIds, long changedSince,
@Nullable ComponentName componentName, int queryFlags,
int userId, ArrayList<ShortcutInfo> ret, int cloneFlag,
int callingPid, int callingUid) {
final ArraySet<String> ids = shortcutIds == null ? null
: new ArraySet<>(shortcutIds);
+ final ArraySet<LocusId> locIds = locusIds == null ? null
+ : new ArraySet<>(locusIds);
final ShortcutUser user = getUserShortcutsLocked(userId);
final ShortcutPackage p = user.getPackageShortcutsIfExists(packageName);
@@ -2702,6 +2665,9 @@
if (ids != null && !ids.contains(si.getId())) {
return false;
}
+ if (locIds != null && !locIds.contains(si.getLocusId())) {
+ return false;
+ }
if (componentName != null) {
if (si.getActivity() != null
&& !si.getActivity().equals(componentName)) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 46893b2..e6eaf21 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -720,12 +720,9 @@
userId, STORAGE_PERMISSIONS);
// TextClassifier Service
- final String[] packages = mContext.getPackageManager().getSystemTextClassifierPackages();
- if (packages.length > 0) {
- // We have a list of supported system TextClassifier package names, the first one
- // package is the default system TextClassifier service. Grant permissions to default
- // TextClassifier Service.
- grantPermissionsToSystemPackage(packages[0], userId,
+ for (String textClassifierPackage :
+ getKnownPackages(PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER, userId)) {
+ grantPermissionsToSystemPackage(textClassifierPackage, userId,
COARSE_BACKGROUND_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 3dee853..34d2c16 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -36,7 +36,7 @@
import android.service.textclassifier.TextClassifierService;
import android.service.textclassifier.TextClassifierService.ConnectionState;
import android.text.TextUtils;
-import android.util.ArrayMap;
+import android.util.LruCache;
import android.util.Slog;
import android.util.SparseArray;
import android.view.textclassifier.ConversationActions;
@@ -63,7 +63,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayDeque;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.Queue;
@@ -132,9 +133,7 @@
synchronized (mManagerService.mLock) {
UserState userState = mManagerService.peekUserStateLocked(userId);
if (userState != null) {
- if (userState.mConnection != null) {
- userState.mConnection.cleanupService();
- }
+ userState.cleanupServiceLocked();
mManagerService.mUserStates.remove(userId);
}
}
@@ -147,22 +146,31 @@
private final Object mLock;
@GuardedBy("mLock")
final SparseArray<UserState> mUserStates = new SparseArray<>();
+ // SystemTextClassifier.onDestroy() is not guaranteed to be called, use LruCache here
+ // to avoid leak.
@GuardedBy("mLock")
- private final Map<TextClassificationSessionId, Integer> mSessionUserIds = new ArrayMap<>();
- @GuardedBy("mLock")
- private TextClassificationConstants mSettings;
+ private final LruCache<TextClassificationSessionId, TextClassificationContext>
+ mSessionContextCache = new LruCache<>(40);
+ private final TextClassificationConstants mSettings;
+ @Nullable
+ private final String mDefaultTextClassifierPackage;
+ @Nullable
+ private final String mSystemTextClassifierPackage;
private TextClassificationManagerService(Context context) {
mContext = Objects.requireNonNull(context);
mLock = new Object();
+ mSettings = new TextClassificationConstants();
mSettingsListener = new TextClassifierSettingsListener(mContext);
+ PackageManager packageManager = mContext.getPackageManager();
+ mDefaultTextClassifierPackage = packageManager.getDefaultTextClassifierPackageName();
+ mSystemTextClassifierPackage = packageManager.getSystemTextClassifierPackageName();
}
private void startListenSettings() {
mSettingsListener.registerObserver();
}
-
@Override
public void onConnectedStateChanged(@ConnectionState int connected) {
}
@@ -178,6 +186,7 @@
request.getUserId(),
request.getCallingPackageName(),
/* attemptToBind= */ true,
+ request.getUseDefaultTextClassifier(),
service -> service.onSuggestSelection(sessionId, request, callback),
"onSuggestSelection",
callback);
@@ -194,6 +203,7 @@
request.getUserId(),
request.getCallingPackageName(),
/* attemptToBind= */ true,
+ request.getUseDefaultTextClassifier(),
service -> service.onClassifyText(sessionId, request, callback),
"onClassifyText",
callback);
@@ -210,6 +220,7 @@
request.getUserId(),
request.getCallingPackageName(),
/* attemptToBind= */ true,
+ request.getUseDefaultTextClassifier(),
service -> service.onGenerateLinks(sessionId, request, callback),
"onGenerateLinks",
callback);
@@ -223,28 +234,32 @@
handleRequest(
event.getUserId(),
- event.getPackageName(),
+ /* callingPackageName= */ null,
/* attemptToBind= */ false,
+ event.getUseDefaultTextClassifier(),
service -> service.onSelectionEvent(sessionId, event),
"onSelectionEvent",
NO_OP_CALLBACK);
}
+
@Override
public void onTextClassifierEvent(
@Nullable TextClassificationSessionId sessionId,
TextClassifierEvent event) throws RemoteException {
Objects.requireNonNull(event);
- final String packageName = event.getEventContext() == null
- ? null
- : event.getEventContext().getPackageName();
final int userId = event.getEventContext() == null
? UserHandle.getCallingUserId()
: event.getEventContext().getUserId();
+ final boolean useDefaultTextClassifier =
+ event.getEventContext() != null
+ ? event.getEventContext().getUseDefaultTextClassifier()
+ : true;
handleRequest(
userId,
- packageName,
+ /* callingPackageName= */ null,
/* attemptToBind= */ false,
+ useDefaultTextClassifier,
service -> service.onTextClassifierEvent(sessionId, event),
"onTextClassifierEvent",
NO_OP_CALLBACK);
@@ -261,6 +276,7 @@
request.getUserId(),
request.getCallingPackageName(),
/* attemptToBind= */ true,
+ request.getUseDefaultTextClassifier(),
service -> service.onDetectLanguage(sessionId, request, callback),
"onDetectLanguage",
callback);
@@ -277,6 +293,7 @@
request.getUserId(),
request.getCallingPackageName(),
/* attemptToBind= */ true,
+ request.getUseDefaultTextClassifier(),
service -> service.onSuggestConversationActions(sessionId, request, callback),
"onSuggestConversationActions",
callback);
@@ -294,9 +311,10 @@
userId,
classificationContext.getPackageName(),
/* attemptToBind= */ false,
+ classificationContext.getUseDefaultTextClassifier(),
service -> {
service.onCreateTextClassificationSession(classificationContext, sessionId);
- mSessionUserIds.put(sessionId, userId);
+ mSessionContextCache.put(sessionId, classificationContext);
},
"onCreateTextClassificationSession",
NO_OP_CALLBACK);
@@ -308,16 +326,23 @@
Objects.requireNonNull(sessionId);
synchronized (mLock) {
- final int userId = mSessionUserIds.containsKey(sessionId)
- ? mSessionUserIds.get(sessionId)
+ TextClassificationContext textClassificationContext =
+ mSessionContextCache.get(sessionId);
+ final int userId = textClassificationContext != null
+ ? textClassificationContext.getUserId()
: UserHandle.getCallingUserId();
+ final boolean useDefaultTextClassifier =
+ textClassificationContext != null
+ ? textClassificationContext.getUseDefaultTextClassifier()
+ : true;
handleRequest(
userId,
/* callingPackageName= */ null,
/* attemptToBind= */ false,
+ useDefaultTextClassifier,
service -> {
service.onDestroyTextClassificationSession(sessionId);
- mSessionUserIds.remove(sessionId);
+ mSessionContextCache.remove(sessionId);
},
"onDestroyTextClassificationSession",
NO_OP_CALLBACK);
@@ -328,7 +353,7 @@
private UserState getUserStateLocked(int userId) {
UserState result = mUserStates.get(userId);
if (result == null) {
- result = new UserState(userId, mContext, mLock);
+ result = new UserState(userId);
mUserStates.put(userId, result);
}
return result;
@@ -339,6 +364,19 @@
return mUserStates.get(userId);
}
+ private int resolvePackageToUid(@Nullable String packageName, @UserIdInt int userId) {
+ if (packageName == null) {
+ return Process.INVALID_UID;
+ }
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ return pm.getPackageUidAsUser(packageName, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(LOG_TAG, "Could not get the UID for " + packageName);
+ }
+ return Process.INVALID_UID;
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
@@ -353,18 +391,25 @@
pw.printPair("context", mContext);
pw.println();
+ pw.printPair("defaultTextClassifierPackage", mDefaultTextClassifierPackage);
+ pw.println();
+ pw.printPair("systemTextClassifierPackage", mSystemTextClassifierPackage);
+ pw.println();
synchronized (mLock) {
int size = mUserStates.size();
- pw.print("Number user states: "); pw.println(size);
+ pw.print("Number user states: ");
+ pw.println(size);
if (size > 0) {
for (int i = 0; i < size; i++) {
pw.increaseIndent();
UserState userState = mUserStates.valueAt(i);
- pw.print(i); pw.print(":"); userState.dump(pw); pw.println();
+ pw.printPair("User", mUserStates.keyAt(i));
+ pw.println();
+ userState.dump(pw);
pw.decreaseIndent();
}
}
- pw.println("Number of active sessions: " + mSessionUserIds.size());
+ pw.println("Number of active sessions: " + mSessionContextCache.size());
}
}
@@ -372,57 +417,55 @@
@UserIdInt int userId,
@Nullable String callingPackageName,
boolean attemptToBind,
+ boolean useDefaultTextClassifier,
@NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
@NonNull String methodName,
- @NonNull ITextClassifierCallback callback)
- throws RemoteException {
+ @NonNull ITextClassifierCallback callback) throws RemoteException {
Objects.requireNonNull(textClassifierServiceConsumer);
Objects.requireNonNull(methodName);
Objects.requireNonNull(callback);
- validateInput(mContext, callingPackageName, userId);
+ try {
+ validateCallingPackage(callingPackageName);
+ validateUser(userId);
+ } catch (Exception e) {
+ throw new RemoteException("Invalid request: " + e.getMessage(), e,
+ /* enableSuppression */ true, /* writableStackTrace */ true);
+ }
synchronized (mLock) {
UserState userState = getUserStateLocked(userId);
- if (attemptToBind && !userState.bindLocked()) {
+ ServiceState serviceState =
+ userState.getServiceStateLocked(useDefaultTextClassifier);
+ if (serviceState == null) {
+ Slog.d(LOG_TAG, "No configured system TextClassifierService");
+ callback.onFailure();
+ } else if (attemptToBind && !serviceState.bindLocked()) {
Slog.d(LOG_TAG, "Unable to bind TextClassifierService at " + methodName);
callback.onFailure();
- } else if (userState.isBoundLocked()) {
- if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) {
+ } else if (serviceState.isBoundLocked()) {
+ if (!serviceState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) {
return;
}
- textClassifierServiceConsumer.accept(userState.mService);
+ textClassifierServiceConsumer.accept(serviceState.mService);
} else {
- userState.mPendingRequests.add(
+ serviceState.mPendingRequests.add(
new PendingRequest(
methodName,
- () -> textClassifierServiceConsumer.accept(userState.mService),
+ () -> textClassifierServiceConsumer.accept(serviceState.mService),
callback::onFailure, callback.asBinder(),
this,
- userState,
+ serviceState,
Binder.getCallingUid()));
}
}
}
- private void unbindServiceIfNecessary() {
- final ComponentName serviceComponentName =
- TextClassifierService.getServiceComponentName(mContext);
- if (serviceComponentName == null) {
- // It should not occur if we had defined default service names in config.xml
- Slog.w(LOG_TAG, "No default configured system TextClassifierService.");
- return;
- }
+ private void onTextClassifierServicePackageOverrideChanged(String overriddenPackage) {
synchronized (mLock) {
final int size = mUserStates.size();
for (int i = 0; i < size; i++) {
UserState userState = mUserStates.valueAt(i);
- // Only unbind for a new service
- if (userState.isCurrentlyBoundToComponentLocked(serviceComponentName)) {
- return;
- }
- if (userState.isBoundLocked()) {
- userState.unbindLocked();
- }
+ userState.onTextClassifierServicePackageOverrideChangedLocked(overriddenPackage);
}
}
}
@@ -430,27 +473,35 @@
private static final class PendingRequest implements IBinder.DeathRecipient {
private final int mUid;
- @Nullable private final String mName;
- @Nullable private final IBinder mBinder;
- @NonNull private final Runnable mRequest;
- @Nullable private final Runnable mOnServiceFailure;
+ @Nullable
+ private final String mName;
+ @Nullable
+ private final IBinder mBinder;
+ @NonNull
+ private final Runnable mRequest;
+ @Nullable
+ private final Runnable mOnServiceFailure;
@GuardedBy("mLock")
- @NonNull private final UserState mOwningUser;
- @NonNull private final TextClassificationManagerService mService;
+ @NonNull
+ private final ServiceState mServiceState;
+ @NonNull
+ private final TextClassificationManagerService mService;
/**
* Initializes a new pending request.
- * @param request action to perform when the service is bound
+ *
+ * @param request action to perform when the service is bound
* @param onServiceFailure action to perform when the service dies or disconnects
- * @param binder binder to the process that made this pending request
- * @param service
- * @param owningUser
+ * @param binder binder to the process that made this pending request
+ * @parm service the TCMS instance.
+ * @param serviceState the service state of the service that will execute the request.
+ * @param uid the calling uid of the request.
*/
PendingRequest(@Nullable String name,
@NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
@Nullable IBinder binder,
- TextClassificationManagerService service,
- UserState owningUser, int uid) {
+ @NonNull TextClassificationManagerService service,
+ @NonNull ServiceState serviceState, int uid) {
mName = name;
mRequest =
logOnFailure(Objects.requireNonNull(request), "handling pending request");
@@ -458,7 +509,7 @@
logOnFailure(onServiceFailure, "notifying callback of service failure");
mBinder = binder;
mService = service;
- mOwningUser = owningUser;
+ mServiceState = Objects.requireNonNull(serviceState);
if (mBinder != null) {
try {
mBinder.linkToDeath(this, 0);
@@ -479,7 +530,7 @@
@GuardedBy("mLock")
private void removeLocked() {
- mOwningUser.mPendingRequests.remove(this);
+ mServiceState.mPendingRequests.remove(this);
if (mBinder != null) {
mBinder.unlinkToDeath(this, 0);
}
@@ -492,59 +543,172 @@
e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
}
- private static void validateInput(
- Context context, @Nullable String packageName, @UserIdInt int userId)
- throws RemoteException {
+ private void validateCallingPackage(@Nullable String callingPackage)
+ throws PackageManager.NameNotFoundException {
+ if (callingPackage != null) {
+ final int packageUid = mContext.getPackageManager()
+ .getPackageUidAsUser(callingPackage, UserHandle.getCallingUserId());
+ final int callingUid = Binder.getCallingUid();
+ Preconditions.checkArgument(
+ callingUid == packageUid
+ // Trust the system process:
+ || callingUid == android.os.Process.SYSTEM_UID,
+ "Invalid package name. callingPackage=" + callingPackage
+ + ", callingUid=" + callingUid);
+ }
+ }
- try {
- if (packageName != null) {
- final int packageUid = context.getPackageManager()
- .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
- final int callingUid = Binder.getCallingUid();
- Preconditions.checkArgument(callingUid == packageUid
- // Trust the system process:
- || callingUid == android.os.Process.SYSTEM_UID,
- "Invalid package name. Package=" + packageName
- + ", CallingUid=" + callingUid);
- }
-
- Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId");
- final int callingUserId = UserHandle.getCallingUserId();
- if (callingUserId != userId) {
- context.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId);
- }
- } catch (Exception e) {
- throw new RemoteException("Invalid request: " + e.getMessage(), e,
- /* enableSuppression */ true, /* writableStackTrace */ true);
+ private void validateUser(@UserIdInt int userId) {
+ Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId");
+ final int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId);
}
}
private final class UserState {
- @UserIdInt final int mUserId;
+ @UserIdInt
+ final int mUserId;
+ @Nullable
+ private final ServiceState mDefaultServiceState;
+ @Nullable
+ private final ServiceState mSystemServiceState;
@GuardedBy("mLock")
- TextClassifierServiceConnection mConnection = null;
+ @Nullable
+ private ServiceState mUntrustedServiceState;
+
+ private UserState(int userId) {
+ mUserId = userId;
+ mDefaultServiceState = TextUtils.isEmpty(mDefaultTextClassifierPackage)
+ ? null
+ : new ServiceState(userId, mDefaultTextClassifierPackage, /* isTrusted= */true);
+ mSystemServiceState = TextUtils.isEmpty(mSystemTextClassifierPackage)
+ ? null
+ : new ServiceState(userId, mSystemTextClassifierPackage, /* isTrusted= */ true);
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ ServiceState getServiceStateLocked(boolean useDefaultTextClassifier) {
+ if (useDefaultTextClassifier) {
+ return mDefaultServiceState;
+ }
+ String textClassifierServicePackageOverride =
+ mSettings.getTextClassifierServicePackageOverride();
+ if (!TextUtils.isEmpty(textClassifierServicePackageOverride)) {
+ if (textClassifierServicePackageOverride.equals(mDefaultTextClassifierPackage)) {
+ return mDefaultServiceState;
+ }
+ if (textClassifierServicePackageOverride.equals(mSystemTextClassifierPackage)
+ && mSystemServiceState != null) {
+ return mSystemServiceState;
+ }
+ if (mUntrustedServiceState == null) {
+ mUntrustedServiceState =
+ new ServiceState(
+ mUserId,
+ textClassifierServicePackageOverride,
+ /* isTrusted= */false);
+ }
+ return mUntrustedServiceState;
+ }
+ return mSystemServiceState != null ? mSystemServiceState : mDefaultServiceState;
+ }
+
+ @GuardedBy("mLock")
+ void onTextClassifierServicePackageOverrideChangedLocked(String overriddenPackageName) {
+ // The override config is just used for testing, and the flag value is not expected
+ // to change often. So, let's keep it simple and just unbind all the services here. The
+ // right service will be bound when the next request comes.
+ for (ServiceState serviceState : getAllServiceStatesLocked()) {
+ serviceState.unbindIfBoundLocked();
+ }
+ mUntrustedServiceState = null;
+ }
+
+ @GuardedBy("mLock")
+ void bindIfHasPendingRequestsLocked() {
+ for (ServiceState serviceState : getAllServiceStatesLocked()) {
+ serviceState.bindIfHasPendingRequestsLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ void cleanupServiceLocked() {
+ for (ServiceState serviceState : getAllServiceStatesLocked()) {
+ if (serviceState.mConnection != null) {
+ serviceState.mConnection.cleanupService();
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ private List<ServiceState> getAllServiceStatesLocked() {
+ List<ServiceState> serviceStates = new ArrayList<>();
+ if (mDefaultServiceState != null) {
+ serviceStates.add(mDefaultServiceState);
+ }
+ if (mSystemServiceState != null) {
+ serviceStates.add(mSystemServiceState);
+ }
+ if (mUntrustedServiceState != null) {
+ serviceStates.add(mUntrustedServiceState);
+ }
+ return serviceStates;
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.increaseIndent();
+ dump(pw, mDefaultServiceState, "Default");
+ dump(pw, mSystemServiceState, "System");
+ dump(pw, mUntrustedServiceState, "Untrusted");
+ pw.decreaseIndent();
+ }
+ }
+
+ private void dump(
+ IndentingPrintWriter pw, @Nullable ServiceState serviceState, String name) {
+ synchronized (mLock) {
+ if (serviceState != null) {
+ pw.print(name + ": ");
+ serviceState.dump(pw);
+ pw.println();
+ }
+ }
+ }
+ }
+
+ private final class ServiceState {
+ @UserIdInt
+ final int mUserId;
+ @NonNull
+ final String mPackageName;
+ @NonNull
+ final TextClassifierServiceConnection mConnection;
+ final boolean mIsTrusted;
+ @NonNull
@GuardedBy("mLock")
final Queue<PendingRequest> mPendingRequests = new ArrayDeque<>();
+ @Nullable
@GuardedBy("mLock")
ITextClassifierService mService;
@GuardedBy("mLock")
boolean mBinding;
+ @Nullable
@GuardedBy("mLock")
ComponentName mBoundComponentName = null;
@GuardedBy("mLock")
- boolean mBoundToDefaultTrustService;
- @GuardedBy("mLock")
- int mBoundServiceUid;
+ int mBoundServiceUid = Process.INVALID_UID;
- private final Context mContext;
- private final Object mLock;
-
- private UserState(int userId, Context context, Object lock) {
+ private ServiceState(@UserIdInt int userId, String packageName, boolean isTrusted) {
mUserId = userId;
- mContext = Objects.requireNonNull(context);
- mLock = Objects.requireNonNull(lock);
+ mPackageName = packageName;
+ mConnection = new TextClassifierServiceConnection(mUserId);
+ mIsTrusted = isTrusted;
}
@GuardedBy("mLock")
@@ -581,18 +745,12 @@
}
@GuardedBy("mLock")
- private boolean isCurrentlyBoundToComponentLocked(@NonNull ComponentName componentName) {
- return (mBoundComponentName != null
- && mBoundComponentName.getPackageName().equals(
- componentName.getPackageName()));
- }
-
- @GuardedBy("mLock")
- private void unbindLocked() {
- Slog.v(LOG_TAG, "unbinding from " + mBoundComponentName + " for " + mUserId);
- mContext.unbindService(mConnection);
- mConnection.cleanupService();
- mConnection = null;
+ void unbindIfBoundLocked() {
+ if (isBoundLocked()) {
+ Slog.v(LOG_TAG, "Unbinding " + mBoundComponentName + " for " + mUserId);
+ mContext.unbindService(mConnection);
+ mConnection.cleanupService();
+ }
}
/**
@@ -609,8 +767,7 @@
final boolean willBind;
final long identity = Binder.clearCallingIdentity();
try {
- final ComponentName componentName =
- TextClassifierService.getServiceComponentName(mContext);
+ final ComponentName componentName = getTextClassifierServiceComponent();
if (componentName == null) {
// Might happen if the storage is encrypted and the user is not unlocked
return false;
@@ -618,7 +775,6 @@
Intent serviceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
.setComponent(componentName);
Slog.d(LOG_TAG, "Binding to " + serviceIntent.getComponent());
- mConnection = new TextClassifierServiceConnection(mUserId);
willBind = mContext.bindServiceAsUser(
serviceIntent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
@@ -631,12 +787,21 @@
return willBind;
}
+ @Nullable
+ private ComponentName getTextClassifierServiceComponent() {
+ return TextClassifierService.getServiceComponentName(
+ mContext,
+ mPackageName,
+ mIsTrusted ? PackageManager.MATCH_SYSTEM_ONLY : 0);
+ }
+
private void dump(IndentingPrintWriter pw) {
pw.printPair("context", mContext);
pw.printPair("userId", mUserId);
synchronized (mLock) {
+ pw.printPair("packageName", mPackageName);
pw.printPair("boundComponentName", mBoundComponentName);
- pw.printPair("boundToDefaultTrustService", mBoundToDefaultTrustService);
+ pw.printPair("isTrusted", mIsTrusted);
pw.printPair("boundServiceUid", mBoundServiceUid);
pw.printPair("binding", mBinding);
pw.printPair("numberRequests", mPendingRequests.size());
@@ -645,7 +810,7 @@
@GuardedBy("mLock")
private boolean checkRequestAcceptedLocked(int requestUid, @NonNull String methodName) {
- if (mBoundToDefaultTrustService || (requestUid == mBoundServiceUid)) {
+ if (mIsTrusted || (requestUid == mBoundServiceUid)) {
return true;
}
Slog.w(LOG_TAG, String.format(
@@ -654,47 +819,19 @@
return false;
}
- private boolean isDefaultTrustService(@NonNull ComponentName currentService) {
- final String[] defaultServiceNames =
- mContext.getPackageManager().getSystemTextClassifierPackages();
- final String servicePackageName = currentService.getPackageName();
-
- for (int i = 0; i < defaultServiceNames.length; i++) {
- if (defaultServiceNames[i].equals(servicePackageName)) {
- return true;
- }
- }
- return false;
- }
-
- private int getServiceUid(@Nullable ComponentName service, int userId) {
- if (service == null) {
- return Process.INVALID_UID;
- }
- final String servicePackageName = service.getPackageName();
- final PackageManager pm = mContext.getPackageManager();
- final int serviceUid;
-
- try {
- serviceUid = pm.getPackageUidAsUser(servicePackageName, userId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(LOG_TAG, "Could not verify UID for " + service);
- return Process.INVALID_UID;
- }
- return serviceUid;
- }
-
@GuardedBy("mLock")
private void updateServiceInfoLocked(int userId, @Nullable ComponentName componentName) {
mBoundComponentName = componentName;
- mBoundToDefaultTrustService = (mBoundComponentName != null && isDefaultTrustService(
- mBoundComponentName));
- mBoundServiceUid = getServiceUid(mBoundComponentName, userId);
+ mBoundServiceUid =
+ mBoundComponentName == null
+ ? Process.INVALID_UID
+ : resolvePackageToUid(mBoundComponentName.getPackageName(), userId);
}
private final class TextClassifierServiceConnection implements ServiceConnection {
- @UserIdInt private final int mUserId;
+ @UserIdInt
+ private final int mUserId;
TextClassifierServiceConnection(int userId) {
mUserId = userId;
@@ -745,18 +882,18 @@
private final class TextClassifierSettingsListener implements
DeviceConfig.OnPropertiesChangedListener {
+ @NonNull
+ private final Context mContext;
+ @Nullable
+ private String mServicePackageOverride;
- @NonNull private final Context mContext;
- @NonNull private final TextClassificationConstants mSettings;
- @Nullable private String mServicePackageName;
TextClassifierSettingsListener(Context context) {
mContext = context;
- mSettings = TextClassificationManager.getSettings(mContext);
- mServicePackageName = mSettings.getTextClassifierServicePackageOverride();
+ mServicePackageOverride = mSettings.getTextClassifierServicePackageOverride();
}
- public void registerObserver() {
+ void registerObserver() {
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
mContext.getMainExecutor(),
@@ -765,13 +902,13 @@
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
- final String overrideServiceName = mSettings.getTextClassifierServicePackageOverride();
-
- if (TextUtils.equals(overrideServiceName, mServicePackageName)) {
+ final String currentServicePackageOverride =
+ mSettings.getTextClassifierServicePackageOverride();
+ if (TextUtils.equals(currentServicePackageOverride, mServicePackageOverride)) {
return;
}
- mServicePackageName = overrideServiceName;
- unbindServiceIfNecessary();
+ mServicePackageOverride = currentServicePackageOverride;
+ onTextClassifierServicePackageOverrideChanged(currentServicePackageOverride);
}
}
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index b7d6360..0bb0f94 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -37,6 +37,9 @@
import java.io.PrintWriter;
import java.util.Objects;
+/**
+ * The implementation of ITimeDetectorService.aidl.
+ */
public final class TimeDetectorService extends ITimeDetectorService.Stub {
private static final String TAG = "TimeDetectorService";
@@ -75,7 +78,7 @@
Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
new ContentObserver(handler) {
public void onChange(boolean selfChange) {
- timeDetectorService.handleAutoTimeDetectionToggle();
+ timeDetectorService.handleAutoTimeDetectionChanged();
}
});
@@ -114,8 +117,9 @@
mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
}
+ /** Internal method for handling the auto time setting being changed. */
@VisibleForTesting
- public void handleAutoTimeDetectionToggle() {
+ public void handleAutoTimeDetectionChanged() {
mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged);
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 468b806..a7c3b4d 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -26,8 +26,8 @@
import java.io.PrintWriter;
/**
- * The interface for classes that implement the time detection algorithm used by the
- * TimeDetectorService.
+ * The interface for the class that implements the time detection algorithm used by the
+ * {@link TimeDetectorService}.
*
* <p>Most calls will be handled by a single thread but that is not true for all calls. For example
* {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index a1e643f..19435ee 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -38,7 +38,7 @@
import java.lang.annotation.RetentionPolicy;
/**
- * An implementation of TimeDetectorStrategy that passes phone and manual suggestions to
+ * An implementation of {@link TimeDetectorStrategy} that passes phone and manual suggestions to
* {@link AlarmManager}. When there are multiple phone sources, the one with the lowest ID is used
* unless the data becomes too stale.
*
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index adf6d7e..2520316 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -24,9 +24,9 @@
import android.provider.Settings;
/**
- * The real implementation of {@link TimeZoneDetectorStrategy.Callback}.
+ * The real implementation of {@link TimeZoneDetectorStrategyImpl.Callback}.
*/
-public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategy.Callback {
+public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategyImpl.Callback {
private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 9a1fe65..381ee10 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -67,19 +67,21 @@
private static TimeZoneDetectorService create(@NonNull Context context) {
final TimeZoneDetectorStrategy timeZoneDetectorStrategy =
- TimeZoneDetectorStrategy.create(context);
+ TimeZoneDetectorStrategyImpl.create(context);
Handler handler = FgThread.getHandler();
+ TimeZoneDetectorService service =
+ new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy);
+
ContentResolver contentResolver = context.getContentResolver();
contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
new ContentObserver(handler) {
public void onChange(boolean selfChange) {
- timeZoneDetectorStrategy.handleAutoTimeZoneDetectionChange();
+ service.handleAutoTimeZoneDetectionChanged();
}
});
-
- return new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy);
+ return service;
}
@VisibleForTesting
@@ -111,17 +113,25 @@
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- mTimeZoneDetectorStrategy.dumpState(pw, args);
+ mTimeZoneDetectorStrategy.dump(pw, args);
+ }
+
+ /** Internal method for handling the auto time zone setting being changed. */
+ @VisibleForTesting
+ public void handleAutoTimeZoneDetectionChanged() {
+ mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneDetectionChanged);
}
private void enforceSuggestPhoneTimeZonePermission() {
mContext.enforceCallingPermission(
- android.Manifest.permission.SET_TIME_ZONE, "set time zone");
+ android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE,
+ "suggest phone time and time zone");
}
private void enforceSuggestManualTimeZonePermission() {
mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.SET_TIME_ZONE, "set time zone");
+ android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
+ "suggest manual time and time zone");
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index b0e0069..1d439e9 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -15,192 +15,26 @@
*/
package com.android.server.timezonedetector;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
-
-import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.PhoneTimeZoneSuggestion;
-import android.content.Context;
-import android.util.LocalLog;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
/**
- * A singleton, stateful time zone detection strategy that is aware of user (manual) suggestions and
- * suggestions from multiple phone devices. Suggestions are acted on or ignored as needed, dependent
- * on the current "auto time zone detection" setting.
+ * The interface for the class that implement the time detection algorithm used by the
+ * {@link TimeZoneDetectorService}.
*
- * <p>For automatic detection it keeps track of the most recent suggestion from each phone it uses
- * the best suggestion based on a scoring algorithm. If several phones provide the same score then
- * the phone with the lowest numeric ID "wins". If the situation changes and it is no longer
- * possible to be confident about the time zone, phones must submit an empty suggestion in order to
- * "withdraw" their previous suggestion.
+ * <p>Most calls will be handled by a single thread but that is not true for all calls. For example
+ * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
+ * handle thread safety.
+ *
+ * @hide
*/
-public class TimeZoneDetectorStrategy {
+public interface TimeZoneDetectorStrategy {
- /**
- * Used by {@link TimeZoneDetectorStrategy} to interact with the surrounding service. It can be
- * faked for tests.
- *
- * <p>Note: Because the system properties-derived values like
- * {@link #isAutoTimeZoneDetectionEnabled()}, {@link #isAutoTimeZoneDetectionEnabled()},
- * {@link #getDeviceTimeZone()} can be modified independently and from different threads (and
- * processes!), their use are prone to race conditions. That will be true until the
- * responsibility for setting their values is moved to {@link TimeZoneDetectorStrategy}.
- */
- @VisibleForTesting
- public interface Callback {
-
- /**
- * Returns true if automatic time zone detection is enabled in settings.
- */
- boolean isAutoTimeZoneDetectionEnabled();
-
- /**
- * Returns true if the device has had an explicit time zone set.
- */
- boolean isDeviceTimeZoneInitialized();
-
- /**
- * Returns the device's currently configured time zone.
- */
- String getDeviceTimeZone();
-
- /**
- * Sets the device's time zone.
- */
- void setDeviceTimeZone(@NonNull String zoneId);
- }
-
- private static final String LOG_TAG = "TimeZoneDetectorStrategy";
- private static final boolean DBG = false;
-
- @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Origin {}
-
- /** Used when a time value originated from a telephony signal. */
- @Origin
- private static final int ORIGIN_PHONE = 1;
-
- /** Used when a time value originated from a user / manual settings. */
- @Origin
- private static final int ORIGIN_MANUAL = 2;
-
- /**
- * The abstract score for an empty or invalid phone suggestion.
- *
- * Used to score phone suggestions where there is no zone.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_NONE = 0;
-
- /**
- * The abstract score for a low quality phone suggestion.
- *
- * Used to score suggestions where:
- * The suggested zone ID is one of several possibilities, and the possibilities have different
- * offsets.
- *
- * You would have to be quite desperate to want to use this choice.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_LOW = 1;
-
- /**
- * The abstract score for a medium quality phone suggestion.
- *
- * Used for:
- * The suggested zone ID is one of several possibilities but at least the possibilities have the
- * same offset. Users would get the correct time but for the wrong reason. i.e. their device may
- * switch to DST at the wrong time and (for example) their calendar events.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_MEDIUM = 2;
-
- /**
- * The abstract score for a high quality phone suggestion.
- *
- * Used for:
- * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
- * the info available.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_HIGH = 3;
-
- /**
- * The abstract score for a highest quality phone suggestion.
- *
- * Used for:
- * Suggestions that must "win" because they constitute test or emulator zone ID.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_HIGHEST = 4;
-
- /**
- * The threshold at which phone suggestions are good enough to use to set the device's time
- * zone.
- */
- @VisibleForTesting
- public static final int PHONE_SCORE_USAGE_THRESHOLD = PHONE_SCORE_MEDIUM;
-
- /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
- private static final int KEEP_PHONE_SUGGESTION_HISTORY_SIZE = 30;
-
- @NonNull
- private final Callback mCallback;
-
- /**
- * A log that records the decisions / decision metadata that affected the device's time zone
- * (for use during debugging).
- */
- @NonNull
- private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
-
- /**
- * A mapping from slotIndex to a phone time zone suggestion. We typically expect one or two
- * mappings: devices will have a small number of telephony devices and slotIndexs are assumed to
- * be stable.
- */
- @GuardedBy("this")
- private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionBySlotIndex =
- new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE);
-
- /**
- * Creates a new instance of {@link TimeZoneDetectorStrategy}.
- */
- public static TimeZoneDetectorStrategy create(Context context) {
- Callback timeZoneDetectionServiceHelper = new TimeZoneDetectorCallbackImpl(context);
- return new TimeZoneDetectorStrategy(timeZoneDetectionServiceHelper);
- }
-
- @VisibleForTesting
- public TimeZoneDetectorStrategy(Callback callback) {
- mCallback = Objects.requireNonNull(callback);
- }
-
- /** Process the suggested manually- / user-entered time zone. */
- public synchronized void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion) {
- Objects.requireNonNull(suggestion);
-
- String timeZoneId = suggestion.getZoneId();
- String cause = "Manual time suggestion received: suggestion=" + suggestion;
- setDeviceTimeZoneIfRequired(ORIGIN_MANUAL, timeZoneId, cause);
- }
+ /** Process the suggested manually-entered (i.e. user sourced) time zone. */
+ void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion);
/**
* Suggests a time zone for the device, or withdraws a previous suggestion if
@@ -210,312 +44,15 @@
* suggestion. The strategy uses suggestions to decide whether to modify the device's time zone
* setting and what to set it to.
*/
- public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
- if (DBG) {
- Slog.d(LOG_TAG, "Phone suggestion received. newSuggestion=" + suggestion);
- }
- Objects.requireNonNull(suggestion);
-
- // Score the suggestion.
- int score = scorePhoneSuggestion(suggestion);
- QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
- new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
-
- // Store the suggestion against the correct slotIndex.
- mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
-
- // Now perform auto time zone detection. The new suggestion may be used to modify the time
- // zone setting.
- String reason = "New phone time suggested. suggestion=" + suggestion;
- doAutoTimeZoneDetection(reason);
- }
-
- private static int scorePhoneSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
- int score;
- if (suggestion.getZoneId() == null) {
- score = PHONE_SCORE_NONE;
- } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY
- || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) {
- // Handle emulator / test cases : These suggestions should always just be used.
- score = PHONE_SCORE_HIGHEST;
- } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) {
- score = PHONE_SCORE_HIGH;
- } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) {
- // The suggestion may be wrong, but at least the offset should be correct.
- score = PHONE_SCORE_MEDIUM;
- } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
- // The suggestion has a good chance of being wrong.
- score = PHONE_SCORE_LOW;
- } else {
- throw new AssertionError();
- }
- return score;
- }
-
- /**
- * Finds the best available time zone suggestion from all phones. If it is high-enough quality
- * and automatic time zone detection is enabled then it will be set on the device. The outcome
- * can be that this strategy becomes / remains un-opinionated and nothing is set.
- */
- @GuardedBy("this")
- private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
- if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
- // Avoid doing unnecessary work with this (race-prone) check.
- return;
- }
-
- QualifiedPhoneTimeZoneSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
-
- // Work out what to do with the best suggestion.
- if (bestPhoneSuggestion == null) {
- // There is no phone suggestion available at all. Become un-opinionated.
- if (DBG) {
- Slog.d(LOG_TAG, "Could not determine time zone: No best phone suggestion."
- + " detectionReason=" + detectionReason);
- }
- return;
- }
-
- // Special case handling for uninitialized devices. This should only happen once.
- String newZoneId = bestPhoneSuggestion.suggestion.getZoneId();
- if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
- String cause = "Device has no time zone set. Attempting to set the device to the best"
- + " available suggestion."
- + " bestPhoneSuggestion=" + bestPhoneSuggestion
- + ", detectionReason=" + detectionReason;
- Slog.i(LOG_TAG, cause);
- setDeviceTimeZoneIfRequired(ORIGIN_PHONE, newZoneId, cause);
- return;
- }
-
- boolean suggestionGoodEnough = bestPhoneSuggestion.score >= PHONE_SCORE_USAGE_THRESHOLD;
- if (!suggestionGoodEnough) {
- if (DBG) {
- Slog.d(LOG_TAG, "Best suggestion not good enough."
- + " bestPhoneSuggestion=" + bestPhoneSuggestion
- + ", detectionReason=" + detectionReason);
- }
- return;
- }
-
- // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
- // zone ID.
- if (newZoneId == null) {
- Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
- + " bestPhoneSuggestion=" + bestPhoneSuggestion
- + " detectionReason=" + detectionReason);
- return;
- }
-
- String zoneId = bestPhoneSuggestion.suggestion.getZoneId();
- String cause = "Found good suggestion."
- + ", bestPhoneSuggestion=" + bestPhoneSuggestion
- + ", detectionReason=" + detectionReason;
- setDeviceTimeZoneIfRequired(ORIGIN_PHONE, zoneId, cause);
- }
-
- @GuardedBy("this")
- private void setDeviceTimeZoneIfRequired(
- @Origin int origin, @NonNull String newZoneId, @NonNull String cause) {
- Objects.requireNonNull(newZoneId);
- Objects.requireNonNull(cause);
-
- boolean isOriginAutomatic = isOriginAutomatic(origin);
- if (isOriginAutomatic) {
- if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
- if (DBG) {
- Slog.d(LOG_TAG, "Auto time zone detection is not enabled."
- + " origin=" + origin
- + ", newZoneId=" + newZoneId
- + ", cause=" + cause);
- }
- return;
- }
- } else {
- if (mCallback.isAutoTimeZoneDetectionEnabled()) {
- if (DBG) {
- Slog.d(LOG_TAG, "Auto time zone detection is enabled."
- + " origin=" + origin
- + ", newZoneId=" + newZoneId
- + ", cause=" + cause);
- }
- return;
- }
- }
-
- String currentZoneId = mCallback.getDeviceTimeZone();
-
- // Avoid unnecessary changes / intents.
- if (newZoneId.equals(currentZoneId)) {
- // No need to set the device time zone - the setting is already what we would be
- // suggesting.
- if (DBG) {
- Slog.d(LOG_TAG, "No need to change the time zone;"
- + " device is already set to the suggested zone."
- + " origin=" + origin
- + ", newZoneId=" + newZoneId
- + ", cause=" + cause);
- }
- return;
- }
-
- mCallback.setDeviceTimeZone(newZoneId);
- String msg = "Set device time zone."
- + " origin=" + origin
- + ", currentZoneId=" + currentZoneId
- + ", newZoneId=" + newZoneId
- + ", cause=" + cause;
- if (DBG) {
- Slog.d(LOG_TAG, msg);
- }
- mTimeZoneChangesLog.log(msg);
- }
-
- private static boolean isOriginAutomatic(@Origin int origin) {
- return origin != ORIGIN_MANUAL;
- }
-
- @GuardedBy("this")
- @Nullable
- private QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestion() {
- QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
-
- // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
- // and find the best. Note that we deliberately do not look at age: the caller can
- // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
- // expected to withdraw suggestions they no longer have confidence in.
- for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
- QualifiedPhoneTimeZoneSuggestion candidateSuggestion =
- mSuggestionBySlotIndex.valueAt(i);
- if (candidateSuggestion == null) {
- // Unexpected
- continue;
- }
-
- if (bestSuggestion == null) {
- bestSuggestion = candidateSuggestion;
- } else if (candidateSuggestion.score > bestSuggestion.score) {
- bestSuggestion = candidateSuggestion;
- } else if (candidateSuggestion.score == bestSuggestion.score) {
- // Tie! Use the suggestion with the lowest slotIndex.
- int candidateSlotIndex = candidateSuggestion.suggestion.getSlotIndex();
- int bestSlotIndex = bestSuggestion.suggestion.getSlotIndex();
- if (candidateSlotIndex < bestSlotIndex) {
- bestSuggestion = candidateSuggestion;
- }
- }
- }
- return bestSuggestion;
- }
-
- /**
- * Returns the current best phone suggestion. Not intended for general use: it is used during
- * tests to check strategy behavior.
- */
- @VisibleForTesting
- @Nullable
- public synchronized QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestionForTests() {
- return findBestPhoneSuggestion();
- }
+ void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion);
/**
* Called when there has been a change to the automatic time zone detection setting.
*/
- @VisibleForTesting
- public synchronized void handleAutoTimeZoneDetectionChange() {
- if (DBG) {
- Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
- }
- if (mCallback.isAutoTimeZoneDetectionEnabled()) {
- // When the user enabled time zone detection, run the time zone detection and change the
- // device time zone if possible.
- String reason = "Auto time zone detection setting enabled.";
- doAutoTimeZoneDetection(reason);
- }
- }
+ void handleAutoTimeZoneDetectionChanged();
/**
* Dumps internal state such as field values.
*/
- public synchronized void dumpState(PrintWriter pw, String[] args) {
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println("TimeZoneDetectorStrategy:");
-
- ipw.increaseIndent(); // level 1
- ipw.println("mCallback.isTimeZoneDetectionEnabled()="
- + mCallback.isAutoTimeZoneDetectionEnabled());
- ipw.println("mCallback.isDeviceTimeZoneInitialized()="
- + mCallback.isDeviceTimeZoneInitialized());
- ipw.println("mCallback.getDeviceTimeZone()="
- + mCallback.getDeviceTimeZone());
-
- ipw.println("Time zone change log:");
- ipw.increaseIndent(); // level 2
- mTimeZoneChangesLog.dump(ipw);
- ipw.decreaseIndent(); // level 2
-
- ipw.println("Phone suggestion history:");
- ipw.increaseIndent(); // level 2
- mSuggestionBySlotIndex.dump(ipw);
- ipw.decreaseIndent(); // level 2
- ipw.decreaseIndent(); // level 1
- ipw.flush();
- }
-
- /**
- * A method used to inspect strategy state during tests. Not intended for general use.
- */
- @VisibleForTesting
- public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int slotIndex) {
- return mSuggestionBySlotIndex.get(slotIndex);
- }
-
- /**
- * A {@link PhoneTimeZoneSuggestion} with additional qualifying metadata.
- */
- @VisibleForTesting
- public static class QualifiedPhoneTimeZoneSuggestion {
-
- @VisibleForTesting
- public final PhoneTimeZoneSuggestion suggestion;
-
- /**
- * The score the suggestion has been given. This can be used to rank against other
- * suggestions of the same type.
- */
- @VisibleForTesting
- public final int score;
-
- @VisibleForTesting
- public QualifiedPhoneTimeZoneSuggestion(PhoneTimeZoneSuggestion suggestion, int score) {
- this.suggestion = suggestion;
- this.score = score;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- QualifiedPhoneTimeZoneSuggestion that = (QualifiedPhoneTimeZoneSuggestion) o;
- return score == that.score
- && suggestion.equals(that.suggestion);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(score, suggestion);
- }
-
- @Override
- public String toString() {
- return "QualifiedPhoneTimeZoneSuggestion{"
- + "suggestion=" + suggestion
- + ", score=" + score
- + '}';
- }
- }
+ void dump(PrintWriter pw, String[] args);
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
new file mode 100644
index 0000000..f85f9fe
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timezonedetector;
+
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.content.Context;
+import android.util.LocalLog;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * An implementation of {@link TimeZoneDetectorStrategy} that handle telephony and manual
+ * suggestions. Suggestions are acted on or ignored as needed, dependent on the current "auto time
+ * zone detection" setting.
+ *
+ * <p>For automatic detection it keeps track of the most recent suggestion from each phone it uses
+ * the best suggestion based on a scoring algorithm. If several phones provide the same score then
+ * the phone with the lowest numeric ID "wins". If the situation changes and it is no longer
+ * possible to be confident about the time zone, phones must submit an empty suggestion in order to
+ * "withdraw" their previous suggestion.
+ *
+ * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
+ */
+public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrategy {
+
+ /**
+ * Used by {@link TimeZoneDetectorStrategyImpl} to interact with the surrounding service. It can
+ * be faked for tests.
+ *
+ * <p>Note: Because the system properties-derived values like
+ * {@link #isAutoTimeZoneDetectionEnabled()}, {@link #isAutoTimeZoneDetectionEnabled()},
+ * {@link #getDeviceTimeZone()} can be modified independently and from different threads (and
+ * processes!), their use are prone to race conditions. That will be true until the
+ * responsibility for setting their values is moved to {@link TimeZoneDetectorStrategyImpl}.
+ */
+ @VisibleForTesting
+ public interface Callback {
+
+ /**
+ * Returns true if automatic time zone detection is enabled in settings.
+ */
+ boolean isAutoTimeZoneDetectionEnabled();
+
+ /**
+ * Returns true if the device has had an explicit time zone set.
+ */
+ boolean isDeviceTimeZoneInitialized();
+
+ /**
+ * Returns the device's currently configured time zone.
+ */
+ String getDeviceTimeZone();
+
+ /**
+ * Sets the device's time zone.
+ */
+ void setDeviceTimeZone(@NonNull String zoneId);
+ }
+
+ private static final String LOG_TAG = "TimeZoneDetectorStrategy";
+ private static final boolean DBG = false;
+
+ @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Origin {}
+
+ /** Used when a time value originated from a telephony signal. */
+ @Origin
+ private static final int ORIGIN_PHONE = 1;
+
+ /** Used when a time value originated from a user / manual settings. */
+ @Origin
+ private static final int ORIGIN_MANUAL = 2;
+
+ /**
+ * The abstract score for an empty or invalid phone suggestion.
+ *
+ * Used to score phone suggestions where there is no zone.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_NONE = 0;
+
+ /**
+ * The abstract score for a low quality phone suggestion.
+ *
+ * Used to score suggestions where:
+ * The suggested zone ID is one of several possibilities, and the possibilities have different
+ * offsets.
+ *
+ * You would have to be quite desperate to want to use this choice.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_LOW = 1;
+
+ /**
+ * The abstract score for a medium quality phone suggestion.
+ *
+ * Used for:
+ * The suggested zone ID is one of several possibilities but at least the possibilities have the
+ * same offset. Users would get the correct time but for the wrong reason. i.e. their device may
+ * switch to DST at the wrong time and (for example) their calendar events.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_MEDIUM = 2;
+
+ /**
+ * The abstract score for a high quality phone suggestion.
+ *
+ * Used for:
+ * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
+ * the info available.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_HIGH = 3;
+
+ /**
+ * The abstract score for a highest quality phone suggestion.
+ *
+ * Used for:
+ * Suggestions that must "win" because they constitute test or emulator zone ID.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_HIGHEST = 4;
+
+ /**
+ * The threshold at which phone suggestions are good enough to use to set the device's time
+ * zone.
+ */
+ @VisibleForTesting
+ public static final int PHONE_SCORE_USAGE_THRESHOLD = PHONE_SCORE_MEDIUM;
+
+ /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
+ private static final int KEEP_PHONE_SUGGESTION_HISTORY_SIZE = 30;
+
+ @NonNull
+ private final Callback mCallback;
+
+ /**
+ * A log that records the decisions / decision metadata that affected the device's time zone
+ * (for use during debugging).
+ */
+ @NonNull
+ private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
+
+ /**
+ * A mapping from slotIndex to a phone time zone suggestion. We typically expect one or two
+ * mappings: devices will have a small number of telephony devices and slotIndexs are assumed to
+ * be stable.
+ */
+ @GuardedBy("this")
+ private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionBySlotIndex =
+ new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE);
+
+ /**
+ * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
+ */
+ public static TimeZoneDetectorStrategyImpl create(Context context) {
+ Callback timeZoneDetectionServiceHelper = new TimeZoneDetectorCallbackImpl(context);
+ return new TimeZoneDetectorStrategyImpl(timeZoneDetectionServiceHelper);
+ }
+
+ @VisibleForTesting
+ public TimeZoneDetectorStrategyImpl(Callback callback) {
+ mCallback = Objects.requireNonNull(callback);
+ }
+
+ @Override
+ public synchronized void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion) {
+ Objects.requireNonNull(suggestion);
+
+ String timeZoneId = suggestion.getZoneId();
+ String cause = "Manual time suggestion received: suggestion=" + suggestion;
+ setDeviceTimeZoneIfRequired(ORIGIN_MANUAL, timeZoneId, cause);
+ }
+
+ @Override
+ public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "Phone suggestion received. newSuggestion=" + suggestion);
+ }
+ Objects.requireNonNull(suggestion);
+
+ // Score the suggestion.
+ int score = scorePhoneSuggestion(suggestion);
+ QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
+
+ // Store the suggestion against the correct slotIndex.
+ mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
+
+ // Now perform auto time zone detection. The new suggestion may be used to modify the time
+ // zone setting.
+ String reason = "New phone time suggested. suggestion=" + suggestion;
+ doAutoTimeZoneDetection(reason);
+ }
+
+ private static int scorePhoneSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
+ int score;
+ if (suggestion.getZoneId() == null) {
+ score = PHONE_SCORE_NONE;
+ } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY
+ || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) {
+ // Handle emulator / test cases : These suggestions should always just be used.
+ score = PHONE_SCORE_HIGHEST;
+ } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) {
+ score = PHONE_SCORE_HIGH;
+ } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) {
+ // The suggestion may be wrong, but at least the offset should be correct.
+ score = PHONE_SCORE_MEDIUM;
+ } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
+ // The suggestion has a good chance of being wrong.
+ score = PHONE_SCORE_LOW;
+ } else {
+ throw new AssertionError();
+ }
+ return score;
+ }
+
+ /**
+ * Finds the best available time zone suggestion from all phones. If it is high-enough quality
+ * and automatic time zone detection is enabled then it will be set on the device. The outcome
+ * can be that this strategy becomes / remains un-opinionated and nothing is set.
+ */
+ @GuardedBy("this")
+ private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
+ if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
+ // Avoid doing unnecessary work with this (race-prone) check.
+ return;
+ }
+
+ QualifiedPhoneTimeZoneSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
+
+ // Work out what to do with the best suggestion.
+ if (bestPhoneSuggestion == null) {
+ // There is no phone suggestion available at all. Become un-opinionated.
+ if (DBG) {
+ Slog.d(LOG_TAG, "Could not determine time zone: No best phone suggestion."
+ + " detectionReason=" + detectionReason);
+ }
+ return;
+ }
+
+ // Special case handling for uninitialized devices. This should only happen once.
+ String newZoneId = bestPhoneSuggestion.suggestion.getZoneId();
+ if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
+ String cause = "Device has no time zone set. Attempting to set the device to the best"
+ + " available suggestion."
+ + " bestPhoneSuggestion=" + bestPhoneSuggestion
+ + ", detectionReason=" + detectionReason;
+ Slog.i(LOG_TAG, cause);
+ setDeviceTimeZoneIfRequired(ORIGIN_PHONE, newZoneId, cause);
+ return;
+ }
+
+ boolean suggestionGoodEnough = bestPhoneSuggestion.score >= PHONE_SCORE_USAGE_THRESHOLD;
+ if (!suggestionGoodEnough) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "Best suggestion not good enough."
+ + " bestPhoneSuggestion=" + bestPhoneSuggestion
+ + ", detectionReason=" + detectionReason);
+ }
+ return;
+ }
+
+ // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
+ // zone ID.
+ if (newZoneId == null) {
+ Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
+ + " bestPhoneSuggestion=" + bestPhoneSuggestion
+ + " detectionReason=" + detectionReason);
+ return;
+ }
+
+ String zoneId = bestPhoneSuggestion.suggestion.getZoneId();
+ String cause = "Found good suggestion."
+ + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+ + ", detectionReason=" + detectionReason;
+ setDeviceTimeZoneIfRequired(ORIGIN_PHONE, zoneId, cause);
+ }
+
+ @GuardedBy("this")
+ private void setDeviceTimeZoneIfRequired(
+ @Origin int origin, @NonNull String newZoneId, @NonNull String cause) {
+ Objects.requireNonNull(newZoneId);
+ Objects.requireNonNull(cause);
+
+ boolean isOriginAutomatic = isOriginAutomatic(origin);
+ if (isOriginAutomatic) {
+ if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "Auto time zone detection is not enabled."
+ + " origin=" + origin
+ + ", newZoneId=" + newZoneId
+ + ", cause=" + cause);
+ }
+ return;
+ }
+ } else {
+ if (mCallback.isAutoTimeZoneDetectionEnabled()) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "Auto time zone detection is enabled."
+ + " origin=" + origin
+ + ", newZoneId=" + newZoneId
+ + ", cause=" + cause);
+ }
+ return;
+ }
+ }
+
+ String currentZoneId = mCallback.getDeviceTimeZone();
+
+ // Avoid unnecessary changes / intents.
+ if (newZoneId.equals(currentZoneId)) {
+ // No need to set the device time zone - the setting is already what we would be
+ // suggesting.
+ if (DBG) {
+ Slog.d(LOG_TAG, "No need to change the time zone;"
+ + " device is already set to the suggested zone."
+ + " origin=" + origin
+ + ", newZoneId=" + newZoneId
+ + ", cause=" + cause);
+ }
+ return;
+ }
+
+ mCallback.setDeviceTimeZone(newZoneId);
+ String msg = "Set device time zone."
+ + " origin=" + origin
+ + ", currentZoneId=" + currentZoneId
+ + ", newZoneId=" + newZoneId
+ + ", cause=" + cause;
+ if (DBG) {
+ Slog.d(LOG_TAG, msg);
+ }
+ mTimeZoneChangesLog.log(msg);
+ }
+
+ private static boolean isOriginAutomatic(@Origin int origin) {
+ return origin != ORIGIN_MANUAL;
+ }
+
+ @GuardedBy("this")
+ @Nullable
+ private QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestion() {
+ QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
+
+ // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
+ // and find the best. Note that we deliberately do not look at age: the caller can
+ // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
+ // expected to withdraw suggestions they no longer have confidence in.
+ for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
+ QualifiedPhoneTimeZoneSuggestion candidateSuggestion =
+ mSuggestionBySlotIndex.valueAt(i);
+ if (candidateSuggestion == null) {
+ // Unexpected
+ continue;
+ }
+
+ if (bestSuggestion == null) {
+ bestSuggestion = candidateSuggestion;
+ } else if (candidateSuggestion.score > bestSuggestion.score) {
+ bestSuggestion = candidateSuggestion;
+ } else if (candidateSuggestion.score == bestSuggestion.score) {
+ // Tie! Use the suggestion with the lowest slotIndex.
+ int candidateSlotIndex = candidateSuggestion.suggestion.getSlotIndex();
+ int bestSlotIndex = bestSuggestion.suggestion.getSlotIndex();
+ if (candidateSlotIndex < bestSlotIndex) {
+ bestSuggestion = candidateSuggestion;
+ }
+ }
+ }
+ return bestSuggestion;
+ }
+
+ /**
+ * Returns the current best phone suggestion. Not intended for general use: it is used during
+ * tests to check strategy behavior.
+ */
+ @VisibleForTesting
+ @Nullable
+ public synchronized QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestionForTests() {
+ return findBestPhoneSuggestion();
+ }
+
+ @Override
+ public synchronized void handleAutoTimeZoneDetectionChanged() {
+ if (DBG) {
+ Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
+ }
+ if (mCallback.isAutoTimeZoneDetectionEnabled()) {
+ // When the user enabled time zone detection, run the time zone detection and change the
+ // device time zone if possible.
+ String reason = "Auto time zone detection setting enabled.";
+ doAutoTimeZoneDetection(reason);
+ }
+ }
+
+ /**
+ * Dumps internal state such as field values.
+ */
+ @Override
+ public synchronized void dump(PrintWriter pw, String[] args) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("TimeZoneDetectorStrategy:");
+
+ ipw.increaseIndent(); // level 1
+ ipw.println("mCallback.isTimeZoneDetectionEnabled()="
+ + mCallback.isAutoTimeZoneDetectionEnabled());
+ ipw.println("mCallback.isDeviceTimeZoneInitialized()="
+ + mCallback.isDeviceTimeZoneInitialized());
+ ipw.println("mCallback.getDeviceTimeZone()="
+ + mCallback.getDeviceTimeZone());
+
+ ipw.println("Time zone change log:");
+ ipw.increaseIndent(); // level 2
+ mTimeZoneChangesLog.dump(ipw);
+ ipw.decreaseIndent(); // level 2
+
+ ipw.println("Phone suggestion history:");
+ ipw.increaseIndent(); // level 2
+ mSuggestionBySlotIndex.dump(ipw);
+ ipw.decreaseIndent(); // level 2
+ ipw.decreaseIndent(); // level 1
+ ipw.flush();
+ }
+
+ /**
+ * A method used to inspect strategy state during tests. Not intended for general use.
+ */
+ @VisibleForTesting
+ public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int slotIndex) {
+ return mSuggestionBySlotIndex.get(slotIndex);
+ }
+
+ /**
+ * A {@link PhoneTimeZoneSuggestion} with additional qualifying metadata.
+ */
+ @VisibleForTesting
+ public static class QualifiedPhoneTimeZoneSuggestion {
+
+ @VisibleForTesting
+ public final PhoneTimeZoneSuggestion suggestion;
+
+ /**
+ * The score the suggestion has been given. This can be used to rank against other
+ * suggestions of the same type.
+ */
+ @VisibleForTesting
+ public final int score;
+
+ @VisibleForTesting
+ public QualifiedPhoneTimeZoneSuggestion(PhoneTimeZoneSuggestion suggestion, int score) {
+ this.suggestion = suggestion;
+ this.score = score;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ QualifiedPhoneTimeZoneSuggestion that = (QualifiedPhoneTimeZoneSuggestion) o;
+ return score == that.score
+ && suggestion.equals(that.suggestion);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(score, suggestion);
+ }
+
+ @Override
+ public String toString() {
+ return "QualifiedPhoneTimeZoneSuggestion{"
+ + "suggestion=" + suggestion
+ + ", score=" + score
+ + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index d5961a8..d380f8c 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -356,6 +356,8 @@
// TODO(task-hierarchy): remove when tiles can be actual parents
TaskTile mTile = null;
+ private int mLastTaskOrganizerWindowingMode = -1;
+
private final Handler mHandler;
private class ActivityStackHandler extends Handler {
@@ -794,6 +796,13 @@
}
final int windowingMode = getWindowingMode();
+ if (windowingMode == mLastTaskOrganizerWindowingMode) {
+ // If our windowing mode hasn't actually changed, then just stick
+ // with our old organizer. This lets us implement the semantic
+ // where SysUI can continue to manage it's old tasks
+ // while CTS temporarily takes over the registration.
+ return;
+ }
/*
* Different windowing modes may be managed by different task organizers. If
* getTaskOrganizer returns null, we still call setTaskOrganizer to
@@ -802,6 +811,7 @@
final ITaskOrganizer org =
mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
setTaskOrganizer(org);
+ mLastTaskOrganizerWindowingMode = windowingMode;
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 44a6fc9..0733a72 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -74,11 +74,10 @@
@Override
public void binderDied() {
synchronized (mGlobalLock) {
- final TaskOrganizerState state = mTaskOrganizerStates.get(mTaskOrganizer);
- for (int i = 0; i < state.mOrganizedTasks.size(); i++) {
- state.mOrganizedTasks.get(i).taskOrganizerDied();
- }
- mTaskOrganizerStates.remove(mTaskOrganizer);
+ final TaskOrganizerState state =
+ mTaskOrganizerStates.get(mTaskOrganizer.asBinder());
+ state.releaseTasks();
+ mTaskOrganizerStates.remove(mTaskOrganizer.asBinder());
if (mTaskOrganizersForWindowingMode.get(mWindowingMode) == mTaskOrganizer) {
mTaskOrganizersForWindowingMode.remove(mWindowingMode);
}
@@ -89,26 +88,76 @@
class TaskOrganizerState {
ITaskOrganizer mOrganizer;
DeathRecipient mDeathRecipient;
+ int mWindowingMode;
ArrayList<Task> mOrganizedTasks = new ArrayList<>();
+ // Save the TaskOrganizer which we replaced registration for
+ // so it can be re-registered if we unregister.
+ TaskOrganizerState mReplacementFor;
+ boolean mDisposed = false;
+
+
+ TaskOrganizerState(ITaskOrganizer organizer, int windowingMode,
+ TaskOrganizerState replacing) {
+ mOrganizer = organizer;
+ mDeathRecipient = new DeathRecipient(organizer, windowingMode);
+ try {
+ organizer.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "TaskOrganizer failed to register death recipient");
+ }
+ mWindowingMode = windowingMode;
+ mReplacementFor = replacing;
+ }
+
void addTask(Task t) {
mOrganizedTasks.add(t);
+ try {
+ mOrganizer.taskAppeared(t.getTaskInfo());
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception sending taskAppeared callback" + e);
+ }
}
void removeTask(Task t) {
+ try {
+ mOrganizer.taskVanished(t.getRemoteToken());
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception sending taskVanished callback" + e);
+ }
mOrganizedTasks.remove(t);
}
- TaskOrganizerState(ITaskOrganizer organizer, DeathRecipient deathRecipient) {
- mOrganizer = organizer;
- mDeathRecipient = deathRecipient;
+ void dispose() {
+ mDisposed = true;
+ releaseTasks();
+ handleReplacement();
+ }
+
+ void releaseTasks() {
+ for (int i = mOrganizedTasks.size() - 1; i >= 0; i--) {
+ final Task t = mOrganizedTasks.get(i);
+ t.taskOrganizerDied();
+ removeTask(t);
+ }
+ }
+
+ void handleReplacement() {
+ if (mReplacementFor != null && !mReplacementFor.mDisposed) {
+ mTaskOrganizersForWindowingMode.put(mWindowingMode, mReplacementFor);
+ }
+ }
+
+ void unlinkDeath() {
+ mDisposed = true;
+ mOrganizer.asBinder().unlinkToDeath(mDeathRecipient, 0);
}
};
final HashMap<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap();
- final HashMap<ITaskOrganizer, TaskOrganizerState> mTaskOrganizerStates = new HashMap();
+ final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap();
final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = new HashMap();
@@ -128,17 +177,10 @@
mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
}
- private void clearIfNeeded(int windowingMode) {
- final TaskOrganizerState oldState = mTaskOrganizersForWindowingMode.get(windowingMode);
- if (oldState != null) {
- oldState.mOrganizer.asBinder().unlinkToDeath(oldState.mDeathRecipient, 0);
- }
- }
-
/**
* Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
* If there was already a TaskOrganizer for this windowing mode it will be evicted
- * and receive taskVanished callbacks in the process.
+ * but will continue to organize it's existing tasks.
*/
@Override
public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
@@ -153,24 +195,25 @@
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- clearIfNeeded(windowingMode);
- DeathRecipient dr = new DeathRecipient(organizer, windowingMode);
- try {
- organizer.asBinder().linkToDeath(dr, 0);
- } catch (RemoteException e) {
- Slog.e(TAG, "TaskOrganizer failed to register death recipient");
- }
-
- final TaskOrganizerState state = new TaskOrganizerState(organizer, dr);
+ final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode,
+ mTaskOrganizersForWindowingMode.get(windowingMode));
mTaskOrganizersForWindowingMode.put(windowingMode, state);
-
- mTaskOrganizerStates.put(organizer, state);
+ mTaskOrganizerStates.put(organizer.asBinder(), state);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
+ void unregisterTaskOrganizer(ITaskOrganizer organizer) {
+ final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
+ state.unlinkDeath();
+ if (mTaskOrganizersForWindowingMode.get(state.mWindowingMode) == state) {
+ mTaskOrganizersForWindowingMode.remove(state.mWindowingMode);
+ }
+ state.dispose();
+ }
+
ITaskOrganizer getTaskOrganizer(int windowingMode) {
final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode);
if (state == null) {
@@ -179,35 +222,13 @@
return state.mOrganizer;
}
- private void sendTaskAppeared(ITaskOrganizer organizer, Task task) {
- try {
- organizer.taskAppeared(task.getTaskInfo());
- } catch (Exception e) {
- Slog.e(TAG, "Exception sending taskAppeared callback" + e);
- }
- }
-
- private void sendTaskVanished(ITaskOrganizer organizer, Task task) {
- try {
- organizer.taskVanished(task.getRemoteToken());
- } catch (Exception e) {
- Slog.e(TAG, "Exception sending taskVanished callback" + e);
- }
- }
-
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
- TaskOrganizerState state = mTaskOrganizerStates.get(organizer);
-
+ TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
state.addTask(task);
- sendTaskAppeared(organizer, task);
}
void onTaskVanished(ITaskOrganizer organizer, Task task) {
- final TaskOrganizerState state = mTaskOrganizerStates.get(organizer);
- sendTaskVanished(organizer, task);
-
- // This could trigger TaskAppeared for other tasks in the same stack so make sure
- // we do this AFTER sending taskVanished.
+ final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
state.removeTask(task);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6e243f0..59eee9c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -562,4 +562,14 @@
*/
public abstract void setAccessibilityIdToSurfaceMetadata(
IBinder windowToken, int accessibilityWindowId);
+
+ /**
+ * Transfers input focus from a given input token to that of the IME window.
+ *
+ * @param sourceInputToken The source token.
+ * @param displayId The display hosting the IME window.
+ * @return Whether transfer was successful.
+ */
+ public abstract boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
+ int displayId);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a5b99b0..6e1f46bb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7558,6 +7558,29 @@
}
}
}
+
+ @Override
+ public boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
+ int displayId) {
+ final IBinder destinationInputToken;
+
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ return false;
+ }
+ final WindowState imeWindow = displayContent.mInputMethodWindow;
+ if (imeWindow == null) {
+ return false;
+ }
+ if (imeWindow.mInputChannel == null) {
+ return false;
+ }
+ destinationInputToken = imeWindow.mInputChannel.getToken();
+ }
+
+ return mInputManager.transferTouchFocus(sourceInputToken, destinationInputToken);
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
index 9f307bb..59abaab 100644
--- a/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
+++ b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
@@ -59,7 +59,7 @@
}
/**
- * Compute bounds after rotating teh screen.
+ * Compute bounds after rotating the screen.
*
* @param bounds Bounds before the rotation. The array must contain exactly 4 non-null elements.
* @param rotation rotation constant defined in android.view.Surface.
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index 7644ade..aa7067e 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -137,7 +137,7 @@
#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
#define FRAME_COUNTS_FIELD_NUMBER 2
-static void writeCpuHistogram(stats_event* event,
+static void writeCpuHistogram(AStatsEvent* event,
const uirenderer::protos::GraphicsStatsProto& stat) {
util::ProtoOutputStream proto;
for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
@@ -154,10 +154,10 @@
}
std::vector<uint8_t> outVector;
proto.serializeToVector(&outVector);
- stats_event_write_byte_array(event, outVector.data(), outVector.size());
+ AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
}
-static void writeGpuHistogram(stats_event* event,
+static void writeGpuHistogram(AStatsEvent* event,
const uirenderer::protos::GraphicsStatsProto& stat) {
util::ProtoOutputStream proto;
for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
@@ -174,20 +174,20 @@
}
std::vector<uint8_t> outVector;
proto.serializeToVector(&outVector);
- stats_event_write_byte_array(event, outVector.data(), outVector.size());
+ AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
}
// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
-static status_pull_atom_return_t graphicsStatsPullCallback(int32_t atom_tag,
- pulled_stats_event_list* data,
- void* cookie) {
+static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
+ AStatsEventList* data,
+ void* cookie) {
JNIEnv* env = getJNIEnv();
if (!env) {
return false;
}
if (gGraphicsStatsServiceObject == nullptr) {
ALOGE("Failed to get graphicsstats service");
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
for (bool lastFullDay : {true, false}) {
@@ -199,7 +199,7 @@
env->ExceptionDescribe();
env->ExceptionClear();
ALOGE("Failed to invoke graphicsstats service");
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
if (!jdata) {
// null means data is not available for that day.
@@ -218,49 +218,51 @@
if (!success) {
ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'",
serviceDump.InitializationErrorString().c_str(), dataSize);
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
auto& stat = serviceDump.stats(stat_index);
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event, android::util::GRAPHICS_STATS);
- stats_event_write_string8(event, stat.package_name().c_str());
- stats_event_write_int64(event, (int64_t)stat.version_code());
- stats_event_write_int64(event, (int64_t)stat.stats_start());
- stats_event_write_int64(event, (int64_t)stat.stats_end());
- stats_event_write_int32(event, (int32_t)stat.pipeline());
- stats_event_write_int32(event, (int32_t)stat.summary().total_frames());
- stats_event_write_int32(event, (int32_t)stat.summary().missed_vsync_count());
- stats_event_write_int32(event, (int32_t)stat.summary().high_input_latency_count());
- stats_event_write_int32(event, (int32_t)stat.summary().slow_ui_thread_count());
- stats_event_write_int32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
- stats_event_write_int32(event, (int32_t)stat.summary().slow_draw_count());
- stats_event_write_int32(event, (int32_t)stat.summary().missed_deadline_count());
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS);
+ AStatsEvent_writeString(event, stat.package_name().c_str());
+ AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
+ AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
+ AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
+ AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
+ AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
writeCpuHistogram(event, stat);
writeGpuHistogram(event, stat);
// TODO: fill in UI mainline module version, when the feature is available.
- stats_event_write_int64(event, (int64_t)0);
- stats_event_write_bool(event, !lastFullDay);
- stats_event_build(event);
+ AStatsEvent_writeInt64(event, (int64_t)0);
+ AStatsEvent_writeBool(event, !lastFullDay);
+ AStatsEvent_build(event);
}
}
- return STATS_PULL_SUCCESS;
+ return AStatsManager_PULL_SUCCESS;
}
// Register a puller for GRAPHICS_STATS atom with the statsd service.
static void nativeInit(JNIEnv* env, jobject javaObject) {
gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
- pull_atom_metadata metadata = {.cool_down_ns = 10 * 1000000, // 10 milliseconds
- .timeout_ns = 2 * NS_PER_SEC, // 2 seconds
- .additive_fields = nullptr,
- .additive_fields_size = 0};
- register_stats_pull_atom_callback(android::util::GRAPHICS_STATS, &graphicsStatsPullCallback,
- &metadata, nullptr);
+ AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
+ AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000); // 10 milliseconds
+ AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds
+
+ AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS,
+ &graphicsStatsPullCallback, metadata, nullptr);
+
+ AStatsManager_PullAtomMetadata_release(metadata);
}
static void nativeDestructor(JNIEnv* env, jobject javaObject) {
- //TODO: Unregister the puller callback when a new API is available.
+ AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS);
env->DeleteGlobalRef(gGraphicsStatsServiceObject);
gGraphicsStatsServiceObject = nullptr;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 212a3a6..49db3d5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1563,20 +1563,17 @@
}
static jboolean nativeTransferTouchFocus(JNIEnv* env,
- jclass /* clazz */, jlong ptr, jobject fromChannelObj, jobject toChannelObj) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-
- sp<InputChannel> fromChannel =
- android_view_InputChannel_getInputChannel(env, fromChannelObj);
- sp<InputChannel> toChannel =
- android_view_InputChannel_getInputChannel(env, toChannelObj);
-
- if (fromChannel == nullptr || toChannel == nullptr) {
+ jclass /* clazz */, jlong ptr, jobject fromChannelTokenObj, jobject toChannelTokenObj) {
+ if (fromChannelTokenObj == nullptr || toChannelTokenObj == nullptr) {
return JNI_FALSE;
}
+ sp<IBinder> fromChannelToken = ibinderForJavaObject(env, fromChannelTokenObj);
+ sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj);
+
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
if (im->getInputManager()->getDispatcher()->transferTouchFocus(
- fromChannel->getConnectionToken(), toChannel->getConnectionToken())) {
+ fromChannelToken, toChannelToken)) {
return JNI_TRUE;
} else {
return JNI_FALSE;
@@ -1784,7 +1781,7 @@
(void*) nativeSetInputDispatchMode },
{ "nativeSetSystemUiVisibility", "(JI)V",
(void*) nativeSetSystemUiVisibility },
- { "nativeTransferTouchFocus", "(JLandroid/view/InputChannel;Landroid/view/InputChannel;)Z",
+ { "nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z",
(void*) nativeTransferTouchFocus },
{ "nativeSetPointerSpeed", "(JI)V",
(void*) nativeSetPointerSpeed },
diff --git a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
index f5b778e..43cd0a2 100644
--- a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
+++ b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
@@ -31,32 +31,32 @@
static server::stats::PowerStatsPuller gPowerStatsPuller;
static server::stats::SubsystemSleepStatePuller gSubsystemSleepStatePuller;
-static status_pull_atom_return_t onDevicePowerMeasurementCallback(int32_t atom_tag,
- pulled_stats_event_list* data,
- void* cookie) {
+static AStatsManager_PullAtomCallbackReturn onDevicePowerMeasurementCallback(int32_t atom_tag,
+ AStatsEventList* data,
+ void* cookie) {
return gPowerStatsPuller.Pull(atom_tag, data);
}
-static status_pull_atom_return_t subsystemSleepStateCallback(int32_t atom_tag,
- pulled_stats_event_list* data,
- void* cookie) {
+static AStatsManager_PullAtomCallbackReturn subsystemSleepStateCallback(int32_t atom_tag,
+ AStatsEventList* data,
+ void* cookie) {
return gSubsystemSleepStatePuller.Pull(atom_tag, data);
}
static void nativeInit(JNIEnv* env, jobject javaObject) {
// on device power measurement
gPowerStatsPuller = server::stats::PowerStatsPuller();
- register_stats_pull_atom_callback(android::util::ON_DEVICE_POWER_MEASUREMENT,
- onDevicePowerMeasurementCallback,
- /* metadata= */ nullptr,
- /* cookie= */ nullptr);
+ AStatsManager_registerPullAtomCallback(android::util::ON_DEVICE_POWER_MEASUREMENT,
+ onDevicePowerMeasurementCallback,
+ /* metadata= */ nullptr,
+ /* cookie= */ nullptr);
// subsystem sleep state
gSubsystemSleepStatePuller = server::stats::SubsystemSleepStatePuller();
- register_stats_pull_atom_callback(android::util::SUBSYSTEM_SLEEP_STATE,
- subsystemSleepStateCallback,
- /* metadata= */ nullptr,
- /* cookie= */ nullptr);
+ AStatsManager_registerPullAtomCallback(android::util::SUBSYSTEM_SLEEP_STATE,
+ subsystemSleepStateCallback,
+ /* metadata= */ nullptr,
+ /* cookie= */ nullptr);
}
static const JNINativeMethod sMethods[] = {{"nativeInit", "()V", (void*)nativeInit}};
diff --git a/services/core/jni/stats/PowerStatsPuller.cpp b/services/core/jni/stats/PowerStatsPuller.cpp
index e80b5cf..d8f6faa 100644
--- a/services/core/jni/stats/PowerStatsPuller.cpp
+++ b/services/core/jni/stats/PowerStatsPuller.cpp
@@ -78,11 +78,12 @@
PowerStatsPuller::PowerStatsPuller() {}
-status_pull_atom_return_t PowerStatsPuller::Pull(int32_t atomTag, pulled_stats_event_list* data) {
+AStatsManager_PullAtomCallbackReturn PowerStatsPuller::Pull(int32_t atomTag,
+ AStatsEventList* data) {
std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
if (!getPowerStatsHalLocked()) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
// Pull getRailInfo if necessary
@@ -100,14 +101,14 @@
if (!resultSuccess || !ret.isOk()) {
ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str());
gPowerStatsHal = nullptr;
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
// If SUCCESS but empty, or if NOT_SUPPORTED, then never try again.
if (gRailInfo.empty()) {
ALOGE("power.stats has no rail information");
gPowerStatsExist = false; // No rail info, so never try again.
gPowerStatsHal = nullptr;
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
}
@@ -134,15 +135,16 @@
}
const RailInfo& rail = gRailInfo[energyData.index];
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event,
- android::util::ON_DEVICE_POWER_MEASUREMENT);
- stats_event_write_string8(event,
- rail.subsysName.c_str());
- stats_event_write_string8(event, rail.railName.c_str());
- stats_event_write_int64(event, energyData.timestamp);
- stats_event_write_int64(event, energyData.energy);
- stats_event_build(event);
+ AStatsEvent* event =
+ AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(
+ event,
+ android::util::ON_DEVICE_POWER_MEASUREMENT);
+ AStatsEvent_writeString(event, rail.subsysName.c_str());
+ AStatsEvent_writeString(event, rail.railName.c_str());
+ AStatsEvent_writeInt64(event, energyData.timestamp);
+ AStatsEvent_writeInt64(event, energyData.energy);
+ AStatsEvent_build(event);
ALOGV("power.stat: %s.%s: %llu, %llu",
rail.subsysName.c_str(), rail.railName.c_str(),
@@ -153,9 +155,9 @@
if (!resultSuccess || !ret.isOk()) {
ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str());
gPowerStatsHal = nullptr;
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
- return STATS_PULL_SUCCESS;
+ return AStatsManager_PULL_SUCCESS;
}
} // namespace stats
diff --git a/services/core/jni/stats/PowerStatsPuller.h b/services/core/jni/stats/PowerStatsPuller.h
index 048dbb9..db07d60 100644
--- a/services/core/jni/stats/PowerStatsPuller.h
+++ b/services/core/jni/stats/PowerStatsPuller.h
@@ -29,7 +29,7 @@
class PowerStatsPuller {
public:
PowerStatsPuller();
- status_pull_atom_return_t Pull(int32_t atomTag, pulled_stats_event_list* data);
+ AStatsManager_PullAtomCallbackReturn Pull(int32_t atomTag, AStatsEventList* data);
};
} // namespace stats
diff --git a/services/core/jni/stats/SubsystemSleepStatePuller.cpp b/services/core/jni/stats/SubsystemSleepStatePuller.cpp
index c6a836c..45afb5e 100644
--- a/services/core/jni/stats/SubsystemSleepStatePuller.cpp
+++ b/services/core/jni/stats/SubsystemSleepStatePuller.cpp
@@ -55,7 +55,7 @@
namespace server {
namespace stats {
-static std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list* data)>
+static std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList* data)>
gPuller = {};
static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
@@ -176,12 +176,12 @@
}
// The caller must be holding gPowerHalMutex.
-static status_pull_atom_return_t getIPowerStatsDataLocked(int32_t atomTag,
- pulled_stats_event_list* data) {
+static AStatsManager_PullAtomCallbackReturn getIPowerStatsDataLocked(int32_t atomTag,
+ AStatsEventList* data) {
using android::hardware::power::stats::V1_0::Status;
if(!getPowerStatsHalLocked()) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
// Get power entity state residency data
bool success = false;
@@ -194,17 +194,17 @@
}
for (auto result : results) {
for (auto stateResidency : result.stateResidencyData) {
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
- stats_event_write_string8(event,
- gEntityNames.at(result.powerEntityId).c_str());
- stats_event_write_string8(event,
- gStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId)
- .c_str());
- stats_event_write_int64(event, stateResidency.totalStateEntryCount);
- stats_event_write_int64(event, stateResidency.totalTimeInStateMs);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, android::util::SUBSYSTEM_SLEEP_STATE);
+ AStatsEvent_writeString(event,
+ gEntityNames.at(result.powerEntityId).c_str());
+ AStatsEvent_writeString(event,
+ gStateNames.at(result.powerEntityId)
+ .at(stateResidency.powerEntityStateId)
+ .c_str());
+ AStatsEvent_writeInt64(event, stateResidency.totalStateEntryCount);
+ AStatsEvent_writeInt64(event, stateResidency.totalTimeInStateMs);
+ AStatsEvent_build(event);
}
}
success = true;
@@ -213,9 +213,9 @@
// bool success determines if this succeeded or not.
checkResultLocked(ret, __func__);
if (!success) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
- return STATS_PULL_SUCCESS;
+ return AStatsManager_PULL_SUCCESS;
}
// The caller must be holding gPowerHalMutex.
@@ -244,12 +244,12 @@
}
// The caller must be holding gPowerHalMutex.
-static status_pull_atom_return_t getIPowerDataLocked(int32_t atomTag,
- pulled_stats_event_list* data) {
+static AStatsManager_PullAtomCallbackReturn getIPowerDataLocked(int32_t atomTag,
+ AStatsEventList* data) {
using android::hardware::power::V1_0::Status;
if(!getPowerHalLocked()) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
Return<void> ret;
@@ -259,26 +259,26 @@
for (size_t i = 0; i < states.size(); i++) {
const PowerStatePlatformSleepState& state = states[i];
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
- stats_event_write_string8(event, state.name.c_str());
- stats_event_write_string8(event, "");
- stats_event_write_int64(event, state.totalTransitions);
- stats_event_write_int64(event, state.residencyInMsecSinceBoot);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, android::util::SUBSYSTEM_SLEEP_STATE);
+ AStatsEvent_writeString(event, state.name.c_str());
+ AStatsEvent_writeString(event, "");
+ AStatsEvent_writeInt64(event, state.totalTransitions);
+ AStatsEvent_writeInt64(event, state.residencyInMsecSinceBoot);
+ AStatsEvent_build(event);
ALOGV("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
(long long)state.residencyInMsecSinceBoot,
(long long)state.totalTransitions,
state.supportedOnlyInSuspend ? 1 : 0);
for (const auto& voter : state.voters) {
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
- stats_event_write_string8(event, state.name.c_str());
- stats_event_write_string8(event, voter.name.c_str());
- stats_event_write_int64(event, voter.totalNumberOfTimesVotedSinceBoot);
- stats_event_write_int64(event, voter.totalTimeInMsecVotedForSinceBoot);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event, android::util::SUBSYSTEM_SLEEP_STATE);
+ AStatsEvent_writeString(event, state.name.c_str());
+ AStatsEvent_writeString(event, voter.name.c_str());
+ AStatsEvent_writeInt64(event, voter.totalNumberOfTimesVotedSinceBoot);
+ AStatsEvent_writeInt64(event, voter.totalTimeInMsecVotedForSinceBoot);
+ AStatsEvent_build(event);
ALOGV("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
voter.name.c_str(),
@@ -288,7 +288,7 @@
}
});
if (!checkResultLocked(ret, __func__)) {
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
// Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
@@ -305,14 +305,14 @@
for (size_t j = 0; j < subsystem.states.size(); j++) {
const PowerStateSubsystemSleepState& state =
subsystem.states[j];
- stats_event* event = add_stats_event_to_pull_data(data);
- stats_event_set_atom_id(event,
- android::util::SUBSYSTEM_SLEEP_STATE);
- stats_event_write_string8(event, subsystem.name.c_str());
- stats_event_write_string8(event, state.name.c_str());
- stats_event_write_int64(event, state.totalTransitions);
- stats_event_write_int64(event, state.residencyInMsecSinceBoot);
- stats_event_build(event);
+ AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+ AStatsEvent_setAtomId(event,
+ android::util::SUBSYSTEM_SLEEP_STATE);
+ AStatsEvent_writeString(event, subsystem.name.c_str());
+ AStatsEvent_writeString(event, state.name.c_str());
+ AStatsEvent_writeInt64(event, state.totalTransitions);
+ AStatsEvent_writeInt64(event, state.residencyInMsecSinceBoot);
+ AStatsEvent_build(event);
ALOGV("subsystemstate: %s, %s, %lld, %lld, %lld",
subsystem.name.c_str(), state.name.c_str(),
@@ -324,14 +324,14 @@
}
});
}
- return STATS_PULL_SUCCESS;
+ return AStatsManager_PULL_SUCCESS;
}
// The caller must be holding gPowerHalMutex.
-std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list* data)>
+std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList* data)>
getPullerLocked() {
- std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list * data)> ret =
- {};
+ std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList * data)>
+ ret = {};
// First see if power.stats HAL is available. Fall back to power HAL if
// power.stats HAL is unavailable.
@@ -346,8 +346,8 @@
return ret;
}
-status_pull_atom_return_t SubsystemSleepStatePuller::Pull(int32_t atomTag,
- pulled_stats_event_list* data) {
+AStatsManager_PullAtomCallbackReturn SubsystemSleepStatePuller::Pull(int32_t atomTag,
+ AStatsEventList* data) {
std::lock_guard<std::mutex> lock(gPowerHalMutex);
if(!gPuller) {
@@ -359,7 +359,7 @@
}
ALOGE("Unable to load Power Hal or power.stats HAL");
- return STATS_PULL_SKIP;
+ return AStatsManager_PULL_SKIP;
}
} // namespace stats
diff --git a/services/core/jni/stats/SubsystemSleepStatePuller.h b/services/core/jni/stats/SubsystemSleepStatePuller.h
index 59dbbd2..da9679c 100644
--- a/services/core/jni/stats/SubsystemSleepStatePuller.h
+++ b/services/core/jni/stats/SubsystemSleepStatePuller.h
@@ -29,7 +29,7 @@
class SubsystemSleepStatePuller {
public:
SubsystemSleepStatePuller();
- status_pull_atom_return_t Pull(int32_t atomTag, pulled_stats_event_list* data);
+ AStatsManager_PullAtomCallbackReturn Pull(int32_t atomTag, AStatsEventList* data);
};
} // namespace stats
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2636586..28e44f1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4680,12 +4680,15 @@
private void ensureMinimumQuality(
int userId, ActiveAdmin admin, int minimumQuality, String operation) {
- if (admin.mPasswordPolicy.quality < minimumQuality
- && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(),
- userId)) {
- throw new IllegalStateException(String.format(
- "password quality should be at least %d for %s", minimumQuality, operation));
- }
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ if (admin.mPasswordPolicy.quality < minimumQuality
+ && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(),
+ userId)) {
+ throw new IllegalStateException(String.format(
+ "password quality should be at least %d for %s",
+ minimumQuality, operation));
+ }
+ });
}
@Override
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 79503f7..43e7738 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -293,13 +293,14 @@
| ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
return mShortcutServiceInternal.getShortcuts(
mInjector.getCallingUserId(), /*callingPackage=*/ PLATFORM_PACKAGE_NAME,
- /*changedSince=*/ 0, packageName, shortcutIds, /*componentName=*/ null, queryFlags,
- userId, MY_PID, MY_UID);
+ /*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
+ /*componentName=*/ null, queryFlags, userId, MY_PID, MY_UID);
}
private void forAllUnlockedUsers(Consumer<UserData> consumer) {
for (int i = 0; i < mUserDataArray.size(); i++) {
- UserData userData = mUserDataArray.get(i);
+ int userId = mUserDataArray.keyAt(i);
+ UserData userData = mUserDataArray.get(userId);
if (userData.isUnlocked()) {
consumer.accept(userData);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 4a40b80..6d15302 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -20,6 +20,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -29,6 +31,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.view.Display;
import android.view.DisplayAddress;
import android.view.SurfaceControl;
@@ -47,6 +50,7 @@
import org.mockito.quality.Strictness;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedList;
@@ -167,6 +171,7 @@
*/
@Test
public void testDpiValues() throws Exception {
+ // needs default one always
setUpDisplay(new FakeDisplay(PORT_A));
setUpDisplay(new FakeDisplay(PORT_B));
updateAvailableDisplays();
@@ -182,6 +187,67 @@
16000);
}
+ @Test
+ public void testAfterDisplayChange_ModesAreUpdated() throws Exception {
+ SurfaceControl.DisplayConfig displayInfo = createFakeDisplayConfig(1920, 1080, 60f);
+ SurfaceControl.DisplayConfig[] configs =
+ new SurfaceControl.DisplayConfig[]{displayInfo};
+ FakeDisplay display = new FakeDisplay(PORT_A, configs, 0);
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays).isEmpty();
+
+ DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
+ 0).getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayInfo);
+
+ Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
+ assertThat(defaultMode.matches(displayInfo.width, displayInfo.height,
+ displayInfo.refreshRate)).isTrue();
+
+ Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
+ assertThat(activeMode.matches(displayInfo.width, displayInfo.height,
+ displayInfo.refreshRate)).isTrue();
+
+ // Change the display
+ SurfaceControl.DisplayConfig addedDisplayInfo = createFakeDisplayConfig(3840, 2160,
+ 60f);
+ configs = new SurfaceControl.DisplayConfig[]{displayInfo, addedDisplayInfo};
+ display.configs = configs;
+ display.activeConfig = 1;
+ setUpDisplay(display);
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+ assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
+ assertThat(SurfaceControl.getDisplayConfigs(display.token).length).isEqualTo(2);
+
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+
+ DisplayDevice displayDevice = mListener.changedDisplays.get(0);
+ displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
+ displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
+
+ assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayInfo);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo);
+
+ activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
+ assertThat(activeMode.matches(addedDisplayInfo.width, addedDisplayInfo.height,
+ addedDisplayInfo.refreshRate)).isTrue();
+
+ defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
+ assertThat(defaultMode.matches(addedDisplayInfo.width, addedDisplayInfo.height,
+ addedDisplayInfo.refreshRate)).isTrue();
+ }
+
private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort,
float expectedXdpi,
float expectedYDpi,
@@ -194,16 +260,40 @@
assertEquals(expectedDensityDpi, info.densityDpi);
}
+ private Display.Mode getModeById(DisplayDeviceInfo displayDeviceInfo, int modeId) {
+ return Arrays.stream(displayDeviceInfo.supportedModes)
+ .filter(mode -> mode.getModeId() == modeId)
+ .findFirst()
+ .get();
+ }
+
+ private void assertModeIsSupported(Display.Mode[] supportedModes,
+ SurfaceControl.DisplayConfig mode) {
+ assertThat(Arrays.stream(supportedModes).anyMatch(
+ x -> x.matches(mode.width, mode.height, mode.refreshRate))).isTrue();
+ }
+
private static class FakeDisplay {
public final DisplayAddress.Physical address;
public final IBinder token = new Binder();
public final SurfaceControl.DisplayInfo info;
- public final SurfaceControl.DisplayConfig config;
+ public SurfaceControl.DisplayConfig[] configs;
+ public int activeConfig;
private FakeDisplay(int port) {
this.address = createDisplayAddress(port);
this.info = createFakeDisplayInfo();
- this.config = createFakeDisplayConfig();
+ this.configs = new SurfaceControl.DisplayConfig[]{
+ createFakeDisplayConfig(800, 600, 60f)
+ };
+ this.activeConfig = 0;
+ }
+
+ private FakeDisplay(int port, SurfaceControl.DisplayConfig[] configs, int activeConfig) {
+ this.address = createDisplayAddress(port);
+ this.info = createFakeDisplayInfo();
+ this.configs = configs;
+ this.activeConfig = activeConfig;
}
}
@@ -212,9 +302,9 @@
doReturn(display.token).when(() ->
SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId()));
doReturn(display.info).when(() -> SurfaceControl.getDisplayInfo(display.token));
- doReturn(new SurfaceControl.DisplayConfig[] { display.config }).when(
+ doReturn(display.configs).when(
() -> SurfaceControl.getDisplayConfigs(display.token));
- doReturn(0).when(() -> SurfaceControl.getActiveConfig(display.token));
+ doReturn(display.activeConfig).when(() -> SurfaceControl.getActiveConfig(display.token));
doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token));
doReturn(new int[] { 0 }).when(
() -> SurfaceControl.getDisplayColorModes(display.token));
@@ -242,10 +332,12 @@
return info;
}
- private static SurfaceControl.DisplayConfig createFakeDisplayConfig() {
+ private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
+ float refreshRate) {
final SurfaceControl.DisplayConfig config = new SurfaceControl.DisplayConfig();
- config.width = 800;
- config.height = 600;
+ config.width = width;
+ config.height = height;
+ config.refreshRate = refreshRate;
config.xDpi = 100;
config.yDpi = 100;
return config;
@@ -266,17 +358,19 @@
private class TestListener implements DisplayAdapter.Listener {
public ArrayList<DisplayDevice> addedDisplays = new ArrayList<>();
+ public ArrayList<DisplayDevice> changedDisplays = new ArrayList<>();
@Override
public void onDisplayDeviceEvent(DisplayDevice device, int event) {
if (event == DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED) {
addedDisplays.add(device);
+ } else if (event == DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED) {
+ changedDisplays.add(device);
}
}
@Override
public void onTraversalRequested() {
-
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e0c6d6a..39a749f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -96,6 +96,7 @@
import android.util.ArraySet;
import android.util.Pair;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -5446,26 +5447,28 @@
assertTrue(dpms.isAdminActive(admin1, UserHandle.USER_SYSTEM));
}
- public void testRevertDeviceOwnership_adminAndDeviceMigrated() throws Exception {
- DpmTestUtils.writeInputStreamToFile(
- getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated),
- getDeviceOwnerPoliciesFile());
- DpmTestUtils.writeInputStreamToFile(
- getRawStream(com.android.frameworks.servicestests.R.raw.device_owner_migrated),
- getDeviceOwnerFile());
- assertDeviceOwnershipRevertedWithFakeTransferMetadata();
- }
+ // @FlakyTest(bugId = 148934649)
+ // public void testRevertDeviceOwnership_adminAndDeviceMigrated() throws Exception {
+ // DpmTestUtils.writeInputStreamToFile(
+ // getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated),
+ // getDeviceOwnerPoliciesFile());
+ // DpmTestUtils.writeInputStreamToFile(
+ // getRawStream(com.android.frameworks.servicestests.R.raw.device_owner_migrated),
+ // getDeviceOwnerFile());
+ // assertDeviceOwnershipRevertedWithFakeTransferMetadata();
+ // }
- public void testRevertDeviceOwnership_deviceNotMigrated()
- throws Exception {
- DpmTestUtils.writeInputStreamToFile(
- getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated),
- getDeviceOwnerPoliciesFile());
- DpmTestUtils.writeInputStreamToFile(
- getRawStream(com.android.frameworks.servicestests.R.raw.device_owner_not_migrated),
- getDeviceOwnerFile());
- assertDeviceOwnershipRevertedWithFakeTransferMetadata();
- }
+ // @FlakyTest(bugId = 148934649)
+ // public void testRevertDeviceOwnership_deviceNotMigrated()
+ // throws Exception {
+ // DpmTestUtils.writeInputStreamToFile(
+ // getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated),
+ // getDeviceOwnerPoliciesFile());
+ // DpmTestUtils.writeInputStreamToFile(
+ // getRawStream(com.android.frameworks.servicestests.R.raw.device_owner_not_migrated),
+ // getDeviceOwnerFile());
+ // assertDeviceOwnershipRevertedWithFakeTransferMetadata();
+ // }
public void testRevertDeviceOwnership_adminAndDeviceNotMigrated()
throws Exception {
@@ -5487,29 +5490,31 @@
UserHandle userHandle = UserHandle.of(DpmMockContext.CALLER_USER_HANDLE);
}
- public void testRevertProfileOwnership_adminAndProfileMigrated() throws Exception {
- getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0,
- UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM);
- DpmTestUtils.writeInputStreamToFile(
- getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated),
- getProfileOwnerPoliciesFile());
- DpmTestUtils.writeInputStreamToFile(
- getRawStream(com.android.frameworks.servicestests.R.raw.profile_owner_migrated),
- getProfileOwnerFile());
- assertProfileOwnershipRevertedWithFakeTransferMetadata();
- }
+ // @FlakyTest(bugId = 148934649)
+ // public void testRevertProfileOwnership_adminAndProfileMigrated() throws Exception {
+ // getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0,
+ // UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM);
+ // DpmTestUtils.writeInputStreamToFile(
+ // getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated),
+ // getProfileOwnerPoliciesFile());
+ // DpmTestUtils.writeInputStreamToFile(
+ // getRawStream(com.android.frameworks.servicestests.R.raw.profile_owner_migrated),
+ // getProfileOwnerFile());
+ // assertProfileOwnershipRevertedWithFakeTransferMetadata();
+ // }
- public void testRevertProfileOwnership_profileNotMigrated() throws Exception {
- getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0,
- UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM);
- DpmTestUtils.writeInputStreamToFile(
- getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated),
- getProfileOwnerPoliciesFile());
- DpmTestUtils.writeInputStreamToFile(
- getRawStream(com.android.frameworks.servicestests.R.raw.profile_owner_not_migrated),
- getProfileOwnerFile());
- assertProfileOwnershipRevertedWithFakeTransferMetadata();
- }
+ // @FlakyTest(bugId = 148934649)
+ // public void testRevertProfileOwnership_profileNotMigrated() throws Exception {
+ // getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0,
+ // UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM);
+ // DpmTestUtils.writeInputStreamToFile(
+ // getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated),
+ // getProfileOwnerPoliciesFile());
+ // DpmTestUtils.writeInputStreamToFile(
+ // getRawStream(com.android.frameworks.servicestests.R.raw.profile_owner_not_migrated),
+ // getProfileOwnerFile());
+ // assertProfileOwnershipRevertedWithFakeTransferMetadata();
+ // }
public void testRevertProfileOwnership_adminAndProfileNotMigrated() throws Exception {
getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0,
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 62ea425..9d2091a 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -232,7 +232,7 @@
mDataManager.getShortcut(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID);
verify(mShortcutServiceInternal).getShortcuts(anyInt(), anyString(), anyLong(),
eq(TEST_PKG_NAME), eq(Collections.singletonList(TEST_SHORTCUT_ID)),
- eq(null), anyInt(), eq(USER_ID_PRIMARY), anyInt(), anyInt());
+ eq(null), eq(null), anyInt(), eq(USER_ID_PRIMARY), anyInt(), anyInt());
}
@Test
@@ -263,6 +263,7 @@
@Test
public void testContactsChanged() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
@@ -289,6 +290,7 @@
@Test
public void testNotificationListener() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
@@ -341,6 +343,7 @@
@Test
public void testCallLogContentObserver() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
@@ -368,6 +371,7 @@
@Test
public void testMmsSmsContentObserver() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 41416f1..3d190be 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -52,6 +52,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ILauncherApps;
@@ -1600,6 +1601,22 @@
}
/**
+ * Make a shortcut with an ID and a locus ID.
+ */
+ protected ShortcutInfo makeShortcutWithLocusId(String id, LocusId locusId) {
+ final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id)
+ .setActivity(new ComponentName(mClientContext.getPackageName(), "main"))
+ .setShortLabel("title-" + id)
+ .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class))
+ .setLocusId(locusId);
+ final ShortcutInfo s = b.build();
+
+ s.setTimestamp(mInjectedCurrentTimeMillis); // HACK
+
+ return s;
+ }
+
+ /**
* Make an intent.
*/
protected Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) {
@@ -1618,6 +1635,13 @@
}
/**
+ * Make a LocusId.
+ */
+ protected LocusId makeLocusId(String id) {
+ return new LocusId(id);
+ }
+
+ /**
* Make an component name, with the client context.
*/
@NonNull
@@ -1955,16 +1979,17 @@
protected static ShortcutQuery buildQuery(long changedSince,
String packageName, ComponentName componentName,
/* @ShortcutQuery.QueryFlags */ int flags) {
- return buildQuery(changedSince, packageName, null, componentName, flags);
+ return buildQuery(changedSince, packageName, null, null, componentName, flags);
}
protected static ShortcutQuery buildQuery(long changedSince,
- String packageName, List<String> shortcutIds, ComponentName componentName,
- /* @ShortcutQuery.QueryFlags */ int flags) {
+ String packageName, List<String> shortcutIds, List<LocusId> locusIds,
+ ComponentName componentName, /* @ShortcutQuery.QueryFlags */ int flags) {
final ShortcutQuery q = new ShortcutQuery();
q.setChangedSince(changedSince);
q.setPackage(packageName);
q.setShortcutIds(shortcutIds);
+ q.setLocusIds(locusIds);
q.setActivity(componentName);
q.setQueryFlags(flags);
return q;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 798420e..63da5fb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -1372,7 +1372,7 @@
setCaller(CALLING_PACKAGE_1);
final ShortcutInfo s1_1 = makeShortcut("s1");
- final ShortcutInfo s1_2 = makeShortcut("s2");
+ final ShortcutInfo s1_2 = makeShortcutWithLocusId("s2", makeLocusId("l1"));
assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
@@ -1394,7 +1394,7 @@
getCallerShortcut("s4").setTimestamp(500);
setCaller(CALLING_PACKAGE_3);
- final ShortcutInfo s3_2 = makeShortcut("s3");
+ final ShortcutInfo s3_2 = makeShortcutWithLocusId("s3", makeLocusId("l2"));
assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
getCallerShortcut("s3").setTimestamp(START_TIME + 5000);
@@ -1446,7 +1446,7 @@
// With ID.
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
- /* time =*/ 1000, CALLING_PACKAGE_2, list("s3"),
+ /* time =*/ 1000, CALLING_PACKAGE_2, list("s3"), /* locusIds =*/ null,
/* activity =*/ null,
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
getCallingUser())),
@@ -1454,20 +1454,51 @@
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2, list("s3", "s2", "ss"),
- /* activity =*/ null,
+ /* locusIds =*/ null, /* activity =*/ null,
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
getCallingUser())),
"s2", "s3"))));
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
/* time =*/ 1000, CALLING_PACKAGE_2, list("s3x", "s2x"),
- /* activity =*/ null,
+ /* locusIds =*/ null, /* activity =*/ null,
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
getCallingUser()))
/* empty */))));
assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
- /* time =*/ 1000, CALLING_PACKAGE_2, list(),
+ /* time =*/ 1000, CALLING_PACKAGE_2, list(), /* locusIds =*/ null,
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser()))
+ /* empty */))));
+
+ // With locus ID.
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_3, /* shortcutIds =*/ null,
+ list(makeLocusId("l2")), /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser())),
+ "s3"))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_1, /* shortcutIds =*/ null,
+ list(makeLocusId("l1"), makeLocusId("l2"), makeLocusId("l3")),
+ /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser())),
+ "s2"))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_1, /* shortcutIds =*/ null,
+ list(makeLocusId("lx1"), makeLocusId("lx2")), /* activity =*/ null,
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
+ getCallingUser()))
+ /* empty */))));
+ assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+ assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery(
+ /* time =*/ 1000, CALLING_PACKAGE_3, /* shortcutIds =*/ null, list(),
/* activity =*/ null,
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY),
getCallingUser()))
@@ -1498,7 +1529,7 @@
assertExpectException(
IllegalArgumentException.class, "package name must also be set", () -> {
mLauncherApps.getShortcuts(buildQuery(
- /* time =*/ 0, /* package= */ null, list("id"),
+ /* time =*/ 0, /* package= */ null, list("id"), /* locusIds =*/ null,
/* activity =*/ null, /* flags */ 0), getCallingUser());
});
@@ -1537,7 +1568,7 @@
assertExpectException(
IllegalArgumentException.class, "package name must also be set", () -> {
mLauncherApps.getShortcuts(buildQuery(
- /* time =*/ 0, /* package= */ null, list("id"),
+ /* time =*/ 0, /* package= */ null, list("id"), /* locusIds= */ null,
/* activity =*/ null, /* flags */ 0), getCallingUser());
});
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index ae53692..218f43c 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -33,14 +33,13 @@
import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
import android.os.TimestampedValue;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.timezonedetector.TestHandler;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -108,7 +107,7 @@
eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
anyString());
- mTestHandler.waitForEmptyQueue();
+ mTestHandler.waitForMessagesToBeProcessed();
mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
}
@@ -140,7 +139,7 @@
eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
anyString());
- mTestHandler.waitForEmptyQueue();
+ mTestHandler.waitForMessagesToBeProcessed();
mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion);
}
@@ -170,7 +169,7 @@
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
- mTestHandler.waitForEmptyQueue();
+ mTestHandler.waitForMessagesToBeProcessed();
mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion);
}
@@ -187,21 +186,23 @@
@Test
public void testAutoTimeDetectionToggle() throws Exception {
- mTimeDetectorService.handleAutoTimeDetectionToggle();
+ mTimeDetectorService.handleAutoTimeDetectionChanged();
mTestHandler.assertTotalMessagesEnqueued(1);
- mTestHandler.waitForEmptyQueue();
- mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled();
- mTimeDetectorService.handleAutoTimeDetectionToggle();
+ mStubbedTimeDetectorStrategy.resetCallTracking();
+
+ mTimeDetectorService.handleAutoTimeDetectionChanged();
mTestHandler.assertTotalMessagesEnqueued(2);
- mTestHandler.waitForEmptyQueue();
- mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled();
}
private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
- int phoneId = 1234;
+ int slotIndex = 1234;
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
- return new PhoneTimeSuggestion.Builder(phoneId)
+ return new PhoneTimeSuggestion.Builder(slotIndex)
.setUtcTime(timeValue)
.build();
}
@@ -222,7 +223,7 @@
private PhoneTimeSuggestion mLastPhoneSuggestion;
private ManualTimeSuggestion mLastManualSuggestion;
private NetworkTimeSuggestion mLastNetworkSuggestion;
- private boolean mLastAutoTimeDetectionToggleCalled;
+ private boolean mHandleAutoTimeDetectionChangedCalled;
private boolean mDumpCalled;
@Override
@@ -231,31 +232,26 @@
@Override
public void suggestPhoneTime(PhoneTimeSuggestion timeSuggestion) {
- resetCallTracking();
mLastPhoneSuggestion = timeSuggestion;
}
@Override
public void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
- resetCallTracking();
mLastManualSuggestion = timeSuggestion;
}
@Override
public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
- resetCallTracking();
mLastNetworkSuggestion = timeSuggestion;
}
@Override
public void handleAutoTimeDetectionChanged() {
- resetCallTracking();
- mLastAutoTimeDetectionToggleCalled = true;
+ mHandleAutoTimeDetectionChangedCalled = true;
}
@Override
public void dump(PrintWriter pw, String[] args) {
- resetCallTracking();
mDumpCalled = true;
}
@@ -263,7 +259,7 @@
mLastPhoneSuggestion = null;
mLastManualSuggestion = null;
mLastNetworkSuggestion = null;
- mLastAutoTimeDetectionToggleCalled = false;
+ mHandleAutoTimeDetectionChangedCalled = false;
mDumpCalled = false;
}
@@ -279,45 +275,12 @@
assertEquals(expectedSuggestion, mLastNetworkSuggestion);
}
- void verifyHandleAutoTimeDetectionToggleCalled() {
- assertTrue(mLastAutoTimeDetectionToggleCalled);
+ void verifyHandleAutoTimeDetectionChangedCalled() {
+ assertTrue(mHandleAutoTimeDetectionChangedCalled);
}
void verifyDumpCalled() {
assertTrue(mDumpCalled);
}
}
-
- /**
- * A Handler that can track posts/sends and wait for work to be completed.
- */
- private static class TestHandler extends Handler {
-
- private int mMessagesSent;
-
- TestHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- mMessagesSent++;
- return super.sendMessageAtTime(msg, uptimeMillis);
- }
-
- /** Asserts the number of messages posted or sent is as expected. */
- void assertTotalMessagesEnqueued(int expected) {
- assertEquals(expected, mMessagesSent);
- }
-
- /**
- * Waits for all currently enqueued work due to be processed to be completed before
- * returning.
- */
- void waitForEmptyQueue() throws InterruptedException {
- while (!getLooper().getQueue().isIdle()) {
- Thread.sleep(100);
- }
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java
new file mode 100644
index 0000000..21c9685
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * A Handler that can track posts/sends and wait for them to be completed.
+ */
+public class TestHandler extends Handler {
+
+ private final Object mMonitor = new Object();
+ private int mMessagesProcessed = 0;
+ private int mMessagesSent = 0;
+
+ public TestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ synchronized (mMonitor) {
+ mMessagesSent++;
+ }
+
+ Runnable callback = msg.getCallback();
+ // Have the callback increment the mMessagesProcessed when it is done. It will notify
+ // any threads waiting for all messages to be processed if appropriate.
+ Runnable newCallback = () -> {
+ callback.run();
+ synchronized (mMonitor) {
+ mMessagesProcessed++;
+ if (mMessagesSent == mMessagesProcessed) {
+ mMonitor.notifyAll();
+ }
+ }
+ };
+ msg.setCallback(newCallback);
+ return super.sendMessageAtTime(msg, uptimeMillis);
+ }
+
+ /** Asserts the number of messages posted or sent is as expected. */
+ public void assertTotalMessagesEnqueued(int expected) {
+ synchronized (mMonitor) {
+ assertEquals(expected, mMessagesSent);
+ }
+ }
+
+ /**
+ * Waits for all enqueued work to be completed before returning.
+ */
+ public void waitForMessagesToBeProcessed() throws InterruptedException {
+ synchronized (mMonitor) {
+ if (mMessagesSent != mMessagesProcessed) {
+ mMonitor.wait();
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
new file mode 100644
index 0000000..3e7d40a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.HandlerThread;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.PrintWriter;
+
+@RunWith(AndroidJUnit4.class)
+public class TimeZoneDetectorServiceTest {
+
+ private Context mMockContext;
+ private StubbedTimeZoneDetectorStrategy mStubbedTimeZoneDetectorStrategy;
+
+ private TimeZoneDetectorService mTimeZoneDetectorService;
+ private HandlerThread mHandlerThread;
+ private TestHandler mTestHandler;
+
+
+ @Before
+ public void setUp() {
+ mMockContext = mock(Context.class);
+
+ // Create a thread + handler for processing the work that the service posts.
+ mHandlerThread = new HandlerThread("TimeZoneDetectorServiceTest");
+ mHandlerThread.start();
+ mTestHandler = new TestHandler(mHandlerThread.getLooper());
+
+ mStubbedTimeZoneDetectorStrategy = new StubbedTimeZoneDetectorStrategy();
+
+ mTimeZoneDetectorService = new TimeZoneDetectorService(
+ mMockContext, mTestHandler, mStubbedTimeZoneDetectorStrategy);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ mHandlerThread.join();
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testSuggestPhoneTime_withoutPermission() {
+ doThrow(new SecurityException("Mock"))
+ .when(mMockContext).enforceCallingPermission(anyString(), any());
+ PhoneTimeZoneSuggestion timeZoneSuggestion = createPhoneTimeZoneSuggestion();
+
+ try {
+ mTimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion);
+ fail();
+ } finally {
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+ anyString());
+ }
+ }
+
+ @Test
+ public void testSuggestPhoneTimeZone() throws Exception {
+ doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+
+ PhoneTimeZoneSuggestion timeZoneSuggestion = createPhoneTimeZoneSuggestion();
+ mTimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion);
+ mTestHandler.assertTotalMessagesEnqueued(1);
+
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+ anyString());
+
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeZoneDetectorStrategy.verifySuggestPhoneTimeZoneCalled(timeZoneSuggestion);
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testSuggestManualTime_withoutPermission() {
+ doThrow(new SecurityException("Mock"))
+ .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+ ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
+
+ try {
+ mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
+ fail();
+ } finally {
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
+ anyString());
+ }
+ }
+
+ @Test
+ public void testSuggestManualTimeZone() throws Exception {
+ doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+
+ ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
+ mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
+ mTestHandler.assertTotalMessagesEnqueued(1);
+
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
+ anyString());
+
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled(timeZoneSuggestion);
+ }
+
+ @Test
+ public void testDump() {
+ when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ mTimeZoneDetectorService.dump(null, null, null);
+
+ verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
+ mStubbedTimeZoneDetectorStrategy.verifyDumpCalled();
+ }
+
+ @Test
+ public void testAutoTimeZoneDetectionChanged() throws Exception {
+ mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged();
+ mTestHandler.assertTotalMessagesEnqueued(1);
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneDetectionChangedCalled();
+
+ mStubbedTimeZoneDetectorStrategy.resetCallTracking();
+
+ mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged();
+ mTestHandler.assertTotalMessagesEnqueued(2);
+ mTestHandler.waitForMessagesToBeProcessed();
+ mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneDetectionChangedCalled();
+ }
+
+ private static PhoneTimeZoneSuggestion createPhoneTimeZoneSuggestion() {
+ int slotIndex = 1234;
+ return new PhoneTimeZoneSuggestion.Builder(slotIndex)
+ .setZoneId("TestZoneId")
+ .setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET)
+ .setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE)
+ .build();
+ }
+
+ private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() {
+ return new ManualTimeZoneSuggestion("TestZoneId");
+ }
+
+ private static class StubbedTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
+
+ // Call tracking.
+ private PhoneTimeZoneSuggestion mLastPhoneSuggestion;
+ private ManualTimeZoneSuggestion mLastManualSuggestion;
+ private boolean mHandleAutoTimeZoneDetectionChangedCalled;
+ private boolean mDumpCalled;
+
+ @Override
+ public void suggestPhoneTimeZone(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ mLastPhoneSuggestion = timeZoneSuggestion;
+ }
+
+ @Override
+ public void suggestManualTimeZone(ManualTimeZoneSuggestion timeZoneSuggestion) {
+ mLastManualSuggestion = timeZoneSuggestion;
+ }
+
+ @Override
+ public void handleAutoTimeZoneDetectionChanged() {
+ mHandleAutoTimeZoneDetectionChangedCalled = true;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
+ mDumpCalled = true;
+ }
+
+ void resetCallTracking() {
+ mLastPhoneSuggestion = null;
+ mLastManualSuggestion = null;
+ mHandleAutoTimeZoneDetectionChangedCalled = false;
+ mDumpCalled = false;
+ }
+
+ void verifySuggestPhoneTimeZoneCalled(PhoneTimeZoneSuggestion expectedSuggestion) {
+ assertEquals(expectedSuggestion, mLastPhoneSuggestion);
+ }
+
+ public void verifySuggestManualTimeZoneCalled(ManualTimeZoneSuggestion expectedSuggestion) {
+ assertEquals(expectedSuggestion, mLastManualSuggestion);
+ }
+
+ void verifyHandleAutoTimeZoneDetectionChangedCalled() {
+ assertTrue(mHandleAutoTimeZoneDetectionChangedCalled);
+ }
+
+ void verifyDumpCalled() {
+ assertTrue(mDumpCalled);
+ }
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 2429cfc..1e38711 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -24,12 +24,12 @@
import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_HIGH;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_HIGHEST;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_LOW;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_MEDIUM;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_NONE;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_USAGE_THRESHOLD;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_HIGH;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_HIGHEST;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_LOW;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_MEDIUM;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_NONE;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_USAGE_THRESHOLD;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -41,7 +41,7 @@
import android.app.timezonedetector.PhoneTimeZoneSuggestion.MatchType;
import android.app.timezonedetector.PhoneTimeZoneSuggestion.Quality;
-import com.android.server.timezonedetector.TimeZoneDetectorStrategy.QualifiedPhoneTimeZoneSuggestion;
+import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedPhoneTimeZoneSuggestion;
import org.junit.Before;
import org.junit.Test;
@@ -52,9 +52,9 @@
import java.util.LinkedList;
/**
- * White-box unit tests for {@link TimeZoneDetectorStrategy}.
+ * White-box unit tests for {@link TimeZoneDetectorStrategyImpl}.
*/
-public class TimeZoneDetectorStrategyTest {
+public class TimeZoneDetectorStrategyImplTest {
/** A time zone used for initialization that does not occur elsewhere in tests. */
private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
@@ -78,14 +78,14 @@
newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGHEST),
};
- private TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
+ private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
private FakeTimeZoneDetectorStrategyCallback mFakeTimeZoneDetectorStrategyCallback;
@Before
public void setUp() {
mFakeTimeZoneDetectorStrategyCallback = new FakeTimeZoneDetectorStrategyCallback();
mTimeZoneDetectorStrategy =
- new TimeZoneDetectorStrategy(mFakeTimeZoneDetectorStrategyCallback);
+ new TimeZoneDetectorStrategyImpl(mFakeTimeZoneDetectorStrategyCallback);
}
@Test
@@ -364,7 +364,7 @@
}
/**
- * The {@link TimeZoneDetectorStrategy.Callback} is left to detect whether changing the time
+ * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time
* zone is actually necessary. This test proves that the service doesn't assume it knows the
* current setting.
*/
@@ -441,7 +441,8 @@
return new PhoneTimeZoneSuggestion.Builder(PHONE2_ID).build();
}
- static class FakeTimeZoneDetectorStrategyCallback implements TimeZoneDetectorStrategy.Callback {
+ static class FakeTimeZoneDetectorStrategyCallback
+ implements TimeZoneDetectorStrategyImpl.Callback {
private boolean mAutoTimeZoneDetectionEnabled;
private TestState<String> mTimeZoneId = new TestState<>();
@@ -560,7 +561,7 @@
Script autoTimeZoneDetectionEnabled(boolean enabled) {
mFakeTimeZoneDetectorStrategyCallback.setAutoTimeZoneDetectionEnabled(enabled);
- mTimeZoneDetectorStrategy.handleAutoTimeZoneDetectionChange();
+ mTimeZoneDetectorStrategy.handleAutoTimeZoneDetectionChanged();
return this;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ad5be43..e0ee3ce 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -70,6 +70,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -3644,6 +3645,33 @@
}
@Test
+ public void updateUriPermissions_posterDoesNotOwnUri() throws Exception {
+ NotificationChannel c = new NotificationChannel(
+ TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
+ c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ Message message1 = new Message("", 0, "");
+ message1.setData("",
+ ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1));
+
+ Notification.Builder nbA = new Notification.Builder(mContext, c.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setStyle(new Notification.MessagingStyle("")
+ .addMessage(message1));
+ NotificationRecord recordA = new NotificationRecord(mContext, new StatusBarNotification(
+ PKG, PKG, 0, "tag", mUid, 0, nbA.build(), new UserHandle(mUid), null, 0), c);
+
+ doThrow(new SecurityException("no access")).when(mUgm)
+ .grantUriPermissionFromOwner(
+ any(), anyInt(), any(), any(), anyInt(), anyInt(), anyInt());
+
+ when(mUgmInternal.newUriPermissionOwner(any())).thenReturn(new Binder());
+ mService.updateUriPermissions(recordA, null, mContext.getPackageName(), USER_SYSTEM);
+
+ // yay, no crash
+ }
+
+ @Test
public void testVisitUris() throws Exception {
final Uri audioContents = Uri.parse("content://com.example/audio");
final Uri backgroundImage = Uri.parse("content://com.example/background");
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 5acc0f2..956c200 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -126,7 +126,8 @@
mTarget = new TestDragDropController(mWm, mWm.mH.getLooper());
mWindow = createDropTargetWindow("Drag test window", 0);
doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
- when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
+ when(mWm.mInputManager.transferTouchFocus(any(InputChannel.class),
+ any(InputChannel.class))).thenReturn(true);
mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
}
@@ -176,7 +177,8 @@
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- assertTrue(mWm.mInputManager.transferTouchFocus(null, null));
+ assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
+ new InputChannel()));
mToken = mTarget.performDrag(
new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
data);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 078347e..a9a20f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -139,6 +139,52 @@
}
@Test
+ public void testUnregisterOrganizer() throws RemoteException {
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final ITaskOrganizer organizer = registerMockOrganizer();
+
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ verify(organizer).taskAppeared(any());
+ assertTrue(stack.isControlledByTaskOrganizer());
+
+ mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
+ verify(organizer).taskVanished(any());
+ assertFalse(stack.isControlledByTaskOrganizer());
+ }
+
+ @Test
+ public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException {
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
+ final Task task2 = createTaskInStack(stack2, 0 /* userId */);
+ final ActivityStack stack3 = createTaskStackOnDisplay(mDisplayContent);
+ final Task task3 = createTaskInStack(stack3, 0 /* userId */);
+ final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+
+ // First organizer is registered, verify a task appears when changing windowing mode
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ verify(organizer, times(1)).taskAppeared(any());
+ assertTrue(stack.isControlledByTaskOrganizer());
+
+ // Now we replace the registration and1 verify the new organizer receives tasks
+ // newly entering the windowing mode.
+ final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+ stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ verify(organizer2).taskAppeared(any());
+ assertTrue(stack2.isControlledByTaskOrganizer());
+
+ // Now we unregister the second one, the first one should automatically be reregistered
+ // so we verify that it's now seeing changes.
+ mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2);
+
+ stack3.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ verify(organizer, times(2)).taskAppeared(any());
+ assertTrue(stack3.isControlledByTaskOrganizer());
+ }
+
+ @Test
public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 6723522..e3d031d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -11290,14 +11290,6 @@
*/
public static final int INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF = 2;
- /** @hide */
- @IntDef(prefix = { "INDICATION_UPDATE_MODE_" }, value = {
- INDICATION_UPDATE_MODE_NORMAL,
- INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface IndicationUpdateMode{}
-
/**
* The indication for signal strength update.
* @hide
@@ -11328,51 +11320,6 @@
*/
public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG = 0x10;
- /** @hide */
- @IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = {
- INDICATION_FILTER_SIGNAL_STRENGTH,
- INDICATION_FILTER_FULL_NETWORK_STATE,
- INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED,
- INDICATION_FILTER_LINK_CAPACITY_ESTIMATE,
- INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface IndicationFilters{}
-
- /**
- * Sets radio indication update mode. This can be used to control the behavior of indication
- * update from modem to Android frameworks. For example, by default several indication updates
- * are turned off when screen is off, but in some special cases (e.g. carkit is connected but
- * screen is off) we want to turn on those indications even when the screen is off.
- *
- * <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
- *
- * @param filters Indication filters. Should be a bitmask of INDICATION_FILTER_XXX.
- * @see #INDICATION_FILTER_SIGNAL_STRENGTH
- * @see #INDICATION_FILTER_FULL_NETWORK_STATE
- * @see #INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED
- * @param updateMode The voice activation state
- * @see #INDICATION_UPDATE_MODE_NORMAL
- * @see #INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void setRadioIndicationUpdateMode(@IndicationFilters int filters,
- @IndicationUpdateMode int updateMode) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- telephony.setRadioIndicationUpdateMode(getSubId(), filters, updateMode);
- }
- } catch (RemoteException ex) {
- // This could happen if binder process crashes.
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
- }
- }
-
/**
* A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2,
* plmn and spn. This would be handy for, eg, forcing a particular carrier id, carrier's config
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index beb3c8c..168c8b6 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1827,14 +1827,6 @@
boolean switchSlots(in int[] physicalSlots);
/**
- * Sets radio indication update mode. This can be used to control the behavior of indication
- * update from modem to Android frameworks. For example, by default several indication updates
- * are turned off when screen is off, but in some special cases (e.g. carkit is connected but
- * screen is off) we want to turn on those indications even when the screen is off.
- */
- void setRadioIndicationUpdateMode(int subId, int filters, int mode);
-
- /**
* Returns whether mobile data roaming is enabled on the subscription with id {@code subId}.
*
* @param subId the subscription id
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 14b847f..5f95bc1 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -1249,12 +1249,4 @@
int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
throw new UnsupportedOperationException();
}
-
- /**
- * @hide
- */
- @Override
- public String getSystemTextClassifierPackageName() {
- throw new UnsupportedOperationException();
- }
}
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 2d5df4f..0628691 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -38,6 +38,8 @@
import android.content.Context;
import android.os.PersistableBundle;
+import androidx.test.InstrumentationRegistry;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -58,21 +60,26 @@
private static final Executor INLINE_EXECUTOR = x -> x.run();
- @Mock private Context mContext;
@Mock private IConnectivityManager mService;
@Mock private ConnectivityDiagnosticsCallback mCb;
+ private Context mContext;
private ConnectivityDiagnosticsBinder mBinder;
private ConnectivityDiagnosticsManager mManager;
+ private String mPackageName;
+
@Before
public void setUp() {
- mContext = mock(Context.class);
+ mContext = InstrumentationRegistry.getContext();
+
mService = mock(IConnectivityManager.class);
mCb = mock(ConnectivityDiagnosticsCallback.class);
mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR);
mManager = new ConnectivityDiagnosticsManager(mContext, mService);
+
+ mPackageName = mContext.getOpPackageName();
}
@After
@@ -271,7 +278,7 @@
mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
verify(mService).registerConnectivityDiagnosticsCallback(
- any(ConnectivityDiagnosticsBinder.class), eq(request));
+ any(ConnectivityDiagnosticsBinder.class), eq(request), eq(mPackageName));
assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
}
@@ -302,7 +309,7 @@
// verify that re-registering is successful
mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
verify(mService, times(2)).registerConnectivityDiagnosticsCallback(
- any(ConnectivityDiagnosticsBinder.class), eq(request));
+ any(ConnectivityDiagnosticsBinder.class), eq(request), eq(mPackageName));
assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2573bba..b4f32e7 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,6 +23,8 @@
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL;
@@ -119,6 +121,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.app.AlarmManager;
+import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -132,6 +135,7 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.location.LocationManager;
import android.net.CaptivePortalData;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
@@ -177,6 +181,7 @@
import android.net.util.MultinetworkPolicyTracker;
import android.os.BadParcelableException;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -187,6 +192,7 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -218,6 +224,7 @@
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.Vpn;
@@ -292,6 +299,8 @@
private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 1000;
+ private static final long TIMESTAMP = 1234L;
+
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
@@ -327,6 +336,8 @@
@Mock AlarmManager mAlarmManager;
@Mock IConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback;
@Mock IBinder mIBinder;
+ @Mock LocationManager mLocationManager;
+ @Mock AppOpsManager mAppOpsManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -412,6 +423,8 @@
if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
if (Context.USER_SERVICE.equals(name)) return mUserManager;
if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
+ if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
+ if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
return super.getSystemService(name);
}
@@ -558,12 +571,17 @@
| NETWORK_VALIDATION_RESULT_PARTIAL;
private static final int VALIDATION_RESULT_INVALID = 0;
+ private static final long DATA_STALL_TIMESTAMP = 10L;
+ private static final int DATA_STALL_DETECTION_METHOD = 1;
+
private INetworkMonitor mNetworkMonitor;
private INetworkMonitorCallbacks mNmCallbacks;
private int mNmValidationResult = VALIDATION_RESULT_BASE;
private int mProbesCompleted;
private int mProbesSucceeded;
private String mNmValidationRedirectUrl = null;
+ private PersistableBundle mValidationExtras = PersistableBundle.EMPTY;
+ private PersistableBundle mDataStallExtras = PersistableBundle.EMPTY;
private boolean mNmProvNotificationRequested = false;
private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
@@ -631,8 +649,8 @@
}
mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded);
- mNmCallbacks.notifyNetworkTested(
- mNmValidationResult, mNmValidationRedirectUrl);
+ mNmCallbacks.notifyNetworkTestedWithExtras(
+ mNmValidationResult, mNmValidationRedirectUrl, TIMESTAMP, mValidationExtras);
if (mNmValidationRedirectUrl != null) {
mNmCallbacks.showProvisioningNotification(
@@ -791,6 +809,11 @@
public void expectPreventReconnectReceived() {
expectPreventReconnectReceived(TIMEOUT_MS);
}
+
+ void notifyDataStallSuspected() throws Exception {
+ mNmCallbacks.notifyDataStallSuspected(
+ DATA_STALL_TIMESTAMP, DATA_STALL_DETECTION_METHOD, mDataStallExtras);
+ }
}
/**
@@ -970,6 +993,8 @@
// not inherit from NetworkAgent.
private TestNetworkAgentWrapper mMockNetworkAgent;
+ private VpnInfo mVpnInfo;
+
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
userId);
@@ -1041,6 +1066,17 @@
mConnected = false;
mConfig = null;
}
+
+ @Override
+ public synchronized VpnInfo getVpnInfo() {
+ if (mVpnInfo != null) return mVpnInfo;
+
+ return super.getVpnInfo();
+ }
+
+ private void setVpnInfo(VpnInfo vpnInfo) {
+ mVpnInfo = vpnInfo;
+ }
}
private void mockVpn(int uid) {
@@ -6400,7 +6436,7 @@
new NetworkCapabilities(), TYPE_ETHERNET, 0, NetworkRequest.Type.NONE);
try {
mService.registerConnectivityDiagnosticsCallback(
- mConnectivityDiagnosticsCallback, request);
+ mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
fail("registerConnectivityDiagnosticsCallback should throw on invalid NetworkRequest");
} catch (IllegalArgumentException expected) {
}
@@ -6414,7 +6450,7 @@
when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
mService.registerConnectivityDiagnosticsCallback(
- mConnectivityDiagnosticsCallback, wifiRequest);
+ mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
verify(mIBinder, timeout(TIMEOUT_MS))
.linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
@@ -6438,7 +6474,7 @@
when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
mService.registerConnectivityDiagnosticsCallback(
- mConnectivityDiagnosticsCallback, wifiRequest);
+ mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
verify(mIBinder, timeout(TIMEOUT_MS))
.linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
@@ -6449,7 +6485,7 @@
// Register the same callback again
mService.registerConnectivityDiagnosticsCallback(
- mConnectivityDiagnosticsCallback, wifiRequest);
+ mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
// Block until all other events are done processing.
HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
@@ -6458,4 +6494,182 @@
mService.mConnectivityDiagnosticsCallbacks.containsKey(
mConnectivityDiagnosticsCallback));
}
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), null,
+ mServiceContext, null, null, mService, null, null, null, 0);
+
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+ assertTrue(
+ "NetworkStack permission not applied",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithoutUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), null,
+ mServiceContext, null, null, mService, null, null, null, 0);
+
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ assertFalse(
+ "ACCESS_FINE_LOCATION permission necessary for Connectivity Diagnostics",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithoutUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), null,
+ mServiceContext, null, null, mService, null, null, null, 0);
+
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be
+ // active
+ final VpnInfo info = new VpnInfo();
+ info.ownerUid = Process.myUid();
+ info.vpnIface = "interface";
+ mMockVpn.setVpnInfo(info);
+ assertTrue(
+ "Active VPN permission not applied",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithoutUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+ final NetworkAgentInfo naiWithUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, nc, null, mServiceContext, null, null,
+ mService, null, null, null, 0);
+
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ // Disconnect mock vpn so the uid check on NetworkAgentInfo is tested
+ mMockVpn.disconnect();
+ assertTrue(
+ "NetworkCapabilities administrator uid permission not applied",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithUid, mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsFails() throws Exception {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setOwnerUid(Process.myUid());
+ nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+ final NetworkAgentInfo naiWithUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, nc, null, mServiceContext, null, null,
+ mService, null, null, null, 0);
+
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ // Use wrong pid and uid
+ assertFalse(
+ "Permissions allowed when they shouldn't be granted",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid() + 1, Process.myUid() + 1, naiWithUid,
+ mContext.getOpPackageName()));
+ }
+
+ private void setupLocationPermissions(
+ int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = targetSdk;
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+ .thenReturn(applicationInfo);
+
+ when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
+
+ when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+
+ mServiceContext.setPermission(perm, PERMISSION_GRANTED);
+ }
+
+ private void setUpConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ // Connect the cell agent verify that it notifies TestNetworkCallback that it is available
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callback.assertNoCallback();
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReport() throws Exception {
+ setUpConnectivityDiagnosticsCallback();
+
+ // Wait for onConnectivityReport to fire
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onConnectivityReport(any(ConnectivityReport.class));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() throws Exception {
+ setUpConnectivityDiagnosticsCallback();
+
+ // Trigger notifyDataStallSuspected() on the INetworkMonitorCallbacks instance in the
+ // cellular network agent
+ mCellNetworkAgent.notifyDataStallSuspected();
+
+ // Wait for onDataStallSuspected to fire
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onDataStallSuspected(any(DataStallReport.class));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReported() throws Exception {
+ setUpConnectivityDiagnosticsCallback();
+
+ final Network n = mCellNetworkAgent.getNetwork();
+ final boolean hasConnectivity = true;
+ mService.reportNetworkConnectivity(n, hasConnectivity);
+
+ // Wait for onNetworkConnectivityReported to fire
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onNetworkConnectivityReported(eq(n), eq(hasConnectivity));
+
+ final boolean noConnectivity = false;
+ mService.reportNetworkConnectivity(n, noConnectivity);
+
+ // Wait for onNetworkConnectivityReported to fire
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onNetworkConnectivityReported(eq(n), eq(noConnectivity));
+ }
}
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index d3958a6..a251c05 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -30,7 +30,7 @@
"utils.cpp",
],
cflags: [
- "-DSTATS_SCHEMA_LEGACY",
+ //"-DSTATS_SCHEMA_LEGACY",
"-Wall",
"-Werror",
],
diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp
index a68c3a2..12c050d 100644
--- a/tools/stats_log_api_gen/java_writer_q.cpp
+++ b/tools/stats_log_api_gen/java_writer_q.cpp
@@ -175,9 +175,7 @@
indent.c_str());
fprintf(out,
"%s android.util.SparseArray<Float> floatMap = null;\n", indent.c_str());
- fprintf(out,
- "%s int keyValuePairSize = LIST_TYPE_OVERHEAD * 5;\n",
- indent.c_str());
+ fprintf(out, "%s int keyValuePairSize = LIST_TYPE_OVERHEAD;\n", indent.c_str());
fprintf(out,
"%s for (int i = 0; i < count; i++) {\n", indent.c_str());
fprintf(out,
@@ -360,8 +358,9 @@
requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
requiredHelpers |= JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS;
fprintf(out,
- "%s writeKeyValuePairs(buff, pos, intMap, longMap, stringMap, "
- "floatMap);\n", indent.c_str());
+ "%s writeKeyValuePairs(buff, pos, (byte) count, intMap, longMap, "
+ "stringMap, floatMap);\n",
+ indent.c_str());
fprintf(out, "%s pos += keyValuePairSize;\n", indent.c_str());
break;
default:
@@ -472,7 +471,8 @@
}
if (requiredHelpers & JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS) {
- fprintf(out, "%sprivate static void writeKeyValuePairs(byte[] buff, int pos,\n",
+ fprintf(out,
+ "%sprivate static void writeKeyValuePairs(byte[] buff, int pos, byte numPairs,\n",
indent.c_str());
fprintf(out, "%s final android.util.SparseIntArray intMap,\n", indent.c_str());
fprintf(out, "%s final android.util.SparseLongArray longMap,\n", indent.c_str());
@@ -483,15 +483,12 @@
// Start list of lists.
fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) 4;\n", indent.c_str());
+ fprintf(out, "%s buff[pos + 1] = (byte) numPairs;\n", indent.c_str());
fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
// Write integers.
fprintf(out, "%s final int intMapSize = null == intMap ? 0 : intMap.size();\n",
indent.c_str());
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) intMapSize;\n", indent.c_str());
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
fprintf(out, "%s for (int i = 0; i < intMapSize; i++) {\n", indent.c_str());
fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str());
@@ -509,9 +506,6 @@
// Write longs.
fprintf(out, "%s final int longMapSize = null == longMap ? 0 : longMap.size();\n",
indent.c_str());
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) longMapSize;\n", indent.c_str());
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
fprintf(out, "%s for (int i = 0; i < longMapSize; i++) {\n", indent.c_str());
fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str());
@@ -529,9 +523,6 @@
// Write Strings.
fprintf(out, "%s final int stringMapSize = null == stringMap ? 0 : stringMap.size();\n",
indent.c_str());
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) stringMapSize;\n", indent.c_str());
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
fprintf(out, "%s for (int i = 0; i < stringMapSize; i++) {\n", indent.c_str());
fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str());
@@ -556,9 +547,6 @@
// Write floats.
fprintf(out, "%s final int floatMapSize = null == floatMap ? 0 : floatMap.size();\n",
indent.c_str());
- fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
- fprintf(out, "%s buff[pos + 1] = (byte) floatMapSize;\n", indent.c_str());
- fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
fprintf(out, "%s for (int i = 0; i < floatMapSize; i++) {\n", indent.c_str());
fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str());
fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str());
diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp
index c7a34fe..285514d 100644
--- a/tools/stats_log_api_gen/native_writer.cpp
+++ b/tools/stats_log_api_gen/native_writer.cpp
@@ -22,15 +22,6 @@
namespace stats_log_api_gen {
#if !defined(STATS_SCHEMA_LEGACY)
-static void write_native_key_value_pairs_for_type(FILE* out, const int argIndex,
- const int typeIndex, const string& type, const string& valueFieldName) {
- fprintf(out, " for (const auto& it : arg%d_%d) {\n", argIndex, typeIndex);
- fprintf(out, " pairs.push_back("
- "{ .key = it.first, .valueType = %s, .%s = it.second });\n",
- type.c_str(), valueFieldName.c_str());
- fprintf(out, " }\n");
-
-}
static int write_native_stats_write_methods(FILE* out, const Atoms& atoms,
const AtomDecl& attributionDecl, const string& moduleName, const bool supportQ) {
@@ -41,7 +32,10 @@
continue;
}
vector<java_type_t> signature = signature_to_modules_it->first;
-
+ // Key value pairs not supported in native.
+ if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+ continue;
+ }
write_native_method_signature(out, "int stats_write", signature,
attributionDecl, " {");
@@ -59,11 +53,6 @@
uidName, uidName, tagName);
break;
}
- case JAVA_TYPE_KEY_VALUE_PAIR:
- fprintf(out, " event.writeKeyValuePairs("
- "arg%d_1, arg%d_2, arg%d_3, arg%d_4);\n",
- argIndex, argIndex, argIndex, argIndex);
- break;
case JAVA_TYPE_BYTE_ARRAY:
fprintf(out, " event.writeByteArray(arg%d.arg, arg%d.arg_length);\n",
argIndex, argIndex);
@@ -85,7 +74,7 @@
fprintf(out, " event.writeString(arg%d);\n", argIndex);
break;
default:
- // Unsupported types: OBJECT, DOUBLE.
+ // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS.
fprintf(stderr, "Encountered unsupported type.");
return 1;
}
@@ -93,8 +82,8 @@
}
fprintf(out, " return event.writeToSocket();\n");
} else {
- fprintf(out, " struct stats_event* event = stats_event_obtain();\n");
- fprintf(out, " stats_event_set_atom_id(event, code);\n");
+ fprintf(out, " AStatsEvent* event = AStatsEvent_obtain();\n");
+ fprintf(out, " AStatsEvent_setAtomId(event, code);\n");
for (vector<java_type_t>::const_iterator arg = signature.begin();
arg != signature.end(); arg++) {
switch (*arg) {
@@ -102,57 +91,43 @@
const char* uidName = attributionDecl.fields.front().name.c_str();
const char* tagName = attributionDecl.fields.back().name.c_str();
fprintf(out,
- " stats_event_write_attribution_chain(event, "
+ " AStatsEvent_writeAttributionChain(event, "
"reinterpret_cast<const uint32_t*>(%s), %s.data(), "
"static_cast<uint8_t>(%s_length));\n",
uidName, tagName, uidName);
break;
}
- case JAVA_TYPE_KEY_VALUE_PAIR:
- fprintf(out, " std::vector<key_value_pair> pairs;\n");
- write_native_key_value_pairs_for_type(
- out, argIndex, 1, "INT32_TYPE", "int32Value");
- write_native_key_value_pairs_for_type(
- out, argIndex, 2, "INT64_TYPE", "int64Value");
- write_native_key_value_pairs_for_type(
- out, argIndex, 3, "STRING_TYPE", "stringValue");
- write_native_key_value_pairs_for_type(
- out, argIndex, 4, "FLOAT_TYPE", "floatValue");
- fprintf(out,
- " stats_event_write_key_value_pairs(event, pairs.data(), "
- "static_cast<uint8_t>(pairs.size()));\n");
- break;
case JAVA_TYPE_BYTE_ARRAY:
fprintf(out,
- " stats_event_write_byte_array(event, "
+ " AStatsEvent_writeByteArray(event, "
"reinterpret_cast<const uint8_t*>(arg%d.arg), arg%d.arg_length);\n",
argIndex, argIndex);
break;
case JAVA_TYPE_BOOLEAN:
- fprintf(out, " stats_event_write_bool(event, arg%d);\n", argIndex);
+ fprintf(out, " AStatsEvent_writeBool(event, arg%d);\n", argIndex);
break;
case JAVA_TYPE_INT: // Fall through.
case JAVA_TYPE_ENUM:
- fprintf(out, " stats_event_write_int32(event, arg%d);\n", argIndex);
+ fprintf(out, " AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
break;
case JAVA_TYPE_FLOAT:
- fprintf(out, " stats_event_write_float(event, arg%d);\n", argIndex);
+ fprintf(out, " AStatsEvent_writeFloat(event, arg%d);\n", argIndex);
break;
case JAVA_TYPE_LONG:
- fprintf(out, " stats_event_write_int64(event, arg%d);\n", argIndex);
+ fprintf(out, " AStatsEvent_writeInt64(event, arg%d);\n", argIndex);
break;
case JAVA_TYPE_STRING:
- fprintf(out, " stats_event_write_string8(event, arg%d);\n", argIndex);
+ fprintf(out, " AStatsEvent_writeString(event, arg%d);\n", argIndex);
break;
default:
- // Unsupported types: OBJECT, DOUBLE.
+ // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS
fprintf(stderr, "Encountered unsupported type.");
return 1;
}
argIndex++;
}
- fprintf(out, " const int ret = stats_event_write(event);\n");
- fprintf(out, " stats_event_release(event);\n");
+ fprintf(out, " const int ret = AStatsEvent_write(event);\n");
+ fprintf(out, " AStatsEvent_release(event);\n");
fprintf(out, " return ret;\n");
}
fprintf(out, "}\n\n");
@@ -169,6 +144,10 @@
continue;
}
vector<java_type_t> signature = signature_it->first;
+ // Key value pairs not supported in native.
+ if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+ continue;
+ }
write_native_method_signature(out, "int stats_write_non_chained", signature,
attributionDecl, " {");
@@ -210,8 +189,14 @@
if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
continue;
}
-
vector<java_type_t> signature = signature_to_modules_it->first;
+
+#if !defined(STATS_SCHEMA_LEGACY)
+ // Key value pairs not supported in native.
+ if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+ continue;
+ }
+#endif
write_native_method_signature(out, methodName, signature, attributionDecl, ";");
}
}
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
index d6b9d81..fe9a438 100644
--- a/tools/streaming_proto/cpp/main.cpp
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -33,13 +33,13 @@
if (GENERATE_MAPPING) {
string name = make_constant_name(enu.name());
string prefix = name + "_";
- text << indent << "const int _ENUM_" << name << "_COUNT = " << N << ";" << endl;
- text << indent << "const char* _ENUM_" << name << "_NAMES[" << N << "] = {" << endl;
+ text << indent << "static const int _ENUM_" << name << "_COUNT = " << N << ";" << endl;
+ text << indent << "static const char* _ENUM_" << name << "_NAMES[" << N << "] = {" << endl;
for (int i=0; i<N; i++) {
text << indent << INDENT << "\"" << stripPrefix(enu.value(i).name(), prefix) << "\"," << endl;
}
text << indent << "};" << endl;
- text << indent << "const int _ENUM_" << name << "_VALUES[" << N << "] = {" << endl;
+ text << indent << "static const int _ENUM_" << name << "_VALUES[" << N << "] = {" << endl;
for (int i=0; i<N; i++) {
text << indent << INDENT << make_constant_name(enu.value(i).name()) << "," << endl;
}
@@ -102,13 +102,13 @@
if (GENERATE_MAPPING) {
N = message.field_size();
- text << indented << "const int _FIELD_COUNT = " << N << ";" << endl;
- text << indented << "const char* _FIELD_NAMES[" << N << "] = {" << endl;
+ text << indented << "static const int _FIELD_COUNT = " << N << ";" << endl;
+ text << indented << "static const char* _FIELD_NAMES[" << N << "] = {" << endl;
for (int i=0; i<N; i++) {
text << indented << INDENT << "\"" << message.field(i).name() << "\"," << endl;
}
text << indented << "};" << endl;
- text << indented << "const uint64_t _FIELD_IDS[" << N << "] = {" << endl;
+ text << indented << "static const uint64_t _FIELD_IDS[" << N << "] = {" << endl;
for (int i=0; i<N; i++) {
text << indented << INDENT << make_constant_name(message.field(i).name()) << "," << endl;
}
@@ -152,7 +152,7 @@
write_message(text, file_descriptor.message_type(i), "");
}
- for (vector<string>::iterator it = namespaces.begin(); it != namespaces.end(); it++) {
+ for (vector<string>::reverse_iterator it = namespaces.rbegin(); it != namespaces.rend(); it++) {
text << "} // " << *it << endl;
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 91b7df3..62d6067 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -568,14 +568,12 @@
* 2GHz band.
* @hide
*/
- @SystemApi
public static final int AP_BAND_2GHZ = 0;
/**
* 5GHz band.
* @hide
*/
- @SystemApi
public static final int AP_BAND_5GHZ = 1;
/**
@@ -583,7 +581,6 @@
* operating country code and current radio conditions.
* @hide
*/
- @SystemApi
public static final int AP_BAND_ANY = -1;
/**
@@ -593,7 +590,7 @@
*
* @hide
*/
- @SystemApi
+ @UnsupportedAppUsage
@ApBand
public int apBand = AP_BAND_2GHZ;
diff --git a/wifi/java/android/net/wifi/WifiFrameworkInitializer.java b/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
index 002820b..1507199 100644
--- a/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
+++ b/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
@@ -102,15 +102,6 @@
}
);
SystemServiceRegistry.registerContextAwareService(
- Context.WIFI_RTT_SERVICE,
- RttManager.class,
- (context, serviceBinder) -> {
- IWifiRttManager service = IWifiRttManager.Stub.asInterface(serviceBinder);
- WifiRttManager wifiRttManager = new WifiRttManager(context, service);
- return new RttManager(context, wifiRttManager);
- }
- );
- SystemServiceRegistry.registerContextAwareService(
Context.WIFI_RTT_RANGING_SERVICE,
WifiRttManager.class,
(context, serviceBinder) -> {
@@ -118,5 +109,13 @@
return new WifiRttManager(context, service);
}
);
+ SystemServiceRegistry.registerContextAwareService(
+ Context.WIFI_RTT_SERVICE,
+ RttManager.class,
+ context -> {
+ WifiRttManager wifiRttManager = context.getSystemService(WifiRttManager.class);
+ return new RttManager(context, wifiRttManager);
+ }
+ );
}
}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index a85f40b..b4eb30b 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -293,26 +293,34 @@
public final List<HiddenNetwork> hiddenNetworks = new ArrayList<>();
/**
* period of background scan; in millisecond, 0 => single shot scan
- * @deprecated Background scan support is removed.
+ * @deprecated Background scan support has always been hardware vendor dependent. This
+ * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
+ * ScanListener)} instead for single scans.
*/
@Deprecated
public int periodInMs;
/**
* must have a valid REPORT_EVENT value
- * @deprecated Background scan support is removed.
+ * @deprecated Background scan support has always been hardware vendor dependent. This
+ * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
+ * ScanListener)} instead for single scans.
*/
@Deprecated
public int reportEvents;
/**
* defines number of bssids to cache from each scan
- * @deprecated Background scan support is removed.
+ * @deprecated Background scan support has always been hardware vendor dependent. This
+ * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
+ * ScanListener)} instead for single scans.
*/
@Deprecated
public int numBssidsPerScan;
/**
* defines number of scans to cache; use it with REPORT_EVENT_AFTER_BUFFER_FULL
* to wake up at fixed interval
- * @deprecated Background scan support is removed.
+ * @deprecated Background scan support has always been hardware vendor dependent. This
+ * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
+ * ScanListener)} instead for single scans.
*/
@Deprecated
public int maxScansToCache;
@@ -321,14 +329,18 @@
* a truncated binary exponential backoff bucket and the scan period will grow
* exponentially as per formula: actual_period(N) = period * (2 ^ (N/stepCount))
* to maxPeriodInMs
- * @deprecated Background scan support is removed.
+ * @deprecated Background scan support has always been hardware vendor dependent. This
+ * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
+ * ScanListener)} instead for single scans.
*/
@Deprecated
public int maxPeriodInMs;
/**
* for truncated binary exponential back off bucket, number of scans to perform
* for a given period
- * @deprecated Background scan support is removed.
+ * @deprecated Background scan support has always been hardware vendor dependent. This
+ * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
+ * ScanListener)} instead for single scans.
*/
@Deprecated
public int stepCount;
@@ -806,7 +818,9 @@
/**
* Framework co-ordinates scans across multiple apps; so it may not give exactly the
* same period requested. If period of a scan is changed; it is reported by this event.
- * @deprecated Background scan support is removed.
+ * @deprecated Background scan support has always been hardware vendor dependent. This
+ * support may not be present on newer devices. Use {@link #startScan(ScanSettings,
+ * ScanListener)} instead for single scans.
*/
@Deprecated
public void onPeriodChanged(int periodInMs);
@@ -913,7 +927,9 @@
* @param listener specifies the object to report events to. This object is also treated as a
* key for this scan, and must also be specified to cancel the scan. Multiple
* scans should also not share this object.
- * @deprecated Background scan support is removed.
+ * @deprecated Background scan support has always been hardware vendor dependent. This support
+ * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)}
+ * instead for single scans.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
@@ -935,7 +951,9 @@
* stop an ongoing wifi scan
* @param listener specifies which scan to cancel; must be same object as passed in {@link
* #startBackgroundScan}
- * @deprecated Background scan support is removed.
+ * @deprecated Background scan support has always been hardware vendor dependent. This support
+ * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)}
+ * instead for single scans.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
@@ -953,7 +971,9 @@
/**
* reports currently available scan results on appropriate listeners
* @return true if all scan results were reported correctly
- * @deprecated Background scan support is removed.
+ * @deprecated Background scan support has always been hardware vendor dependent. This support
+ * may not be present on newer devices. Use {@link #startScan(ScanSettings, ScanListener)}
+ * instead for single scans.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
diff --git a/wifi/java/android/net/wifi/wificond/NativeScanResult.java b/wifi/java/android/net/wifi/wificond/NativeScanResult.java
index 85251e8b..7cc617d 100644
--- a/wifi/java/android/net/wifi/wificond/NativeScanResult.java
+++ b/wifi/java/android/net/wifi/wificond/NativeScanResult.java
@@ -24,7 +24,6 @@
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
-import java.util.BitSet;
import java.util.List;
/**
@@ -34,8 +33,6 @@
*/
@SystemApi
public final class NativeScanResult implements Parcelable {
- private static final int CAPABILITY_SIZE = 16;
-
/** @hide */
@VisibleForTesting
public byte[] ssid;
@@ -56,7 +53,7 @@
public long tsf;
/** @hide */
@VisibleForTesting
- public BitSet capability;
+ public int capability;
/** @hide */
@VisibleForTesting
public boolean associated;
@@ -134,7 +131,7 @@
* Returns the capabilities of the AP repseresented by this scan result as advertised in the
* received probe response or beacon.
*
- * This is a bit mask describing the capabilities of a BSS. See IEEE Std 802.11: 8.4.1.4:
+ * This is a bit mask describing the capabilities of a BSS. See IEEE Std 802.11: 9.4.1.4:
* Bit 0 - ESS
* Bit 1 - IBSS
* Bit 2 - CF Pollable
@@ -143,7 +140,7 @@
* Bit 5 - Short Preamble
* Bit 6 - PBCC
* Bit 7 - Channel Agility
- * Bit 8 - Spectrum Mgmt
+ * Bit 8 - Spectrum Management
* Bit 9 - QoS
* Bit 10 - Short Slot Time
* Bit 11 - APSD
@@ -154,7 +151,7 @@
*
* @return a bit mask of capabilities.
*/
- @NonNull public BitSet getCapabilities() {
+ @NonNull public int getCapabilities() {
return capability;
}
@@ -188,13 +185,7 @@
out.writeInt(frequency);
out.writeInt(signalMbm);
out.writeLong(tsf);
- int capabilityInt = 0;
- for (int i = 0; i < CAPABILITY_SIZE; i++) {
- if (capability.get(i)) {
- capabilityInt |= 1 << i;
- }
- }
- out.writeInt(capabilityInt);
+ out.writeInt(capability);
out.writeInt(associated ? 1 : 0);
out.writeTypedList(radioChainInfos);
}
@@ -220,13 +211,7 @@
result.frequency = in.readInt();
result.signalMbm = in.readInt();
result.tsf = in.readLong();
- int capabilityInt = in.readInt();
- result.capability = new BitSet(CAPABILITY_SIZE);
- for (int i = 0; i < CAPABILITY_SIZE; i++) {
- if ((capabilityInt & (1 << i)) != 0) {
- result.capability.set(i);
- }
- }
+ result.capability = in.readInt();
result.associated = (in.readInt() != 0);
result.radioChainInfos = new ArrayList<>();
in.readTypedList(result.radioChainInfos, RadioChainInfo.CREATOR);
diff --git a/wifi/java/android/net/wifi/wificond/PnoSettings.java b/wifi/java/android/net/wifi/wificond/PnoSettings.java
index 57c9ca5..533d37d 100644
--- a/wifi/java/android/net/wifi/wificond/PnoSettings.java
+++ b/wifi/java/android/net/wifi/wificond/PnoSettings.java
@@ -16,6 +16,7 @@
package android.net.wifi.wificond;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
@@ -33,7 +34,7 @@
*/
@SystemApi
public final class PnoSettings implements Parcelable {
- private int mIntervalMs;
+ private long mIntervalMs;
private int mMin2gRssi;
private int mMin5gRssi;
private int mMin6gRssi;
@@ -47,17 +48,17 @@
*
* @return An interval in milliseconds.
*/
- public int getIntervalMillis() {
+ public @DurationMillisLong long getIntervalMillis() {
return mIntervalMs;
}
/**
* Set the requested PNO scan interval in milliseconds.
*
- * @param intervalMs An interval in milliseconds.
+ * @param intervalMillis An interval in milliseconds.
*/
- public void setIntervalMillis(int intervalMs) {
- this.mIntervalMs = intervalMs;
+ public void setIntervalMillis(@DurationMillisLong long intervalMillis) {
+ this.mIntervalMs = intervalMillis;
}
/**
@@ -176,7 +177,7 @@
**/
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
- out.writeInt(mIntervalMs);
+ out.writeLong(mIntervalMs);
out.writeInt(mMin2gRssi);
out.writeInt(mMin5gRssi);
out.writeInt(mMin6gRssi);
@@ -189,7 +190,7 @@
@Override
public PnoSettings createFromParcel(Parcel in) {
PnoSettings result = new PnoSettings();
- result.mIntervalMs = in.readInt();
+ result.mIntervalMs = in.readLong();
result.mMin2gRssi = in.readInt();
result.mMin5gRssi = in.readInt();
result.mMin6gRssi = in.readInt();
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
index 43aa1b6..7a31a5a 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
@@ -496,22 +496,17 @@
}
/**
- * Initializes WifiCondManager & registers a death notification for the WifiCondManager which
- * acts as a proxy for the wificond daemon (i.e. the death listener will be called when and if
- * the wificond daemon dies).
- *
- * Note: This method clears any existing state in wificond daemon.
+ * Register a death notification for the WifiCondManager which acts as a proxy for the
+ * wificond daemon (i.e. the death listener will be called when and if the wificond daemon
+ * dies).
*
* @param deathEventHandler A {@link Runnable} to be called whenever the wificond daemon dies.
- * @return Returns true on success.
*/
- public boolean initialize(@NonNull Runnable deathEventHandler) {
+ public void setOnServiceDeadCallback(@NonNull Runnable deathEventHandler) {
if (mDeathEventHandler != null) {
Log.e(TAG, "Death handler already present");
}
mDeathEventHandler = deathEventHandler;
- tearDownInterfaces();
- return true;
}
/**
@@ -603,11 +598,12 @@
}
/**
- * Tear down a specific client (STA) interface, initially configured using
+ * Tear down a specific client (STA) interface configured using
* {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}.
*
* @param ifaceName Name of the interface to tear down.
- * @return Returns true on success.
+ * @return Returns true on success, false on failure (e.g. when called before an interface was
+ * set up).
*/
public boolean tearDownClientInterface(@NonNull String ifaceName) {
if (getClientInterface(ifaceName) == null) {
@@ -681,11 +677,12 @@
}
/**
- * Tear down a Soft AP interface initially configured using
+ * Tear down a Soft AP interface configured using
* {@link #setupInterfaceForSoftApMode(String)}.
*
* @param ifaceName Name of the interface to tear down.
- * @return Returns true on success.
+ * @return Returns true on success, false on failure (e.g. when called before an interface was
+ * set up).
*/
public boolean tearDownSoftApInterface(@NonNull String ifaceName) {
if (getApInterface(ifaceName) == null) {
@@ -750,9 +747,13 @@
/**
* Request signal polling.
*
- * @param ifaceName Name of the interface on which to poll.
+ * @param ifaceName Name of the interface on which to poll. The interface must have been
+ * already set up using
+ *{@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}
+ * or {@link #setupInterfaceForSoftApMode(String)}.
+ *
* @return A {@link SignalPollResult} object containing interface statistics, or a null on
- * error.
+ * error (e.g. the interface hasn't been set up yet).
*/
@Nullable public SignalPollResult signalPoll(@NonNull String ifaceName) {
IClientInterface iface = getClientInterface(ifaceName);
@@ -776,10 +777,14 @@
}
/**
- * Get current transmit (Tx) packet counters of the specified interface.
+ * Get current transmit (Tx) packet counters of the specified interface. The interface must
+ * have been already set up using
+ * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}
+ * or {@link #setupInterfaceForSoftApMode(String)}.
*
* @param ifaceName Name of the interface.
- * @return {@link TxPacketCounters} of the current interface or null on error.
+ * @return {@link TxPacketCounters} of the current interface or null on error (e.g. when
+ * called before the interface has been set up).
*/
@Nullable public TxPacketCounters getTxPacketCounters(@NonNull String ifaceName) {
IClientInterface iface = getClientInterface(ifaceName);
@@ -813,10 +818,15 @@
* be done using {@link #startScan(String, int, Set, List)} or
* {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}.
*
+ * Note: The interface must have been already set up using
+ * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}
+ * or {@link #setupInterfaceForSoftApMode(String)}.
+ *
* @param ifaceName Name of the interface.
* @param scanType The type of scan result to be returned, can be
* {@link #SCAN_TYPE_SINGLE_SCAN} or {@link #SCAN_TYPE_PNO_SCAN}.
- * @return Returns an array of {@link NativeScanResult} or an empty array on failure.
+ * @return Returns an array of {@link NativeScanResult} or an empty array on failure (e.g. when
+ * called before the interface has been set up).
*/
@NonNull public List<NativeScanResult> getScanResults(@NonNull String ifaceName,
@ScanResultType int scanType) {
@@ -869,13 +879,19 @@
* The latest scans can be obtained using {@link #getScanResults(String, int)} and using a
* {@link #SCAN_TYPE_SINGLE_SCAN} for the {@code scanType}.
*
+ * Note: The interface must have been already set up using
+ * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}
+ * or {@link #setupInterfaceForSoftApMode(String)}.
+ *
* @param ifaceName Name of the interface on which to initiate the scan.
* @param scanType Type of scan to perform, can be any of
* {@link WifiScanner#SCAN_TYPE_HIGH_ACCURACY}, {@link WifiScanner#SCAN_TYPE_LOW_POWER}, or
* {@link WifiScanner#SCAN_TYPE_LOW_LATENCY}.
* @param freqs list of frequencies to scan for, if null scan all supported channels.
- * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
- * @return Returns true on success.
+ * @param hiddenNetworkSSIDs List of hidden networks to be scanned for, a null indicates that
+ * no hidden frequencies will be scanned for.
+ * @return Returns true on success, false on failure (e.g. when called before the interface
+ * has been set up).
*/
public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
@Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) {
@@ -931,11 +947,16 @@
* The latest PNO scans can be obtained using {@link #getScanResults(String, int)} with the
* {@code scanType} set to {@link #SCAN_TYPE_PNO_SCAN}.
*
+ * Note: The interface must have been already set up using
+ * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}
+ * or {@link #setupInterfaceForSoftApMode(String)}.
+ *
* @param ifaceName Name of the interface on which to request a PNO.
* @param pnoSettings PNO scan configuration.
* @param executor The Executor on which to execute the callback.
* @param callback Callback for the results of the offload request.
- * @return true on success.
+ * @return true on success, false on failure (e.g. when called before the interface has been set
+ * up).
*/
public boolean startPnoScan(@NonNull String ifaceName, @NonNull PnoSettings pnoSettings,
@NonNull @CallbackExecutor Executor executor,
@@ -969,8 +990,13 @@
* Stop PNO scan configured with
* {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}.
*
+ * Note: The interface must have been already set up using
+ * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}
+ * or {@link #setupInterfaceForSoftApMode(String)}.
+ *
* @param ifaceName Name of the interface on which the PNO scan was configured.
- * @return true on success.
+ * @return true on success, false on failure (e.g. when called before the interface has been
+ * set up).
*/
public boolean stopPnoScan(@NonNull String ifaceName) {
IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
@@ -987,7 +1013,13 @@
}
/**
- * Abort ongoing single scan started with {@link #startScan(String, int, Set, List)}.
+ * Abort ongoing single scan started with {@link #startScan(String, int, Set, List)}. No failure
+ * callback, e.g. {@link ScanEventCallback#onScanFailed()}, is triggered by this operation.
+ *
+ * Note: The interface must have been already set up using
+ * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}
+ * or {@link #setupInterfaceForSoftApMode(String)}. If the interface has not been set up then
+ * this method has no impact.
*
* @param ifaceName Name of the interface on which the scan was started.
*/
@@ -1055,7 +1087,14 @@
}
/**
- * Get the device phy capabilities for a given interface
+ * Get the device phy capabilities for a given interface.
+ *
+ * Note: The interface must have been already set up using
+ * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}
+ * or {@link #setupInterfaceForSoftApMode(String)}.
+ *
+ * @return DeviceWiphyCapabilities or null on error (e.g. when called on an interface which has
+ * not been set up).
*/
@Nullable public DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String ifaceName) {
if (mWificond == null) {
@@ -1071,13 +1110,19 @@
}
/**
- * Register the provided callback handler for SoftAp events. Note that the Soft AP itself is
- * configured using {@link #setupInterfaceForSoftApMode(String)}.
+ * Register the provided callback handler for SoftAp events. The interface must first be created
+ * using {@link #setupInterfaceForSoftApMode(String)}. The callback registration is valid until
+ * the interface is deleted using {@link #tearDownSoftApInterface(String)} (no deregistration
+ * method is provided).
+ * <p>
+ * Note that only one callback can be registered at a time - any registration overrides previous
+ * registrations.
*
* @param ifaceName Name of the interface on which to register the callback.
* @param executor The Executor on which to execute the callbacks.
* @param callback Callback for AP events.
- * @return true on success, false otherwise.
+ * @return true on success, false on failure (e.g. when called on an interface which has not
+ * been set up).
*/
public boolean registerApCallback(@NonNull String ifaceName,
@NonNull @CallbackExecutor Executor executor,
@@ -1113,6 +1158,10 @@
* Send a management frame on the specified interface at the specified rate. Useful for probing
* the link with arbitrary frames.
*
+ * Note: The interface must have been already set up using
+ * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}
+ * or {@link #setupInterfaceForSoftApMode(String)}.
+ *
* @param ifaceName The interface on which to send the frame.
* @param frame The raw byte array of the management frame to tramit.
* @param mcs The MCS (modulation and coding scheme), i.e. rate, at which to transmit the
diff --git a/wifi/tests/src/android/net/wifi/wificond/NativeScanResultTest.java b/wifi/tests/src/android/net/wifi/wificond/NativeScanResultTest.java
index 06f12f7..0df170f 100644
--- a/wifi/tests/src/android/net/wifi/wificond/NativeScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/NativeScanResultTest.java
@@ -28,7 +28,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.BitSet;
/**
* Unit tests for {@link android.net.wifi.wificond.NativeScanResult}.
@@ -46,7 +45,7 @@
private static final int TEST_FREQUENCY = 2456;
private static final int TEST_SIGNAL_MBM = -45;
private static final long TEST_TSF = 34455441;
- private static final BitSet TEST_CAPABILITY = new BitSet(16) {{ set(2); set(5); }};
+ private static final int TEST_CAPABILITY = (0x1 << 2) | (0x1 << 5);
private static final boolean TEST_ASSOCIATED = true;
private static final int[] RADIO_CHAIN_IDS = { 0, 1 };
private static final int[] RADIO_CHAIN_LEVELS = { -56, -65 };
diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
index 5ba02a7..32105be 100644
--- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
@@ -720,8 +720,7 @@
@Test
public void testRegisterDeathHandler() throws Exception {
Runnable deathHandler = mock(Runnable.class);
- assertTrue(mWificondControl.initialize(deathHandler));
- verify(mWificond).tearDownInterfaces();
+ mWificondControl.setOnServiceDeadCallback(deathHandler);
mWificondControl.binderDied();
mLooper.dispatchAll();
verify(deathHandler).run();
@@ -734,7 +733,7 @@
@Test
public void testDeathHandling() throws Exception {
Runnable deathHandler = mock(Runnable.class);
- assertTrue(mWificondControl.initialize(deathHandler));
+ mWificondControl.setOnServiceDeadCallback(deathHandler);
testSetupInterfaceForClientMode();