Merge "Fix WebViewUpdateService test expectation to match reality."
diff --git a/Android.bp b/Android.bp
index 5c1ccb7..2ea4894 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,9 +58,12 @@
// runtime, as well as the only protos that are actually
// needed by the device.
srcs: [
+ "core/proto/android/os/cpufreq.proto",
+ "core/proto/android/os/cpuinfo.proto",
"core/proto/android/os/kernelwake.proto",
"core/proto/android/os/pagetypeinfo.proto",
"core/proto/android/os/procrank.proto",
+ "core/proto/android/os/system_properties.proto",
"core/proto/android/service/graphicsstats.proto",
"tools/streaming_proto/stream.proto",
],
@@ -81,9 +84,12 @@
],
srcs: [
+ "core/proto/android/os/cpufreq.proto",
+ "core/proto/android/os/cpuinfo.proto",
"core/proto/android/os/kernelwake.proto",
"core/proto/android/os/pagetypeinfo.proto",
"core/proto/android/os/procrank.proto",
+ "core/proto/android/os/system_properties.proto",
],
// Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
@@ -128,3 +134,29 @@
dxflags: ["--core-library"],
installable: false,
}
+
+python_defaults {
+ name: "base_default",
+ version: {
+ py2: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ py3: {
+ enabled: false,
+ embedded_launcher: false,
+ },
+ },
+}
+
+python_binary_host {
+ name: "fontchain_linter",
+ defaults: ["base_default"],
+ main: "tools/fonts/fontchain_linter.py",
+ srcs: [
+ "tools/fonts/fontchain_linter.py",
+ ],
+ libs: [
+ "fontTools",
+ ],
+}
diff --git a/Android.mk b/Android.mk
index 817aa80..08a8b64 100644
--- a/Android.mk
+++ b/Android.mk
@@ -79,6 +79,7 @@
core/java/android/app/IAlarmManager.aidl \
core/java/android/app/IAppTask.aidl \
core/java/android/app/IApplicationThread.aidl \
+ core/java/android/app/IAssistDataReceiver.aidl \
core/java/android/app/ITaskStackListener.aidl \
core/java/android/app/IBackupAgent.aidl \
core/java/android/app/IEphemeralResolver.aidl \
@@ -159,6 +160,7 @@
core/java/android/content/ISyncServiceAdapter.aidl \
core/java/android/content/ISyncStatusObserver.aidl \
core/java/android/content/om/IOverlayManager.aidl \
+ core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl \
core/java/android/content/pm/IDexModuleRegisterCallback.aidl \
core/java/android/content/pm/ILauncherApps.aidl \
core/java/android/content/pm/IOnAppsChangedListener.aidl \
@@ -167,7 +169,6 @@
core/java/android/content/pm/IPackageDataObserver.aidl \
core/java/android/content/pm/IPackageDeleteObserver.aidl \
core/java/android/content/pm/IPackageDeleteObserver2.aidl \
- core/java/android/content/pm/IPackageInstallObserver.aidl \
core/java/android/content/pm/IPackageInstallObserver2.aidl \
core/java/android/content/pm/IPackageInstaller.aidl \
core/java/android/content/pm/IPackageInstallerCallback.aidl \
@@ -272,7 +273,6 @@
core/java/android/os/IRecoverySystemProgressListener.aidl \
core/java/android/os/IRemoteCallback.aidl \
core/java/android/os/ISchedulingPolicyService.aidl \
- core/java/android/os/IStatsCallbacks.aidl \
core/java/android/os/IStatsCompanionService.aidl \
core/java/android/os/IStatsManager.aidl \
core/java/android/os/IThermalEventListener.aidl \
@@ -382,7 +382,6 @@
core/java/android/speech/tts/ITextToSpeechService.aidl \
core/java/com/android/internal/app/IAppOpsCallback.aidl \
core/java/com/android/internal/app/IAppOpsService.aidl \
- core/java/com/android/internal/app/IAssistDataReceiver.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
core/java/com/android/internal/app/ISoundTriggerService.aidl \
core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
@@ -637,6 +636,7 @@
framework-protos \
android.hidl.base-V1.0-java \
android.hardware.cas-V1.0-java \
+ android.hardware.contexthub-V1.0-java \
android.hardware.health-V1.0-java-constants \
android.hardware.thermal-V1.0-java-constants \
android.hardware.tv.input-V1.0-java-constants \
@@ -930,7 +930,7 @@
../opt/net/voip/src/java/android/net/sip
framework_base_android_test_mock_src_files := \
- $(call all-java-files-under, test-runner/src/android/test/mock)
+ $(call all-java-files-under, test-mock/src/android/test/mock)
framework_base_android_test_runner_excluding_mock_src_files := \
$(filter-out $(framework_base_android_test_mock_src_files), $(call all-java-files-under, test-runner/src))
@@ -1557,6 +1557,7 @@
LOCAL_SOURCE_FILES_ALL_GENERATED := true
LOCAL_SRC_FILES := \
tools/streaming_proto/stream.proto \
+ cmds/am/proto/instrumentation_data.proto \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto)
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 50a5974..711c12d 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,3 +7,4 @@
services/print/
services/usb/
+api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREUPLOAD_COMMIT}
diff --git a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
index a92597f..6e4c9c5 100644
--- a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
@@ -27,6 +27,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
@RunWith(AndroidJUnit4.class)
@LargeTest
public class ParcelPerfTest {
@@ -167,4 +171,80 @@
Parcel.obtain().recycle();
}
}
+
+ @Test
+ public void timeWriteException() {
+ timeWriteException(false);
+ }
+
+ @Test
+ public void timeWriteExceptionWithStackTraceParceling() {
+ timeWriteException(true);
+ }
+
+ @Test
+ public void timeReadException() {
+ timeReadException(false);
+ }
+
+ @Test
+ public void timeReadExceptionWithStackTraceParceling() {
+ timeReadException(true);
+ }
+
+ private void timeWriteException(boolean enableParceling) {
+ if (enableParceling) {
+ Parcel.setStackTraceParceling(true);
+ }
+ try {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ Parcel p = Parcel.obtain();
+ SecurityException e = new SecurityException("TestMessage");
+ while (state.keepRunning()) {
+ p.setDataPosition(0);
+ p.writeException(e);
+ }
+ } finally {
+ if (enableParceling) {
+ Parcel.setStackTraceParceling(false);
+ }
+ }
+ }
+
+ private void timeReadException(boolean enableParceling) {
+ if (enableParceling) {
+ Parcel.setStackTraceParceling(true);
+ }
+ try {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ Parcel p = Parcel.obtain();
+ String msg = "TestMessage";
+ p.writeException(new SecurityException(msg));
+ p.setDataPosition(0);
+ // First verify that remote cause is set (if parceling is enabled)
+ try {
+ p.readException();
+ } catch (SecurityException e) {
+ assertEquals(e.getMessage(), msg);
+ if (enableParceling) {
+ assertTrue(e.getCause() instanceof RemoteException);
+ } else {
+ assertNull(e.getCause());
+ }
+ }
+
+ while (state.keepRunning()) {
+ p.setDataPosition(0);
+ try {
+ p.readException();
+ } catch (SecurityException expected) {
+ }
+ }
+ } finally {
+ if (enableParceling) {
+ Parcel.setStackTraceParceling(false);
+ }
+ }
+ }
+
}
diff --git a/api/current.txt b/api/current.txt
index 7b90d35..c6d5430 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3609,11 +3609,11 @@
method public android.transition.Scene getContentScene();
method public android.transition.TransitionManager getContentTransitionManager();
method public android.view.View getCurrentFocus();
- method public android.app.FragmentManager getFragmentManager();
+ method public deprecated android.app.FragmentManager getFragmentManager();
method public android.content.Intent getIntent();
method public java.lang.Object getLastNonConfigurationInstance();
method public android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
@@ -3653,7 +3653,7 @@
method public void onActionModeStarted(android.view.ActionMode);
method public void onActivityReenter(int, android.content.Intent);
method protected void onActivityResult(int, int, android.content.Intent);
- method public void onAttachFragment(android.app.Fragment);
+ method public deprecated void onAttachFragment(android.app.Fragment);
method public void onAttachedToWindow();
method public void onBackPressed();
method protected void onChildTitleChanged(android.app.Activity, java.lang.CharSequence);
@@ -3795,8 +3795,8 @@
method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int, android.os.Bundle);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
method public boolean startActivityIfNeeded(android.content.Intent, int);
method public boolean startActivityIfNeeded(android.content.Intent, int, android.os.Bundle);
method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
@@ -4455,7 +4455,7 @@
method public void unregisterForContextMenu(android.view.View);
}
- public class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+ public deprecated class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
ctor public DialogFragment();
method public void dismiss();
method public void dismissAllowingStateLoss();
@@ -4573,7 +4573,7 @@
method public void setSelectedGroup(int);
}
- public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
+ public deprecated class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
ctor public Fragment();
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public final boolean equals(java.lang.Object);
@@ -4589,7 +4589,7 @@
method public final java.lang.Object getHost();
method public final int getId();
method public final android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public final android.app.Fragment getParentFragment();
method public android.transition.Transition getReenterTransition();
method public final android.content.res.Resources getResources();
@@ -4684,11 +4684,11 @@
method public void unregisterForContextMenu(android.view.View);
}
- public static class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
+ public static deprecated class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
}
- public static class Fragment.SavedState implements android.os.Parcelable {
+ public static deprecated class Fragment.SavedState implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.ClassLoaderCreator<android.app.Fragment.SavedState> CREATOR;
@@ -4707,17 +4707,17 @@
method public void setTitle(java.lang.CharSequence, java.lang.CharSequence);
}
- public static abstract interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
+ public static abstract deprecated interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
method public abstract boolean onBreadCrumbClick(android.app.FragmentManager.BackStackEntry, int);
}
- public abstract class FragmentContainer {
+ public abstract deprecated class FragmentContainer {
ctor public FragmentContainer();
method public abstract <T extends android.view.View> T onFindViewById(int);
method public abstract boolean onHasView();
}
- public class FragmentController {
+ public deprecated class FragmentController {
method public void attachHost(android.app.Fragment);
method public static final android.app.FragmentController createController(android.app.FragmentHostCallback<?>);
method public void dispatchActivityCreated();
@@ -4760,7 +4760,7 @@
method public android.os.Parcelable saveAllState();
}
- public abstract class FragmentHostCallback<E> extends android.app.FragmentContainer {
+ public abstract deprecated class FragmentHostCallback<E> extends android.app.FragmentContainer {
ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
method public void onAttachFragment(android.app.Fragment);
method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -4778,7 +4778,7 @@
method public boolean onUseFragmentManagerInflaterFactory();
}
- public abstract class FragmentManager {
+ public abstract deprecated class FragmentManager {
ctor public FragmentManager();
method public abstract void addOnBackStackChangedListener(android.app.FragmentManager.OnBackStackChangedListener);
method public abstract android.app.FragmentTransaction beginTransaction();
@@ -4809,7 +4809,7 @@
field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
}
- public static abstract interface FragmentManager.BackStackEntry {
+ public static abstract deprecated interface FragmentManager.BackStackEntry {
method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
method public abstract int getBreadCrumbShortTitleRes();
method public abstract java.lang.CharSequence getBreadCrumbTitle();
@@ -4818,7 +4818,7 @@
method public abstract java.lang.String getName();
}
- public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+ public static abstract deprecated class FragmentManager.FragmentLifecycleCallbacks {
ctor public FragmentManager.FragmentLifecycleCallbacks();
method public void onFragmentActivityCreated(android.app.FragmentManager, android.app.Fragment, android.os.Bundle);
method public void onFragmentAttached(android.app.FragmentManager, android.app.Fragment, android.content.Context);
@@ -4836,14 +4836,14 @@
method public void onFragmentViewDestroyed(android.app.FragmentManager, android.app.Fragment);
}
- public static abstract interface FragmentManager.OnBackStackChangedListener {
+ public static abstract deprecated interface FragmentManager.OnBackStackChangedListener {
method public abstract void onBackStackChanged();
}
- public class FragmentManagerNonConfig {
+ public deprecated class FragmentManagerNonConfig {
}
- public abstract class FragmentTransaction {
+ public abstract deprecated class FragmentTransaction {
ctor public FragmentTransaction();
method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
@@ -4943,6 +4943,7 @@
method public void setInTouchMode(boolean);
method public void start();
method public android.app.Activity startActivitySync(android.content.Intent);
+ method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle);
method public deprecated void startAllocCounting();
method public void startPerformanceSnapshot();
method public void startProfiling();
@@ -5048,7 +5049,7 @@
method public void setSelection(int);
}
- public class ListFragment extends android.app.Fragment {
+ public deprecated class ListFragment extends android.app.Fragment {
ctor public ListFragment();
method public android.widget.ListAdapter getListAdapter();
method public android.widget.ListView getListView();
@@ -5062,7 +5063,7 @@
method public void setSelection(int);
}
- public abstract class LoaderManager {
+ public abstract deprecated class LoaderManager {
ctor public LoaderManager();
method public abstract void destroyLoader(int);
method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -5072,7 +5073,7 @@
method public abstract <D> android.content.Loader<D> restartLoader(int, android.os.Bundle, android.app.LoaderManager.LoaderCallbacks<D>);
}
- public static abstract interface LoaderManager.LoaderCallbacks<D> {
+ public static abstract deprecated interface LoaderManager.LoaderCallbacks<D> {
method public abstract android.content.Loader<D> onCreateLoader(int, android.os.Bundle);
method public abstract void onLoadFinished(android.content.Loader<D>, D);
method public abstract void onLoaderReset(android.content.Loader<D>);
@@ -6973,6 +6974,7 @@
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
+ method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
@@ -7041,6 +7043,7 @@
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
@@ -7050,27 +7053,6 @@
}
-package android.app.slice.widget {
-
- public class SliceView extends android.view.ViewGroup {
- ctor public SliceView(android.content.Context);
- ctor public SliceView(android.content.Context, android.util.AttributeSet);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
- method public void clearSlice();
- method public java.lang.String getMode();
- method protected void onLayout(boolean, int, int, int, int);
- method public void setMode(java.lang.String);
- method public void setScrollable(boolean);
- method public boolean setSlice(android.net.Uri);
- method public void showSlice(android.app.slice.Slice);
- field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
- field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
- field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
- }
-
-}
-
package android.app.usage {
public final class ConfigurationStats implements android.os.Parcelable {
@@ -8532,7 +8514,7 @@
ctor public AsyncQueryHandler.WorkerHandler(android.os.Looper);
}
- public abstract class AsyncTaskLoader<D> extends android.content.Loader {
+ public abstract deprecated class AsyncTaskLoader<D> extends android.content.Loader {
ctor public AsyncTaskLoader(android.content.Context);
method public void cancelLoadInBackground();
method public boolean isLoadInBackgroundCanceled();
@@ -9105,6 +9087,7 @@
field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
field public static final int CONTEXT_INCLUDE_CODE = 1; // 0x1
field public static final int CONTEXT_RESTRICTED = 4; // 0x4
+ field public static final java.lang.String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
field public static final java.lang.String DEVICE_POLICY_SERVICE = "device_policy";
field public static final java.lang.String DISPLAY_SERVICE = "display";
field public static final java.lang.String DOWNLOAD_SERVICE = "download";
@@ -9271,7 +9254,7 @@
method public void unregisterReceiver(android.content.BroadcastReceiver);
}
- public class CursorLoader extends android.content.AsyncTaskLoader {
+ public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
ctor public CursorLoader(android.content.Context);
ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public void deliverResult(android.database.Cursor);
@@ -9877,7 +9860,7 @@
ctor public IntentSender.SendIntentException(java.lang.Exception);
}
- public class Loader<D> {
+ public deprecated class Loader<D> {
ctor public Loader(android.content.Context);
method public void abandon();
method public boolean cancelLoad();
@@ -9910,15 +9893,15 @@
method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
}
- public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+ public final deprecated class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
ctor public Loader.ForceLoadContentObserver();
}
- public static abstract interface Loader.OnLoadCanceledListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCanceledListener<D> {
method public abstract void onLoadCanceled(android.content.Loader<D>);
}
- public static abstract interface Loader.OnLoadCompleteListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCompleteListener<D> {
method public abstract void onLoadComplete(android.content.Loader<D>, D);
}
@@ -10572,6 +10555,7 @@
field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1
field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8
field public static final int FLAG_MATCH_PINNED = 2; // 0x2
+ field public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1024; // 0x400
}
public class PackageInfo implements android.os.Parcelable {
@@ -11226,6 +11210,15 @@
}
+package android.content.pm.crossprofile {
+
+ public class CrossProfileApps {
+ method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+ method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ }
+
+}
+
package android.content.res {
public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -12125,9 +12118,11 @@
method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
method public android.database.DatabaseErrorHandler getErrorHandler();
method public long getIdleConnectionTimeout();
+ method public java.lang.String getJournalMode();
method public int getLookasideSlotCount();
method public int getLookasideSlotSize();
method public int getOpenFlags();
+ method public java.lang.String getSynchronousMode();
}
public static final class SQLiteDatabase.OpenParams.Builder {
@@ -12139,8 +12134,10 @@
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setErrorHandler(android.database.DatabaseErrorHandler);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(long);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setJournalMode(java.lang.String);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setLookasideConfig(int, int);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setOpenFlags(int);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setSynchronousMode(java.lang.String);
}
public class SQLiteDatabaseCorruptException extends android.database.sqlite.SQLiteException {
@@ -12187,6 +12184,7 @@
public abstract class SQLiteOpenHelper {
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
+ ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, int, android.database.sqlite.SQLiteDatabase.OpenParams);
method public synchronized void close();
method public java.lang.String getDatabaseName();
method public android.database.sqlite.SQLiteDatabase getReadableDatabase();
@@ -15267,6 +15265,7 @@
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+ method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
}
public static abstract class CameraCaptureSession.CaptureCallback {
@@ -15908,9 +15907,11 @@
method public void addSurface(android.view.Surface);
method public int describeContents();
method public void enableSurfaceSharing();
+ method public static int getMaxSharedSurfaceCount();
method public android.view.Surface getSurface();
method public int getSurfaceGroupId();
method public java.util.List<android.view.Surface> getSurfaces();
+ method public void removeSurface(android.view.Surface);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -32607,7 +32608,7 @@
method public default void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
}
- public abstract class PreferenceFragment extends android.app.Fragment {
+ public abstract deprecated class PreferenceFragment extends android.app.Fragment {
ctor public PreferenceFragment();
method public void addPreferencesFromIntent(android.content.Intent);
method public void addPreferencesFromResource(int);
@@ -32618,7 +32619,7 @@
method public void setPreferenceScreen(android.preference.PreferenceScreen);
}
- public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+ public static abstract deprecated interface PreferenceFragment.OnPreferenceStartFragmentCallback {
method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
}
@@ -38602,7 +38603,6 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -38628,7 +38628,6 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -39205,6 +39204,7 @@
method public android.telecom.Call.RttCall getRttCall();
method public int getState();
method public android.telecom.InCallService.VideoCall getVideoCall();
+ method public void handoverTo(android.telecom.PhoneAccountHandle, int, android.os.Bundle);
method public void hold();
method public boolean isRttActive();
method public void mergeConference();
@@ -39249,6 +39249,8 @@
method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
+ method public void onHandoverComplete(android.telecom.Call);
+ method public void onHandoverFailed(android.telecom.Call, int);
method public void onParentChanged(android.telecom.Call, android.telecom.Call);
method public void onPostDialWait(android.telecom.Call, java.lang.String);
method public void onRttInitiationFailure(android.telecom.Call, int);
@@ -39257,6 +39259,10 @@
method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall);
method public void onStateChanged(android.telecom.Call, int);
method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
+ field public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1; // 0x1
+ field public static final int HANDOVER_FAILURE_DEST_INVALID_PERM = 3; // 0x3
+ field public static final int HANDOVER_FAILURE_DEST_NOT_SUPPORTED = 2; // 0x2
+ field public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4; // 0x4
}
public static class Call.Details {
@@ -39325,7 +39331,9 @@
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
method public int describeContents();
+ method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
method public int getRoute();
+ method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
method public int getSupportedRouteMask();
method public boolean isMuted();
method public void writeToParcel(android.os.Parcel, int);
@@ -39457,6 +39465,7 @@
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void requestBluetoothAudio(java.lang.String);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
@@ -39588,8 +39597,11 @@
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onHandoverFailed(android.telecom.ConnectionRequest, int);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -39647,6 +39659,7 @@
method public void onCanAddCallChanged(boolean);
method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onSilenceRinger();
+ method public final void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -39711,6 +39724,9 @@
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
field public static final java.lang.String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
field public static final java.lang.String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
+ field public static final java.lang.String EXTRA_LOG_SELF_MANAGED_CALLS = "android.telecom.extra.LOG_SELF_MANAGED_CALLS";
+ field public static final java.lang.String EXTRA_SUPPORTS_HANDOVER_FROM = "android.telecom.extra.SUPPORTS_HANDOVER_FROM";
+ field public static final java.lang.String EXTRA_SUPPORTS_HANDOVER_TO = "android.telecom.extra.SUPPORTS_HANDOVER_TO";
field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
field public static final java.lang.String SCHEME_SIP = "sip";
@@ -39871,6 +39887,7 @@
}
public class TelecomManager {
+ method public void acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle);
method public void acceptRingingCall();
method public void acceptRingingCall(int);
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
@@ -46047,6 +46064,7 @@
method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public void addFocusables(java.util.ArrayList<android.view.View>, int);
method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
+ method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
@@ -46381,6 +46399,7 @@
method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
method public boolean onKeyDown(int, android.view.KeyEvent);
+ method public boolean onKeyFallback(android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
method public boolean onKeyPreIme(int, android.view.KeyEvent);
@@ -46434,6 +46453,7 @@
method public void refreshDrawableState();
method public void releasePointerCapture();
method public boolean removeCallbacks(java.lang.Runnable);
+ method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
method public void requestApplyInsets();
@@ -46852,6 +46872,10 @@
method public abstract boolean onHover(android.view.View, android.view.MotionEvent);
}
+ public static abstract interface View.OnKeyFallbackListener {
+ method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent);
+ }
+
public static abstract interface View.OnKeyListener {
method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
}
@@ -49969,7 +49993,7 @@
method public abstract void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
}
- public class WebViewFragment extends android.app.Fragment {
+ public deprecated class WebViewFragment extends android.app.Fragment {
ctor public WebViewFragment();
method public android.webkit.WebView getWebView();
}
diff --git a/api/system-current.txt b/api/system-current.txt
index ddeab8d..94e3786 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3754,11 +3754,11 @@
method public android.transition.Scene getContentScene();
method public android.transition.TransitionManager getContentTransitionManager();
method public android.view.View getCurrentFocus();
- method public android.app.FragmentManager getFragmentManager();
+ method public deprecated android.app.FragmentManager getFragmentManager();
method public android.content.Intent getIntent();
method public java.lang.Object getLastNonConfigurationInstance();
method public android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
@@ -3799,7 +3799,7 @@
method public void onActionModeStarted(android.view.ActionMode);
method public void onActivityReenter(int, android.content.Intent);
method protected void onActivityResult(int, int, android.content.Intent);
- method public void onAttachFragment(android.app.Fragment);
+ method public deprecated void onAttachFragment(android.app.Fragment);
method public void onAttachedToWindow();
method public void onBackPressed();
method public deprecated void onBackgroundVisibleBehindChanged(boolean);
@@ -3943,8 +3943,8 @@
method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int, android.os.Bundle);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
method public boolean startActivityIfNeeded(android.content.Intent, int);
method public boolean startActivityIfNeeded(android.content.Intent, int, android.os.Bundle);
method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
@@ -4628,7 +4628,7 @@
method public void unregisterForContextMenu(android.view.View);
}
- public class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+ public deprecated class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
ctor public DialogFragment();
method public void dismiss();
method public void dismissAllowingStateLoss();
@@ -4747,7 +4747,7 @@
method public void setSelectedGroup(int);
}
- public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
+ public deprecated class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
ctor public Fragment();
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public final boolean equals(java.lang.Object);
@@ -4763,7 +4763,7 @@
method public final java.lang.Object getHost();
method public final int getId();
method public final android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public final android.app.Fragment getParentFragment();
method public android.transition.Transition getReenterTransition();
method public final android.content.res.Resources getResources();
@@ -4858,11 +4858,11 @@
method public void unregisterForContextMenu(android.view.View);
}
- public static class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
+ public static deprecated class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
}
- public static class Fragment.SavedState implements android.os.Parcelable {
+ public static deprecated class Fragment.SavedState implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.ClassLoaderCreator<android.app.Fragment.SavedState> CREATOR;
@@ -4881,17 +4881,17 @@
method public void setTitle(java.lang.CharSequence, java.lang.CharSequence);
}
- public static abstract interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
+ public static abstract deprecated interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
method public abstract boolean onBreadCrumbClick(android.app.FragmentManager.BackStackEntry, int);
}
- public abstract class FragmentContainer {
+ public abstract deprecated class FragmentContainer {
ctor public FragmentContainer();
method public abstract <T extends android.view.View> T onFindViewById(int);
method public abstract boolean onHasView();
}
- public class FragmentController {
+ public deprecated class FragmentController {
method public void attachHost(android.app.Fragment);
method public static final android.app.FragmentController createController(android.app.FragmentHostCallback<?>);
method public void dispatchActivityCreated();
@@ -4934,7 +4934,7 @@
method public android.os.Parcelable saveAllState();
}
- public abstract class FragmentHostCallback<E> extends android.app.FragmentContainer {
+ public abstract deprecated class FragmentHostCallback<E> extends android.app.FragmentContainer {
ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
method public void onAttachFragment(android.app.Fragment);
method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -4952,7 +4952,7 @@
method public boolean onUseFragmentManagerInflaterFactory();
}
- public abstract class FragmentManager {
+ public abstract deprecated class FragmentManager {
ctor public FragmentManager();
method public abstract void addOnBackStackChangedListener(android.app.FragmentManager.OnBackStackChangedListener);
method public abstract android.app.FragmentTransaction beginTransaction();
@@ -4983,7 +4983,7 @@
field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
}
- public static abstract interface FragmentManager.BackStackEntry {
+ public static abstract deprecated interface FragmentManager.BackStackEntry {
method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
method public abstract int getBreadCrumbShortTitleRes();
method public abstract java.lang.CharSequence getBreadCrumbTitle();
@@ -4992,7 +4992,7 @@
method public abstract java.lang.String getName();
}
- public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+ public static abstract deprecated class FragmentManager.FragmentLifecycleCallbacks {
ctor public FragmentManager.FragmentLifecycleCallbacks();
method public void onFragmentActivityCreated(android.app.FragmentManager, android.app.Fragment, android.os.Bundle);
method public void onFragmentAttached(android.app.FragmentManager, android.app.Fragment, android.content.Context);
@@ -5010,14 +5010,14 @@
method public void onFragmentViewDestroyed(android.app.FragmentManager, android.app.Fragment);
}
- public static abstract interface FragmentManager.OnBackStackChangedListener {
+ public static abstract deprecated interface FragmentManager.OnBackStackChangedListener {
method public abstract void onBackStackChanged();
}
- public class FragmentManagerNonConfig {
+ public deprecated class FragmentManagerNonConfig {
}
- public abstract class FragmentTransaction {
+ public abstract deprecated class FragmentTransaction {
ctor public FragmentTransaction();
method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
@@ -5129,6 +5129,7 @@
method public void setInTouchMode(boolean);
method public void start();
method public android.app.Activity startActivitySync(android.content.Intent);
+ method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle);
method public deprecated void startAllocCounting();
method public void startPerformanceSnapshot();
method public void startProfiling();
@@ -5234,7 +5235,7 @@
method public void setSelection(int);
}
- public class ListFragment extends android.app.Fragment {
+ public deprecated class ListFragment extends android.app.Fragment {
ctor public ListFragment();
method public android.widget.ListAdapter getListAdapter();
method public android.widget.ListView getListView();
@@ -5248,7 +5249,7 @@
method public void setSelection(int);
}
- public abstract class LoaderManager {
+ public abstract deprecated class LoaderManager {
ctor public LoaderManager();
method public abstract void destroyLoader(int);
method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -5258,7 +5259,7 @@
method public abstract <D> android.content.Loader<D> restartLoader(int, android.os.Bundle, android.app.LoaderManager.LoaderCallbacks<D>);
}
- public static abstract interface LoaderManager.LoaderCallbacks<D> {
+ public static abstract deprecated interface LoaderManager.LoaderCallbacks<D> {
method public abstract android.content.Loader<D> onCreateLoader(int, android.os.Bundle);
method public abstract void onLoadFinished(android.content.Loader<D>, D);
method public abstract void onLoaderReset(android.content.Loader<D>);
@@ -7417,6 +7418,7 @@
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
+ method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
@@ -7485,6 +7487,7 @@
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
@@ -7494,27 +7497,6 @@
}
-package android.app.slice.widget {
-
- public class SliceView extends android.view.ViewGroup {
- ctor public SliceView(android.content.Context);
- ctor public SliceView(android.content.Context, android.util.AttributeSet);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
- method public void clearSlice();
- method public java.lang.String getMode();
- method protected void onLayout(boolean, int, int, int, int);
- method public void setMode(java.lang.String);
- method public void setScrollable(boolean);
- method public boolean setSlice(android.net.Uri);
- method public void showSlice(android.app.slice.Slice);
- field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
- field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
- field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
- }
-
-}
-
package android.app.usage {
public final class CacheQuotaHint implements android.os.Parcelable {
@@ -9044,7 +9026,7 @@
ctor public AsyncQueryHandler.WorkerHandler(android.os.Looper);
}
- public abstract class AsyncTaskLoader<D> extends android.content.Loader {
+ public abstract deprecated class AsyncTaskLoader<D> extends android.content.Loader {
ctor public AsyncTaskLoader(android.content.Context);
method public void cancelLoadInBackground();
method public boolean isLoadInBackgroundCanceled();
@@ -9626,6 +9608,7 @@
field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
field public static final int CONTEXT_INCLUDE_CODE = 1; // 0x1
field public static final int CONTEXT_RESTRICTED = 4; // 0x4
+ field public static final java.lang.String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
field public static final java.lang.String DEVICE_POLICY_SERVICE = "device_policy";
field public static final java.lang.String DISPLAY_SERVICE = "display";
field public static final java.lang.String DOWNLOAD_SERVICE = "download";
@@ -9666,6 +9649,7 @@
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
+ field public static final java.lang.String STATS_MANAGER = "stats";
field public static final java.lang.String STORAGE_SERVICE = "storage";
field public static final java.lang.String STORAGE_STATS_SERVICE = "storagestats";
field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth";
@@ -9805,7 +9789,7 @@
method public void unregisterReceiver(android.content.BroadcastReceiver);
}
- public class CursorLoader extends android.content.AsyncTaskLoader {
+ public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
ctor public CursorLoader(android.content.Context);
ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public void deliverResult(android.database.Cursor);
@@ -10440,7 +10424,7 @@
ctor public IntentSender.SendIntentException(java.lang.Exception);
}
- public class Loader<D> {
+ public deprecated class Loader<D> {
ctor public Loader(android.content.Context);
method public void abandon();
method public boolean cancelLoad();
@@ -10473,15 +10457,15 @@
method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
}
- public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+ public final deprecated class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
ctor public Loader.ForceLoadContentObserver();
}
- public static abstract interface Loader.OnLoadCanceledListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCanceledListener<D> {
method public abstract void onLoadCanceled(android.content.Loader<D>);
}
- public static abstract interface Loader.OnLoadCompleteListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCompleteListener<D> {
method public abstract void onLoadComplete(android.content.Loader<D>, D);
}
@@ -11190,6 +11174,7 @@
field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1
field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8
field public static final int FLAG_MATCH_PINNED = 2; // 0x2
+ field public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1024; // 0x400
}
public class PackageInfo implements android.os.Parcelable {
@@ -11959,6 +11944,15 @@
}
+package android.content.pm.crossprofile {
+
+ public class CrossProfileApps {
+ method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+ method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ }
+
+}
+
package android.content.pm.permission {
public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
@@ -12872,9 +12866,11 @@
method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
method public android.database.DatabaseErrorHandler getErrorHandler();
method public long getIdleConnectionTimeout();
+ method public java.lang.String getJournalMode();
method public int getLookasideSlotCount();
method public int getLookasideSlotSize();
method public int getOpenFlags();
+ method public java.lang.String getSynchronousMode();
}
public static final class SQLiteDatabase.OpenParams.Builder {
@@ -12886,8 +12882,10 @@
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setErrorHandler(android.database.DatabaseErrorHandler);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(long);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setJournalMode(java.lang.String);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setLookasideConfig(int, int);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setOpenFlags(int);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setSynchronousMode(java.lang.String);
}
public class SQLiteDatabaseCorruptException extends android.database.sqlite.SQLiteException {
@@ -12934,6 +12932,7 @@
public abstract class SQLiteOpenHelper {
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
+ ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, int, android.database.sqlite.SQLiteDatabase.OpenParams);
method public synchronized void close();
method public java.lang.String getDatabaseName();
method public android.database.sqlite.SQLiteDatabase getReadableDatabase();
@@ -16022,6 +16021,7 @@
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+ method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
}
public static abstract class CameraCaptureSession.CaptureCallback {
@@ -16669,10 +16669,12 @@
method public void addSurface(android.view.Surface);
method public int describeContents();
method public void enableSurfaceSharing();
+ method public static int getMaxSharedSurfaceCount();
method public int getRotation();
method public android.view.Surface getSurface();
method public int getSurfaceGroupId();
method public java.util.List<android.view.Surface> getSurfaces();
+ method public void removeSurface(android.view.Surface);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int ROTATION_0 = 0; // 0x0
@@ -34818,11 +34820,13 @@
method public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(java.lang.String, android.os.UserHandle);
method public android.os.Bundle getUserRestrictions();
method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
+ method public boolean hasRestrictedProfiles();
method public boolean hasUserRestriction(java.lang.String);
method public boolean isDemoUser();
method public boolean isManagedProfile();
method public boolean isManagedProfile(int);
method public boolean isQuietModeEnabled(android.os.UserHandle);
+ method public boolean isRestrictedProfile();
method public boolean isSystemUser();
method public boolean isUserAGoat();
method public boolean isUserRunning(android.os.UserHandle);
@@ -35491,7 +35495,7 @@
method public default void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
}
- public abstract class PreferenceFragment extends android.app.Fragment {
+ public abstract deprecated class PreferenceFragment extends android.app.Fragment {
ctor public PreferenceFragment();
method public void addPreferencesFromIntent(android.content.Intent);
method public void addPreferencesFromResource(int);
@@ -35502,7 +35506,7 @@
method public void setPreferenceScreen(android.preference.PreferenceScreen);
}
- public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+ public static abstract deprecated interface PreferenceFragment.OnPreferenceStartFragmentCallback {
method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
}
@@ -41903,7 +41907,6 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -41929,7 +41932,6 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -42525,6 +42527,7 @@
method public android.telecom.Call.RttCall getRttCall();
method public int getState();
method public android.telecom.InCallService.VideoCall getVideoCall();
+ method public void handoverTo(android.telecom.PhoneAccountHandle, int, android.os.Bundle);
method public void hold();
method public boolean isRttActive();
method public void mergeConference();
@@ -42571,6 +42574,8 @@
method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
+ method public void onHandoverComplete(android.telecom.Call);
+ method public void onHandoverFailed(android.telecom.Call, int);
method public void onParentChanged(android.telecom.Call, android.telecom.Call);
method public void onPostDialWait(android.telecom.Call, java.lang.String);
method public void onRttInitiationFailure(android.telecom.Call, int);
@@ -42579,6 +42584,10 @@
method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall);
method public void onStateChanged(android.telecom.Call, int);
method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
+ field public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1; // 0x1
+ field public static final int HANDOVER_FAILURE_DEST_INVALID_PERM = 3; // 0x3
+ field public static final int HANDOVER_FAILURE_DEST_NOT_SUPPORTED = 2; // 0x2
+ field public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4; // 0x4
}
public static class Call.Details {
@@ -42651,7 +42660,9 @@
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
method public int describeContents();
+ method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
method public int getRoute();
+ method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
method public int getSupportedRouteMask();
method public boolean isMuted();
method public void writeToParcel(android.os.Parcel, int);
@@ -42790,6 +42801,7 @@
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void requestBluetoothAudio(java.lang.String);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
@@ -42921,8 +42933,11 @@
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onHandoverFailed(android.telecom.ConnectionRequest, int);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -42983,6 +42998,7 @@
method public deprecated void onPhoneCreated(android.telecom.Phone);
method public deprecated void onPhoneDestroyed(android.telecom.Phone);
method public void onSilenceRinger();
+ method public final void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -43121,6 +43137,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Call> getCalls();
method public final void removeListener(android.telecom.Phone.Listener);
+ method public void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
}
@@ -43168,6 +43185,9 @@
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
field public static final java.lang.String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
field public static final java.lang.String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
+ field public static final java.lang.String EXTRA_LOG_SELF_MANAGED_CALLS = "android.telecom.extra.LOG_SELF_MANAGED_CALLS";
+ field public static final java.lang.String EXTRA_SUPPORTS_HANDOVER_FROM = "android.telecom.extra.SUPPORTS_HANDOVER_FROM";
+ field public static final java.lang.String EXTRA_SUPPORTS_HANDOVER_TO = "android.telecom.extra.SUPPORTS_HANDOVER_TO";
field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
field public static final java.lang.String SCHEME_SIP = "sip";
@@ -43369,6 +43389,7 @@
}
public class TelecomManager {
+ method public void acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle);
method public void acceptRingingCall();
method public void acceptRingingCall(int);
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
@@ -48151,6 +48172,12 @@
field public static final int[] WILD_CARD;
}
+ public final class StatsManager {
+ method public byte[] getData(java.lang.String);
+ method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String);
+ method public boolean removeConfiguration(java.lang.String);
+ }
+
public class StringBuilderPrinter implements android.util.Printer {
ctor public StringBuilderPrinter(java.lang.StringBuilder);
method public void println(java.lang.String);
@@ -49782,6 +49809,7 @@
method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public void addFocusables(java.util.ArrayList<android.view.View>, int);
method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
+ method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
@@ -50116,6 +50144,7 @@
method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
method public boolean onKeyDown(int, android.view.KeyEvent);
+ method public boolean onKeyFallback(android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
method public boolean onKeyPreIme(int, android.view.KeyEvent);
@@ -50169,6 +50198,7 @@
method public void refreshDrawableState();
method public void releasePointerCapture();
method public boolean removeCallbacks(java.lang.Runnable);
+ method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
method public void requestApplyInsets();
@@ -50587,6 +50617,10 @@
method public abstract boolean onHover(android.view.View, android.view.MotionEvent);
}
+ public static abstract interface View.OnKeyFallbackListener {
+ method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent);
+ }
+
public static abstract interface View.OnKeyListener {
method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
}
@@ -53894,7 +53928,7 @@
method public abstract void setWebContentsDebuggingEnabled(boolean);
}
- public class WebViewFragment extends android.app.Fragment {
+ public deprecated class WebViewFragment extends android.app.Fragment {
ctor public WebViewFragment();
method public android.webkit.WebView getWebView();
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 00de88f..aebf380 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3611,11 +3611,11 @@
method public android.transition.Scene getContentScene();
method public android.transition.TransitionManager getContentTransitionManager();
method public android.view.View getCurrentFocus();
- method public android.app.FragmentManager getFragmentManager();
+ method public deprecated android.app.FragmentManager getFragmentManager();
method public android.content.Intent getIntent();
method public java.lang.Object getLastNonConfigurationInstance();
method public android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
@@ -3655,7 +3655,7 @@
method public void onActionModeStarted(android.view.ActionMode);
method public void onActivityReenter(int, android.content.Intent);
method protected void onActivityResult(int, int, android.content.Intent);
- method public void onAttachFragment(android.app.Fragment);
+ method public deprecated void onAttachFragment(android.app.Fragment);
method public void onAttachedToWindow();
method public void onBackPressed();
method protected void onChildTitleChanged(android.app.Activity, java.lang.CharSequence);
@@ -3797,8 +3797,8 @@
method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int, android.os.Bundle);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
method public boolean startActivityIfNeeded(android.content.Intent, int);
method public boolean startActivityIfNeeded(android.content.Intent, int, android.os.Bundle);
method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
@@ -4484,7 +4484,7 @@
method public void unregisterForContextMenu(android.view.View);
}
- public class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+ public deprecated class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
ctor public DialogFragment();
method public void dismiss();
method public void dismissAllowingStateLoss();
@@ -4602,7 +4602,7 @@
method public void setSelectedGroup(int);
}
- public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
+ public deprecated class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
ctor public Fragment();
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public final boolean equals(java.lang.Object);
@@ -4618,7 +4618,7 @@
method public final java.lang.Object getHost();
method public final int getId();
method public final android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public final android.app.Fragment getParentFragment();
method public android.transition.Transition getReenterTransition();
method public final android.content.res.Resources getResources();
@@ -4713,11 +4713,11 @@
method public void unregisterForContextMenu(android.view.View);
}
- public static class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
+ public static deprecated class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
}
- public static class Fragment.SavedState implements android.os.Parcelable {
+ public static deprecated class Fragment.SavedState implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.ClassLoaderCreator<android.app.Fragment.SavedState> CREATOR;
@@ -4736,17 +4736,17 @@
method public void setTitle(java.lang.CharSequence, java.lang.CharSequence);
}
- public static abstract interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
+ public static abstract deprecated interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
method public abstract boolean onBreadCrumbClick(android.app.FragmentManager.BackStackEntry, int);
}
- public abstract class FragmentContainer {
+ public abstract deprecated class FragmentContainer {
ctor public FragmentContainer();
method public abstract <T extends android.view.View> T onFindViewById(int);
method public abstract boolean onHasView();
}
- public class FragmentController {
+ public deprecated class FragmentController {
method public void attachHost(android.app.Fragment);
method public static final android.app.FragmentController createController(android.app.FragmentHostCallback<?>);
method public void dispatchActivityCreated();
@@ -4789,7 +4789,7 @@
method public android.os.Parcelable saveAllState();
}
- public abstract class FragmentHostCallback<E> extends android.app.FragmentContainer {
+ public abstract deprecated class FragmentHostCallback<E> extends android.app.FragmentContainer {
ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
method public void onAttachFragment(android.app.Fragment);
method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -4807,7 +4807,7 @@
method public boolean onUseFragmentManagerInflaterFactory();
}
- public abstract class FragmentManager {
+ public abstract deprecated class FragmentManager {
ctor public FragmentManager();
method public abstract void addOnBackStackChangedListener(android.app.FragmentManager.OnBackStackChangedListener);
method public abstract android.app.FragmentTransaction beginTransaction();
@@ -4838,7 +4838,7 @@
field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
}
- public static abstract interface FragmentManager.BackStackEntry {
+ public static abstract deprecated interface FragmentManager.BackStackEntry {
method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
method public abstract int getBreadCrumbShortTitleRes();
method public abstract java.lang.CharSequence getBreadCrumbTitle();
@@ -4847,7 +4847,7 @@
method public abstract java.lang.String getName();
}
- public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+ public static abstract deprecated class FragmentManager.FragmentLifecycleCallbacks {
ctor public FragmentManager.FragmentLifecycleCallbacks();
method public void onFragmentActivityCreated(android.app.FragmentManager, android.app.Fragment, android.os.Bundle);
method public void onFragmentAttached(android.app.FragmentManager, android.app.Fragment, android.content.Context);
@@ -4865,14 +4865,14 @@
method public void onFragmentViewDestroyed(android.app.FragmentManager, android.app.Fragment);
}
- public static abstract interface FragmentManager.OnBackStackChangedListener {
+ public static abstract deprecated interface FragmentManager.OnBackStackChangedListener {
method public abstract void onBackStackChanged();
}
- public class FragmentManagerNonConfig {
+ public deprecated class FragmentManagerNonConfig {
}
- public abstract class FragmentTransaction {
+ public abstract deprecated class FragmentTransaction {
ctor public FragmentTransaction();
method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
@@ -4972,6 +4972,7 @@
method public void setInTouchMode(boolean);
method public void start();
method public android.app.Activity startActivitySync(android.content.Intent);
+ method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle);
method public deprecated void startAllocCounting();
method public void startPerformanceSnapshot();
method public void startProfiling();
@@ -5077,7 +5078,7 @@
method public void setSelection(int);
}
- public class ListFragment extends android.app.Fragment {
+ public deprecated class ListFragment extends android.app.Fragment {
ctor public ListFragment();
method public android.widget.ListAdapter getListAdapter();
method public android.widget.ListView getListView();
@@ -5091,7 +5092,7 @@
method public void setSelection(int);
}
- public abstract class LoaderManager {
+ public abstract deprecated class LoaderManager {
ctor public LoaderManager();
method public abstract void destroyLoader(int);
method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -5101,7 +5102,7 @@
method public abstract <D> android.content.Loader<D> restartLoader(int, android.os.Bundle, android.app.LoaderManager.LoaderCallbacks<D>);
}
- public static abstract interface LoaderManager.LoaderCallbacks<D> {
+ public static abstract deprecated interface LoaderManager.LoaderCallbacks<D> {
method public abstract android.content.Loader<D> onCreateLoader(int, android.os.Bundle);
method public abstract void onLoadFinished(android.content.Loader<D>, D);
method public abstract void onLoaderReset(android.content.Loader<D>);
@@ -7047,6 +7048,7 @@
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
+ method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
@@ -7115,6 +7117,7 @@
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
@@ -7124,27 +7127,6 @@
}
-package android.app.slice.widget {
-
- public class SliceView extends android.view.ViewGroup {
- ctor public SliceView(android.content.Context);
- ctor public SliceView(android.content.Context, android.util.AttributeSet);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
- ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
- method public void clearSlice();
- method public java.lang.String getMode();
- method protected void onLayout(boolean, int, int, int, int);
- method public void setMode(java.lang.String);
- method public void setScrollable(boolean);
- method public boolean setSlice(android.net.Uri);
- method public void showSlice(android.app.slice.Slice);
- field public static final java.lang.String MODE_LARGE = "SLICE_LARGE";
- field public static final java.lang.String MODE_SHORTCUT = "SLICE_ICON";
- field public static final java.lang.String MODE_SMALL = "SLICE_SMALL";
- }
-
-}
-
package android.app.usage {
public final class ConfigurationStats implements android.os.Parcelable {
@@ -8607,7 +8589,7 @@
ctor public AsyncQueryHandler.WorkerHandler(android.os.Looper);
}
- public abstract class AsyncTaskLoader<D> extends android.content.Loader {
+ public abstract deprecated class AsyncTaskLoader<D> extends android.content.Loader {
ctor public AsyncTaskLoader(android.content.Context);
method public void cancelLoadInBackground();
method public boolean isLoadInBackgroundCanceled();
@@ -9182,6 +9164,7 @@
field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
field public static final int CONTEXT_INCLUDE_CODE = 1; // 0x1
field public static final int CONTEXT_RESTRICTED = 4; // 0x4
+ field public static final java.lang.String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
field public static final java.lang.String DEVICE_POLICY_SERVICE = "device_policy";
field public static final java.lang.String DISPLAY_SERVICE = "display";
field public static final java.lang.String DOWNLOAD_SERVICE = "download";
@@ -9349,7 +9332,7 @@
method public void unregisterReceiver(android.content.BroadcastReceiver);
}
- public class CursorLoader extends android.content.AsyncTaskLoader {
+ public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
ctor public CursorLoader(android.content.Context);
ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public void deliverResult(android.database.Cursor);
@@ -9955,7 +9938,7 @@
ctor public IntentSender.SendIntentException(java.lang.Exception);
}
- public class Loader<D> {
+ public deprecated class Loader<D> {
ctor public Loader(android.content.Context);
method public void abandon();
method public boolean cancelLoad();
@@ -9988,15 +9971,15 @@
method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
}
- public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+ public final deprecated class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
ctor public Loader.ForceLoadContentObserver();
}
- public static abstract interface Loader.OnLoadCanceledListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCanceledListener<D> {
method public abstract void onLoadCanceled(android.content.Loader<D>);
}
- public static abstract interface Loader.OnLoadCompleteListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCompleteListener<D> {
method public abstract void onLoadComplete(android.content.Loader<D>, D);
}
@@ -10651,10 +10634,10 @@
method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>);
field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
- field public static final int FLAG_MATCH_ALL_PINNED = 1024; // 0x400
field public static final int FLAG_MATCH_DYNAMIC = 1; // 0x1
field public static final int FLAG_MATCH_MANIFEST = 8; // 0x8
field public static final int FLAG_MATCH_PINNED = 2; // 0x2
+ field public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1024; // 0x400
}
public class PackageInfo implements android.os.Parcelable {
@@ -11315,6 +11298,15 @@
}
+package android.content.pm.crossprofile {
+
+ public class CrossProfileApps {
+ method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+ method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ }
+
+}
+
package android.content.res {
public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -12215,9 +12207,11 @@
method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
method public android.database.DatabaseErrorHandler getErrorHandler();
method public long getIdleConnectionTimeout();
+ method public java.lang.String getJournalMode();
method public int getLookasideSlotCount();
method public int getLookasideSlotSize();
method public int getOpenFlags();
+ method public java.lang.String getSynchronousMode();
}
public static final class SQLiteDatabase.OpenParams.Builder {
@@ -12229,8 +12223,10 @@
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setErrorHandler(android.database.DatabaseErrorHandler);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(long);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setJournalMode(java.lang.String);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setLookasideConfig(int, int);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setOpenFlags(int);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setSynchronousMode(java.lang.String);
}
public class SQLiteDatabaseCorruptException extends android.database.sqlite.SQLiteException {
@@ -12324,6 +12320,7 @@
public abstract class SQLiteOpenHelper {
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
+ ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, int, android.database.sqlite.SQLiteDatabase.OpenParams);
method public synchronized void close();
method public java.lang.String getDatabaseName();
method public android.database.sqlite.SQLiteDatabase getReadableDatabase();
@@ -15409,6 +15406,7 @@
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+ method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
}
public static abstract class CameraCaptureSession.CaptureCallback {
@@ -16054,9 +16052,11 @@
method public void addSurface(android.view.Surface);
method public int describeContents();
method public void enableSurfaceSharing();
+ method public static int getMaxSharedSurfaceCount();
method public android.view.Surface getSurface();
method public int getSurfaceGroupId();
method public java.util.List<android.view.Surface> getSurfaces();
+ method public void removeSurface(android.view.Surface);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -32876,7 +32876,7 @@
method public default void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
}
- public abstract class PreferenceFragment extends android.app.Fragment {
+ public abstract deprecated class PreferenceFragment extends android.app.Fragment {
ctor public PreferenceFragment();
method public void addPreferencesFromIntent(android.content.Intent);
method public void addPreferencesFromResource(int);
@@ -32887,7 +32887,7 @@
method public void setPreferenceScreen(android.preference.PreferenceScreen);
}
- public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+ public static abstract deprecated interface PreferenceFragment.OnPreferenceStartFragmentCallback {
method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
}
@@ -38135,6 +38135,7 @@
public abstract class ConditionProviderService extends android.app.Service {
ctor public ConditionProviderService();
+ method public boolean isBound();
method public final void notifyCondition(android.service.notification.Condition);
method public final void notifyConditions(android.service.notification.Condition...);
method public android.os.IBinder onBind(android.content.Intent);
@@ -38993,7 +38994,6 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
@@ -39019,7 +39019,6 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -39596,6 +39595,7 @@
method public android.telecom.Call.RttCall getRttCall();
method public int getState();
method public android.telecom.InCallService.VideoCall getVideoCall();
+ method public void handoverTo(android.telecom.PhoneAccountHandle, int, android.os.Bundle);
method public void hold();
method public boolean isRttActive();
method public void mergeConference();
@@ -39640,6 +39640,8 @@
method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
+ method public void onHandoverComplete(android.telecom.Call);
+ method public void onHandoverFailed(android.telecom.Call, int);
method public void onParentChanged(android.telecom.Call, android.telecom.Call);
method public void onPostDialWait(android.telecom.Call, java.lang.String);
method public void onRttInitiationFailure(android.telecom.Call, int);
@@ -39648,6 +39650,10 @@
method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall);
method public void onStateChanged(android.telecom.Call, int);
method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
+ field public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1; // 0x1
+ field public static final int HANDOVER_FAILURE_DEST_INVALID_PERM = 3; // 0x3
+ field public static final int HANDOVER_FAILURE_DEST_NOT_SUPPORTED = 2; // 0x2
+ field public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4; // 0x4
}
public static class Call.Details {
@@ -39716,7 +39722,9 @@
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
method public int describeContents();
+ method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
method public int getRoute();
+ method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
method public int getSupportedRouteMask();
method public boolean isMuted();
method public void writeToParcel(android.os.Parcel, int);
@@ -39851,6 +39859,7 @@
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void requestBluetoothAudio(java.lang.String);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void sendRemoteRttRequest();
method public final void sendRttInitiationFailure(int);
@@ -39995,8 +40004,11 @@
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onHandoverFailed(android.telecom.ConnectionRequest, int);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -40054,6 +40066,7 @@
method public void onCanAddCallChanged(boolean);
method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onSilenceRinger();
+ method public final void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -40118,6 +40131,9 @@
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
field public static final java.lang.String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
field public static final java.lang.String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
+ field public static final java.lang.String EXTRA_LOG_SELF_MANAGED_CALLS = "android.telecom.extra.LOG_SELF_MANAGED_CALLS";
+ field public static final java.lang.String EXTRA_SUPPORTS_HANDOVER_FROM = "android.telecom.extra.SUPPORTS_HANDOVER_FROM";
+ field public static final java.lang.String EXTRA_SUPPORTS_HANDOVER_TO = "android.telecom.extra.SUPPORTS_HANDOVER_TO";
field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
field public static final java.lang.String SCHEME_SIP = "sip";
@@ -40278,6 +40294,7 @@
}
public class TelecomManager {
+ method public void acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle);
method public void acceptRingingCall();
method public void acceptRingingCall(int);
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
@@ -41443,6 +41460,7 @@
}
public final class FileInfo implements android.os.Parcelable {
+ ctor public FileInfo(android.net.Uri, java.lang.String);
method public int describeContents();
method public java.lang.String getMimeType();
method public android.net.Uri getUri();
@@ -41556,6 +41574,14 @@
field public static final android.os.Parcelable.Creator<android.telephony.mbms.StreamingServiceInfo> CREATOR;
}
+ public final class UriPathPair implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.net.Uri getContentUri();
+ method public android.net.Uri getFilePathUri();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.UriPathPair> CREATOR;
+ }
+
}
package android.telephony.mbms.vendor {
@@ -41587,6 +41613,23 @@
method public void stopStreaming(int, java.lang.String) throws android.os.RemoteException;
}
+ public class VendorUtils {
+ ctor public VendorUtils();
+ method public static android.content.ComponentName getAppReceiverFromPackageName(android.content.Context, java.lang.String);
+ field public static final java.lang.String ACTION_CLEANUP = "android.telephony.mbms.action.CLEANUP";
+ field public static final java.lang.String ACTION_DOWNLOAD_RESULT_INTERNAL = "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
+ field public static final java.lang.String ACTION_FILE_DESCRIPTOR_REQUEST = "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
+ field public static final java.lang.String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
+ field public static final java.lang.String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
+ field public static final java.lang.String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
+ field public static final java.lang.String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
+ field public static final java.lang.String EXTRA_PAUSED_URI_LIST = "android.telephony.mbms.extra.PAUSED_URI_LIST";
+ field public static final java.lang.String EXTRA_SERVICE_ID = "android.telephony.mbms.extra.SERVICE_ID";
+ field public static final java.lang.String EXTRA_TEMP_FILES_IN_USE = "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
+ field public static final java.lang.String EXTRA_TEMP_FILE_ROOT = "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+ field public static final java.lang.String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
+ }
+
}
package android.test {
@@ -46678,6 +46721,7 @@
method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle);
method public void addFocusables(java.util.ArrayList<android.view.View>, int);
method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
+ method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
@@ -47014,6 +47058,7 @@
method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
method public boolean onKeyDown(int, android.view.KeyEvent);
+ method public boolean onKeyFallback(android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
method public boolean onKeyPreIme(int, android.view.KeyEvent);
@@ -47067,6 +47112,7 @@
method public void refreshDrawableState();
method public void releasePointerCapture();
method public boolean removeCallbacks(java.lang.Runnable);
+ method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener);
method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
method public void requestApplyInsets();
@@ -47489,6 +47535,10 @@
method public abstract boolean onHover(android.view.View, android.view.MotionEvent);
}
+ public static abstract interface View.OnKeyFallbackListener {
+ method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent);
+ }
+
public static abstract interface View.OnKeyListener {
method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
}
@@ -50617,7 +50667,7 @@
method public abstract void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
}
- public class WebViewFragment extends android.app.Fragment {
+ public deprecated class WebViewFragment extends android.app.Fragment {
ctor public WebViewFragment();
method public android.webkit.WebView getWebView();
}
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 93b9f58..d79b1a6 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -47,6 +47,16 @@
/**
* Runs the am instrument command
+ *
+ * Test Result Code:
+ * 1 - Test running
+ * 0 - Test passed
+ * -2 - assertion failure
+ * -1 - other exceptions
+ *
+ * Session Result Code:
+ * -1: Success
+ * other: Failure
*/
public class Instrument {
public static final String DEFAULT_LOG_DIR = "instrument-logs";
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index c7d1ca2..db4f586 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -22,23 +22,36 @@
#include <sstream>
#include <unistd.h>
-const ssize_t BUFFER_SIZE = 16 * 1024; // 4KB
+bool isValidChar(char c) {
+ uint8_t v = (uint8_t)c;
+ return (v >= (uint8_t)'a' && v <= (uint8_t)'z')
+ || (v >= (uint8_t)'A' && v <= (uint8_t)'Z')
+ || (v >= (uint8_t)'0' && v <= (uint8_t)'9')
+ || (v == (uint8_t)'_');
+}
-
-static std::string trim(const std::string& s) {
- const auto head = s.find_first_not_of(DEFAULT_WHITESPACE);
+std::string trim(const std::string& s, const std::string& charset) {
+ const auto head = s.find_first_not_of(charset);
if (head == std::string::npos) return "";
- const auto tail = s.find_last_not_of(DEFAULT_WHITESPACE);
+ const auto tail = s.find_last_not_of(charset);
return s.substr(head, tail - head + 1);
}
-static std::string trimHeader(const std::string& s) {
- std::string res = trim(s);
+static inline std::string toLowerStr(const std::string& s) {
+ std::string res(s);
std::transform(res.begin(), res.end(), res.begin(), ::tolower);
return res;
}
+static inline std::string trimDefault(const std::string& s) {
+ return trim(s, DEFAULT_WHITESPACE);
+}
+
+static inline std::string trimHeader(const std::string& s) {
+ return toLowerStr(trimDefault(s));
+}
+
// This is similiar to Split in android-base/file.h, but it won't add empty string
static void split(const std::string& line, std::vector<std::string>& words,
const trans_func& func, const std::string& delimiters) {
@@ -68,22 +81,68 @@
record_t parseRecord(const std::string& line, const std::string& delimiters) {
record_t record;
- trans_func f = &trim;
+ trans_func f = &trimDefault;
split(line, record, f, delimiters);
return record;
}
-bool hasPrefix(std::string* line, const char* key) {
+record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) {
+ record_t record;
+ int lastIndex = 0;
+ int lineSize = (int)line.size();
+ for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) {
+ int idx = *it;
+ if (lastIndex > idx || idx > lineSize) {
+ record.clear(); // The indices is wrong, return empty;
+ return record;
+ }
+ while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos);
+ record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex)));
+ lastIndex = idx;
+ }
+ record.push_back(trimDefault(line.substr(lastIndex, lineSize - lastIndex)));
+ return record;
+}
+
+bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) {
const auto head = line->find_first_not_of(DEFAULT_WHITESPACE);
if (head == std::string::npos) return false;
- auto i = 0;
- auto j = head;
+ int len = (int)line->length();
+ int i = 0;
+ int j = head;
while (key[i] != '\0') {
- if (j >= line->size() || key[i++] != line->at(j++)) {
+ if (j >= len || key[i++] != line->at(j++)) {
return false;
}
}
- line->assign(trim(line->substr(j)));
+
+ if (endAtDelimiter) {
+ // this means if the line only have prefix or no delimiter, we still return false.
+ if (j == len || isValidChar(line->at(j))) return false;
+ }
+
+ line->assign(trimDefault(line->substr(j)));
+ return true;
+}
+
+bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) {
+ const auto tail = line->find_last_not_of(DEFAULT_WHITESPACE);
+ if (tail == std::string::npos) return false;
+ int i = 0;
+ while (key[++i] != '\0'); // compute the size of the key
+ int j = tail;
+ while (i > 0) {
+ if (j < 0 || key[--i] != line->at(j--)) {
+ return false;
+ }
+ }
+
+ if (endAtDelimiter) {
+ // this means if the line only have suffix or no delimiter, we still return false.
+ if (j < 0 || isValidChar(line->at(j))) return false;
+ }
+
+ line->assign(trimDefault(line->substr(0, j+1)));
return true;
}
@@ -95,65 +154,36 @@
return atoll(s.c_str());
}
-// ==============================================================================
-Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {};
+double toDouble(const std::string& s) {
+ return atof(s.c_str());
+}
-Reader::Reader(const int fd, const size_t capacity)
- : mFd(fd), mMaxSize(capacity), mBufSize(0), mRead(0), mFlushed(0)
+// ==============================================================================
+Reader::Reader(const int fd)
{
- mBuf = capacity > 0 ? (char*)malloc(capacity * sizeof(char)) : NULL;
- mStatus = mFd < 0 ? "Negative fd" : (capacity == 0 ? "Zero buffer capacity" : "");
+ mFile = fdopen(fd, "r");
+ mStatus = mFile == NULL ? "Invalid fd " + std::to_string(fd) : "";
}
Reader::~Reader()
{
- free(mBuf);
+ if (mFile != NULL) fclose(mFile);
}
-bool Reader::readLine(std::string* line, const char newline) {
- if (!ok(line)) return false; // bad status
- line->clear();
- std::stringstream ss;
- while (!EOR()) {
- // read if available
- if (mFd != -1 && mBufSize != mMaxSize) {
- ssize_t amt = 0;
- if (mRead >= mFlushed) {
- amt = ::read(mFd, mBuf + mRead, mMaxSize - mRead);
- } else {
- amt = ::read(mFd, mBuf + mRead, mFlushed - mRead);
- }
- if (amt < 0) {
- mStatus = "Fail to read from fd";
- return false;
- } else if (amt == 0) {
- close(mFd);
- mFd = -1;
- }
- mRead += amt;
- mBufSize += amt;
- }
+bool Reader::readLine(std::string* line) {
+ if (mFile == NULL) return false;
- bool meetsNewLine = false;
- if (mBufSize > 0) {
- int start = mFlushed;
- int end = mFlushed < mRead ? mRead : mMaxSize;
- while (mFlushed < end && mBuf[mFlushed++] != newline && mBufSize > 0) mBufSize--;
- meetsNewLine = (mBuf[mFlushed-1] == newline);
- if (meetsNewLine) mBufSize--; // deduct the new line character
- size_t len = meetsNewLine ? mFlushed - start - 1 : mFlushed - start;
- ss.write(mBuf + start, len);
- }
-
- if (mRead >= (int) mMaxSize) mRead = 0;
- if (mFlushed >= (int) mMaxSize) mFlushed = 0;
-
- if (EOR() || meetsNewLine) {
- line->assign(ss.str());
- return true;
- }
+ char* buf = NULL;
+ size_t len = 0;
+ ssize_t read = getline(&buf, &len, mFile);
+ if (read != -1) {
+ std::string s(buf);
+ line->assign(trim(s, DEFAULT_NEWLINE));
+ } else if (errno == EINVAL) {
+ mStatus = "Bad Argument";
}
- return false;
+ free(buf);
+ return read != -1;
}
bool Reader::ok(std::string* error) {
@@ -163,51 +193,104 @@
// ==============================================================================
Table::Table(const char* names[], const uint64_t ids[], const int count)
- :mFieldNames(names),
- mFieldIds(ids),
- mFieldCount(count)
+ :mEnums(),
+ mEnumValuesByName()
{
+ map<std::string, uint64_t> fields;
+ for (int i = 0; i < count; i++) {
+ fields[names[i]] = ids[i];
+ }
+ mFields = fields;
}
Table::~Table()
{
}
-bool
-Table::insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value)
+void
+Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize)
{
- uint64_t found = 0;
- for (int i=0; i<mFieldCount; i++) {
- if (strcmp(name.c_str(), mFieldNames[i]) == 0) {
- found = mFieldIds[i];
- break;
- }
- }
+ if (mFields.find(field) == mFields.end()) return;
- switch (found & FIELD_TYPE_MASK) {
- case FIELD_TYPE_DOUBLE:
- case FIELD_TYPE_FLOAT:
- // TODO: support parse string to float/double
+ map<std::string, int> enu;
+ for (int i = 0; i < enumSize; i++) {
+ enu[enumNames[i]] = enumValues[i];
+ }
+ mEnums[field] = enu;
+}
+
+void
+Table::addEnumNameToValue(const char* enumName, const int enumValue)
+{
+ mEnumValuesByName[enumName] = enumValue;
+}
+
+bool
+Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
+{
+ if (mFields.find(name) == mFields.end()) return false;
+
+ uint64_t found = mFields[name];
+ record_t repeats; // used for repeated fields
+ switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) {
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT:
+ proto->write(found, toDouble(value));
+ break;
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES:
+ proto->write(found, value);
+ break;
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64:
+ proto->write(found, toLongLong(value));
+ break;
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL:
+ if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) {
+ proto->write(found, true);
+ break;
+ }
+ if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) {
+ proto->write(found, false);
+ break;
+ }
return false;
- case FIELD_TYPE_STRING:
- case FIELD_TYPE_BYTES:
- proto.write(found, value);
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM:
+ // if the field has its own enum mapping, use this, otherwise use general name to value mapping.
+ if (mEnums.find(name) != mEnums.end()) {
+ if (mEnums[name].find(value) != mEnums[name].end()) {
+ proto->write(found, mEnums[name][value]);
+ } else {
+ proto->write(found, 0); // TODO: should get the default enum value (Unknown)
+ }
+ } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) {
+ proto->write(found, mEnumValuesByName[value]);
+ } else {
+ return false;
+ }
break;
- case FIELD_TYPE_INT64:
- case FIELD_TYPE_SINT64:
- case FIELD_TYPE_UINT64:
- case FIELD_TYPE_FIXED64:
- case FIELD_TYPE_SFIXED64:
- proto.write(found, toLongLong(value));
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32:
+ case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32:
+ proto->write(found, toInt(value));
break;
- case FIELD_TYPE_BOOL:
- case FIELD_TYPE_ENUM:
- case FIELD_TYPE_INT32:
- case FIELD_TYPE_SINT32:
- case FIELD_TYPE_UINT32:
- case FIELD_TYPE_FIXED32:
- case FIELD_TYPE_SFIXED32:
- proto.write(found, toInt(value));
+ // REPEATED TYPE below:
+ case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32:
+ repeats = parseRecord(value, COMMA_DELIMITER);
+ for (size_t i=0; i<repeats.size(); i++) {
+ proto->write(found, toInt(repeats[i]));
+ }
+ break;
+ case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING:
+ repeats = parseRecord(value, COMMA_DELIMITER);
+ for (size_t i=0; i<repeats.size(); i++) {
+ proto->write(found, repeats[i]);
+ }
break;
default:
return false;
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index 86761e9..4a5fe1d 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -17,9 +17,9 @@
#ifndef INCIDENT_HELPER_UTIL_H
#define INCIDENT_HELPER_UTIL_H
+#include <map>
#include <string>
#include <vector>
-#include <sstream>
#include <android/util/ProtoOutputStream.h>
@@ -29,8 +29,16 @@
typedef std::vector<std::string> record_t;
typedef std::string (*trans_func) (const std::string&);
-const char DEFAULT_NEWLINE = '\n';
const std::string DEFAULT_WHITESPACE = " \t";
+const std::string DEFAULT_NEWLINE = "\r\n";
+const std::string TAB_DELIMITER = "\t";
+const std::string COMMA_DELIMITER = ",";
+
+// returns true if c is a-zA-Z0-9 or underscore _
+bool isValidChar(char c);
+
+// trim the string with the given charset
+std::string trim(const std::string& s, const std::string& charset);
/**
* When a text has a table format like this
@@ -47,19 +55,33 @@
record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
/**
- * When the line starts with the given key, the function returns true
- * as well as the line argument is changed to the rest part of the original.
+ * When a text-format table aligns by its vertical position, it is not possible to split them by purely delimiters.
+ * This function allows to parse record by its header's column position' indices, must in ascending order.
+ * At the same time, it still looks at the char at index, if it doesn't belong to delimiters, moves forward to find the delimiters.
+ */
+record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters = DEFAULT_WHITESPACE);
+
+/**
+ * When the line starts/ends with the given key, the function returns true
+ * as well as the line argument is changed to the rest trimmed part of the original.
* e.g. "ZRAM: 6828K physical used for 31076K in swap (524284K total swap)" becomes
* "6828K physical used for 31076K in swap (524284K total swap)" when given key "ZRAM:",
* otherwise the line is not changed.
+ *
+ * In order to prevent two values have same prefix which cause entering to incorrect conditions,
+ * stripPrefix and stripSuffix can turn on a flag that requires the ending char in the line must not be a valid
+ * character or digits, this feature is off by default.
+ * i.e. ABC%some value, ABCD%other value
*/
-bool hasPrefix(std::string* line, const char* key);
+bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter = false);
+bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter = false);
/**
* Converts string to the desired type
*/
int toInt(const std::string& s);
long long toLongLong(const std::string& s);
+double toDouble(const std::string& s);
/**
* Reader class reads data from given fd in streaming fashion.
@@ -69,23 +91,14 @@
{
public:
Reader(const int fd);
- Reader(const int fd, const size_t capacity);
~Reader();
- bool readLine(std::string* line, const char newline = DEFAULT_NEWLINE);
+ bool readLine(std::string* line);
bool ok(std::string* error);
private:
- int mFd; // set mFd to -1 when read EOF()
- const size_t mMaxSize;
- size_t mBufSize;
- char* mBuf; // implements a circular buffer
-
- int mRead;
- int mFlushed;
+ FILE* mFile;
std::string mStatus;
- // end of read
- inline bool EOR() { return mFd == -1 && mBufSize == 0; };
};
/**
@@ -98,12 +111,17 @@
Table(const char* names[], const uint64_t ids[], const int count);
~Table();
- bool insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value);
+ // Add enum names to values for parsing purpose.
+ void addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize);
+ // manually add enum names to values mapping, useful when an Enum type is used by a lot of fields, and there are no name conflicts
+ void addEnumNameToValue(const char* enumName, const int enumValue);
+
+ bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
private:
- const char** mFieldNames;
- const uint64_t* mFieldIds;
- const int mFieldCount;
+ map<std::string, uint64_t> mFields;
+ map<std::string, map<std::string, int>> mEnums;
+ map<std::string, int> mEnumValuesByName;
};
#endif // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 3da87b9c..c8a0883 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,9 +16,12 @@
#define LOG_TAG "incident_helper"
+#include "parsers/CpuFreqParser.h"
+#include "parsers/CpuInfoParser.h"
#include "parsers/KernelWakesParser.h"
#include "parsers/PageTypeInfoParser.h"
#include "parsers/ProcrankParser.h"
+#include "parsers/SystemPropertiesParser.h"
#include <android-base/file.h>
#include <getopt.h>
@@ -48,12 +51,18 @@
return new ReverseParser();
/* ========================================================================= */
// IDs larger than 1 are section ids reserved in incident.proto
+ case 1000:
+ return new SystemPropertiesParser();
case 2000:
return new ProcrankParser();
case 2001:
return new PageTypeInfoParser();
case 2002:
return new KernelWakesParser();
+ case 2003:
+ return new CpuInfoParser();
+ case 2004:
+ return new CpuFreqParser();
default:
return NULL;
}
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
new file mode 100644
index 0000000..02f1ce7
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+#include <unistd.h>
+
+#include "frameworks/base/core/proto/android/os/cpufreq.proto.h"
+#include "ih_util.h"
+#include "CpuFreqParser.h"
+
+using namespace android::os;
+
+status_t
+CpuFreqParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+
+ // parse header
+ reader.readLine(&line);
+ header_t header = parseHeader(line, TAB_DELIMITER);
+ if (header.size() < 1) {
+ fprintf(stderr, "Bad header: %s\n", line.c_str());
+ return BAD_VALUE;
+ }
+ const int numCpus = (int)header.size() - 1;
+ vector<pair<int, long long>> cpucores[numCpus];
+
+ // parse freq and time
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ record_t record = parseRecord(line, TAB_DELIMITER);
+ if (record.size() != header.size()) {
+ fprintf(stderr, "Bad line: %s\n", line.c_str());
+ continue;
+ }
+
+ int freq = toInt(record[0]);
+ for (int i=0; i<numCpus; i++) {
+ if (strcmp(record[i+1].c_str(), "N/A") == 0) {
+ continue;
+ }
+ cpucores[i].push_back(make_pair(freq, toLongLong(record[i+1])));
+ }
+ }
+
+ ProtoOutputStream proto;
+
+ long jiffyHz = sysconf(_SC_CLK_TCK);
+ proto.write(CpuFreq::JIFFY_HZ, (int)jiffyHz);
+
+ for (int i=0; i<numCpus; i++) {
+ long long token = proto.start(CpuFreq::CPU_FREQS);
+ proto.write(CpuFreqStats::CPU_NAME, header[i+1]);
+ for (vector<pair<int, long long>>::iterator it = cpucores[i].begin(); it != cpucores[i].end(); it++) {
+ long long stateToken = proto.start(CpuFreqStats::TIMES);
+ proto.write(CpuFreqStats::TimeInState::STATE_KHZ, it->first);
+ proto.write(CpuFreqStats::TimeInState::TIME_JIFFY, it->second);
+ proto.end(stateToken);
+ }
+ proto.end(token);
+ }
+
+ if (!reader.ok(&line)) {
+ fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+ return -1;
+ }
+
+ if (!proto.flush(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.h b/cmds/incident_helper/src/parsers/CpuFreqParser.h
new file mode 100644
index 0000000..470d568
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CPU_FREQ_PARSER_H
+#define CPU_FREQ_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Cpu frequency parser, parses text in /sys/devices/system/cpu/cpufreq/all_time_in_state
+ */
+class CpuFreqParser : public TextParserBase {
+public:
+ CpuFreqParser() : TextParserBase(String8("CpuFreqParser")) {};
+ ~CpuFreqParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // CPU_FREQ_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
new file mode 100644
index 0000000..3faca00
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/cpuinfo.proto.h"
+#include "ih_util.h"
+#include "CpuInfoParser.h"
+
+using namespace android::os;
+
+static void writeSuffixLine(ProtoOutputStream* proto, uint64_t fieldId,
+ const string& line, const string& delimiter,
+ const int count, const char* names[], const uint64_t ids[])
+{
+ record_t record = parseRecord(line, delimiter);
+ long long token = proto->start(fieldId);
+ for (int i=0; i<(int)record.size(); i++) {
+ for (int j=0; j<count; j++) {
+ if (stripSuffix(&record[i], names[j], true)) {
+ proto->write(ids[j], toInt(record[i]));
+ break;
+ }
+ }
+ }
+ proto->end(token);
+}
+
+status_t
+CpuInfoParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ header_t header;
+ vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions.
+ record_t record;
+ int nline = 0;
+ bool nextToSwap = false;
+ bool nextToUsage = false;
+
+ ProtoOutputStream proto;
+ Table table(CpuInfo::Task::_FIELD_NAMES, CpuInfo::Task::_FIELD_IDS, CpuInfo::Task::_FIELD_COUNT);
+ table.addEnumTypeMap("s", CpuInfo::Task::_ENUM_STATUS_NAMES,
+ CpuInfo::Task::_ENUM_STATUS_VALUES, CpuInfo::Task::_ENUM_STATUS_COUNT);
+ table.addEnumTypeMap("pcy", CpuInfo::Task::_ENUM_POLICY_NAMES,
+ CpuInfo::Task::_ENUM_POLICY_VALUES, CpuInfo::Task::_ENUM_POLICY_COUNT);
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ nline++;
+
+ if (stripPrefix(&line, "Tasks:")) {
+ writeSuffixLine(&proto, CpuInfo::TASK_STATS, line, COMMA_DELIMITER,
+ CpuInfo::TaskStats::_FIELD_COUNT,
+ CpuInfo::TaskStats::_FIELD_NAMES,
+ CpuInfo::TaskStats::_FIELD_IDS);
+ continue;
+ }
+ if (stripPrefix(&line, "Mem:")) {
+ writeSuffixLine(&proto, CpuInfo::MEM, line, COMMA_DELIMITER,
+ CpuInfo::MemStats::_FIELD_COUNT,
+ CpuInfo::MemStats::_FIELD_NAMES,
+ CpuInfo::MemStats::_FIELD_IDS);
+ continue;
+ }
+ if (stripPrefix(&line, "Swap:")) {
+ writeSuffixLine(&proto, CpuInfo::SWAP, line, COMMA_DELIMITER,
+ CpuInfo::MemStats::_FIELD_COUNT,
+ CpuInfo::MemStats::_FIELD_NAMES,
+ CpuInfo::MemStats::_FIELD_IDS);
+ nextToSwap = true;
+ continue;
+ }
+
+ if (nextToSwap) {
+ writeSuffixLine(&proto, CpuInfo::CPU_USAGE, line, DEFAULT_WHITESPACE,
+ CpuInfo::CpuUsage::_FIELD_COUNT,
+ CpuInfo::CpuUsage::_FIELD_NAMES,
+ CpuInfo::CpuUsage::_FIELD_IDS);
+ nextToUsage = true;
+ nextToSwap = false;
+ continue;
+ }
+
+ // Header of tasks must be next to usage line
+ if (nextToUsage) {
+ // How to parse Header of Tasks:
+ // PID TID USER PR NI[%CPU]S VIRT RES PCY CMD NAME
+ // After parsing, header = { PID, TID, USER, PR, NI, CPU, S, VIRT, RES, PCY, CMD, NAME }
+ // And columnIndices will contain end index of each word.
+ header = parseHeader(line, "[ %]");
+ nextToUsage = false;
+
+ // NAME is not in the list since the last split index is default to the end of line.
+ const char* headerNames[11] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD" };
+ size_t lastIndex = 0;
+ for (int i = 0; i < 11; i++) {
+ string s = headerNames[i];
+ lastIndex = line.find(s, lastIndex);
+ if (lastIndex == string::npos) {
+ fprintf(stderr, "Bad Task Header: %s\n", line.c_str());
+ return -1;
+ }
+ lastIndex += s.length();
+ columnIndices.push_back(lastIndex);
+ }
+ // Need to remove the end index of CMD and use the start index of NAME because CMD values contain spaces.
+ // for example: ... CMD NAME
+ // ... Jit thread pool com.google.android.gms.feedback
+ // If use end index of CMD, parsed result = { "Jit", "thread pool com.google.android.gms.feedback" }
+ // If use start index of NAME, parsed result = { "Jit thread pool", "com.google.android.gms.feedback" }
+ int endCMD = columnIndices.back();
+ columnIndices.pop_back();
+ columnIndices.push_back(line.find("NAME", endCMD) - 1);
+ continue;
+ }
+
+ record = parseRecordByColumns(line, columnIndices);
+ if (record.size() != header.size()) {
+ fprintf(stderr, "[%s]Line %d has missing fields:\n%s\n", this->name.string(), nline, line.c_str());
+ continue;
+ }
+
+ long long token = proto.start(CpuInfo::TASKS);
+ for (int i=0; i<(int)record.size(); i++) {
+ if (!table.insertField(&proto, header[i], record[i])) {
+ fprintf(stderr, "[%s]Line %d fails to insert field %s with value %s\n",
+ this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ }
+ }
+ proto.end(token);
+ }
+
+ if (!reader.ok(&line)) {
+ fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+ return -1;
+ }
+
+ if (!proto.flush(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.h b/cmds/incident_helper/src/parsers/CpuInfoParser.h
new file mode 100644
index 0000000..f57bb4e
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CPU_INFO_PARSER_H
+#define CPU_INFO_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Cpu info parser, parses text produced by command
+ * 'top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name'
+ */
+class CpuInfoParser : public TextParserBase {
+public:
+ CpuInfoParser() : TextParserBase(String8("CpuInfoParser")) {};
+ ~CpuInfoParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // CPU_INFO_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
index cc4a1e1..ada4a5d 100644
--- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -23,8 +23,6 @@
using namespace android::os;
-const std::string LINE_DELIMITER = "\t";
-
status_t
KernelWakesParser::Parse(const int in, const int out) const
{
@@ -42,12 +40,12 @@
if (line.empty()) continue;
// parse head line
if (nline++ == 0) {
- header = parseHeader(line, LINE_DELIMITER);
+ header = parseHeader(line, TAB_DELIMITER);
continue;
}
// parse for each record, the line delimiter is \t only!
- record = parseRecord(line, LINE_DELIMITER);
+ record = parseRecord(line, TAB_DELIMITER);
if (record.size() != header.size()) {
// TODO: log this to incident report!
@@ -57,7 +55,7 @@
long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES);
for (int i=0; i<(int)record.size(); i++) {
- if (!table.insertField(proto, header[i], record[i])) {
+ if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
this->name.string(), nline, header[i].c_str(), record[i].c_str());
}
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
index 6047bd1..f1b93ff 100644
--- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -23,8 +23,6 @@
using namespace android::os;
-const std::string LINE_DELIMITER = ",";
-
status_t
PageTypeInfoParser::Parse(const int in, const int out) const
{
@@ -44,37 +42,37 @@
continue;
}
- if (hasPrefix(&line, "Page block order:")) {
+ if (stripPrefix(&line, "Page block order:")) {
pageBlockOrder = toInt(line);
proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder);
continue;
}
- if (hasPrefix(&line, "Pages per block:")) {
+ if (stripPrefix(&line, "Pages per block:")) {
proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line));
continue;
}
- if (hasPrefix(&line, "Free pages count per migrate type at order")) {
+ if (stripPrefix(&line, "Free pages count per migrate type at order")) {
migrateTypeSession = true;
continue;
}
- if (hasPrefix(&line, "Number of blocks type")) {
+ if (stripPrefix(&line, "Number of blocks type")) {
blockHeader = parseHeader(line);
continue;
}
- record_t record = parseRecord(line, LINE_DELIMITER);
+ record_t record = parseRecord(line, COMMA_DELIMITER);
if (migrateTypeSession && record.size() == 3) {
long long token = proto.start(PageTypeInfo::MIGRATE_TYPES);
// expect part 0 starts with "Node"
- if (hasPrefix(&record[0], "Node")) {
+ if (stripPrefix(&record[0], "Node")) {
proto.write(MigrateTypeProto::NODE, toInt(record[0]));
} else return BAD_VALUE;
// expect part 1 starts with "zone"
- if (hasPrefix(&record[1], "zone")) {
+ if (stripPrefix(&record[1], "zone")) {
proto.write(MigrateTypeProto::ZONE, record[1]);
} else return BAD_VALUE;
// expect part 2 starts with "type"
- if (hasPrefix(&record[2], "type")) {
+ if (stripPrefix(&record[2], "type")) {
// expect the rest of part 2 has number of (pageBlockOrder + 2) parts
// An example looks like:
// header line: type 0 1 2 3 4 5 6 7 8 9 10
@@ -94,16 +92,16 @@
proto.end(token);
} else if (!blockHeader.empty() && record.size() == 2) {
long long token = proto.start(PageTypeInfo::BLOCKS);
- if (hasPrefix(&record[0], "Node")) {
+ if (stripPrefix(&record[0], "Node")) {
proto.write(BlockProto::NODE, toInt(record[0]));
} else return BAD_VALUE;
- if (hasPrefix(&record[1], "zone")) {
+ if (stripPrefix(&record[1], "zone")) {
record_t blockCounts = parseRecord(record[1]);
proto.write(BlockProto::ZONE, blockCounts[0]);
for (size_t i=0; i<blockHeader.size(); i++) {
- if (!table.insertField(proto, blockHeader[i], blockCounts[i+1])) {
+ if (!table.insertField(&proto, blockHeader[i], blockCounts[i+1])) {
return BAD_VALUE;
}
}
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
index 93f970f..a4eb0fd 100644
--- a/cmds/incident_helper/src/parsers/ProcrankParser.cpp
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
@@ -46,11 +46,11 @@
continue;
}
- if (hasPrefix(&line, "ZRAM:")) {
+ if (stripPrefix(&line, "ZRAM:")) {
zram = line;
continue;
}
- if (hasPrefix(&line, "RAM:")) {
+ if (stripPrefix(&line, "RAM:")) {
ram = line;
continue;
}
@@ -68,7 +68,7 @@
long long token = proto.start(Procrank::PROCESSES);
for (int i=0; i<(int)record.size(); i++) {
- if (!table.insertField(proto, header[i], record[i])) {
+ if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
this->name.string(), nline, header[i].c_str(), record[i].c_str());
}
@@ -82,7 +82,7 @@
record = parseRecord(total);
long long token = proto.start(SummaryProto::TOTAL);
for (int i=(int)record.size(); i>0; i--) {
- table.insertField(proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
+ table.insertField(&proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
}
proto.end(token);
}
diff --git a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
new file mode 100644
index 0000000..ee5feb0
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/system_properties.proto.h"
+#include "ih_util.h"
+#include "SystemPropertiesParser.h"
+
+using namespace android::os;
+
+const string LINE_DELIMITER = "]: [";
+
+// system properties' names sometimes are not valid proto field names, make the names valid.
+static string convertToFieldName(const string& name) {
+ int len = (int)name.length();
+ char cstr[len + 1];
+ strcpy(cstr, name.c_str());
+ for (int i = 0; i < len; i++) {
+ if (!isValidChar(cstr[i])) {
+ cstr[i] = '_';
+ }
+ }
+ return string(cstr);
+}
+
+status_t
+SystemPropertiesParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ string name; // the name of the property
+ string value; // the string value of the property
+
+ ProtoOutputStream proto;
+ Table table(SystemPropertiesProto::_FIELD_NAMES, SystemPropertiesProto::_FIELD_IDS, SystemPropertiesProto::_FIELD_COUNT);
+ table.addEnumNameToValue("running", SystemPropertiesProto::STATUS_RUNNING);
+ table.addEnumNameToValue("stopped", SystemPropertiesProto::STATUS_STOPPED);
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ line = line.substr(1, line.size() - 2); // trim []
+ size_t index = line.find(LINE_DELIMITER); // split by "]: ["
+ if (index == string::npos) {
+ fprintf(stderr, "Bad Line %s\n", line.c_str());
+ continue;
+ }
+ name = line.substr(0, index);
+ value = trim(line.substr(index + 4), DEFAULT_WHITESPACE);
+ if (value.empty()) continue;
+
+ // if the property name couldn't be found in proto definition or the value has mistype,
+ // add to extra properties with its name and value
+ if (!table.insertField(&proto, convertToFieldName(name), value)) {
+ long long token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES);
+ proto.write(SystemPropertiesProto::Property::NAME, name);
+ proto.write(SystemPropertiesProto::Property::VALUE, value);
+ proto.end(token);
+ }
+ }
+
+ if (!reader.ok(&line)) {
+ fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+ return -1;
+ }
+
+ if (!proto.flush(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/SystemPropertiesParser.h b/cmds/incident_helper/src/parsers/SystemPropertiesParser.h
new file mode 100644
index 0000000..c401600
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_PROPERTIES_PARSER_H
+#define SYSTEM_PROPERTIES_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * SystemProperties parser, parses text produced by command getprop.
+ */
+class SystemPropertiesParser : public TextParserBase {
+public:
+ SystemPropertiesParser() : TextParserBase(String8("SystemPropertiesParser")) {};
+ ~SystemPropertiesParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // SYSTEM_PROPERTIES_PARSER_H
diff --git a/cmds/incident_helper/testdata/cpufreq.txt b/cmds/incident_helper/testdata/cpufreq.txt
new file mode 100644
index 0000000..6472839
--- /dev/null
+++ b/cmds/incident_helper/testdata/cpufreq.txt
@@ -0,0 +1,6 @@
+freq cpu0 cpu1 cpu2 cpu3
+307200 23860761 23860761 23890935 23890935
+384000 83124 83124 29383 29383
+748800 N/A N/A 10547 10547
+768000 22652 22652 N/A N/A
+825600 N/A N/A 13173 13173
diff --git a/cmds/incident_helper/testdata/cpuinfo.txt b/cmds/incident_helper/testdata/cpuinfo.txt
new file mode 100644
index 0000000..ec4a839
--- /dev/null
+++ b/cmds/incident_helper/testdata/cpuinfo.txt
@@ -0,0 +1,15 @@
+Tasks: 2038 total, 1 running,2033 sleeping, 0 stopped, 0 zombie
+
+Mem: 3842668k total, 3761936k used, 80732k free, 220188k buffers
+
+Swap: 524284k total, 25892k used, 498392k free, 1316952k cached
+
+400%cpu 17%user 0%nice 43%sys 338%idle 0%iow 0%irq 1%sirq 0%host
+
+ PID TID USER PR NI[%CPU]S VIRT RES PCY CMD NAME
+
+
+29438 29438 rootabcdefghij 20 0 57.9 R 14M 3.8M top test top
+ 916 916 system 18 -2 1.4 S 4.6G 404M fg system_server system_server
+ 28 28 root -2 0 1.4 S 0 0 bg rcuc/3 [rcuc/3]
+ 27 27 root RT 0 1.4 S 0 0 ta migration/3 [migration/3]
\ No newline at end of file
diff --git a/cmds/incident_helper/testdata/system_properties.txt b/cmds/incident_helper/testdata/system_properties.txt
new file mode 100644
index 0000000..57c07ee
--- /dev/null
+++ b/cmds/incident_helper/testdata/system_properties.txt
@@ -0,0 +1,14 @@
+[aaudio.hw_burst_min_usec]: [2000]
+[aaudio.mmap_exclusive_policy]: [2]
+[dalvik.vm.appimageformat]: [lz4]
+[gsm.operator.isroaming]: [false]
+[init.svc.vendor.imsqmidaemon]: [running]
+[init.svc.vendor.init-radio-sh]: [stopped]
+[net.dns1]: [2001:4860:4860::8844]
+[net.tcp.buffersize.wifi]: [524288,2097152,4194304,262144,524288,1048576]
+[nfc.initialized]: [True]
+[persist_radio_VT_ENABLE]: [1]
+[ro.boot.boottime]: [1BLL:85,1BLE:898,2BLL:0,2BLE:862,SW:6739,KL:340]
+[ro.bootimage.build.date.utc]: [1509394807]
+[ro.bootimage.build.fingerprint]: [google/marlin/marlin:P/MASTER/jinyithu10301320:eng/dev-keys]
+[ro.wifi.channels]: []
\ No newline at end of file
diff --git a/cmds/incident_helper/tests/CpuFreqParser_test.cpp b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
new file mode 100644
index 0000000..1c2f9e5
--- /dev/null
+++ b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CpuFreqParser.h"
+
+#include "frameworks/base/core/proto/android/os/cpufreq.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class CpuFreqParserTest : public Test {
+public:
+ virtual void SetUp() override {
+ ASSERT_TRUE(tf.fd != -1);
+ }
+
+ string getSerializedString(::google::protobuf::Message& message) {
+ string expectedStr;
+ message.SerializeToFileDescriptor(tf.fd);
+ ReadFileToString(tf.path, &expectedStr);
+ return expectedStr;
+ }
+
+protected:
+ TemporaryFile tf;
+
+ const string kTestPath = GetExecutableDirectory();
+ const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(CpuFreqParserTest, Success) {
+ const string testFile = kTestDataPath + "cpufreq.txt";
+ CpuFreqParser parser;
+ CpuFreq expected;
+
+ long jiffyHz = sysconf(_SC_CLK_TCK);
+ expected.set_jiffy_hz(jiffyHz);
+
+ CpuFreqStats::TimeInState* state;
+
+ CpuFreqStats* cpu0 = expected.add_cpu_freqs();
+ cpu0->set_cpu_name("cpu0");
+ state = cpu0->add_times();
+ state->set_state_khz(307200);
+ state->set_time_jiffy(23860761);
+ state = cpu0->add_times();
+ state->set_state_khz(384000);
+ state->set_time_jiffy(83124);
+ state = cpu0->add_times();
+ state->set_state_khz(768000);
+ state->set_time_jiffy(22652);
+
+ CpuFreqStats* cpu1 = expected.add_cpu_freqs();
+ cpu1->set_cpu_name("cpu1");
+ state = cpu1->add_times();
+ state->set_state_khz(307200);
+ state->set_time_jiffy(23860761);
+ state = cpu1->add_times();
+ state->set_state_khz(384000);
+ state->set_time_jiffy(83124);
+ state = cpu1->add_times();
+ state->set_state_khz(768000);
+ state->set_time_jiffy(22652);
+
+ CpuFreqStats* cpu2 = expected.add_cpu_freqs();
+ cpu2->set_cpu_name("cpu2");
+ state = cpu2->add_times();
+ state->set_state_khz(307200);
+ state->set_time_jiffy(23890935);
+ state = cpu2->add_times();
+ state->set_state_khz(384000);
+ state->set_time_jiffy(29383);
+ state = cpu2->add_times();
+ state->set_state_khz(748800);
+ state->set_time_jiffy(10547);
+ state = cpu2->add_times();
+ state->set_state_khz(825600);
+ state->set_time_jiffy(13173);
+
+ CpuFreqStats* cpu3 = expected.add_cpu_freqs();
+ cpu3->set_cpu_name("cpu3");
+ state = cpu3->add_times();
+ state->set_state_khz(307200);
+ state->set_time_jiffy(23890935);
+ state = cpu3->add_times();
+ state->set_state_khz(384000);
+ state->set_time_jiffy(29383);
+ state = cpu3->add_times();
+ state->set_state_khz(748800);
+ state->set_time_jiffy(10547);
+ state = cpu3->add_times();
+ state->set_state_khz(825600);
+ state->set_time_jiffy(13173);
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
diff --git a/cmds/incident_helper/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
new file mode 100644
index 0000000..bbc14bc
--- /dev/null
+++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CpuInfoParser.h"
+
+#include "frameworks/base/core/proto/android/os/cpuinfo.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class CpuInfoParserTest : public Test {
+public:
+ virtual void SetUp() override {
+ ASSERT_TRUE(tf.fd != -1);
+ }
+
+ string getSerializedString(::google::protobuf::Message& message) {
+ string expectedStr;
+ message.SerializeToFileDescriptor(tf.fd);
+ ReadFileToString(tf.path, &expectedStr);
+ return expectedStr;
+ }
+
+protected:
+ TemporaryFile tf;
+
+ const string kTestPath = GetExecutableDirectory();
+ const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(CpuInfoParserTest, Success) {
+ const string testFile = kTestDataPath + "cpuinfo.txt";
+ CpuInfoParser parser;
+ CpuInfo expected;
+
+ CpuInfo::TaskStats* taskStats = expected.mutable_task_stats();
+ taskStats->set_total(2038);
+ taskStats->set_running(1);
+ taskStats->set_sleeping(2033);
+ taskStats->set_stopped(0);
+ taskStats->set_zombie(0);
+
+ CpuInfo::MemStats* mem = expected.mutable_mem();
+ mem->set_total(3842668);
+ mem->set_used(3761936);
+ mem->set_free(80732);
+ mem->set_buffers(220188);
+
+ CpuInfo::MemStats* swap = expected.mutable_swap();
+ swap->set_total(524284);
+ swap->set_used(25892);
+ swap->set_free(498392);
+ swap->set_cached(1316952);
+
+ CpuInfo::CpuUsage* usage = expected.mutable_cpu_usage();
+ usage->set_cpu(400);
+ usage->set_user(17);
+ usage->set_nice(0);
+ usage->set_sys(43);
+ usage->set_idle(338);
+ usage->set_iow(0);
+ usage->set_irq(0);
+ usage->set_sirq(1);
+ usage->set_host(0);
+
+ // This is a special line which is able to be parsed by the CpuInfoParser
+ CpuInfo::Task* task1 = expected.add_tasks();
+ task1->set_pid(29438);
+ task1->set_tid(29438);
+ task1->set_user("rootabcdefghij");
+ task1->set_pr("20");
+ task1->set_ni(0);
+ task1->set_cpu(57.9);
+ task1->set_s(CpuInfo::Task::STATUS_R);
+ task1->set_virt("14M");
+ task1->set_res("3.8M");
+ task1->set_pcy(CpuInfo::Task::POLICY_UNKNOWN);
+ task1->set_cmd("top test");
+ task1->set_name("top");
+
+ CpuInfo::Task* task2 = expected.add_tasks();
+ task2->set_pid(916);
+ task2->set_tid(916);
+ task2->set_user("system");
+ task2->set_pr("18");
+ task2->set_ni(-2);
+ task2->set_cpu(1.4);
+ task2->set_s(CpuInfo::Task::STATUS_S);
+ task2->set_virt("4.6G");
+ task2->set_res("404M");
+ task2->set_pcy(CpuInfo::Task::POLICY_fg);
+ task2->set_cmd("system_server");
+ task2->set_name("system_server");
+
+ CpuInfo::Task* task3 = expected.add_tasks();
+ task3->set_pid(28);
+ task3->set_tid(28);
+ task3->set_user("root");
+ task3->set_pr("-2");
+ task3->set_ni(0);
+ task3->set_cpu(1.4);
+ task3->set_s(CpuInfo::Task::STATUS_S);
+ task3->set_virt("0");
+ task3->set_res("0");
+ task3->set_pcy(CpuInfo::Task::POLICY_bg);
+ task3->set_cmd("rcuc/3");
+ task3->set_name("[rcuc/3]");
+
+ CpuInfo::Task* task4 = expected.add_tasks();
+ task4->set_pid(27);
+ task4->set_tid(27);
+ task4->set_user("root");
+ task4->set_pr("RT");
+ task4->set_ni(0);
+ task4->set_cpu(1.4);
+ task4->set_s(CpuInfo::Task::STATUS_S);
+ task4->set_virt("0");
+ task4->set_res("0");
+ task4->set_pcy(CpuInfo::Task::POLICY_ta);
+ task4->set_cmd("migration/3");
+ task4->set_name("[migration/3]");
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
diff --git a/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp b/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp
new file mode 100644
index 0000000..23e292a
--- /dev/null
+++ b/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SystemPropertiesParser.h"
+
+#include "frameworks/base/core/proto/android/os/system_properties.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class SystemPropertiesParserTest : public Test {
+public:
+ virtual void SetUp() override {
+ ASSERT_TRUE(tf.fd != -1);
+ }
+
+ string getSerializedString(::google::protobuf::Message& message) {
+ string expectedStr;
+ message.SerializeToFileDescriptor(tf.fd);
+ ReadFileToString(tf.path, &expectedStr);
+ return expectedStr;
+ }
+
+protected:
+ TemporaryFile tf;
+
+ const string kTestPath = GetExecutableDirectory();
+ const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(SystemPropertiesParserTest, HasSwapInfo) {
+ const string testFile = kTestDataPath + "system_properties.txt";
+ SystemPropertiesParser parser;
+ SystemPropertiesProto expected;
+
+ expected.set_aaudio_hw_burst_min_usec(2000);
+ expected.set_aaudio_mmap_exclusive_policy(2);
+ expected.set_dalvik_vm_appimageformat("lz4");
+ expected.set_gsm_operator_isroaming(false);
+ expected.set_init_svc_vendor_imsqmidaemon(SystemPropertiesProto_Status_STATUS_RUNNING);
+ expected.set_init_svc_vendor_init_radio_sh(SystemPropertiesProto_Status_STATUS_STOPPED);
+ expected.set_net_dns1("2001:4860:4860::8844");
+ expected.add_net_tcp_buffersize_wifi(524288);
+ expected.add_net_tcp_buffersize_wifi(2097152);
+ expected.add_net_tcp_buffersize_wifi(4194304);
+ expected.add_net_tcp_buffersize_wifi(262144);
+ expected.add_net_tcp_buffersize_wifi(524288);
+ expected.add_net_tcp_buffersize_wifi(1048576);
+ expected.set_nfc_initialized(true);
+ expected.set_persist_radio_vt_enable(1);
+ expected.add_ro_boot_boottime("1BLL:85");
+ expected.add_ro_boot_boottime("1BLE:898");
+ expected.add_ro_boot_boottime("2BLL:0");
+ expected.add_ro_boot_boottime("2BLE:862");
+ expected.add_ro_boot_boottime("SW:6739");
+ expected.add_ro_boot_boottime("KL:340");
+ expected.set_ro_bootimage_build_date_utc(1509394807LL);
+ expected.set_ro_bootimage_build_fingerprint("google/marlin/marlin:P/MASTER/jinyithu10301320:eng/dev-keys");
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
diff --git a/cmds/incident_helper/tests/ih_util_test.cpp b/cmds/incident_helper/tests/ih_util_test.cpp
index da88ee3..5740b33 100644
--- a/cmds/incident_helper/tests/ih_util_test.cpp
+++ b/cmds/incident_helper/tests/ih_util_test.cpp
@@ -62,10 +62,63 @@
EXPECT_EQ(expected, result);
}
+TEST(IhUtilTest, ParseRecordByColumns) {
+ record_t result, expected;
+ std::vector<int> indices = { 3, 10 };
+
+ result = parseRecordByColumns("12345", indices);
+ expected = {};
+ EXPECT_EQ(expected, result);
+
+ result = parseRecordByColumns("abc \t2345 6789 ", indices);
+ expected = { "abc", "2345", "6789" };
+ EXPECT_EQ(expected, result);
+
+ result = parseRecordByColumns("abc \t23456789 bob", indices);
+ expected = { "abc", "23456789", "bob" };
+ EXPECT_EQ(expected, result);
+}
+
+TEST(IhUtilTest, stripPrefix) {
+ string data1 = "Swap: abc ";
+ EXPECT_TRUE(stripPrefix(&data1, "Swap:"));
+ EXPECT_THAT(data1, StrEq("abc"));
+
+ string data2 = "Swap: abc ";
+ EXPECT_FALSE(stripPrefix(&data2, "Total:"));
+ EXPECT_THAT(data2, StrEq("Swap: abc "));
+
+ string data3 = "Swap: abc ";
+ EXPECT_TRUE(stripPrefix(&data3, "Swa"));
+ EXPECT_THAT(data3, StrEq("p: abc"));
+
+ string data4 = "Swap: abc ";
+ EXPECT_FALSE(stripPrefix(&data4, "Swa", true));
+ EXPECT_THAT(data4, StrEq("Swap: abc "));
+}
+
+TEST(IhUtilTest, stripSuffix) {
+ string data1 = " 243%abc";
+ EXPECT_TRUE(stripSuffix(&data1, "abc"));
+ EXPECT_THAT(data1, StrEq("243%"));
+
+ string data2 = " 243%abc";
+ EXPECT_FALSE(stripSuffix(&data2, "Not right"));
+ EXPECT_THAT(data2, StrEq(" 243%abc"));
+
+ string data3 = " 243%abc";
+ EXPECT_TRUE(stripSuffix(&data3, "bc"));
+ EXPECT_THAT(data3, StrEq("243%a"));
+
+ string data4 = " 243%abc";
+ EXPECT_FALSE(stripSuffix(&data4, "bc", true));
+ EXPECT_THAT(data4, StrEq(" 243%abc"));
+}
+
TEST(IhUtilTest, Reader) {
TemporaryFile tf;
ASSERT_NE(tf.fd, -1);
- ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooo\n", tf.path, false));
+ ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooo\n", tf.path));
Reader r(tf.fd);
string line;
@@ -79,40 +132,22 @@
ASSERT_TRUE(r.ok(&line));
}
-TEST(IhUtilTest, ReaderSmallBufSize) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1);
- ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooiecccojreo", tf.path, false));
-
- Reader r(tf.fd, 5);
- string line;
- ASSERT_TRUE(r.readLine(&line));
- EXPECT_THAT(line, StrEq("test string"));
- ASSERT_TRUE(r.readLine(&line));
- EXPECT_THAT(line, StrEq("second"));
- ASSERT_TRUE(r.readLine(&line));
- EXPECT_THAT(line, StrEq("ooiecccojreo"));
- ASSERT_FALSE(r.readLine(&line));
- ASSERT_TRUE(r.ok(&line));
-}
-
TEST(IhUtilTest, ReaderEmpty) {
TemporaryFile tf;
ASSERT_NE(tf.fd, -1);
- ASSERT_TRUE(WriteStringToFile("", tf.path, false));
+ ASSERT_TRUE(WriteStringToFile("", tf.path));
Reader r(tf.fd);
string line;
- ASSERT_TRUE(r.readLine(&line));
- EXPECT_THAT(line, StrEq(""));
ASSERT_FALSE(r.readLine(&line));
+ EXPECT_THAT(line, StrEq(""));
ASSERT_TRUE(r.ok(&line));
}
TEST(IhUtilTest, ReaderMultipleEmptyLines) {
TemporaryFile tf;
ASSERT_NE(tf.fd, -1);
- ASSERT_TRUE(WriteStringToFile("\n\n", tf.path, false));
+ ASSERT_TRUE(WriteStringToFile("\n\n", tf.path));
Reader r(tf.fd);
string line;
@@ -130,15 +165,7 @@
string line;
EXPECT_FALSE(r.readLine(&line));
EXPECT_FALSE(r.ok(&line));
- EXPECT_THAT(line, StrEq("Negative fd"));
-}
-
-TEST(IhUtilTest, ReaderFailedZeroBufferSize) {
- Reader r(23, 0);
- string line;
- EXPECT_FALSE(r.readLine(&line));
- EXPECT_FALSE(r.ok(&line));
- EXPECT_THAT(line, StrEq("Zero buffer capacity"));
+ EXPECT_THAT(line, StrEq("Invalid fd -123"));
}
TEST(IhUtilTest, ReaderFailedBadFd) {
@@ -146,5 +173,5 @@
string line;
EXPECT_FALSE(r.readLine(&line));
EXPECT_FALSE(r.ok(&line));
- EXPECT_THAT(line, StrEq("Fail to read from fd"));
+ EXPECT_THAT(line, StrEq("Invalid fd 1231432"));
}
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index b7633a4..30dd339 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -26,6 +26,7 @@
#include <unistd.h>
#include <wait.h>
+const bool DEBUG = false;
const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB
const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
@@ -71,9 +72,11 @@
mTimedOut = true;
break;
} else if (count < 0) {
+ if (DEBUG) ALOGD("poll failed: %s", strerror(errno));
return -errno;
} else {
if ((pfds.revents & POLLERR) != 0) {
+ if (DEBUG) ALOGD("return event has error %s", strerror(errno));
return errno != 0 ? -errno : UNKNOWN_ERROR;
} else {
ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
@@ -81,6 +84,7 @@
if (errno == EAGAIN || errno == EWOULDBLOCK) {
continue;
} else {
+ if (DEBUG) ALOGD("Fail to read %d: %s", fd, strerror(errno));
return -errno;
}
} else if (amt == 0) {
@@ -95,7 +99,7 @@
}
status_t
-FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs)
+FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs)
{
struct pollfd pfds[] = {
{ .fd = fd, .events = POLLIN },
@@ -135,12 +139,18 @@
mTimedOut = true;
break;
} else if (count < 0) {
+ if (DEBUG) ALOGD("Fail to poll: %s", strerror(errno));
return -errno;
}
// make sure no errors occur on any fds
for (int i = 0; i < 3; ++i) {
if ((pfds[i].revents & POLLERR) != 0) {
+ if (i == 0 && isSysfs) {
+ if (DEBUG) ALOGD("fd %d is sysfs, ignore its POLLERR return value", fd);
+ continue;
+ }
+ if (DEBUG) ALOGD("fd[%d]=%d returns error events: %s", i, fd, strerror(errno));
return errno != 0 ? -errno : UNKNOWN_ERROR;
}
}
@@ -155,6 +165,7 @@
}
if (amt < 0) {
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+ if (DEBUG) ALOGD("Fail to read fd %d: %s", fd, strerror(errno));
return -errno;
} // otherwise just continue
} else if (amt == 0) { // reach EOF so don't have to poll pfds[0].
@@ -176,6 +187,7 @@
}
if (amt < 0) {
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+ if (DEBUG) ALOGD("Fail to write toFd %d: %s", toFd, strerror(errno));
return -errno;
} // otherwise just continue
} else {
@@ -202,6 +214,7 @@
ssize_t amt = ::read(fromFd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
if (amt < 0) {
if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
+ if (DEBUG) ALOGD("Fail to read fromFd %d: %s", fromFd, strerror(errno));
return -errno;
} // otherwise just continue
} else if (amt == 0) {
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index 8857ae7..48dc855 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -47,8 +47,10 @@
* and stores the processed data from 'fromFd' in memory for later usage.
* This function behaves in a streaming fashion in order to save memory usage.
* Returns NO_ERROR if there were no errors or if we timed out.
+ *
+ * Poll will return POLLERR if fd is from sysfs, handle this edge case.
*/
- status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs);
+ status_t readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeoutMs, const bool isSysfs=false);
/**
* Whether we timed out.
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 892bcca..c08b9ea 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -232,6 +232,7 @@
mFilename(filename)
{
name = filename;
+ mIsSysfs = strncmp(filename, "/sys/", 5) == 0;
}
FileSection::~FileSection() {}
@@ -264,7 +265,7 @@
// parent process
status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
- this->timeoutMs);
+ this->timeoutMs, mIsSysfs);
if (readStatus != NO_ERROR || buffer.timedOut()) {
ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 0a1e03e..64558a6 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -69,6 +69,7 @@
private:
const char* mFilename;
+ bool mIsSysfs; // sysfs files are pollable but return POLLERR by default, handle it separately
};
/**
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index 2afa778..3fd2ed8 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -84,7 +84,7 @@
TEST_F(FdBufferTest, ReadAndWrite) {
std::string testdata = "FdBuffer test string";
- ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
AssertBufferReadSuccessful(testdata.size());
AssertBufferContent(testdata.c_str());
@@ -97,7 +97,7 @@
TEST_F(FdBufferTest, ReadAndIterate) {
std::string testdata = "FdBuffer test string";
- ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
int i=0;
@@ -137,7 +137,7 @@
TEST_F(FdBufferTest, ReadInStreamAndWrite) {
std::string testdata = "simply test read in stream";
std::string expected = HEAD + testdata;
- ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
int pid = fork();
ASSERT_TRUE(pid != -1);
@@ -166,7 +166,7 @@
TEST_F(FdBufferTest, ReadInStreamAndWriteAllAtOnce) {
std::string testdata = "child process flushes only after all data are read.";
std::string expected = HEAD + testdata;
- ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
int pid = fork();
ASSERT_TRUE(pid != -1);
@@ -196,7 +196,7 @@
}
TEST_F(FdBufferTest, ReadInStreamEmpty) {
- ASSERT_TRUE(WriteStringToFile("", tf.path, false));
+ ASSERT_TRUE(WriteStringToFile("", tf.path));
int pid = fork();
ASSERT_TRUE(pid != -1);
@@ -260,7 +260,7 @@
TEST_F(FdBufferTest, ReadInStreamTimeOut) {
std::string testdata = "timeout test";
- ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
int pid = fork();
ASSERT_TRUE(pid != -1);
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index 84a2a82..ca94623 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -60,7 +60,7 @@
}
void writeToFdBuffer(string str) {
- ASSERT_TRUE(WriteStringToFile(str, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(str, tf.path));
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000));
ASSERT_EQ(str.size(), buffer.size());
}
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 25b05b2..649e908 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -99,7 +99,7 @@
ReportRequestSet requests;
ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path, false));
+ ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path));
requests.setMainFd(STDOUT_FILENO);
@@ -173,7 +173,7 @@
ReportRequestSet requests;
ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
requests.setMainFd(STDOUT_FILENO);
@@ -186,7 +186,7 @@
TemporaryFile input;
FileSection fs(NOOP_PARSER, input.path);
ReportRequestSet requests;
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
IncidentReportArgs args;
args.setAll(true);
@@ -205,7 +205,7 @@
TemporaryFile input;
FileSection fs(NOOP_PARSER, input.path);
ReportRequestSet requests;
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
IncidentReportArgs args;
args.setAll(true);
@@ -223,7 +223,7 @@
ASSERT_TRUE(output1.fd != -1);
ASSERT_TRUE(output2.fd != -1);
ASSERT_TRUE(output3.fd != -1);
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
IncidentReportArgs args1, args2, args3;
args1.setAll(true);
@@ -265,7 +265,7 @@
ASSERT_TRUE(output2.fd != -1);
ASSERT_TRUE(output3.fd != -1);
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
IncidentReportArgs args1, args2, args3, args4;
args1.setAll(true);
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 699de94..77e8efa 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -101,6 +101,8 @@
runSetEmulateFbe();
} else if ("get-fbe-mode".equals(op)) {
runGetFbeMode();
+ } else if ("idle-maint".equals(op)) {
+ runIdleMaint();
} else if ("fstrim".equals(op)) {
runFstrim();
} else if ("set-virtual-disk".equals(op)) {
@@ -278,6 +280,15 @@
StorageManager.DEBUG_VIRTUAL_DISK);
}
+ public void runIdleMaint() throws RemoteException {
+ final boolean im_run = "run".equals(nextArg());
+ if (im_run) {
+ mSm.runIdleMaintenance();
+ } else {
+ mSm.abortIdleMaintenance();
+ }
+ }
+
private String nextArg() {
if (mNextArg >= mArgs.length) {
return null;
@@ -300,6 +311,7 @@
System.err.println(" sm unmount VOLUME");
System.err.println(" sm format VOLUME");
System.err.println(" sm benchmark VOLUME");
+ System.err.println(" sm idle-maint [run|abort]");
System.err.println(" sm fstrim");
System.err.println("");
System.err.println(" sm forget [UUID|all]");
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 4ebca84..5fcb8a1 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -20,7 +20,7 @@
cc_library_host_shared {
name: "libstats_proto_host",
srcs: [
- "src/stats_events.proto",
+ "src/atoms.proto",
],
shared_libs: [
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index d99136f..1b2f9da 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -15,13 +15,13 @@
LOCAL_PATH:= $(call my-dir)
statsd_common_src := \
- ../../core/java/android/os/IStatsCallbacks.aidl \
../../core/java/android/os/IStatsCompanionService.aidl \
../../core/java/android/os/IStatsManager.aidl \
src/stats_log.proto \
src/statsd_config.proto \
- src/stats_events_copy.proto \
+ src/atoms_copy.proto \
src/anomaly/AnomalyMonitor.cpp \
+ src/anomaly/AnomalyTracker.cpp \
src/condition/CombinationConditionTracker.cpp \
src/condition/condition_util.cpp \
src/condition/SimpleConditionTracker.cpp \
@@ -31,14 +31,15 @@
src/config/ConfigManager.cpp \
src/external/StatsCompanionServicePuller.cpp \
src/external/ResourcePowerManagerPuller.cpp \
- src/external/StatsPullerManager.cpp \
+ src/external/CpuTimePerUidPuller.cpp \
+ src/external/CpuTimePerUidFreqPuller.cpp \
+ src/external/StatsPullerManagerImpl.cpp \
src/logd/LogEvent.cpp \
src/logd/LogListener.cpp \
src/logd/LogReader.cpp \
src/matchers/CombinationLogMatchingTracker.cpp \
src/matchers/matcher_util.cpp \
src/matchers/SimpleLogMatchingTracker.cpp \
- src/anomaly/DiscreteAnomalyTracker.cpp \
src/metrics/MetricProducer.cpp \
src/metrics/EventMetricProducer.cpp \
src/metrics/CountMetricProducer.cpp \
@@ -151,18 +152,19 @@
$(statsd_common_src) \
tests/AnomalyMonitor_test.cpp \
tests/anomaly/AnomalyTracker_test.cpp \
- tests/ConditionTracker_test.cpp \
tests/ConfigManager_test.cpp \
tests/indexed_priority_queue_test.cpp \
tests/LogEntryMatcher_test.cpp \
tests/LogReader_test.cpp \
tests/MetricsManager_test.cpp \
tests/UidMap_test.cpp \
+ tests/condition/CombinationConditionTracker_test.cpp \
+ tests/condition/SimpleConditionTracker_test.cpp \
tests/metrics/OringDurationTracker_test.cpp \
tests/metrics/MaxDurationTracker_test.cpp \
tests/metrics/CountMetricProducer_test.cpp \
- tests/metrics/EventMetricProducer_test.cpp
-
+ tests/metrics/EventMetricProducer_test.cpp \
+ tests/metrics/ValueMetricProducer_test.cpp
LOCAL_STATIC_LIBRARIES := \
libgmock
@@ -176,4 +178,3 @@
statsd_common_c_includes:=
include $(BUILD_NATIVE_TEST)
-
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 7ff42b6..fcb5107 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -25,6 +25,7 @@
#include <utils/Errors.h>
using namespace android;
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
@@ -46,16 +47,27 @@
const int FIELD_ID_UID_MAP = 3;
// for ConfigKey
const int FIELD_ID_UID = 1;
-const int FIELD_ID_NAME = 1;
+const int FIELD_ID_NAME = 2;
StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
- const std::function<void(const vector<uint8_t>&)>& pushLog)
- : mUidMap(uidMap), mPushLog(pushLog) {
+ const sp<AnomalyMonitor>& anomalyMonitor,
+ const std::function<void(const ConfigKey&)>& sendBroadcast)
+ : mUidMap(uidMap), mAnomalyMonitor(anomalyMonitor), mSendBroadcast(sendBroadcast) {
}
StatsLogProcessor::~StatsLogProcessor() {
}
+void StatsLogProcessor::onAnomalyAlarmFired(
+ const uint64_t timestampNs,
+ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet) {
+ for (const auto& anomaly : anomalySet) {
+ for (const auto& itr : mMetricsManagers) {
+ itr.second->onAnomalyAlarmFired(timestampNs, anomaly);
+ }
+ }
+}
+
// TODO: what if statsd service restarts? How do we know what logs are already processed before?
void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
// pass the event to metrics managers.
@@ -65,7 +77,7 @@
}
// Hard-coded logic to update the isolated uid's in the uid-map.
- // The field numbers need to be currently updated by hand with stats_events.proto
+ // The field numbers need to be currently updated by hand with atoms.proto
if (msg.GetTagId() == android::util::ISOLATED_UID_CHANGED) {
status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR;
bool is_create = msg.GetBool(3, &err);
@@ -92,6 +104,7 @@
unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
if (newMetricsManager->isConfigValid()) {
mUidMap->OnConfigUpdated(key);
+ newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
mMetricsManagers[key] = std::move(newMetricsManager);
// Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
ALOGD("StatsdConfig valid");
@@ -101,12 +114,27 @@
}
}
-vector<uint8_t> StatsLogProcessor::onDumpReport(const ConfigKey& key) {
+size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) {
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end()) {
ALOGW("Config source %s does not exist", key.ToString().c_str());
- return vector<uint8_t>();
+ return 0;
}
+ return it->second->byteSize();
+}
+
+void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) {
+ auto it = mMetricsManagers.find(key);
+ if (it == mMetricsManagers.end()) {
+ ALOGW("Config source %s does not exist", key.ToString().c_str());
+ return;
+ }
+
+ // This allows another broadcast to be sent within the rate-limit period if we get close to
+ // filling the buffer again soon.
+ mBroadcastTimesMutex.lock();
+ mLastBroadcastTimes.erase(key);
+ mBroadcastTimesMutex.unlock();
ProtoOutputStream proto;
@@ -119,8 +147,8 @@
// Fill in StatsLogReport's.
for (auto& m : it->second->onDumpReport()) {
// Add each vector of StatsLogReport into a repeated field.
- proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_METRICS, reinterpret_cast<char*>(m.get()->data()),
- m.get()->size());
+ proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS,
+ reinterpret_cast<char*>(m.get()->data()), m.get()->size());
}
// Fill in UidMap.
@@ -130,17 +158,18 @@
uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize);
proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize);
- vector<uint8_t> buffer(proto.size());
- size_t pos = 0;
- auto iter = proto.data();
- while (iter.readBuffer() != NULL) {
- size_t toRead = iter.currentToRead();
- std::memcpy(&buffer[pos], iter.readBuffer(), toRead);
- pos += toRead;
- iter.rp()->move(toRead);
+ if (outData != nullptr) {
+ outData->clear();
+ outData->resize(proto.size());
+ size_t pos = 0;
+ auto iter = proto.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead);
+ pos += toRead;
+ iter.rp()->move(toRead);
+ }
}
-
- return buffer;
}
void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
@@ -150,42 +179,34 @@
mMetricsManagers.erase(it);
mUidMap->OnConfigRemoved(key);
}
- auto flushTime = mLastFlushTimes.find(key);
- if (flushTime != mLastFlushTimes.end()) {
- mLastFlushTimes.erase(flushTime);
- }
+
+ std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
+ mLastBroadcastTimes.erase(key);
}
void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs,
const ConfigKey& key,
const unique_ptr<MetricsManager>& metricsManager) {
- auto lastFlushNs = mLastFlushTimes.find(key);
- if (lastFlushNs != mLastFlushTimes.end()) {
- if (timestampNs - lastFlushNs->second < kMinFlushPeriod) {
- return;
- }
- }
+ std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
size_t totalBytes = metricsManager->byteSize();
- if (totalBytes > kMaxSerializedBytes) {
- flush();
- mLastFlushTimes[key] = std::move(timestampNs);
+ if (totalBytes > .9 * kMaxSerializedBytes) { // Send broadcast so that receivers can pull data.
+ auto lastFlushNs = mLastBroadcastTimes.find(key);
+ if (lastFlushNs != mLastBroadcastTimes.end()) {
+ if (timestampNs - lastFlushNs->second < kMinBroadcastPeriod) {
+ return;
+ }
+ }
+ mLastBroadcastTimes[key] = timestampNs;
+ ALOGD("StatsD requesting broadcast for %s", key.ToString().c_str());
+ mSendBroadcast(key);
+ } else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data.
+ // We ignore the return value so we force each metric producer to clear its contents.
+ metricsManager->onDumpReport();
+ ALOGD("StatsD had to toss out metrics for %s", key.ToString().c_str());
}
}
-void StatsLogProcessor::flush() {
- // TODO: Take ConfigKey as an argument and flush metrics related to the
- // ConfigKey. Also, create a wrapper that holds a repeated field of
- // StatsLogReport's.
- /*
- StatsLogReport logReport;
- const int numBytes = logReport.ByteSize();
- vector<uint8_t> logReportBuffer(numBytes);
- logReport.SerializeToArray(&logReportBuffer[0], numBytes);
- mPushLog(logReportBuffer);
- */
-}
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index f38d715..510dc51 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -32,8 +32,8 @@
class StatsLogProcessor : public ConfigListener {
public:
- StatsLogProcessor(const sp<UidMap>& uidMap,
- const std::function<void(const vector<uint8_t>&)>& pushLog);
+ StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AnomalyMonitor>& anomalyMonitor,
+ const std::function<void(const ConfigKey&)>& sendBroadcast);
virtual ~StatsLogProcessor();
virtual void OnLogEvent(const LogEvent& event);
@@ -41,18 +41,24 @@
void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config);
void OnConfigRemoved(const ConfigKey& key);
- vector<uint8_t> onDumpReport(const ConfigKey& key);
-
- /* Request a flush through a binder call. */
- void flush();
+ size_t GetMetricsSize(const ConfigKey& key);
+
+ void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData);
+ void onAnomalyAlarmFired(
+ const uint64_t timestampNs,
+ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet);
private:
+ mutable mutex mBroadcastTimesMutex;
+
std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers;
- std::unordered_map<ConfigKey, long> mLastFlushTimes;
+ std::unordered_map<ConfigKey, long> mLastBroadcastTimes;
sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid.
+ sp<AnomalyMonitor> mAnomalyMonitor;
+
/* Max *serialized* size of the logs kept in memory before flushing through binder call.
Proto lite does not implement the SpaceUsed() function which gives the in memory byte size.
So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size
@@ -60,17 +66,18 @@
*/
static const size_t kMaxSerializedBytes = 16 * 1024;
- /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush
- the logs to callback clients if true. */
+ /* Check if we should send a broadcast if approaching memory limits and if we're over, we
+ * actually delete the data. */
void flushIfNecessary(uint64_t timestampNs,
const ConfigKey& key,
const unique_ptr<MetricsManager>& metricsManager);
- std::function<void(const vector<uint8_t>&)> mPushLog;
+ // Function used to send a broadcast so that receiver for the config key can call getData
+ // to retrieve the stored data.
+ std::function<void(const ConfigKey& key)> mSendBroadcast;
- /* Minimum period between two flushes in nanoseconds. Currently set to 10
- * minutes. */
- static const unsigned long long kMinFlushPeriod = 600 * NS_PER_SEC;
+ /* Minimum period between two broadcasts in nanoseconds. Currently set to 60 seconds. */
+ static const unsigned long long kMinBroadcastPeriod = 60 * NS_PER_SEC;
};
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 11c5de1..1a056df 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,17 +17,20 @@
#define DEBUG true
#include "Log.h"
+#include "android-base/stringprintf.h"
#include "StatsService.h"
+#include "config/ConfigKey.h"
+#include "config/ConfigManager.h"
#include "storage/DropboxReader.h"
#include <android-base/file.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
+#include <dirent.h>
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <private/android_filesystem_config.h>
#include <utils/Looper.h>
#include <utils/String16.h>
-
#include <stdio.h>
#include <stdlib.h>
#include <sys/system_properties.h>
@@ -39,6 +42,9 @@
namespace os {
namespace statsd {
+constexpr const char* kPermissionDump = "android.permission.DUMP";
+#define STATS_SERVICE_DIR "/data/system/stats-service"
+
// ======================================================================
/**
* Watches for the death of the stats companion (system process).
@@ -67,8 +73,18 @@
{
mUidMap = new UidMap();
mConfigManager = new ConfigManager();
- mProcessor = new StatsLogProcessor(mUidMap, [this](const vector<uint8_t>& log) {
- pushLog(log);
+ mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, [this](const ConfigKey& key) {
+ auto sc = getStatsCompanionService();
+ auto receiver = mConfigManager->GetConfigReceiver(key);
+ if (sc == nullptr) {
+ ALOGD("Could not find StatsCompanionService");
+ } else if (receiver.first.size() == 0) {
+ ALOGD("Statscompanion could not find a broadcast receiver for %s",
+ key.ToString().c_str());
+ } else {
+ sc->sendBroadcast(String16(receiver.first.c_str()),
+ String16(receiver.second.c_str()));
+ }
});
mConfigManager->AddListener(mProcessor);
@@ -198,6 +214,18 @@
if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
return cmd_print_pulled_metrics(out, args);
}
+
+ if (!args[0].compare(String8("send-broadcast"))) {
+ return cmd_trigger_broadcast(out, args);
+ }
+
+ if (!args[0].compare(String8("print-stats"))) {
+ return cmd_print_stats(out);
+ }
+
+ if (!args[0].compare(String8("clear-config"))) {
+ return cmd_remove_config_files(out);
+ }
}
print_cmd_help(out);
@@ -215,7 +243,12 @@
fprintf(out, " Prints the UID, app name, version mapping.\n");
fprintf(out, "\n");
fprintf(out, "\n");
- fprintf(out, "usage: adb shell cmds stats pull-source [int] \n");
+ fprintf(out, "usage: adb shell cmd stats clear-config \n");
+ fprintf(out, "\n");
+ fprintf(out, " Removes all configs from disk.\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd stats pull-source [int] \n");
fprintf(out, "\n");
fprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
fprintf(out, "\n");
@@ -238,6 +271,65 @@
fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
fprintf(out, " calling uid is used.\n");
fprintf(out, " NAME The name of the configuration\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n");
+ fprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\n");
+ fprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
+ fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
+ fprintf(out, " calling uid is used.\n");
+ fprintf(out, " NAME The name of the configuration\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd stats print-stats\n");
+ fprintf(out, " Prints some basic stats.\n");
+}
+
+status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
+ string name;
+ bool good = false;
+ int uid;
+ const int argCount = args.size();
+ if (argCount == 2) {
+ // Automatically pick the UID
+ uid = IPCThreadState::self()->getCallingUid();
+ // TODO: What if this isn't a binder call? Should we fail?
+ name.assign(args[1].c_str(), args[1].size());
+ good = true;
+ } else if (argCount == 3) {
+ // If it's a userdebug or eng build, then the shell user can
+ // impersonate other uids.
+ if (mEngBuild) {
+ const char* s = args[1].c_str();
+ if (*s != '\0') {
+ char* end = NULL;
+ uid = strtol(s, &end, 0);
+ if (*end == '\0') {
+ name.assign(args[2].c_str(), args[2].size());
+ good = true;
+ }
+ }
+ } else {
+ fprintf(out,
+ "The metrics can only be dumped for other UIDs on eng or userdebug "
+ "builds.\n");
+ }
+ }
+ if (!good) {
+ print_cmd_help(out);
+ return UNKNOWN_ERROR;
+ }
+ auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, name));
+ sp<IStatsCompanionService> sc = getStatsCompanionService();
+ if (sc != nullptr) {
+ sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str()));
+ ALOGD("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
+ args[2].c_str());
+ } else {
+ ALOGD("Could not access statsCompanion");
+ }
+
+ return NO_ERROR;
}
status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
@@ -341,7 +433,8 @@
}
}
if (good) {
- mProcessor->onDumpReport(ConfigKey(uid, name));
+ vector<uint8_t> data;
+ mProcessor->onDumpReport(ConfigKey(uid, name), &data);
// TODO: print the returned StatsLogReport to file instead of printing to logcat.
fprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str());
fprintf(out, "See the StatsLogReport in logcat...\n");
@@ -357,6 +450,15 @@
}
}
+status_t StatsService::cmd_print_stats(FILE* out) {
+ vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
+ for (const ConfigKey& key : configs) {
+ fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
+ mProcessor->GetMetricsSize(key));
+ }
+ return NO_ERROR;
+}
+
status_t StatsService::cmd_print_stats_log(FILE* out, const Vector<String8>& args) {
long msec = 0;
@@ -384,6 +486,27 @@
return UNKNOWN_ERROR;
}
+status_t StatsService::cmd_remove_config_files(FILE* out) {
+ fprintf(out, "Trying to remove config files...\n");
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
+ if (dir == NULL) {
+ fprintf(out, "No existing config files found exiting...\n");
+ return NO_ERROR;
+ }
+
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] == '.') continue;
+ string file_name = StringPrintf("%s/%s", STATS_SERVICE_DIR, name);
+ fprintf(out, "Deleting file %s\n", file_name.c_str());
+ if (remove(file_name.c_str())) {
+ fprintf(out, "Error deleting file %s\n", file_name.c_str());
+ }
+ }
+ return NO_ERROR;
+}
+
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
const vector<String16>& app) {
if (DEBUG) ALOGD("StatsService::informAllUidData was called");
@@ -431,7 +554,10 @@
if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired succeeded");
// TODO: check through all counters/timers and see if an anomaly has indeed occurred.
-
+ uint64_t currentTimeNs = time(nullptr) * NS_PER_SEC;
+ std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet =
+ mAnomalyMonitor->onAlarmFired(currentTimeNs);
+ mProcessor->onAnomalyAlarmFired(currentTimeNs, anomalySet);
return Status::ok();
}
@@ -520,38 +646,53 @@
mProcessor->OnLogEvent(event);
}
-Status StatsService::requestPush() {
- mProcessor->flush();
- return Status::ok();
+Status StatsService::getData(const String16& key, vector <uint8_t>* output) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ ALOGD("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(),
+ ipc->getCallingUid());
+ if (checkCallingPermission(String16(kPermissionDump))) {
+ string keyStr = string(String8(key).string());
+ ConfigKey configKey(ipc->getCallingUid(), keyStr);
+ mProcessor->onDumpReport(configKey, output);
+ return Status::ok();
+ } else {
+ return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+ }
}
-Status StatsService::pushLog(const vector<uint8_t>& log) {
- std::lock_guard<std::mutex> lock(mLock);
- for (size_t i = 0; i < mCallbacks.size(); i++) {
- mCallbacks[i]->onReceiveLogs((vector<uint8_t>*)&log);
+Status StatsService::addConfiguration(const String16& key,
+ const vector <uint8_t>& config,
+ const String16& package, const String16& cls,
+ bool* success) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ if (checkCallingPermission(String16(kPermissionDump))) {
+ string keyString = string(String8(key).string());
+ ConfigKey configKey(ipc->getCallingUid(), keyString);
+ StatsdConfig cfg;
+ cfg.ParseFromArray(&config[0], config.size());
+ mConfigManager->UpdateConfig(configKey, cfg);
+ mConfigManager->SetConfigReceiver(configKey, string(String8(package).string()),
+ string(String8(cls).string()));
+ *success = true;
+ return Status::ok();
+ } else {
+ return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
- return Status::ok();
}
-Status StatsService::subscribeStatsLog(const sp<IStatsCallbacks>& callback) {
- std::lock_guard<std::mutex> lock(mLock);
- for (size_t i = 0; i < mCallbacks.size(); i++) {
- if (mCallbacks[i] == callback) {
- return Status::fromStatusT(-errno);
- }
+Status StatsService::removeConfiguration(const String16& key, bool* success) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ if (checkCallingPermission(String16(kPermissionDump))) {
+ string keyStr = string(String8(key).string());
+ mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), keyStr));
+ return Status::ok();
+ } else {
+ *success = false;
+ return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
- mCallbacks.add(callback);
- IInterface::asBinder(callback)->linkToDeath(this);
- return Status::ok();
}
-void StatsService::binderDied(const wp<IBinder>& who) {
- for (size_t i = 0; i < mCallbacks.size(); i++) {
- if (IInterface::asBinder(mCallbacks[i]) == who) {
- mCallbacks.removeAt(i);
- break;
- }
- }
+void StatsService::binderDied(const wp <IBinder>& who) {
}
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 1d7e5a61..4d768f6 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,6 @@
#include "packages/UidMap.h"
#include <android/os/BnStatsManager.h>
-#include <android/os/IStatsCallbacks.h>
#include <android/os/IStatsCompanionService.h>
#include <binder/IResultReceiver.h>
#include <utils/Looper.h>
@@ -71,20 +70,22 @@
virtual void OnLogEvent(const LogEvent& event);
/**
- * Binder call to force trigger pushLog. This would be called by callback
- * clients.
+ * Binder call for clients to request data for this configuration key.
*/
- virtual Status requestPush() override;
+ virtual Status getData(const String16& key, vector<uint8_t>* output) override;
/**
- * Pushes stats log entries from statsd to callback clients.
+ * Binder call to let clients send a configuration and indicate they're interested when they
+ * should requestData for this configuration.
*/
- Status pushLog(const vector<uint8_t>& log);
+ virtual Status addConfiguration(const String16& key, const vector <uint8_t>& config,
+ const String16& package, const String16& cls, bool* success)
+ override;
/**
- * Binder call to listen to statsd to send stats log entries.
+ * Binder call to allow clients to remove the specified configuration.
*/
- virtual Status subscribeStatsLog(const sp<IStatsCallbacks>& callbacks) override;
+ virtual Status removeConfiguration(const String16& key, bool* success) override;
// TODO: public for testing since statsd doesn't run when system starts. Change to private
// later.
@@ -120,11 +121,21 @@
void print_cmd_help(FILE* out);
/**
+ * Trigger a broadcast.
+ */
+ status_t cmd_trigger_broadcast(FILE* out, Vector<String8>& args);
+
+ /**
* Handle the config sub-command.
*/
status_t cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
/**
+ * Prints some basic stats to std out.
+ */
+ status_t cmd_print_stats(FILE* out);
+
+ /**
* Print the event log.
*/
status_t cmd_print_stats_log(FILE* out, const Vector<String8>& args);
@@ -145,6 +156,11 @@
status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args);
/**
+ * Removes all configs stored on disk.
+ */
+ status_t cmd_remove_config_files(FILE* out);
+
+ /**
* Update a configuration.
*/
void set_config(int uid, const string& name, const StatsdConfig& config);
@@ -157,7 +173,7 @@
/**
* Fetches external metrics.
*/
- StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+ StatsPullerManager mStatsPullerManager;
/**
* Tracks the configurations that have been passed to statsd.
@@ -178,16 +194,6 @@
* Whether this is an eng build.
*/
bool mEngBuild;
-
- /**
- * Lock for callback handling.
- */
- std::mutex mLock;
-
- /**
- * Vector maintaining the list of callbacks for clients.
- */
- Vector< sp<IStatsCallbacks> > mCallbacks;
};
} // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
index 7a46410..da52a9d 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
@@ -129,6 +129,11 @@
return ((int64_t)timeSec) * 1000;
}
+unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::onAlarmFired(
+ uint64_t timestampNs) {
+ return popSoonerThan(static_cast<uint32_t>(timestampNs));
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h
index d9207e9..0bd5055 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.h
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANOMALY_MONITOR_H
-#define ANOMALY_MONITOR_H
+#pragma once
#include "anomaly/indexed_priority_queue.h"
@@ -23,6 +22,8 @@
#include <utils/RefBase.h>
#include <queue>
+#include <set>
+#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -114,6 +115,8 @@
return mRegisteredAlarmTimeSec;
}
+ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> onAlarmFired(uint64_t timestampNs);
+
private:
std::mutex mLock;
@@ -154,5 +157,3 @@
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // ANOMALY_MONITOR_H
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
new file mode 100644
index 0000000..0904a04
--- /dev/null
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "AnomalyTracker.h"
+
+#include <time.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+AnomalyTracker::AnomalyTracker(const Alert& alert, const int64_t& bucketSizeNs)
+ : mAlert(alert),
+ mBucketSizeNs(bucketSizeNs),
+ mNumOfPastPackets(mAlert.number_of_buckets() - 1) {
+ VLOG("AnomalyTracker() called");
+ if (mAlert.number_of_buckets() <= 0) {
+ ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets",
+ (long long)mAlert.number_of_buckets());
+ return;
+ }
+ if (mBucketSizeNs <= 0) {
+ ALOGE("Cannot create DiscreteAnomalyTracker with bucket size %lld ",
+ (long long)mBucketSizeNs);
+ return;
+ }
+ if (!mAlert.has_trigger_if_sum_gt()) {
+ ALOGE("Cannot create DiscreteAnomalyTracker without threshold");
+ return;
+ }
+ reset(); // initialization
+}
+
+AnomalyTracker::~AnomalyTracker() {
+ VLOG("~AnomalyTracker() called");
+ stopAllAlarms();
+}
+
+void AnomalyTracker::reset() {
+ VLOG("reset() called.");
+ stopAllAlarms();
+ mPastBuckets.clear();
+ // Excludes the current bucket.
+ mPastBuckets.resize(mNumOfPastPackets);
+ mSumOverPastBuckets.clear();
+ mMostRecentBucketNum = -1;
+ mLastAlarmTimestampNs = -1;
+}
+
+size_t AnomalyTracker::index(int64_t bucketNum) const {
+ return bucketNum % mNumOfPastPackets;
+}
+
+void AnomalyTracker::flushPastBuckets(const int64_t& latestPastBucketNum) {
+ VLOG("addPastBucket() called.");
+ if (latestPastBucketNum <= mMostRecentBucketNum - mNumOfPastPackets) {
+ ALOGE("Cannot add a past bucket %lld units in past", (long long)latestPastBucketNum);
+ return;
+ }
+
+ // The past packets are ancient. Empty out old mPastBuckets[i] values and reset
+ // mSumOverPastBuckets.
+ if (latestPastBucketNum - mMostRecentBucketNum >= mNumOfPastPackets) {
+ mPastBuckets.clear();
+ mPastBuckets.resize(mNumOfPastPackets);
+ mSumOverPastBuckets.clear();
+ } else {
+ for (int64_t i = std::max(0LL, (long long)(mMostRecentBucketNum - mNumOfPastPackets + 1));
+ i <= latestPastBucketNum - mNumOfPastPackets; i++) {
+ const int idx = index(i);
+ subtractBucketFromSum(mPastBuckets[idx]);
+ mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket.
+ }
+ }
+
+ // It is an update operation.
+ if (latestPastBucketNum <= mMostRecentBucketNum &&
+ latestPastBucketNum > mMostRecentBucketNum - mNumOfPastPackets) {
+ subtractBucketFromSum(mPastBuckets[index(latestPastBucketNum)]);
+ }
+}
+
+void AnomalyTracker::addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+ const int64_t& bucketNum) {
+ flushPastBuckets(bucketNum);
+
+ auto& bucket = mPastBuckets[index(bucketNum)];
+ if (bucket == nullptr) {
+ bucket = std::make_shared<DimToValMap>();
+ }
+ bucket->insert({key, bucketValue});
+ addBucketToSum(bucket);
+ mMostRecentBucketNum = std::max(mMostRecentBucketNum, bucketNum);
+}
+
+void AnomalyTracker::addPastBucket(std::shared_ptr<DimToValMap> bucketValues,
+ const int64_t& bucketNum) {
+ VLOG("addPastBucket() called.");
+ flushPastBuckets(bucketNum);
+ // Replace the oldest bucket with the new bucket we are adding.
+ mPastBuckets[index(bucketNum)] = bucketValues;
+ addBucketToSum(bucketValues);
+ mMostRecentBucketNum = std::max(mMostRecentBucketNum, bucketNum);
+}
+
+void AnomalyTracker::subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket) {
+ if (bucket == nullptr) {
+ return;
+ }
+ // For each dimension present in the bucket, subtract its value from its corresponding sum.
+ for (const auto& keyValuePair : *bucket) {
+ auto itr = mSumOverPastBuckets.find(keyValuePair.first);
+ if (itr == mSumOverPastBuckets.end()) {
+ continue;
+ }
+ itr->second -= keyValuePair.second;
+ // TODO: No need to look up the object twice like this. Use a var.
+ if (itr->second == 0) {
+ mSumOverPastBuckets.erase(itr);
+ }
+ }
+}
+
+void AnomalyTracker::addBucketToSum(const shared_ptr<DimToValMap>& bucket) {
+ if (bucket == nullptr) {
+ return;
+ }
+ // For each dimension present in the bucket, add its value to its corresponding sum.
+ for (const auto& keyValuePair : *bucket) {
+ mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
+ }
+}
+
+int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key,
+ const int64_t& bucketNum) const {
+ const auto& bucket = mPastBuckets[index(bucketNum)];
+ if (bucket == nullptr) {
+ return 0;
+ }
+ const auto& itr = bucket->find(key);
+ return itr == bucket->end() ? 0 : itr->second;
+}
+
+int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) const {
+ const auto& itr = mSumOverPastBuckets.find(key);
+ if (itr != mSumOverPastBuckets.end()) {
+ return itr->second;
+ }
+ return 0;
+}
+
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum,
+ const DimToValMap& currentBucket) {
+ if (currentBucketNum > mMostRecentBucketNum + 1) {
+ addPastBucket(nullptr, currentBucketNum - 1);
+ }
+ for (auto itr = currentBucket.begin(); itr != currentBucket.end(); itr++) {
+ if (itr->second + getSumOverPastBuckets(itr->first) > mAlert.trigger_if_sum_gt()) {
+ return true;
+ }
+ }
+ // In theory, we also need to check the dimsions not in the current bucket. In single-thread
+ // mode, usually we could avoid the following loops.
+ for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) {
+ if (itr->second > mAlert.trigger_if_sum_gt()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key,
+ const int64_t& currentBucketValue) {
+ if (currentBucketNum > mMostRecentBucketNum + 1) {
+ addPastBucket(key, 0, currentBucketNum - 1);
+ }
+ return getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
+}
+
+void AnomalyTracker::declareAnomaly(const uint64_t& timestamp) {
+ if (mLastAlarmTimestampNs >= 0 &&
+ timestamp - mLastAlarmTimestampNs <= mAlert.refractory_period_secs() * NS_PER_SEC) {
+ VLOG("Skipping anomaly check since within refractory period");
+ return;
+ }
+ // TODO(guardrail): Consider guarding against too short refractory periods.
+ mLastAlarmTimestampNs = timestamp;
+
+ if (mAlert.has_incidentd_details()) {
+ // TODO: Can construct a name based on the criteria (and/or relay the criteria).
+ ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
+ // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
+ } else {
+ ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
+ }
+}
+
+void AnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+ const uint64_t& timestamp) {
+ auto itr = mAlarms.find(dimensionKey);
+ if (itr == mAlarms.end()) {
+ return;
+ }
+
+ if (itr->second != nullptr &&
+ static_cast<uint32_t>(timestamp / NS_PER_SEC) >= itr->second->timestampSec) {
+ declareAnomaly(timestamp);
+ stopAlarm(dimensionKey);
+ }
+}
+
+void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestamp,
+ const int64_t& currBucketNum,
+ const HashableDimensionKey& key,
+ const int64_t& currentBucketValue) {
+ if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
+ declareAnomaly(timestamp);
+ }
+}
+
+void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestamp,
+ const int64_t& currBucketNum,
+ const DimToValMap& currentBucket) {
+ if (detectAnomaly(currBucketNum, currentBucket)) {
+ declareAnomaly(timestamp);
+ }
+}
+
+void AnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey,
+ const uint64_t& timestamp) {
+ sp<const AnomalyAlarm> alarm = new AnomalyAlarm{static_cast<uint32_t>(timestamp / NS_PER_SEC)};
+ mAlarms.insert({dimensionKey, alarm});
+ if (mAnomalyMonitor != nullptr) {
+ mAnomalyMonitor->add(alarm);
+ }
+}
+
+void AnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) {
+ auto itr = mAlarms.find(dimensionKey);
+ if (itr != mAlarms.end()) {
+ mAlarms.erase(dimensionKey);
+ }
+ if (mAnomalyMonitor != nullptr) {
+ mAnomalyMonitor->remove(itr->second);
+ }
+}
+
+void AnomalyTracker::stopAllAlarms() {
+ std::set<HashableDimensionKey> keys;
+ for (auto itr = mAlarms.begin(); itr != mAlarms.end(); ++itr) {
+ keys.insert(itr->first);
+ }
+ for (auto key : keys) {
+ stopAlarm(key);
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
new file mode 100644
index 0000000..ce6c995
--- /dev/null
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gtest/gtest_prod.h>
+#include "AnomalyMonitor.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
+#include "stats_util.h" // HashableDimensionKey and DimToValMap
+
+#include <memory> // unique_ptr
+#include <stdlib.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::unordered_map;
+using std::shared_ptr;
+
+// This anomaly track assmues that all values are non-negative.
+class AnomalyTracker : public virtual RefBase {
+public:
+ AnomalyTracker(const Alert& alert, const int64_t& bucketSizeNs);
+
+ virtual ~AnomalyTracker();
+
+ // Adds a bucket.
+ // Bucket index starts from 0.
+ void addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum);
+ void addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+ const int64_t& bucketNum);
+
+ // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
+ bool detectAnomaly(const int64_t& currBucketNum, const DimToValMap& currentBucket);
+ bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key,
+ const int64_t& currentBucketValue);
+
+ // Informs incidentd about the detected alert.
+ void declareAnomaly(const uint64_t& timestamp);
+
+ // Detects the alert and informs the incidentd when applicable.
+ void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum,
+ const DimToValMap& currentBucket);
+ void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum,
+ const HashableDimensionKey& key,
+ const int64_t& currentBucketValue);
+
+ // Starts the alarm at the given timestamp.
+ void startAlarm(const HashableDimensionKey& dimensionKey, const uint64_t& eventTime);
+ // Stops the alarm.
+ void stopAlarm(const HashableDimensionKey& dimensionKey);
+
+ // Stop all the alarms owned by this tracker.
+ void stopAllAlarms();
+
+ // Init the anmaly monitor which is shared across anomaly trackers.
+ inline void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
+ mAnomalyMonitor = anomalyMonitor;
+ }
+
+ // Declares the anomaly when the alarm expired given the current timestamp.
+ void declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+ const uint64_t& timestamp);
+
+ // Helper function to return the sum value of past buckets at given dimension.
+ int64_t getSumOverPastBuckets(const HashableDimensionKey& key) const;
+
+ // Helper function to return the value for a past bucket.
+ int64_t getPastBucketValue(const HashableDimensionKey& key, const int64_t& bucketNum) const;
+
+ // Returns the anomaly threshold.
+ inline int64_t getAnomalyThreshold() const {
+ return mAlert.trigger_if_sum_gt();
+ }
+
+ // Helper function to return the last alarm timestamp.
+ inline int64_t getLastAlarmTimestampNs() const {
+ return mLastAlarmTimestampNs;
+ }
+
+ inline int getNumOfPastPackets() const {
+ return mNumOfPastPackets;
+ }
+
+protected:
+ void flushPastBuckets(const int64_t& currBucketNum);
+ // statsd_config.proto Alert message that defines this tracker.
+ const Alert mAlert;
+
+ // Bucket duration in ns.
+ int64_t mBucketSizeNs = 0;
+
+ // The number of past packets to track in the anomaly detection.
+ int mNumOfPastPackets = 0;
+
+ // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
+ // are still active.
+ std::unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> mAlarms;
+
+ // Anomaly alarm monitor.
+ sp<AnomalyMonitor> mAnomalyMonitor;
+
+ // The exisiting bucket list.
+ std::vector<shared_ptr<DimToValMap>> mPastBuckets;
+
+ // Sum over all existing buckets cached in mPastBuckets.
+ DimToValMap mSumOverPastBuckets;
+
+ // The bucket number of the last added bucket.
+ int64_t mMostRecentBucketNum = -1;
+
+ // The timestamp when the last anomaly was declared.
+ int64_t mLastAlarmTimestampNs = -1;
+
+ // Add the information in the given bucket to mSumOverPastBuckets.
+ void addBucketToSum(const shared_ptr<DimToValMap>& bucket);
+
+ // Subtract the information in the given bucket from mSumOverPastBuckets
+ // and remove any items with value 0.
+ void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
+
+ // Calculates the corresponding bucket index within the circular array.
+ size_t index(int64_t bucketNum) const;
+
+ // Resets all data. For use when all the data gets stale.
+ void reset();
+
+ FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
+ FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
+ FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
+ FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection);
+ FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
+ FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
+ FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
+ FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
+ FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
deleted file mode 100644
index 9c9bde9..0000000
--- a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG true // STOPSHIP if true
-#include "Log.h"
-
-#include "DiscreteAnomalyTracker.h"
-
-#include <time.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-DiscreteAnomalyTracker::DiscreteAnomalyTracker(const Alert& alert) : mAlert(alert) {
- VLOG("DiscreteAnomalyTracker() called");
- if (mAlert.number_of_buckets() <= 0) {
- ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets",
- (long long)mAlert.number_of_buckets());
- return;
- }
- mPastBuckets.resize(mAlert.number_of_buckets());
- reset(); // initialization
-}
-
-DiscreteAnomalyTracker::~DiscreteAnomalyTracker() {
- VLOG("~DiscreteAnomalyTracker() called");
-}
-
-void DiscreteAnomalyTracker::reset() {
- VLOG("reset() called.");
- mPastBuckets.clear();
- mPastBuckets.resize(mAlert.number_of_buckets());
- mSumOverPastBuckets.clear();
- mCurrentBucketIndex = -1;
- mLastAlarmAtBucketIndex = -1;
- mAnomalyDeclared = 0;
-}
-
-size_t DiscreteAnomalyTracker::index(int64_t bucketNum) {
- return bucketNum % mAlert.number_of_buckets();
-}
-
-void DiscreteAnomalyTracker::addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues,
- int64_t bucketIndex) {
- VLOG("addPastBucket() called.");
- if (bucketIndex <= mCurrentBucketIndex - mAlert.number_of_buckets()) {
- ALOGE("Cannot add a past bucket %lld units in past", (long long)bucketIndex);
- return;
- }
-
- // Empty out old mPastBuckets[i] values and update mSumOverPastBuckets.
- if (bucketIndex - mCurrentBucketIndex >= mAlert.number_of_buckets()) {
- mPastBuckets.clear();
- mPastBuckets.resize(mAlert.number_of_buckets());
- mSumOverPastBuckets.clear();
- } else {
- for (int64_t i = std::max(
- 0LL, (long long)(mCurrentBucketIndex - mAlert.number_of_buckets() + 1));
- i < bucketIndex - mAlert.number_of_buckets(); i++) {
- const int idx = index(i);
- subtractBucketFromSum(mPastBuckets[idx]);
- mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket.
- }
- }
- subtractBucketFromSum(mPastBuckets[index(bucketIndex)]);
- mPastBuckets[index(bucketIndex)] = nullptr; // release (but not clear) the old bucket.
-
- // Replace the oldest bucket with the new bucket we are adding.
- mPastBuckets[index(bucketIndex)] = BucketValues;
- addBucketToSum(BucketValues);
-
- mCurrentBucketIndex = std::max(mCurrentBucketIndex, bucketIndex);
-}
-
-void DiscreteAnomalyTracker::subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket) {
- if (bucket == nullptr) {
- return;
- }
- // For each dimension present in the bucket, subtract its value from its corresponding sum.
- for (const auto& keyValuePair : *bucket) {
- auto itr = mSumOverPastBuckets.find(keyValuePair.first);
- if (itr == mSumOverPastBuckets.end()) {
- continue;
- }
- itr->second -= keyValuePair.second;
- // TODO: No need to look up the object twice like this. Use a var.
- if (itr->second == 0) {
- mSumOverPastBuckets.erase(itr);
- }
- }
-}
-
-void DiscreteAnomalyTracker::addBucketToSum(const shared_ptr<const DimToValMap>& bucket) {
- if (bucket == nullptr) {
- return;
- }
- // For each dimension present in the bucket, add its value to its corresponding sum.
- for (const auto& keyValuePair : *bucket) {
- mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
- }
-}
-
-bool DiscreteAnomalyTracker::detectAnomaly() {
- for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) {
- if (mAlert.has_trigger_if_sum_gt() && itr->second > mAlert.trigger_if_sum_gt()) {
- return true;
- }
- }
- return false;
-}
-
-void DiscreteAnomalyTracker::declareAndDeclareAnomaly() {
- if (detectAnomaly()) {
- declareAnomaly();
- }
-}
-
-void DiscreteAnomalyTracker::declareAnomaly() {
- if (mLastAlarmAtBucketIndex >= 0 && mCurrentBucketIndex - mLastAlarmAtBucketIndex <=
- (long long)mAlert.refractory_period_in_buckets()) {
- VLOG("Skipping anomaly check since within refractory period");
- return;
- }
- mAnomalyDeclared++;
- // TODO(guardrail): Consider guarding against too short refractory periods.
- mLastAlarmAtBucketIndex = mCurrentBucketIndex;
-
- if (mAlert.has_incidentd_details()) {
- const Alert_IncidentdDetails& incident = mAlert.incidentd_details();
- if (incident.has_alert_name()) {
- ALOGW("An anomaly (%s) has occurred! Informing incidentd.",
- incident.alert_name().c_str());
- } else {
- // TODO: Can construct a name based on the criteria (and/or relay the criteria).
- ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
- }
- // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
- } else {
- ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
deleted file mode 100644
index ed7d5d7..0000000
--- a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gtest/gtest_prod.h>
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
-#include "stats_util.h" // HashableDimensionKey and DimToValMap
-
-#include <memory> // unique_ptr
-#include <stdlib.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::unordered_map;
-using std::shared_ptr;
-
-// This anomaly track assmues that all values are non-negative.
-class DiscreteAnomalyTracker {
- public:
- DiscreteAnomalyTracker(const Alert& alert);
-
- virtual ~DiscreteAnomalyTracker();
-
- // Adds a new bucket or updates an existing bucket.
- // Bucket index starts from 0.
- void addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues, int64_t bucketIndex);
-
- // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
- bool detectAnomaly();
-
- // Informs incidentd about the detected alert.
- void declareAnomaly();
-
- // Detects the alert and informs the incidentd when applicable.
- void declareAndDeclareAnomaly();
-
-private:
- // statsd_config.proto Alert message that defines this tracker.
- const Alert mAlert;
-
- // The exisiting bucket list.
- std::vector<shared_ptr<const DimToValMap>> mPastBuckets;
-
- // Sum over all existing buckets cached in mPastBuckets.
- DimToValMap mSumOverPastBuckets;
-
- // Current bucket index of the current anomaly detection window. Bucket index starts from 0.
- int64_t mCurrentBucketIndex = -1;
-
- // The bucket index when the last anomaly was declared.
- int64_t mLastAlarmAtBucketIndex = -1;
-
- // The total number of declared anomalies.
- int64_t mAnomalyDeclared = 0;
-
- // Add the information in the given bucket to mSumOverPastBuckets.
- void addBucketToSum(const shared_ptr<const DimToValMap>& bucket);
-
- // Subtract the information in the given bucket from mSumOverPastBuckets
- // and remove any items with value 0.
- void subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket);
-
- // Calculates the corresponding bucket index within the circular array.
- size_t index(int64_t bucketNum);
-
- // Resets all data. For use when all the data gets stale.
- void reset();
-
- FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
- FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/indexed_priority_queue.h b/cmds/statsd/src/anomaly/indexed_priority_queue.h
index 1a2e9c2..4982d4b 100644
--- a/cmds/statsd/src/anomaly/indexed_priority_queue.h
+++ b/cmds/statsd/src/anomaly/indexed_priority_queue.h
@@ -16,8 +16,6 @@
#pragma once
-#include "Log.h"
-
#include <utils/RefBase.h>
#include <unordered_map>
#include <vector>
@@ -28,7 +26,7 @@
namespace os {
namespace statsd {
-/** Defines a hash function for sp<AA>, returning the hash of the underlying pointer. */
+/** Defines a hash function for sp<const AA>, returning the hash of the underlying pointer. */
template <class AA>
struct SpHash {
size_t operator()(const sp<const AA>& k) const {
@@ -39,7 +37,7 @@
/**
* Min priority queue for generic type AA.
* Unlike a regular priority queue, this class is also capable of removing interior elements.
- * @tparam Comparator must implement [bool operator()(sp<const AA> a, sp<const AA> b)], returning
+ * @tparam Comparator must implement [bool operator()(sp< AA> a, sp< AA> b)], returning
* whether a should be closer to the top of the queue than b.
*/
template <class AA, class Comparator>
@@ -104,7 +102,6 @@
if (!contains(a)) return;
size_t idx = indices[a];
if (idx >= pq.size()) {
- ALOGE("indexed_priority_queue: Invalid index in map of indices.");
return;
}
if (idx == size()) { // if a is the last element, i.e. at index idx == size() == (pq.size()-1)
@@ -193,7 +190,6 @@
template <class AA, class Comparator>
bool indexed_priority_queue<AA, Comparator>::higher(size_t idx1, size_t idx2) const {
if (!(0u < idx1 && idx1 < pq.size() && 0u < idx2 && idx2 < pq.size())) {
- ALOGE("indexed_priority_queue: Attempting to access invalid index");
return false; // got to do something.
}
return Comparator()(pq[idx1], pq[idx2]);
@@ -208,7 +204,6 @@
template <class AA, class Comparator>
void indexed_priority_queue<AA, Comparator>::swap_indices(size_t i, size_t j) {
if (!(0u < i && i < pq.size() && 0u < j && j < pq.size())) {
- ALOGE("indexed_priority_queue: Attempting to swap invalid index");
return;
}
sp<const AA> val_i = pq[i];
diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/atoms.proto
similarity index 88%
copy from cmds/statsd/src/stats_events.proto
copy to cmds/statsd/src/atoms.proto
index 8816795..57a92b6 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -19,10 +19,10 @@
// TODO: Not the right package and class name
package android.os.statsd;
option java_package = "com.android.os";
-option java_outer_classname = "StatsEventProto";
+option java_outer_classname = "AtomsProto";
/**
- * The master event class. This message defines all of the available
+ * The master atom class. This message defines all of the available
* raw stats log events from the Android system, also known as "atoms."
*
* This field contains a single oneof with all of the available messages.
@@ -30,12 +30,12 @@
* generates the android.util.StatsLog class, which contains the constants
* and methods that Android uses to log.
*
- * This StatsEvent class is not actually built into the Android system.
+ * This Atom class is not actually built into the Android system.
* Instead, statsd on Android constructs these messages synthetically,
* in the format defined here and in stats_log.proto.
*/
-message StatsEvent {
- // Pushed events start at 2.
+message Atom {
+ // Pushed atoms start at 2.
oneof pushed {
// For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
BleScanStateChanged ble_scan_state_changed = 2;
@@ -73,6 +73,7 @@
SettingChanged setting_changed = 41;
ActivityForegroundStateChanged activity_foreground_state_changed = 42;
IsolatedUidChanged isolated_uid_changed = 43;
+ PacketWakeupOccurred packet_wakeup_occurred = 44;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
@@ -86,6 +87,9 @@
PowerStatePlatformSleepStatePulled power_state_platform_sleep_state_pulled = 1005;
PowerStateVoterPulled power_state_voter_pulled = 1006;
PowerStateSubsystemSleepStatePulled power_state_subsystem_sleep_state_pulled = 1007;
+ CpuTimePerFreqPulled cpu_time_per_freq_pulled = 1008;
+ CpuTimePerUidPulled cpu_time_per_uid_pulled = 1009;
+ CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010;
}
}
@@ -99,8 +103,7 @@
/*
* *****************************************************************************
- * Below are all of the individual atoms that are logged by Android via statsd
- * and Westworld.
+ * Below are all of the individual atoms that are logged by Android via statsd.
*
* RULES:
* - The field ids for each atom must start at 1, and count upwards by 1.
@@ -111,7 +114,7 @@
* - The types must be built-in protocol buffer types, namely, no sub-messages
* are allowed (yet). The bytes type is also not allowed.
* - The CamelCase name of the message type should match the
- * underscore_separated name as defined in StatsEvent.
+ * underscore_separated name as defined in Atom.
* - If an atom represents work that can be attributed to an app, there can
* be exactly one WorkSource field. It must be field number 1.
* - A field that is a uid should be a string field, tagged with the [xxx]
@@ -394,7 +397,7 @@
FULL = 1;
WINDOW = 2;
}
- optional int32 type = 2;
+ optional Type type = 2;
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
optional string tag = 3;
@@ -422,7 +425,7 @@
FULL = 1;
WINDOW = 2;
}
- optional int32 type = 2;
+ optional Type type = 2;
enum State {
OFF = 0;
@@ -866,3 +869,70 @@
// be removed before if it's used for another parent uid.
optional int32 is_create = 3;
}
+
+/*
+ * Pulls Cpu time per frequency.
+ * Note: this should be pulled for gauge metric only, without condition.
+ * The puller keeps internal state of last values. It should not be pulled by
+ * different metrics.
+ * The pulled data is delta of cpu time from last pull, calculated as
+ * following:
+ * if current time is larger than last value, take delta between the two.
+ * if current time is smaller than last value, there must be a cpu
+ * hotplug event, and the current time is taken as delta.
+ */
+message CpuTimePerFreqPulled {
+ optional uint32 cluster = 1;
+ optional uint32 freq_index = 2;
+ optional uint64 time = 3;
+}
+
+/*
+ * Pulls Cpu Time Per Uid.
+ * Note that isolated process uid time should be attributed to host uids.
+ */
+message CpuTimePerUidPulled {
+ optional uint64 uid = 1;
+ optional uint64 user_time_ms = 2;
+ optional uint64 sys_time_ms = 3;
+}
+
+/**
+ * Pulls Cpu Time Per Uid per frequency.
+ * Note that isolated process uid time should be attributed to host uids.
+ * For each uid, we order the time by descending frequencies.
+ */
+message CpuTimePerUidFreqPulled {
+ optional uint64 uid = 1;
+ optional uint64 freq_idx = 2;
+ optional uint64 time_ms = 3;
+}
+
+/*
+ * Logs the reception of an incoming network packet causing the main system to wake up for
+ * processing that packet. These events are notified by the kernel via Netlink NFLOG to Netd
+ * and processed by WakeupController.cpp.
+ */
+message PacketWakeupOccurred {
+ // The uid owning the socket into which the packet was delivered, or -1 if the packet was
+ // delivered nowhere.
+ optional int32 uid = 1;
+ // The interface name on which the packet was received.
+ optional string iface = 2;
+ // The ethertype value of the packet.
+ optional int32 ethertype = 3;
+ // String representation of the destination MAC address of the packet.
+ optional string destination_hardware_address = 4;
+ // String representation of the source address of the packet if this was an IP packet.
+ optional string source_ip = 5;
+ // String representation of the destination address of the packet if this was an IP packet.
+ optional string destination_ip = 6;
+ // The value of the protocol field if this was an IPv4 packet or the value of the Next Header
+ // field if this was an IPv6 packet. The range of possible values is the same for both IP
+ // families.
+ optional int32 ip_next_header = 7;
+ // The source port if this was a TCP or UDP packet.
+ optional int32 source_port = 8;
+ // The destination port if this was a TCP or UDP packet.
+ optional int32 destination_port = 9;
+}
diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/atoms_copy.proto
similarity index 92%
rename from cmds/statsd/src/stats_events.proto
rename to cmds/statsd/src/atoms_copy.proto
index 8816795..58e225a 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/atoms_copy.proto
@@ -15,14 +15,16 @@
*/
syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
// TODO: Not the right package and class name
package android.os.statsd;
option java_package = "com.android.os";
-option java_outer_classname = "StatsEventProto";
+option java_outer_classname = "AtomsProto";
/**
- * The master event class. This message defines all of the available
+ * The master atom class. This message defines all of the available
* raw stats log events from the Android system, also known as "atoms."
*
* This field contains a single oneof with all of the available messages.
@@ -30,12 +32,12 @@
* generates the android.util.StatsLog class, which contains the constants
* and methods that Android uses to log.
*
- * This StatsEvent class is not actually built into the Android system.
+ * This Atom class is not actually built into the Android system.
* Instead, statsd on Android constructs these messages synthetically,
* in the format defined here and in stats_log.proto.
*/
-message StatsEvent {
- // Pushed events start at 2.
+message Atom {
+ // Pushed atoms start at 2.
oneof pushed {
// For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
BleScanStateChanged ble_scan_state_changed = 2;
@@ -86,6 +88,9 @@
PowerStatePlatformSleepStatePulled power_state_platform_sleep_state_pulled = 1005;
PowerStateVoterPulled power_state_voter_pulled = 1006;
PowerStateSubsystemSleepStatePulled power_state_subsystem_sleep_state_pulled = 1007;
+ CpuTimePerFreqPulled cpu_time_per_freq_pulled = 1008;
+ CpuTimePerUidPulled cpu_time_per_uid_pulled = 1009;
+ CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010;
}
}
@@ -99,8 +104,7 @@
/*
* *****************************************************************************
- * Below are all of the individual atoms that are logged by Android via statsd
- * and Westworld.
+ * Below are all of the individual atoms that are logged by Android via statsd.
*
* RULES:
* - The field ids for each atom must start at 1, and count upwards by 1.
@@ -111,7 +115,7 @@
* - The types must be built-in protocol buffer types, namely, no sub-messages
* are allowed (yet). The bytes type is also not allowed.
* - The CamelCase name of the message type should match the
- * underscore_separated name as defined in StatsEvent.
+ * underscore_separated name as defined in Atom.
* - If an atom represents work that can be attributed to an app, there can
* be exactly one WorkSource field. It must be field number 1.
* - A field that is a uid should be a string field, tagged with the [xxx]
@@ -394,7 +398,7 @@
FULL = 1;
WINDOW = 2;
}
- optional int32 type = 2;
+ optional Type type = 2;
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
optional string tag = 3;
@@ -422,7 +426,7 @@
FULL = 1;
WINDOW = 2;
}
- optional int32 type = 2;
+ optional Type type = 2;
enum State {
OFF = 0;
@@ -866,3 +870,41 @@
// be removed before if it's used for another parent uid.
optional int32 is_create = 3;
}
+
+/*
+ * Pulls Cpu time per frequency.
+ * Note: this should be pulled for gauge metric only, without condition.
+ * The puller keeps internal state of last values. It should not be pulled by
+ * different metrics.
+ * The pulled data is delta of cpu time from last pull, calculated as
+ * following:
+ * if current time is larger than last value, take delta between the two.
+ * if current time is smaller than last value, there must be a cpu
+ * hotplug event, and the current time is taken as delta.
+ */
+message CpuTimePerFreqPulled {
+ optional uint32 cluster = 1;
+ optional uint32 freq_index = 2;
+ optional uint64 time = 3;
+}
+
+/*
+ * Pulls Cpu Time Per Uid.
+ * Note that isolated process uid time should be attributed to host uids.
+ */
+message CpuTimePerUidPulled {
+ optional uint64 uid = 1;
+ optional uint64 user_time_ms = 2;
+ optional uint64 sys_time_ms = 3;
+}
+
+/**
+ * Pulls Cpu Time Per Uid per frequency.
+ * Note that isolated process uid time should be attributed to host uids.
+ * For each uid, we order the time by descending frequencies.
+ */
+message CpuTimePerUidFreqPulled {
+ optional uint64 uid = 1;
+ optional uint64 freq_idx = 2;
+ optional uint64 time_ms = 3;
+}
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 953bcb3..41f5fca 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -115,49 +115,47 @@
evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
}
-bool CombinationConditionTracker::evaluateCondition(
+void CombinationConditionTracker::evaluateCondition(
const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& nonSlicedConditionCache,
- std::vector<bool>& nonSlicedChangedCache, vector<bool>& slicedConditionChanged) {
+ std::vector<bool>& conditionChangedCache) {
// value is up to date.
if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
- return false;
+ return;
}
for (const int childIndex : mChildren) {
if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
const sp<ConditionTracker>& child = mAllConditions[childIndex];
child->evaluateCondition(event, eventMatcherValues, mAllConditions,
- nonSlicedConditionCache, nonSlicedChangedCache,
- slicedConditionChanged);
+ nonSlicedConditionCache, conditionChangedCache);
}
}
- ConditionState newCondition =
- evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
+ if (!mSliced) {
+ ConditionState newCondition =
+ evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
- bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
- mNonSlicedConditionState = newCondition;
+ bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
+ mNonSlicedConditionState = newCondition;
- nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
+ nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
- nonSlicedChangedCache[mIndex] = nonSlicedChanged;
-
- if (mSliced) {
+ conditionChangedCache[mIndex] = nonSlicedChanged;
+ } else {
for (const int childIndex : mChildren) {
// If any of the sliced condition in children condition changes, the combination
// condition may be changed too.
- if (slicedConditionChanged[childIndex]) {
- slicedConditionChanged[mIndex] = true;
+ if (conditionChangedCache[childIndex]) {
+ conditionChangedCache[mIndex] = true;
break;
}
}
+ nonSlicedConditionCache[mIndex] = ConditionState::kUnknown;
ALOGD("CombinationCondition %s sliced may changed? %d", mName.c_str(),
- slicedConditionChanged[mIndex] == true);
+ conditionChangedCache[mIndex] == true);
}
-
- return nonSlicedChanged;
}
} // namespace statsd
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index dbdb3b7..3d2c6bb 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -35,12 +35,11 @@
const std::unordered_map<std::string, int>& conditionNameIndexMap,
std::vector<bool>& stack) override;
- bool evaluateCondition(const LogEvent& event,
+ void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache,
- std::vector<bool>& slicedConditionMayChanged) override;
+ std::vector<bool>& changedCache) override;
void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index bb5ddeb..0ac7ef3 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -56,25 +56,20 @@
std::vector<bool>& stack) = 0;
// evaluate current condition given the new event.
- // return true if the condition state changed, false if the condition state is not changed.
// event: the new log event
// eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process
// event before ConditionTrackers, because ConditionTracker depends on
// LogMatchingTrackers.
// mAllConditions: the list of all ConditionTracker
// conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event.
- // nonSlicedConditionChanged: the bit map to record whether non-sliced condition has changed.
- // slicedConditionMayChanged: the bit map to record whether sliced condition may have changed.
- // Because sliced condition needs parameters to determine the value. So the sliced
- // condition is not pushed to metrics. We only inform the relevant metrics that the sliced
- // condition may have changed, and metrics should pull the conditions that they are
- // interested in.
- virtual bool evaluateCondition(const LogEvent& event,
+ // conditionChanged: the bit map to record whether the condition has changed.
+ // If the condition has dimension, then any sub condition changes will report
+ // conditionChanged.
+ virtual void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
- std::vector<bool>& nonSlicedConditionChanged,
- std::vector<bool>& slicedConditionMayChanged) = 0;
+ std::vector<bool>& conditionChanged) = 0;
// Return the current condition state.
virtual ConditionState isConditionMet() {
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index b691faea..60060fe 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -74,13 +74,21 @@
mStopAllLogMatcherIndex = -1;
}
- mDimension.insert(mDimension.begin(), simpleCondition.dimension().begin(),
- simpleCondition.dimension().end());
+ mOutputDimension.insert(mOutputDimension.begin(), simpleCondition.dimension().begin(),
+ simpleCondition.dimension().end());
- if (mDimension.size() > 0) {
+ if (mOutputDimension.size() > 0) {
mSliced = true;
}
+ if (simpleCondition.initial_value() == SimpleCondition_InitialValue_FALSE) {
+ mInitialValue = ConditionState::kFalse;
+ } else {
+ mInitialValue = ConditionState::kUnknown;
+ }
+
+ mNonSlicedConditionState = mInitialValue;
+
mInitialized = true;
}
@@ -97,127 +105,181 @@
return mInitialized;
}
-void print(unordered_map<HashableDimensionKey, ConditionState>& conditions, const string& name) {
+void print(map<HashableDimensionKey, int>& conditions, const string& name) {
VLOG("%s DUMP:", name.c_str());
-
for (const auto& pair : conditions) {
- VLOG("\t%s %d", pair.first.c_str(), pair.second);
+ VLOG("\t%s : %d", pair.first.c_str(), pair.second);
}
}
-bool SimpleConditionTracker::evaluateCondition(const LogEvent& event,
+void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& conditionChangedCache) {
+ // Unless the default condition is false, and there was nothing started, otherwise we have
+ // triggered a condition change.
+ conditionChangedCache[mIndex] =
+ (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
+ : true;
+
+ // After StopAll, we know everything has stopped. From now on, default condition is false.
+ mInitialValue = ConditionState::kFalse;
+ mSlicedConditionState.clear();
+ conditionCache[mIndex] = ConditionState::kFalse;
+}
+
+void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
+ bool matchStart,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& conditionChangedCache) {
+ bool changed = false;
+ auto outputIt = mSlicedConditionState.find(outputKey);
+ ConditionState newCondition;
+ if (outputIt == mSlicedConditionState.end()) {
+ // We get a new output key.
+ newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
+ if (matchStart && mInitialValue != ConditionState::kTrue) {
+ mSlicedConditionState[outputKey] = 1;
+ changed = true;
+ } else if (mInitialValue != ConditionState::kFalse) {
+ // it's a stop and we don't have history about it.
+ // If the default condition is not false, it means this stop is valuable to us.
+ mSlicedConditionState[outputKey] = 0;
+ changed = true;
+ }
+ } else {
+ // we have history about this output key.
+ auto& startedCount = outputIt->second;
+ // assign the old value first.
+ newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ if (matchStart) {
+ if (startedCount == 0) {
+ // This condition for this output key will change from false -> true
+ changed = true;
+ }
+
+ // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
+ // as 1 if not counting nesting.
+ startedCount++;
+ newCondition = ConditionState::kTrue;
+ } else {
+ // This is a stop event.
+ if (startedCount > 0) {
+ if (mCountNesting) {
+ startedCount--;
+ if (startedCount == 0) {
+ newCondition = ConditionState::kFalse;
+ }
+ } else {
+ // not counting nesting, so ignore the number of starts, stop now.
+ startedCount = 0;
+ newCondition = ConditionState::kFalse;
+ }
+ // if everything has stopped for this output key, condition true -> false;
+ if (startedCount == 0) {
+ changed = true;
+ }
+ }
+
+ // if default condition is false, it means we don't need to keep the false values.
+ if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
+ mSlicedConditionState.erase(outputIt);
+ VLOG("erase key %s", outputKey.c_str());
+ }
+ }
+ }
+
+ // dump all dimensions for debugging
+ if (DEBUG) {
+ print(mSlicedConditionState, mName);
+ }
+
+ conditionChangedCache[mIndex] = changed;
+ conditionCache[mIndex] = newCondition;
+
+ VLOG("SimpleCondition %s nonSlicedChange? %d", mName.c_str(),
+ conditionChangedCache[mIndex] == true);
+}
+
+void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
const vector<MatchingState>& eventMatcherValues,
const vector<sp<ConditionTracker>>& mAllConditions,
vector<ConditionState>& conditionCache,
- vector<bool>& nonSlicedConditionChanged,
- std::vector<bool>& slicedConditionChanged) {
+ vector<bool>& conditionChangedCache) {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
- VLOG("Yes, already evaluated, %s %d", mName.c_str(), mNonSlicedConditionState);
- return false;
+ VLOG("Yes, already evaluated, %s %d", mName.c_str(), conditionCache[mIndex]);
+ return;
}
- // Ignore nesting, because we know we cannot trust ourselves on tracking nesting conditions.
+ if (mStopAllLogMatcherIndex >= 0 &&
+ eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
+ handleStopAll(conditionCache, conditionChangedCache);
+ return;
+ }
- ConditionState newCondition = mNonSlicedConditionState;
- bool matched = false;
+ int matchedState = -1;
// Note: The order to evaluate the following start, stop, stop_all matters.
// The priority of overwrite is stop_all > stop > start.
if (mStartLogMatcherIndex >= 0 &&
eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
- matched = true;
- newCondition = ConditionState::kTrue;
+ matchedState = 1;
}
if (mStopLogMatcherIndex >= 0 &&
eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
- matched = true;
- newCondition = ConditionState::kFalse;
+ matchedState = 0;
}
- bool stopAll = false;
- if (mStopAllLogMatcherIndex >= 0 &&
- eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
- matched = true;
- newCondition = ConditionState::kFalse;
- stopAll = true;
- }
-
- if (matched == false) {
- slicedConditionChanged[mIndex] = false;
- nonSlicedConditionChanged[mIndex] = false;
- conditionCache[mIndex] = mNonSlicedConditionState;
- return false;
- }
-
- bool nonSlicedChanged = mNonSlicedConditionState != newCondition;
-
- bool slicedChanged = false;
-
- if (stopAll) {
- // TODO: handle stop all; all dimension should be cleared.
- }
-
-
- if (mDimension.size() > 0) {
- HashableDimensionKey hashableKey = getHashableKey(getDimensionKey(event, mDimension));
- if (mSlicedConditionState.find(hashableKey) == mSlicedConditionState.end() ||
- mSlicedConditionState[hashableKey] != newCondition) {
- slicedChanged = true;
- mSlicedConditionState[hashableKey] = newCondition;
+ if (matchedState < 0) {
+ // The event doesn't match this condition. So we just report existing condition values.
+ conditionChangedCache[mIndex] = false;
+ if (mSliced) {
+ // if the condition result is sliced. metrics won't directly get value from the
+ // cache, so just set any value other than kNotEvaluated.
+ conditionCache[mIndex] = ConditionState::kUnknown;
+ } else if (mSlicedConditionState.find(DEFAULT_DIMENSION_KEY) ==
+ mSlicedConditionState.end()) {
+ // condition not sliced, but we haven't seen the matched start or stop yet. so return
+ // initial value.
+ conditionCache[mIndex] = mInitialValue;
+ } else {
+ // return the cached condition.
+ conditionCache[mIndex] = mSlicedConditionState[DEFAULT_DIMENSION_KEY] > 0
+ ? ConditionState::kTrue
+ : ConditionState::kFalse;
}
- VLOG("key: %s %d", hashableKey.c_str(), newCondition);
- // dump all dimensions for debugging
- if (DEBUG) {
- print(mSlicedConditionState, mName);
- }
+ return;
}
- // even if this SimpleCondition is not sliced, it may be part of a sliced CombinationCondition
- // if the nonSliced condition changed, it may affect the sliced condition in the parent node.
- // so mark the slicedConditionChanged to be true.
- // For example: APP_IN_BACKGROUND_OR_SCREEN_OFF
- // APP_IN_BACKGROUND is sliced [App_A->True, App_B->False].
- // SCREEN_OFF is not sliced, and it changes from False -> True;
- // We need to populate this change to parent condition. Because for App_B,
- // the APP_IN_BACKGROUND_OR_SCREEN_OFF condition would change from False->True.
- slicedConditionChanged[mIndex] = mSliced ? slicedChanged : nonSlicedChanged;
- nonSlicedConditionChanged[mIndex] = nonSlicedChanged;
-
- VLOG("SimpleCondition %s nonSlicedChange? %d SlicedChanged? %d", mName.c_str(),
- nonSlicedConditionChanged[mIndex] == true, slicedConditionChanged[mIndex] == true);
- mNonSlicedConditionState = newCondition;
- conditionCache[mIndex] = mNonSlicedConditionState;
-
- return nonSlicedConditionChanged[mIndex];
+ // outputKey is the output key values. e.g, uid:1234
+ const HashableDimensionKey outputKey = getHashableKey(getDimensionKey(event, mOutputDimension));
+ handleConditionEvent(outputKey, matchedState == 1, conditionCache, conditionChangedCache);
}
void SimpleConditionTracker::isConditionMet(
const map<string, HashableDimensionKey>& conditionParameters,
const vector<sp<ConditionTracker>>& allConditions, vector<ConditionState>& conditionCache) {
const auto pair = conditionParameters.find(mName);
- if (pair == conditionParameters.end()) {
- // the query does not need my sliced condition. just return the non sliced condition.
- conditionCache[mIndex] = mNonSlicedConditionState;
- VLOG("Condition %s return %d", mName.c_str(), mNonSlicedConditionState);
+ HashableDimensionKey key =
+ (pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second;
+
+ if (pair == conditionParameters.end() && mOutputDimension.size() > 0) {
+ ALOGE("Condition %s output has dimension, but it's not specified in the query!",
+ mName.c_str());
+ conditionCache[mIndex] = mInitialValue;
return;
}
- const HashableDimensionKey& key = pair->second;
VLOG("simpleCondition %s query key: %s", mName.c_str(), key.c_str());
- if (mSlicedConditionState.find(key) == mSlicedConditionState.end()) {
- // never seen this key before. the condition is unknown to us.
- conditionCache[mIndex] = ConditionState::kUnknown;
+ auto startedCountIt = mSlicedConditionState.find(key);
+ if (startedCountIt == mSlicedConditionState.end()) {
+ conditionCache[mIndex] = mInitialValue;
} else {
- conditionCache[mIndex] = mSlicedConditionState[key];
+ conditionCache[mIndex] =
+ startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
}
VLOG("Condition %s return %d", mName.c_str(), conditionCache[mIndex]);
-
- if (DEBUG) {
- print(mSlicedConditionState, mName);
- }
}
} // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index b72157b..2eda0b1 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -17,6 +17,7 @@
#ifndef SIMPLE_CONDITION_TRACKER_H
#define SIMPLE_CONDITION_TRACKER_H
+#include <gtest/gtest_prod.h>
#include "ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -38,12 +39,11 @@
const std::unordered_map<std::string, int>& conditionNameIndexMap,
std::vector<bool>& stack) override;
- bool evaluateCondition(const LogEvent& event,
+ void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache,
- std::vector<bool>& slicedChangedCache) override;
+ std::vector<bool>& changedCache) override;
void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
@@ -62,15 +62,22 @@
// The index of the LogEventMatcher which defines the stop all.
int mStopAllLogMatcherIndex;
- // The dimension defines at the atom level, how start and stop should match.
- // e.g., APP_IN_FOREGROUND, the dimension should be the uid field. Each "start" and
- // "stop" tells you the state change of a particular app. Without this dimension, this
- // condition does not make sense.
- std::vector<KeyMatcher> mDimension;
+ ConditionState mInitialValue;
- // Keep the map from the internal HashableDimensionKey to std::vector<KeyValuePair>
- // that StatsLogReport wants.
- std::unordered_map<HashableDimensionKey, ConditionState> mSlicedConditionState;
+ std::vector<KeyMatcher> mOutputDimension;
+
+ std::map<HashableDimensionKey, int> mSlicedConditionState;
+
+ void handleStopAll(std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache);
+
+ void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache);
+
+ FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
+ FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
+ FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
};
} // namespace statsd
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 2618a21..669a4b7 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -86,6 +86,9 @@
case LogicalOperation::NOR:
newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue;
break;
+ case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
+ newCondition = ConditionState::kFalse;
+ break;
}
return newCondition;
}
diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h
index bbf20fd..3489c43 100644
--- a/cmds/statsd/src/config/ConfigKey.h
+++ b/cmds/statsd/src/config/ConfigKey.h
@@ -78,7 +78,7 @@
/**
* A hash function for ConfigKey so it can be used for unordered_map/set.
- * Unfortunately this hast to go in std namespace because C++ is fun!
+ * Unfortunately this has to go in std namespace because C++ is fun!
*/
namespace std {
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 94566ff..2125609 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -18,16 +18,23 @@
#include "stats_util.h"
-#include <vector>
-
+#include <android-base/file.h>
+#include <dirent.h>
#include <stdio.h>
+#include <vector>
+#include "android-base/stringprintf.h"
namespace android {
namespace os {
namespace statsd {
+#define STATS_SERVICE_DIR "/data/system/stats-service"
+
static StatsdConfig build_fake_config();
+using android::base::StringPrintf;
+using std::unique_ptr;
+
ConfigManager::ConfigManager() {
}
@@ -35,11 +42,10 @@
}
void ConfigManager::Startup() {
- // TODO: Implement me -- read from storage and call onto all of the listeners.
- // Instead, we'll just make a fake one.
+ readConfigFromDisk();
// this should be called from StatsService when it receives a statsd_config
- UpdateConfig(ConfigKey(0, "fake"), build_fake_config());
+ UpdateConfig(ConfigKey(1000, "fake"), build_fake_config());
}
void ConfigManager::AddListener(const sp<ConfigListener>& listener) {
@@ -52,7 +58,7 @@
// Why doesn't this work? mConfigs.insert({key, config});
// Save to disk
- update_saved_configs();
+ update_saved_configs(key, config);
// Tell everyone
for (auto& listener : mListeners) {
@@ -60,21 +66,47 @@
}
}
+void ConfigManager::SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls) {
+ mConfigReceivers[key] = pair<string, string>(pkg, cls);
+}
+
+void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
+ mConfigReceivers.erase(key);
+}
+
void ConfigManager::RemoveConfig(const ConfigKey& key) {
unordered_map<ConfigKey, StatsdConfig>::iterator it = mConfigs.find(key);
if (it != mConfigs.end()) {
// Remove from map
mConfigs.erase(it);
- // Save to disk
- update_saved_configs();
-
// Tell everyone
for (auto& listener : mListeners) {
listener->OnConfigRemoved(key);
}
}
- // If we didn't find it, just quietly ignore it.
+
+ // Remove from disk. There can still be a lingering file on disk so we check
+ // whether or not the config was on memory.
+ remove_saved_configs(key);
+}
+
+void ConfigManager::remove_saved_configs(const ConfigKey& key) {
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
+ if (dir == NULL) {
+ ALOGD("no default config on disk");
+ return;
+ }
+ string prefix = StringPrintf("%d-%s", key.GetUid(), key.GetName().c_str());
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] != '.' && strncmp(name, prefix.c_str(), prefix.size()) == 0) {
+ if (remove(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str()) != 0) {
+ ALOGD("no file found");
+ }
+ }
+ }
}
void ConfigManager::RemoveConfigs(int uid) {
@@ -85,6 +117,7 @@
if (it->first.GetUid() == uid) {
removed.push_back(it->first);
it = mConfigs.erase(it);
+ mConfigReceivers.erase(it->first);
} else {
it++;
}
@@ -99,24 +132,107 @@
}
}
+vector<ConfigKey> ConfigManager::GetAllConfigKeys() {
+ vector<ConfigKey> ret;
+ for (auto it = mConfigs.cbegin(); it != mConfigs.cend(); ++it) {
+ ret.push_back(it->first);
+ }
+ return ret;
+}
+
+const pair<string, string> ConfigManager::GetConfigReceiver(const ConfigKey& key) {
+ auto it = mConfigReceivers.find(key);
+ if (it == mConfigReceivers.end()) {
+ return pair<string,string>();
+ } else {
+ return it->second;
+ }
+}
+
void ConfigManager::Dump(FILE* out) {
fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size());
fprintf(out, " uid name\n");
for (unordered_map<ConfigKey, StatsdConfig>::const_iterator it = mConfigs.begin();
it != mConfigs.end(); it++) {
fprintf(out, " %6d %s\n", it->first.GetUid(), it->first.GetName().c_str());
+ auto receiverIt = mConfigReceivers.find(it->first);
+ if (receiverIt != mConfigReceivers.end()) {
+ fprintf(out, " -> received by %s, %s\n", receiverIt->second.first.c_str(),
+ receiverIt->second.second.c_str());
+ }
// TODO: Print the contents of the config too.
}
}
-void ConfigManager::update_saved_configs() {
- // TODO: Implement me -- write to disk.
+void ConfigManager::readConfigFromDisk() {
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
+ if (dir == NULL) {
+ ALOGD("no default config on disk");
+ return;
+ }
+
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] == '.') continue;
+ ALOGD("file %s", name);
+
+ int index = 0;
+ int uid = 0;
+ string configName;
+ char* substr = strtok(name, "-");
+ // Timestamp lives at index 2 but we skip parsing it as it's not needed.
+ while (substr != nullptr && index < 2) {
+ if (index) {
+ uid = atoi(substr);
+ } else {
+ configName = substr;
+ }
+ index++;
+ }
+ if (index < 2) continue;
+ string file_name = StringPrintf("%s/%s", STATS_SERVICE_DIR, name);
+ ALOGD("full file %s", file_name.c_str());
+ int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd != -1) {
+ string content;
+ if (android::base::ReadFdToString(fd, &content)) {
+ StatsdConfig config;
+ if (config.ParseFromString(content)) {
+ mConfigs[ConfigKey(uid, configName)] = config;
+ ALOGD("map key uid=%d|name=%s", uid, name);
+ }
+ }
+ close(fd);
+ }
+ }
+}
+
+void ConfigManager::update_saved_configs(const ConfigKey& key, const StatsdConfig& config) {
+ mkdir(STATS_SERVICE_DIR, S_IRWXU);
+
+ // If there is a pre-existing config with same key we should first delete it.
+ remove_saved_configs(key);
+
+ // Then we save the latest config.
+ string file_name = StringPrintf("%s/%d-%s-%ld", STATS_SERVICE_DIR, key.GetUid(),
+ key.GetName().c_str(), time(nullptr));
+ int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+ if (fd != -1) {
+ const int numBytes = config.ByteSize();
+ vector<uint8_t> buffer(numBytes);
+ config.SerializeToArray(&buffer[0], numBytes);
+ int result = write(fd, &buffer[0], numBytes);
+ close(fd);
+ bool wroteKey = (result == numBytes);
+ ALOGD("wrote to file %d", wroteKey);
+ }
}
static StatsdConfig build_fake_config() {
// HACK: Hard code a test metric for counting screen on events...
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
int WAKE_LOCK_TAG_ID = 1111; // put a fake id here to make testing easier.
int WAKE_LOCK_UID_KEY_ID = 1;
@@ -147,33 +263,35 @@
// Count Screen ON events.
CountMetric* metric = config.add_count_metric();
- metric->set_metric_id(1);
+ metric->set_name("1");
metric->set_what("SCREEN_TURNED_ON");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
// Anomaly threshold for screen-on count.
- Alert* alert = metric->add_alerts();
+ Alert* alert = config.add_alert();
+ alert->set_name("1");
alert->set_number_of_buckets(6);
alert->set_trigger_if_sum_gt(10);
alert->set_refractory_period_secs(30);
// Count process state changes, slice by uid.
metric = config.add_count_metric();
- metric->set_metric_id(2);
+ metric->set_name("2");
metric->set_what("PROCESS_STATE_CHANGE");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
KeyMatcher* keyMatcher = metric->add_dimension();
keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
// Anomaly threshold for background count.
- alert = metric->add_alerts();
+ alert = config.add_alert();
+ alert->set_name("2");
alert->set_number_of_buckets(4);
alert->set_trigger_if_sum_gt(30);
alert->set_refractory_period_secs(20);
// Count process state changes, slice by uid, while SCREEN_IS_OFF
metric = config.add_count_metric();
- metric->set_metric_id(3);
+ metric->set_name("3");
metric->set_what("PROCESS_STATE_CHANGE");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
keyMatcher = metric->add_dimension();
@@ -182,7 +300,7 @@
// Count wake lock, slice by uid, while SCREEN_IS_ON and app in background
metric = config.add_count_metric();
- metric->set_metric_id(4);
+ metric->set_name("4");
metric->set_what("APP_GET_WL");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
keyMatcher = metric->add_dimension();
@@ -195,13 +313,13 @@
// Duration of an app holding any wl, while screen on and app in background, slice by uid
DurationMetric* durationMetric = config.add_duration_metric();
- durationMetric->set_metric_id(5);
+ durationMetric->set_name("5");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
keyMatcher = durationMetric->add_dimension();
keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
- durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
- durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
+ durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
+ durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
@@ -209,13 +327,13 @@
// max Duration of an app holding any wl, while screen on and app in background, slice by uid
durationMetric = config.add_duration_metric();
- durationMetric->set_metric_id(6);
+ durationMetric->set_name("6");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
keyMatcher = durationMetric->add_dimension();
keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
- durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
- durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
+ durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
+ durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
@@ -223,11 +341,11 @@
// Duration of an app holding any wl, while screen on and app in background
durationMetric = config.add_duration_metric();
- durationMetric->set_metric_id(7);
+ durationMetric->set_name("7");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
- durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
- durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
+ durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
+ durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
@@ -235,14 +353,14 @@
// Duration of screen on time.
durationMetric = config.add_duration_metric();
- durationMetric->set_metric_id(8);
+ durationMetric->set_name("8");
durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L);
- durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
durationMetric->set_what("SCREEN_IS_ON");
// Value metric to count KERNEL_WAKELOCK when screen turned on
ValueMetric* valueMetric = config.add_value_metric();
- valueMetric->set_metric_id(6);
+ valueMetric->set_name("6");
valueMetric->set_what("KERNEL_WAKELOCK");
valueMetric->set_value_field(1);
valueMetric->set_condition("SCREEN_IS_ON");
@@ -253,12 +371,12 @@
// Add an EventMetric to log process state change events.
EventMetric* eventMetric = config.add_event_metric();
- eventMetric->set_metric_id(9);
+ eventMetric->set_name("9");
eventMetric->set_what("SCREEN_TURNED_ON");
// Add an GaugeMetric.
GaugeMetric* gaugeMetric = config.add_gauge_metric();
- gaugeMetric->set_metric_id(10);
+ gaugeMetric->set_name("10");
gaugeMetric->set_what("DEVICE_TEMPERATURE");
gaugeMetric->set_gauge_field(DEVICE_TEMPERATURE_KEY);
gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
@@ -334,12 +452,14 @@
SimpleCondition* simpleCondition = condition->mutable_simple_condition();
simpleCondition->set_start("SCREEN_TURNED_ON");
simpleCondition->set_stop("SCREEN_TURNED_OFF");
+ simpleCondition->set_count_nesting(false);
condition = config.add_condition();
condition->set_name("SCREEN_IS_OFF");
simpleCondition = condition->mutable_simple_condition();
simpleCondition->set_start("SCREEN_TURNED_OFF");
simpleCondition->set_stop("SCREEN_TURNED_ON");
+ simpleCondition->set_count_nesting(false);
condition = config.add_condition();
condition->set_name("APP_IS_BACKGROUND");
@@ -348,6 +468,7 @@
simpleCondition->set_stop("APP_GOES_FOREGROUND");
KeyMatcher* condition_dimension1 = simpleCondition->add_dimension();
condition_dimension1->set_key(APP_USAGE_UID_KEY_ID);
+ simpleCondition->set_count_nesting(false);
condition = config.add_condition();
condition->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
@@ -357,7 +478,7 @@
combination_condition->add_condition("SCREEN_IS_ON");
condition = config.add_condition();
- condition->set_name("WL_STATE_PER_APP_PER_NAME");
+ condition->set_name("WL_HELD_PER_APP_PER_NAME");
simpleCondition = condition->mutable_simple_condition();
simpleCondition->set_start("APP_GET_WL");
simpleCondition->set_stop("APP_RELEASE_WL");
@@ -365,6 +486,17 @@
condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
condition_dimension = simpleCondition->add_dimension();
condition_dimension->set_key(WAKE_LOCK_NAME_KEY);
+ simpleCondition->set_count_nesting(true);
+
+ condition = config.add_condition();
+ condition->set_name("WL_HELD_PER_APP");
+ simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("APP_GET_WL");
+ simpleCondition->set_stop("APP_RELEASE_WL");
+ simpleCondition->set_initial_value(SimpleCondition_InitialValue_FALSE);
+ condition_dimension = simpleCondition->add_dimension();
+ condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+ simpleCondition->set_count_nesting(true);
return config;
}
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 5d73eaf..01d7fb9 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -32,6 +32,7 @@
using std::string;
using std::unordered_map;
using std::vector;
+using std::pair;
/**
* Keeps track of which configurations have been set from various sources.
@@ -45,9 +46,7 @@
virtual ~ConfigManager();
/**
- * Call to load the saved configs from disk.
- *
- * TODO: Implement me
+ * Initialize ConfigListener by reading from disk and get updates.
*/
void Startup();
@@ -64,6 +63,26 @@
void UpdateConfig(const ConfigKey& key, const StatsdConfig& data);
/**
+ * Sets the broadcast receiver for a configuration key.
+ */
+ void SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls);
+
+ /**
+ * Returns the package name and class name representing the broadcast receiver for this config.
+ */
+ const pair<string, string> GetConfigReceiver(const ConfigKey& key);
+
+ /**
+ * Returns all config keys registered.
+ */
+ vector<ConfigKey> GetAllConfigKeys();
+
+ /**
+ * Erase any broadcast receiver associated with this config key.
+ */
+ void RemoveConfigReceiver(const ConfigKey& key);
+
+ /**
* A configuration was removed.
*
* Reports this to listeners.
@@ -84,17 +103,33 @@
/**
* Save the configs to disk.
*/
- void update_saved_configs();
+ void update_saved_configs(const ConfigKey& key, const StatsdConfig& config);
/**
- * The Configs that have been set
+ * Remove saved configs from disk.
+ */
+ void remove_saved_configs(const ConfigKey& key);
+
+ /**
+ * The Configs that have been set. Each config should
*/
unordered_map<ConfigKey, StatsdConfig> mConfigs;
/**
+ * Each config key can be subscribed by up to one receiver, specified as the package name and
+ * class name.
+ */
+ unordered_map<ConfigKey, pair<string, string>> mConfigReceivers;
+
+ /**
* The ConfigListeners that will be told about changes.
*/
vector<sp<ConfigListener>> mListeners;
+
+ /**
+ * Call to load the saved configs from disk.
+ */
+ void readConfigFromDisk();
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
new file mode 100644
index 0000000..e2745d2
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+#include "external/CpuTimePerUidFreqPuller.h"
+
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_cputime/show_uid_stat";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_time_in_state which has the format:
+ *
+ * uid: [freq1] [freq2] [freq3] ...
+ * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
+ * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
+ * ...
+ *
+ * This provides the times a UID's processes spent executing at each different cpu frequency.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+bool CpuTimePerUidFreqPuller::Pull(const int tagId, vector<shared_ptr<LogEvent>>* data) {
+ data->clear();
+
+ ifstream fin;
+ fin.open(sProcFile);
+ if (!fin.good()) {
+ VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+ char buf[kLineBufferSize];
+ // first line prints the format and frequencies
+ fin.getline(buf, kLineBufferSize);
+ char * pch;
+ while(!fin.eof()){
+ fin.getline(buf, kLineBufferSize);
+ pch = strtok (buf, " :");
+ if (pch == NULL) break;
+ uint64_t uid = std::stoull(pch);
+ pch = strtok(NULL, " ");
+ uint64_t timeMs;
+ int idx = 0;
+ do {
+ timeMs = std::stoull(pch);
+ auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_FREQ_PULLED, timestamp);
+ ptr->write(uid);
+ ptr->write(idx);
+ ptr->write(timeMs);
+ ptr->init();
+ data->push_back(ptr);
+ VLOG("uid %lld, freq idx %d, sys time %lld", (long long)uid, idx, (long long)timeMs);
+ idx ++;
+ pch = strtok(NULL, " ");
+ } while (pch != NULL);
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h
new file mode 100644
index 0000000..839e5aa
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+class CpuTimePerUidFreqPuller : public StatsPuller {
+ public:
+ bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
new file mode 100644
index 0000000..e0572dc
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+#include "external/CpuTimePerUidPuller.h"
+
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_cputime/show_uid_stat";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+bool CpuTimePerUidPuller::Pull(const int tagId, vector<shared_ptr<LogEvent>>* data) {
+ data->clear();
+
+ ifstream fin;
+ fin.open(sProcFile);
+ if (!fin.good()) {
+ VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+ char buf[kLineBufferSize];
+ char * pch;
+ while(!fin.eof()){
+ fin.getline(buf, kLineBufferSize);
+ pch = strtok(buf, " :");
+ if (pch == NULL) break;
+ uint64_t uid = std::stoull(pch);
+ pch = strtok(buf, " ");
+ uint64_t userTimeMs = std::stoull(pch);
+ pch = strtok(buf, " ");
+ uint64_t sysTimeMs = std::stoull(pch);
+
+ auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_PULLED, timestamp);
+ ptr->write(uid);
+ ptr->write(userTimeMs);
+ ptr->write(sysTimeMs);
+ ptr->init();
+ data->push_back(ptr);
+ VLOG("uid %lld, user time %lld, sys time %lld", (long long)uid, (long long)userTimeMs, (long long)sysTimeMs);
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.h b/cmds/statsd/src/external/CpuTimePerUidPuller.h
new file mode 100644
index 0000000..9bb8946
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+class CpuTimePerUidPuller : public StatsPuller {
+ public:
+ bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
index 3608ee4..3ee636d 100644
--- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
@@ -34,6 +34,7 @@
#include "external/StatsPuller.h"
#include "logd/LogEvent.h"
+#include "statslog.h"
using android::hardware::hidl_vec;
using android::hardware::power::V1_0::IPower;
@@ -57,10 +58,6 @@
std::mutex gPowerHalMutex;
bool gPowerHalExists = true;
-static const int power_state_platform_sleep_state_tag = 1011;
-static const int power_state_voter_tag = 1012;
-static const int power_state_subsystem_state_tag = 1013;
-
bool getPowerHal() {
if (gPowerHalExists && gPowerHalV1_0 == nullptr) {
gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
@@ -94,25 +91,25 @@
for (size_t i = 0; i < states.size(); i++) {
const PowerStatePlatformSleepState& state = states[i];
- auto statePtr =
- make_shared<LogEvent>(power_state_platform_sleep_state_tag, timestamp);
- auto elemList = statePtr->GetAndroidLogEventList();
- *elemList << state.name;
- *elemList << state.residencyInMsecSinceBoot;
- *elemList << state.totalTransitions;
- *elemList << state.supportedOnlyInSuspend;
+ auto statePtr = make_shared<LogEvent>(
+ android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, timestamp);
+ statePtr->write(state.name);
+ statePtr->write(state.residencyInMsecSinceBoot);
+ statePtr->write(state.totalTransitions);
+ statePtr->write(state.supportedOnlyInSuspend);
statePtr->init();
data->push_back(statePtr);
VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
(long long)state.residencyInMsecSinceBoot,
(long long)state.totalTransitions, state.supportedOnlyInSuspend ? 1 : 0);
for (auto voter : state.voters) {
- auto voterPtr = make_shared<LogEvent>(power_state_voter_tag, timestamp);
- auto elemList = voterPtr->GetAndroidLogEventList();
- *elemList << state.name;
- *elemList << voter.name;
- *elemList << voter.totalTimeInMsecVotedForSinceBoot;
- *elemList << voter.totalNumberOfTimesVotedSinceBoot;
+ auto voterPtr =
+ make_shared<LogEvent>(android::util::POWER_STATE_VOTER_PULLED, timestamp);
+ voterPtr->write(state.name);
+ voterPtr->write(voter.name);
+ voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
+ voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot);
+ voterPtr->init();
data->push_back(voterPtr);
VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
voter.name.c_str(), (long long)voter.totalTimeInMsecVotedForSinceBoot,
@@ -141,14 +138,13 @@
for (size_t j = 0; j < subsystem.states.size(); j++) {
const PowerStateSubsystemSleepState& state = subsystem.states[j];
auto subsystemStatePtr = make_shared<LogEvent>(
- power_state_subsystem_state_tag, timestamp);
- auto elemList = subsystemStatePtr->GetAndroidLogEventList();
- *elemList << subsystem.name;
- *elemList << state.name;
- *elemList << state.residencyInMsecSinceBoot;
- *elemList << state.totalTransitions;
- *elemList << state.lastEntryTimestampMs;
- *elemList << state.supportedOnlyInSuspend;
+ android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, timestamp);
+ subsystemStatePtr->write(subsystem.name);
+ subsystemStatePtr->write(state.name);
+ subsystemStatePtr->write(state.residencyInMsecSinceBoot);
+ subsystemStatePtr->write(state.totalTransitions);
+ subsystemStatePtr->write(state.lastEntryTimestampMs);
+ subsystemStatePtr->write(state.supportedOnlyInSuspend);
subsystemStatePtr->init();
data->push_back(subsystemStatePtr);
VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 67580d6..2e803c9 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -16,71 +16,39 @@
#pragma once
-#include <android/os/IStatsCompanionService.h>
-#include <binder/IServiceManager.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-#include <utils/String8.h>
-#include <utils/threads.h>
-#include <string>
-#include <unordered_map>
-#include <vector>
-#include "PullDataReceiver.h"
-#include "StatsPuller.h"
-#include "logd/LogEvent.h"
+#include "StatsPullerManagerImpl.h"
namespace android {
namespace os {
namespace statsd {
-class StatsPullerManager : public virtual RefBase {
-public:
- static StatsPullerManager& GetInstance();
+class StatsPullerManager{
+ public:
+ virtual ~StatsPullerManager() {}
- void RegisterReceiver(int tagId, sp<PullDataReceiver> receiver, long intervalMs);
+ virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, long intervalMs) {
+ mPullerManager.RegisterReceiver(tagId, receiver, intervalMs);
+ };
- void UnRegisterReceiver(int tagId, sp<PullDataReceiver> receiver);
+ virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
+ mPullerManager.UnRegisterReceiver(tagId, receiver);
+ };
- // Verify if we know how to pull for this matcher
- bool PullerForMatcherExists(int tagId);
+ // Verify if we know how to pull for this matcher
+ bool PullerForMatcherExists(int tagId) {
+ return mPullerManager.PullerForMatcherExists(tagId);
+ }
- void OnAlarmFired();
+ void OnAlarmFired() {
+ mPullerManager.OnAlarmFired();
+ }
- bool Pull(const int pullCode, vector<std::shared_ptr<LogEvent>>* data);
+ virtual bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ return mPullerManager.Pull(tagId, data);
+ }
-private:
- StatsPullerManager();
-
- // use this to update alarm
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
-
- sp<IStatsCompanionService> get_stats_companion_service();
-
- // mapping from simple matcher tagId to puller
- std::map<int, std::shared_ptr<StatsPuller>> mPullers;
-
- typedef struct {
- // pull_interval_sec : last_pull_time_sec
- std::pair<uint64_t, uint64_t> timeInfo;
- sp<PullDataReceiver> receiver;
- } ReceiverInfo;
-
- // mapping from simple matcher tagId to receivers
- std::map<int, std::vector<ReceiverInfo>> mReceivers;
-
- Mutex mReceiversLock;
-
- long mCurrentPullingInterval;
-
- // for pulled metrics, it is important for the buckets to be aligned to multiple of smallest
- // bucket size. All pulled metrics start pulling based on this time, so that they can be
- // correctly attributed to the correct buckets. Pulled data attach a timestamp which is the
- // request time.
- const long mPullStartTimeMs;
-
- long get_pull_start_time_ms();
-
- LogEvent parse_pulled_data(String16 data);
+ private:
+ StatsPullerManagerImpl& mPullerManager = StatsPullerManagerImpl::GetInstance();
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
similarity index 62%
rename from cmds/statsd/src/external/StatsPullerManager.cpp
rename to cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 43543cc..07d0b3e 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -21,9 +21,11 @@
#include <cutils/log.h>
#include <algorithm>
#include <climits>
+#include "CpuTimePerUidFreqPuller.h"
+#include "CpuTimePerUidPuller.h"
#include "ResourcePowerManagerPuller.h"
#include "StatsCompanionServicePuller.h"
-#include "StatsPullerManager.h"
+#include "StatsPullerManagerImpl.h"
#include "StatsService.h"
#include "logd/LogEvent.h"
#include "statslog.h"
@@ -35,33 +37,42 @@
using std::shared_ptr;
using std::string;
using std::vector;
+using std::list;
namespace android {
namespace os {
namespace statsd {
-StatsPullerManager::StatsPullerManager()
+StatsPullerManagerImpl::StatsPullerManagerImpl()
: mCurrentPullingInterval(LONG_MAX), mPullStartTimeMs(get_pull_start_time_ms()) {
- shared_ptr<StatsPuller> statsCompanionServicePuller =
- make_shared<StatsCompanionServicePuller>();
+ shared_ptr<StatsPuller> statsCompanionServicePuller = make_shared<StatsCompanionServicePuller>();
shared_ptr<StatsPuller> resourcePowerManagerPuller = make_shared<ResourcePowerManagerPuller>();
+ shared_ptr<StatsPuller> cpuTimePerUidPuller = make_shared<CpuTimePerUidPuller>();
+ shared_ptr<StatsPuller> cpuTimePerUidFreqPuller = make_shared<CpuTimePerUidFreqPuller>();
- mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED, statsCompanionServicePuller});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED, statsCompanionServicePuller});
- mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED, statsCompanionServicePuller});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG, statsCompanionServicePuller});
- mPullers.insert(
- {android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG, statsCompanionServicePuller});
- mPullers.insert(
- {android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, resourcePowerManagerPuller});
- mPullers.insert({android::util::POWER_STATE_VOTER_PULLED, resourcePowerManagerPuller});
- mPullers.insert(
- {android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, resourcePowerManagerPuller});
+ mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED,
+ statsCompanionServicePuller});
+ mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED,
+ statsCompanionServicePuller});
+ mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED,
+ statsCompanionServicePuller});
+ mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG,
+ statsCompanionServicePuller});
+ mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG,
+ statsCompanionServicePuller});
+ mPullers.insert({android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED,
+ resourcePowerManagerPuller});
+ mPullers.insert({android::util::POWER_STATE_VOTER_PULLED,
+ resourcePowerManagerPuller});
+ mPullers.insert({android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED,
+ resourcePowerManagerPuller});
+ mPullers.insert({android::util::CPU_TIME_PER_UID_PULLED, cpuTimePerUidPuller});
+ mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ_PULLED, cpuTimePerUidFreqPuller});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
-bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
+bool StatsPullerManagerImpl::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
if (DEBUG) ALOGD("Initiating pulling %d", tagId);
if (mPullers.find(tagId) != mPullers.end()) {
@@ -72,26 +83,26 @@
}
}
-StatsPullerManager& StatsPullerManager::GetInstance() {
- static StatsPullerManager instance;
+StatsPullerManagerImpl& StatsPullerManagerImpl::GetInstance() {
+ static StatsPullerManagerImpl instance;
return instance;
}
-bool StatsPullerManager::PullerForMatcherExists(int tagId) {
+bool StatsPullerManagerImpl::PullerForMatcherExists(int tagId) {
return mPullers.find(tagId) != mPullers.end();
}
-long StatsPullerManager::get_pull_start_time_ms() {
+long StatsPullerManagerImpl::get_pull_start_time_ms() {
// TODO: limit and align pull intervals to 10min boundaries if this turns out to be a problem
return time(nullptr) * 1000;
}
-void StatsPullerManager::RegisterReceiver(int tagId, sp<PullDataReceiver> receiver,
- long intervalMs) {
+void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
+ long intervalMs) {
AutoMutex _l(mReceiversLock);
- vector<ReceiverInfo>& receivers = mReceivers[tagId];
+ auto& receivers = mReceivers[tagId];
for (auto it = receivers.begin(); it != receivers.end(); it++) {
- if (it->receiver.get() == receiver.get()) {
+ if (it->receiver == receiver) {
VLOG("Receiver already registered of %d", (int)receivers.size());
return;
}
@@ -114,7 +125,7 @@
VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
}
-void StatsPullerManager::UnRegisterReceiver(int tagId, sp<PullDataReceiver> receiver) {
+void StatsPullerManagerImpl::UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
AutoMutex _l(mReceiversLock);
if (mReceivers.find(tagId) == mReceivers.end()) {
VLOG("Unknown pull code or no receivers: %d", tagId);
@@ -122,7 +133,7 @@
}
auto& receivers = mReceivers.find(tagId)->second;
for (auto it = receivers.begin(); it != receivers.end(); it++) {
- if (receiver.get() == it->receiver.get()) {
+ if (receiver == it->receiver) {
receivers.erase(it);
VLOG("Puller for tagId %d unregistered of %d", tagId, (int)receivers.size());
return;
@@ -130,7 +141,7 @@
}
}
-void StatsPullerManager::OnAlarmFired() {
+void StatsPullerManagerImpl::OnAlarmFired() {
AutoMutex _l(mReceiversLock);
uint64_t currentTimeMs = time(nullptr) * 1000;
@@ -155,8 +166,13 @@
vector<shared_ptr<LogEvent>> data;
if (Pull(pullInfo.first, &data)) {
for (const auto& receiverInfo : pullInfo.second) {
- receiverInfo->receiver->onDataPulled(data);
- receiverInfo->timeInfo.second = currentTimeMs;
+ sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
+ if (receiverPtr != nullptr) {
+ receiverPtr->onDataPulled(data);
+ receiverInfo->timeInfo.second = currentTimeMs;
+ } else {
+ VLOG("receiver already gone.");
+ }
}
}
}
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
new file mode 100644
index 0000000..0b9f21e
--- /dev/null
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/os/IStatsCompanionService.h>
+#include <binder/IServiceManager.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include <list>
+#include "PullDataReceiver.h"
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StatsPullerManagerImpl : public virtual RefBase {
+public:
+ static StatsPullerManagerImpl& GetInstance();
+
+ void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, long intervalMs);
+
+ void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
+
+ // Verify if we know how to pull for this matcher
+ bool PullerForMatcherExists(int tagId);
+
+ void OnAlarmFired();
+
+ bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data);
+
+private:
+ StatsPullerManagerImpl();
+
+ // use this to update alarm
+ sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+
+ sp<IStatsCompanionService> get_stats_companion_service();
+
+ // mapping from simple matcher tagId to puller
+ std::map<int, std::shared_ptr<StatsPuller>> mPullers;
+
+ typedef struct {
+ // pull_interval_sec : last_pull_time_sec
+ std::pair<uint64_t, uint64_t> timeInfo;
+ wp<PullDataReceiver> receiver;
+ } ReceiverInfo;
+
+ // mapping from simple matcher tagId to receivers
+ std::map<int, std::list<ReceiverInfo>> mReceivers;
+
+ Mutex mReceiversLock;
+
+ long mCurrentPullingInterval;
+
+ // for pulled metrics, it is important for the buckets to be aligned to multiple of smallest
+ // bucket size. All pulled metrics start pulling based on this time, so that they can be
+ // correctly attributed to the correct buckets. Pulled data attach a timestamp which is the
+ // request time.
+ const long mPullStartTimeMs;
+
+ long get_pull_start_time_ms();
+
+ LogEvent parse_pulled_data(String16 data);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 913b906..1032138 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -29,21 +29,71 @@
using std::string;
using android::util::ProtoOutputStream;
-// We need to keep a copy of the android_log_event_list owned by this instance so that the char*
-// for strings is not cleared before we can read them.
-LogEvent::LogEvent(log_msg& msg) : mList(msg) {
- init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList);
+LogEvent::LogEvent(log_msg& msg) {
+ mContext =
+ create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
+ mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
+ init(mContext);
}
-LogEvent::LogEvent(int tag, uint64_t timestampNs) : mList(tag), mTimestampNs(timestampNs) {
-}
-
-LogEvent::~LogEvent() {
+LogEvent::LogEvent(int32_t tagId, uint64_t timestampNs) {
+ mTimestampNs = timestampNs;
+ mTagId = tagId;
+ mContext = create_android_logger(1937006964); // the event tag shared by all stats logs
+ if (mContext) {
+ android_log_write_int32(mContext, tagId);
+ }
}
void LogEvent::init() {
- mList.convert_to_reader();
- init(mTimestampNs, &mList);
+ if (mContext) {
+ const char* buffer;
+ size_t len = android_log_write_list_buffer(mContext, &buffer);
+ // turns to reader mode
+ mContext = create_android_log_parser(buffer, len);
+ init(mContext);
+ }
+}
+
+bool LogEvent::write(int32_t value) {
+ if (mContext) {
+ return android_log_write_int32(mContext, value) >= 0;
+ }
+ return false;
+}
+
+bool LogEvent::write(uint32_t value) {
+ if (mContext) {
+ return android_log_write_int32(mContext, value) >= 0;
+ }
+ return false;
+}
+
+bool LogEvent::write(uint64_t value) {
+ if (mContext) {
+ return android_log_write_int64(mContext, value) >= 0;
+ }
+ return false;
+}
+
+bool LogEvent::write(const string& value) {
+ if (mContext) {
+ return android_log_write_string8_len(mContext, value.c_str(), value.length()) >= 0;
+ }
+ return false;
+}
+
+bool LogEvent::write(float value) {
+ if (mContext) {
+ return android_log_write_float32(mContext, value) >= 0;
+ }
+ return false;
+}
+
+LogEvent::~LogEvent() {
+ if (mContext) {
+ android_log_destroy(&mContext);
+ }
}
/**
@@ -51,22 +101,25 @@
* The goal is to do as little preprocessing as possible, because we read a tiny fraction
* of the elements that are written to the log.
*/
-void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) {
- mTimestampNs = timestampNs;
- mTagId = reader->tag();
-
+void LogEvent::init(android_log_context context) {
mElements.clear();
android_log_list_element elem;
-
// TODO: The log is actually structured inside one list. This is convenient
// because we'll be able to use it to put the attribution (WorkSource) block first
// without doing our own tagging scheme. Until that change is in, just drop the
// list-related log elements and the order we get there is our index-keyed data
// structure.
+ int i = 0;
do {
- elem = android_log_read_next(reader->context());
+ elem = android_log_read_next(context);
switch ((int)elem.type) {
case EVENT_TYPE_INT:
+ // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id. If we add WorkSource, it would
+ // be the list starting at [2].
+ if (i == 1) {
+ mTagId = elem.data.int32;
+ break;
+ }
case EVENT_TYPE_FLOAT:
case EVENT_TYPE_STRING:
case EVENT_TYPE_LONG:
@@ -81,13 +134,10 @@
default:
break;
}
+ i++;
} while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
}
-android_log_event_list* LogEvent::GetAndroidLogEventList() {
- return &mList;
-}
-
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
if (key < 1 || (key - 1) >= mElements.size()) {
*err = BAD_INDEX;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 2984940..7e8a96b 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -21,6 +21,7 @@
#include <android/util/ProtoOutputStream.h>
#include <log/log_event_list.h>
#include <log/log_read.h>
+#include <private/android_logger.h>
#include <utils/Errors.h>
#include <memory>
@@ -45,12 +46,9 @@
explicit LogEvent(log_msg& msg);
/**
- * Constructs a LogEvent with the specified tag and creates an android_log_event_list in write
- * mode. Obtain this list with the getter. Make sure to call init() before attempting to read
- * any of the values. This constructor is useful for unit-testing since we can't pass in an
- * android_log_event_list since there is no copy constructor or assignment operator available.
+ * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
*/
- explicit LogEvent(int tag, uint64_t timestampNs);
+ explicit LogEvent(int32_t tagId, uint64_t timestampNs);
~LogEvent();
@@ -76,6 +74,17 @@
float GetFloat(size_t key, status_t* err) const;
/**
+ * Write test data to the LogEvent. This can only be used when the LogEvent is constructed
+ * using LogEvent(tagId, timestampNs). You need to call init() before you can read from it.
+ */
+ bool write(uint32_t value);
+ bool write(int32_t value);
+ bool write(uint64_t value);
+ bool write(int64_t value);
+ bool write(const string& value);
+ bool write(float value);
+
+ /**
* Return a string representation of this event.
*/
string ToString() const;
@@ -91,13 +100,6 @@
KeyValuePair GetKeyValueProto(size_t key) const;
/**
- * A pointer to the contained log_event_list.
- *
- * @return The android_log_event_list contained within.
- */
- android_log_event_list* GetAndroidLogEventList();
-
- /**
* Used with the constructor where tag is passed in. Converts the log_event_list to read mode
* and prepares the list for reading.
*/
@@ -113,16 +115,11 @@
/**
* Parses a log_msg into a LogEvent object.
*/
- void init(const log_msg& msg);
-
- /**
- * Parses a log_msg into a LogEvent object.
- */
- void init(int64_t timestampNs, android_log_event_list* reader);
+ void init(android_log_context context);
vector<android_log_list_element> mElements;
- // Need a copy of the android_log_event_list so the strings are not cleared.
- android_log_event_list mList;
+
+ android_log_context mContext;
uint64_t mTimestampNs;
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index a740280..41b24bc 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index cccc9b3..f7352cd 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -84,6 +84,9 @@
}
}
break;
+ case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
+ matched = false;
+ break;
}
return matched;
}
@@ -112,6 +115,7 @@
if (err == NO_ERROR && val != NULL) {
if (!(cur.eq_string() == val)) {
allMatched = false;
+ break;
}
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt ||
@@ -126,26 +130,30 @@
if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt) {
if (!(val == cur.eq_int())) {
allMatched = false;
+ break;
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtInt) {
if (!(val < cur.lt_int())) {
allMatched = false;
+ break;
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtInt) {
if (!(val > cur.gt_int())) {
allMatched = false;
+ break;
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLteInt) {
if (!(val <= cur.lte_int())) {
allMatched = false;
+ break;
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGteInt) {
if (!(val >= cur.gte_int())) {
allMatched = false;
+ break;
}
}
}
- break;
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqBool) {
// Boolean fields
status_t err = NO_ERROR;
@@ -153,21 +161,24 @@
if (err == NO_ERROR) {
if (!(cur.eq_bool() == val)) {
allMatched = false;
+ break;
}
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat ||
matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
// Float fields
status_t err = NO_ERROR;
- bool val = event.GetFloat(key, &err);
+ float val = event.GetFloat(key, &err);
if (err == NO_ERROR) {
if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat) {
- if (!(cur.lt_float() <= val)) {
+ if (!(val < cur.lt_float())) {
allMatched = false;
+ break;
}
} else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
- if (!(cur.gt_float() >= val)) {
+ if (!(val > cur.gt_float())) {
allMatched = false;
+ break;
}
}
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 0c9e522..d47bd4f 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -17,18 +17,19 @@
#define DEBUG true // STOPSHIP if true
#include "Log.h"
-#include "../anomaly/DiscreteAnomalyTracker.h"
#include "CountMetricProducer.h"
#include "stats_util.h"
#include <limits.h>
#include <stdlib.h>
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::map;
using std::string;
@@ -40,7 +41,7 @@
namespace statsd {
// for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
const int FIELD_ID_START_REPORT_NANOS = 2;
const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_COUNT_METRICS = 5;
@@ -73,17 +74,6 @@
mBucketSizeNs = LLONG_MAX;
}
- mAnomalyTrackers.reserve(metric.alerts_size());
- for (int i = 0; i < metric.alerts_size(); i++) {
- const Alert& alert = metric.alerts(i);
- if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) {
- mAnomalyTrackers.push_back(std::make_unique<DiscreteAnomalyTracker>(alert));
- } else {
- ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d",
- alert.trigger_if_sum_gt(), alert.number_of_buckets());
- }
- }
-
// TODO: use UidMap if uid->pkg_name is required
mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
@@ -95,7 +85,7 @@
startNewProtoOutputStream(mStartTimeNs);
- VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -105,7 +95,7 @@
void CountMetricProducer::startNewProtoOutputStream(long long startTime) {
mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
}
@@ -114,7 +104,7 @@
}
void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+ VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
}
std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() {
@@ -123,8 +113,8 @@
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
- flushCounterIfNeeded(endTime);
- VLOG("metric %lld dump report now...", mMetric.metric_id());
+ flushIfNeeded(endTime);
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
for (const auto& counter : mPastBuckets) {
const HashableDimensionKey& hashableKey = counter.first;
@@ -134,11 +124,13 @@
ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
continue;
}
- long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+ long long wrapperToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+ long long dimensionToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -154,7 +146,8 @@
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
- long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+ long long bucketInfoToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)bucket.mBucketStartNs);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -171,12 +164,11 @@
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
- VLOG("metric %lld dump report now...", mMetric.metric_id());
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
mPastBuckets.clear();
- mByteSize = 0;
return buffer;
@@ -184,7 +176,7 @@
}
void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
- VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+ VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
mCondition = conditionMet;
}
@@ -194,7 +186,7 @@
const LogEvent& event, bool scheduledPull) {
uint64_t eventTimeNs = event.GetTimestampNs();
- flushCounterIfNeeded(eventTimeNs);
+ flushIfNeeded(eventTimeNs);
if (condition == false) {
return;
@@ -211,44 +203,44 @@
count++;
}
- VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
- (*mCurrentSlicedCounter)[eventKey]);
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
+ mCurrentSlicedCounter->find(eventKey)->second);
+ }
+
+ VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(),
+ (long long)(*mCurrentSlicedCounter)[eventKey]);
}
// When a new matched event comes in, we check if event falls into the current
// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
-void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) {
- if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
+void CountMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
+ if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
return;
}
- // adjust the bucket start time
- // TODO: This (and addPastBucket to which it goes) doesn't really need to be an int64.
- uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
-
CountBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ info.mBucketNum = mCurrentBucketNum;
for (const auto& counter : *mCurrentSlicedCounter) {
info.mCount = counter.second;
auto& bucketList = mPastBuckets[counter.first];
bucketList.push_back(info);
- VLOG("metric %lld, dump key value: %s -> %d", mMetric.metric_id(), counter.first.c_str(),
- counter.second);
- mByteSize += sizeof(info);
+ VLOG("metric %s, dump key value: %s -> %lld", mMetric.name().c_str(), counter.first.c_str(),
+ (long long)counter.second);
}
for (auto& tracker : mAnomalyTrackers) {
- tracker->addOrUpdateBucket(mCurrentSlicedCounter, mCurrentBucketNum);
- tracker->declareAndDeclareAnomaly();
+ tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum);
}
// Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers).
mCurrentSlicedCounter = std::make_shared<DimToValMap>();
-
+ uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
mCurrentBucketNum += numBucketsForward;
- VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+ VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
(long long)mCurrentBucketStartTimeNs);
}
@@ -256,7 +248,11 @@
// greater than actual data size as it contains each dimension of
// CountMetricData is duplicated.
size_t CountMetricProducer::byteSize() {
- return mByteSize;
+ size_t totalSize = 0;
+ for (const auto& pair : mPastBuckets) {
+ totalSize += pair.second.size() * kBucketSize;
+ }
+ return totalSize;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index b7e480c..b3f8ee3 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -21,9 +21,9 @@
#include <android/util/ProtoOutputStream.h>
#include <gtest/gtest_prod.h>
+#include "../anomaly/AnomalyTracker.h"
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
-#include "../anomaly/DiscreteAnomalyTracker.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -36,6 +36,7 @@
int64_t mBucketStartNs;
int64_t mBucketEndNs;
int64_t mCount;
+ uint64_t mBucketNum;
};
class CountMetricProducer : public MetricProducer {
@@ -50,6 +51,8 @@
void finish() override;
+ void flushIfNeeded(const uint64_t newEventTime) override;
+
// TODO: Pass a timestamp as a parameter in onDumpReport.
std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -76,18 +79,15 @@
// TODO: Add a lock to mPastBuckets.
std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
- size_t mByteSize;
-
// The current bucket.
std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
- vector<std::unique_ptr<DiscreteAnomalyTracker>> mAnomalyTrackers;
-
- void flushCounterIfNeeded(const uint64_t newEventTime);
+ static const size_t kBucketSize = sizeof(CountBucket{});
FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
+ FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 2783bce..b0a97b1 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -23,11 +23,13 @@
#include <limits.h>
#include <stdlib.h>
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::string;
using std::unordered_map;
@@ -38,7 +40,7 @@
namespace statsd {
// for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
const int FIELD_ID_START_REPORT_NANOS = 2;
const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_DURATION_METRICS = 6;
@@ -61,6 +63,7 @@
DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
const int conditionIndex, const size_t startIndex,
const size_t stopIndex, const size_t stopAllIndex,
+ const bool nesting,
const sp<ConditionWizard>& wizard,
const vector<KeyMatcher>& internalDimension,
const uint64_t startTimeNs)
@@ -69,6 +72,7 @@
mStartIndex(startIndex),
mStopIndex(stopIndex),
mStopAllIndex(stopAllIndex),
+ mNested(nesting),
mInternalDimension(internalDimension) {
// TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
// them in the base class, because the proto generated CountMetric, and DurationMetric are
@@ -90,7 +94,7 @@
startNewProtoOutputStream(mStartTimeNs);
- VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -100,22 +104,22 @@
void DurationMetricProducer::startNewProtoOutputStream(long long startTime) {
mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
}
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
- vector<DurationBucket>& bucket) {
- switch (mMetric.type()) {
- case DurationMetric_AggregationType_DURATION_SUM:
- return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex,
- mCurrentBucketStartTimeNs, mBucketSizeNs,
- bucket);
- case DurationMetric_AggregationType_DURATION_MAX_SPARSE:
- return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex,
- mCurrentBucketStartTimeNs, mBucketSizeNs,
- bucket);
+ const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) {
+ switch (mMetric.aggregation_type()) {
+ case DurationMetric_AggregationType_SUM:
+ return make_unique<OringDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
+ mNested, mCurrentBucketStartTimeNs,
+ mBucketSizeNs, mAnomalyTrackers, bucket);
+ case DurationMetric_AggregationType_MAX_SPARSE:
+ return make_unique<MaxDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
+ mNested, mCurrentBucketStartTimeNs,
+ mBucketSizeNs, mAnomalyTrackers, bucket);
}
}
@@ -125,40 +129,23 @@
}
void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+ VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
// Now for each of the on-going event, check if the condition has changed for them.
- flushIfNeeded(eventTime);
for (auto& pair : mCurrentSlicedDuration) {
pair.second->onSlicedConditionMayChange(eventTime);
}
}
void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
- VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+ VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
mCondition = conditionMet;
// TODO: need to populate the condition change time from the event which triggers the condition
// change, instead of using current time.
-
- flushIfNeeded(eventTime);
for (auto& pair : mCurrentSlicedDuration) {
pair.second->onConditionChanged(conditionMet, eventTime);
}
}
-static void addDurationBucketsToReport(StatsLogReport_DurationMetricDataWrapper& wrapper,
- const vector<KeyValuePair>& key,
- const vector<DurationBucketInfo>& buckets) {
- DurationMetricData* data = wrapper.add_data();
- for (const auto& kv : key) {
- data->add_dimension()->CopyFrom(kv);
- }
- for (const auto& bucket : buckets) {
- data->add_bucket_info()->CopyFrom(bucket);
- VLOG("\t bucket [%lld - %lld] duration(ns): %lld", bucket.start_bucket_nanos(),
- bucket.end_bucket_nanos(), bucket.duration_nanos());
- }
-}
-
std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NS_PER_SEC;
@@ -166,7 +153,7 @@
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
flushIfNeeded(endTime);
- VLOG("metric %lld dump report now...", mMetric.metric_id());
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
@@ -176,11 +163,13 @@
ALOGW("Dimension key %s not found?!?! skip...", hashableKey.c_str());
continue;
}
- long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+ long long wrapperToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+ long long dimensionToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -196,7 +185,8 @@
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
- long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+ long long bucketInfoToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)bucket.mBucketStartNs);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -215,10 +205,8 @@
(long long)mCurrentBucketStartTimeNs);
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
-
startNewProtoOutputStream(endTime);
- mPastBuckets.clear();
-
+ // TODO: Properly clear the old buckets.
return buffer;
}
@@ -226,17 +214,19 @@
if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
return;
}
-
VLOG("flushing...........");
- for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end(); ++it) {
+ for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) {
if (it->second->flushIfNeeded(eventTime)) {
VLOG("erase bucket for key %s", it->first.c_str());
- mCurrentSlicedDuration.erase(it);
+ it = mCurrentSlicedDuration.erase(it);
+ } else {
+ ++it;
}
}
int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
}
void DurationMetricProducer::onMatchedLogEventInternal(
@@ -255,7 +245,7 @@
HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
- mCurrentSlicedDuration[eventKey] = createDurationTracker(mPastBuckets[eventKey]);
+ mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
}
auto it = mCurrentSlicedDuration.find(eventKey);
@@ -263,7 +253,7 @@
if (matcherIndex == mStartIndex) {
it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
} else if (matcherIndex == mStopIndex) {
- it->second->noteStop(atomKey, event.GetTimestampNs());
+ it->second->noteStop(atomKey, event.GetTimestampNs(), false);
}
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index eea00454..4c8dbcb 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef DURATION_METRIC_PRODUCER_H
-#define DURATION_METRIC_PRODUCER_H
+#pragma once
+
#include <unordered_map>
@@ -39,7 +39,8 @@
public:
DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
const size_t startIndex, const size_t stopIndex,
- const size_t stopAllIndex, const sp<ConditionWizard>& wizard,
+ const size_t stopAllIndex, const bool nesting,
+ const sp<ConditionWizard>& wizard,
const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs);
virtual ~DurationMetricProducer();
@@ -47,6 +48,7 @@
void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
void finish() override;
+ void flushIfNeeded(const uint64_t newEventTime) override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -80,6 +82,9 @@
// Index of the SimpleLogEntryMatcher which defines the stop all for all dimensions.
const size_t mStopAllIndex;
+ // nest counting -- for the same key, stops must match the number of starts to make real stop
+ const bool mNested;
+
// The dimension from the atom predicate. e.g., uid, wakelock name.
const vector<KeyMatcher> mInternalDimension;
@@ -91,11 +96,8 @@
std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
mCurrentSlicedDuration;
- void flushDurationIfNeeded(const uint64_t newEventTime);
-
- std::unique_ptr<DurationTracker> createDurationTracker(std::vector<DurationBucket>& bucket);
-
- void flushIfNeeded(uint64_t timestamp);
+ std::unique_ptr<DurationTracker> createDurationTracker(const HashableDimensionKey& eventKey,
+ std::vector<DurationBucket>& bucket);
static const size_t kBucketSize = sizeof(DurationBucket{});
};
@@ -104,4 +106,3 @@
} // namespace os
} // namespace android
-#endif // DURATION_METRIC_PRODUCER_H
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index f516cc7..9a94a0e 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -23,10 +23,12 @@
#include <limits.h>
#include <stdlib.h>
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
+using android::util::FIELD_TYPE_STRING;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::ProtoOutputStream;
using std::map;
@@ -39,7 +41,7 @@
namespace statsd {
// for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
const int FIELD_ID_START_REPORT_NANOS = 2;
const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_EVENT_METRICS = 4;
@@ -47,7 +49,7 @@
const int FIELD_ID_DATA = 1;
// for EventMetricData
const int FIELD_ID_TIMESTAMP_NANOS = 1;
-const int FIELD_ID_STATS_EVENTS = 2;
+const int FIELD_ID_ATOMS = 2;
EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex,
const sp<ConditionWizard>& wizard,
@@ -61,7 +63,7 @@
startNewProtoOutputStream(mStartTimeNs);
- VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -73,7 +75,7 @@
mProto = std::make_unique<ProtoOutputStream>();
// TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData,
// and StatsEvent.
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS);
}
@@ -90,7 +92,7 @@
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
size_t bufferSize = mProto->size();
- VLOG("metric %lld dump report now... proto size: %zu ", mMetric.metric_id(), bufferSize);
+ VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize);
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
@@ -99,7 +101,7 @@
}
void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
- VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+ VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
mCondition = conditionMet;
}
@@ -111,16 +113,17 @@
return;
}
- long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+ long long wrapperToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_TIMESTAMP_NANOS, (long long)event.GetTimestampNs());
- long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_STATS_EVENTS);
+ long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS);
event.ToProto(*mProto);
mProto->end(eventToken);
mProto->end(wrapperToken);
}
size_t EventMetricProducer::byteSize() {
- return mProto->size();
+ return mProto->bytesWritten();
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 0dccdf4..0c453cd 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -47,6 +47,8 @@
void onConditionChanged(const bool conditionMet, const uint64_t eventTime) override;
void finish() override;
+ void flushIfNeeded(const uint64_t newEventTime) override {
+ }
// TODO: Pass a timestamp as a parameter in onDumpReport.
std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 285d29b..42ac1a2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -24,11 +24,13 @@
#include <limits.h>
#include <stdlib.h>
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::map;
using std::string;
@@ -40,7 +42,7 @@
namespace statsd {
// for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
const int FIELD_ID_START_REPORT_NANOS = 2;
const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_GAUGE_METRICS = 8;
@@ -61,8 +63,9 @@
const int FIELD_ID_GAUGE = 3;
GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex,
- const sp<ConditionWizard>& wizard, const int pullTagId)
- : MetricProducer((time(nullptr) * NS_PER_SEC), conditionIndex, wizard),
+ const sp<ConditionWizard>& wizard, const int pullTagId,
+ const int64_t startTimeNs)
+ : MetricProducer(startTimeNs, conditionIndex, wizard),
mMetric(metric),
mPullTagId(pullTagId) {
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
@@ -88,7 +91,7 @@
startNewProtoOutputStream(mStartTimeNs);
- VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -98,7 +101,7 @@
void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) {
mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
}
@@ -107,12 +110,12 @@
}
std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
- VLOG("gauge metric %lld dump report now...", mMetric.metric_id());
+ VLOG("gauge metric %s dump report now...", mMetric.name().c_str());
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
- flushGaugeIfNeededLocked(time(nullptr) * NS_PER_SEC);
+ flushIfNeeded(time(nullptr) * NS_PER_SEC);
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
@@ -123,11 +126,13 @@
}
VLOG(" dimension key %s", hashableKey.c_str());
- long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+ long long wrapperToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+ long long dimensionToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -143,7 +148,8 @@
// Then fill bucket_info (GaugeBucketInfo).
for (const auto& bucket : pair.second) {
- long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+ long long bucketInfoToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)bucket.mBucketStartNs);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -163,7 +169,6 @@
startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
mPastBuckets.clear();
- mByteSize = 0;
return buffer;
@@ -172,16 +177,19 @@
void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
AutoMutex _l(mLock);
- VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+ VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+ flushIfNeeded(eventTime);
mCondition = conditionMet;
- // Push mode. Nothing to do.
+ // Push mode. No need to proactively pull the gauge data.
if (mPullTagId == -1) {
return;
}
- // If (1) the condition is not met or (2) we already pulled the gauge metric in the current
- // bucket, do not pull gauge again.
- if (!mCondition || mCurrentSlicedBucket.size() > 0) {
+ if (!mCondition) {
+ return;
+ }
+ // Already have gauge metric for the current bucket, do not do it again.
+ if (mCurrentSlicedBucket->size() > 0) {
return;
}
vector<std::shared_ptr<LogEvent>> allData;
@@ -192,16 +200,16 @@
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, false /*scheduledPull*/);
}
- flushGaugeIfNeededLocked(eventTime);
+ flushIfNeeded(eventTime);
}
void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+ VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
}
-long GaugeMetricProducer::getGauge(const LogEvent& event) {
+int64_t GaugeMetricProducer::getGauge(const LogEvent& event) {
status_t err = NO_ERROR;
- long val = event.GetLong(mMetric.gauge_field(), &err);
+ int64_t val = event.GetLong(mMetric.gauge_field(), &err);
if (err == NO_ERROR) {
return val;
} else {
@@ -212,14 +220,9 @@
void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
AutoMutex mutex(mLock);
- if (allData.size() == 0) {
- return;
- }
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, true /*scheduledPull*/);
}
- uint64_t eventTime = allData.at(0)->GetTimestampNs();
- flushGaugeIfNeededLocked(eventTime);
}
void GaugeMetricProducer::onMatchedLogEventInternal(
@@ -236,15 +239,21 @@
return;
}
- // For gauge metric, we just simply use the latest guage in the given bucket.
- const long gauge = getGauge(event);
- if (gauge < 0) {
- VLOG("Invalid gauge at event Time: %lld", (long long)eventTimeNs);
+ // When the event happens in a new bucket, flush the old buckets.
+ if (eventTimeNs >= mCurrentBucketStartTimeNs + mBucketSizeNs) {
+ flushIfNeeded(eventTimeNs);
+ }
+
+ // For gauge metric, we just simply use the first guage in the given bucket.
+ if (!mCurrentSlicedBucket->empty()) {
return;
}
- mCurrentSlicedBucket[eventKey] = gauge;
- if (mPullTagId < 0) {
- flushGaugeIfNeededLocked(eventTimeNs);
+ const long gauge = getGauge(event);
+ if (gauge >= 0) {
+ (*mCurrentSlicedBucket)[eventKey] = gauge;
+ }
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, gauge);
}
}
@@ -253,39 +262,45 @@
// bucket.
// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
// the GaugeMetricProducer while holding the lock.
-void GaugeMetricProducer::flushGaugeIfNeededLocked(const uint64_t eventTimeNs) {
- if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
- VLOG("event time is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
- (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
+void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
+ if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
return;
}
- // Adjusts the bucket start time
- int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
-
GaugeBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ info.mBucketNum = mCurrentBucketNum;
- for (const auto& slice : mCurrentSlicedBucket) {
+ for (const auto& slice : *mCurrentSlicedBucket) {
info.mGauge = slice.second;
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
- mByteSize += sizeof(info);
-
- VLOG("gauge metric %lld, dump key value: %s -> %ld", mMetric.metric_id(),
- slice.first.c_str(), slice.second);
+ VLOG("gauge metric %s, dump key value: %s -> %lld", mMetric.name().c_str(),
+ slice.first.c_str(), (long long)slice.second);
}
- // Reset counters
- mCurrentSlicedBucket.clear();
+ // Reset counters
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->addPastBucket(mCurrentSlicedBucket, mCurrentBucketNum);
+ }
+
+ mCurrentSlicedBucket = std::make_shared<DimToValMap>();
+
+ // Adjusts the bucket start time
+ int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
- VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+ mCurrentBucketNum += numBucketsForward;
+ VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
(long long)mCurrentBucketStartTimeNs);
}
size_t GaugeMetricProducer::byteSize() {
- return mByteSize;
+ size_t totalSize = 0;
+ for (const auto& pair : mPastBuckets) {
+ totalSize += pair.second.size() * kBucketSize;
+ }
+ return totalSize;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index d80672d..930afb2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -19,6 +19,7 @@
#include <unordered_map>
#include <android/util/ProtoOutputStream.h>
+#include <gtest/gtest_prod.h>
#include "../condition/ConditionTracker.h"
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
@@ -35,6 +36,7 @@
int64_t mBucketStartNs;
int64_t mBucketEndNs;
int64_t mGauge;
+ uint64_t mBucketNum;
};
// This gauge metric producer first register the puller to automatically pull the gauge at the
@@ -46,7 +48,8 @@
// TODO: Pass in the start time from MetricsManager, it should be consistent
// for all metrics.
GaugeMetricProducer(const GaugeMetric& countMetric, const int conditionIndex,
- const sp<ConditionWizard>& wizard, const int pullTagId);
+ const sp<ConditionWizard>& wizard, const int pullTagId,
+ const int64_t startTimeNs);
virtual ~GaugeMetricProducer();
@@ -57,6 +60,7 @@
void onSlicedConditionMayChange(const uint64_t eventTime) override;
void finish() override;
+ void flushIfNeeded(const uint64_t newEventTime) override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -81,7 +85,7 @@
static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000;
const GaugeMetric mMetric;
- StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+ StatsPullerManager mStatsPullerManager;
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
@@ -92,13 +96,15 @@
std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
// The current bucket.
- std::unordered_map<HashableDimensionKey, long> mCurrentSlicedBucket;
+ std::shared_ptr<DimToValMap> mCurrentSlicedBucket = std::make_shared<DimToValMap>();
- void flushGaugeIfNeededLocked(const uint64_t newEventTime);
+ int64_t getGauge(const LogEvent& event);
- long getGauge(const LogEvent& event);
+ static const size_t kBucketSize = sizeof(GaugeBucket{});
- size_t mByteSize;
+ FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 3c4dd90..62fb632 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -73,7 +73,7 @@
auto it = mProto->data();
while (it.readBuffer() != NULL) {
size_t toRead = it.currentToRead();
- std::memcpy(&buffer.get()[pos], it.readBuffer(), toRead);
+ std::memcpy(&((*buffer)[pos]), it.readBuffer(), toRead);
pos += toRead;
it.rp()->move(toRead);
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index c0930e3..0f93744 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -17,6 +17,7 @@
#ifndef METRIC_PRODUCER_H
#define METRIC_PRODUCER_H
+#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
#include "matchers/matcher_util.h"
#include "packages/PackageInfoListener.h"
@@ -58,17 +59,29 @@
// This is called when the metric collecting is done, e.g., when there is a new configuration
// coming. MetricProducer should do the clean up, and dump existing data to dropbox.
virtual void finish() = 0;
+ virtual void flushIfNeeded(const uint64_t newEventTime) = 0;
// TODO: Pass a timestamp as a parameter in onDumpReport and update all its
// implementations.
+ // onDumpReport returns the proto-serialized output and clears the previously stored contents.
virtual std::unique_ptr<std::vector<uint8_t>> onDumpReport() = 0;
virtual bool isConditionSliced() const {
return mConditionSliced;
};
+ // Returns the memory in bytes currently used to store this metric's data. Does not change
+ // state.
virtual size_t byteSize() = 0;
+ void addAnomalyTracker(sp<AnomalyTracker> tracker) {
+ mAnomalyTrackers.push_back(tracker);
+ }
+
+ int64_t getBuckeSizeInNs() const {
+ return mBucketSizeNs;
+ }
+
protected:
const uint64_t mStartTimeNs;
@@ -94,6 +107,8 @@
std::vector<EventConditionLink> mConditionLinks;
+ std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
+
/*
* Individual metrics can implement their own business logic here. All pre-processing is done.
*
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 19317ee..5916b040 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -38,8 +38,8 @@
MetricsManager::MetricsManager(const StatsdConfig& config) {
mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
- mAllMetricProducers, mConditionToMetricMap, mTrackerToMetricMap,
- mTrackerToConditionMap);
+ mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
+ mTrackerToMetricMap, mTrackerToConditionMap);
}
MetricsManager::~MetricsManager() {
@@ -80,8 +80,6 @@
return;
}
- // Since at least one of the metrics is interested in this event, we parse it now.
- ALOGD("%s", event.ToString().c_str());
vector<MatchingState> matcherCache(mAllLogEntryMatchers.size(), MatchingState::kNotComputed);
for (auto& matcher : mAllLogEntryMatchers) {
@@ -104,18 +102,17 @@
ConditionState::kNotEvaluated);
// A bitmap to track if a condition has changed value.
vector<bool> changedCache(mAllConditionTrackers.size(), false);
- vector<bool> slicedChangedCache(mAllConditionTrackers.size(), false);
for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
if (conditionToBeEvaluated[i] == false) {
continue;
}
sp<ConditionTracker>& condition = mAllConditionTrackers[i];
condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
- changedCache, slicedChangedCache);
+ changedCache);
}
for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
- if (changedCache[i] == false && slicedChangedCache[i] == false) {
+ if (changedCache[i] == false) {
continue;
}
auto pair = mConditionToMetricMap.find(i);
@@ -124,14 +121,13 @@
for (auto metricIndex : metricList) {
// metric cares about non sliced condition, and it's changed.
// Push the new condition to it directly.
- if (!mAllMetricProducers[metricIndex]->isConditionSliced() && changedCache[i]) {
+ if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
eventTime);
// metric cares about sliced conditions, and it may have changed. Send
// notification, and the metric can query the sliced conditions that are
// interesting to it.
- } else if (mAllMetricProducers[metricIndex]->isConditionSliced() &&
- slicedChangedCache[i]) {
+ } else if (mAllMetricProducers[metricIndex]->isConditionSliced()) {
mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(eventTime);
}
}
@@ -154,6 +150,19 @@
}
}
+void MetricsManager::onAnomalyAlarmFired(const uint64_t timestampNs,
+ sp<const AnomalyAlarm> anomaly) {
+ for (const auto& itr : mAllAnomalyTrackers) {
+ itr->declareAnomaly(timestampNs);
+ }
+}
+
+void MetricsManager::setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor) {
+ for (auto& itr : mAllAnomalyTrackers) {
+ itr->setAnomalyMonitor(anomalyMonitor);
+ }
+}
+
// Returns the total byte size of all metrics managed by a single config source.
size_t MetricsManager::byteSize() {
size_t totalSize = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 39c79f9..59ade7c 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -16,6 +16,8 @@
#pragma once
+#include "anomaly/AnomalyMonitor.h"
+#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "logd/LogEvent.h"
@@ -43,9 +45,15 @@
// Called when everything should wrap up. We are about to finish (e.g., new config comes).
void finish();
+ void onAnomalyAlarmFired(const uint64_t timestampNs, sp<const AnomalyAlarm> anomaly);
+
+ void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor);
+
// Config source owner can call onDumpReport() to get all the metrics collected.
std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport();
+ // Computes the total byte size of all metrics managed by a single config source.
+ // Does not change the state.
size_t byteSize();
private:
@@ -68,6 +76,9 @@
// Hold all metrics from the config.
std::vector<sp<MetricProducer>> mAllMetricProducers;
+ // Hold all alert trackers.
+ std::vector<sp<AnomalyTracker>> mAllAnomalyTrackers;
+
// To make the log processing more efficient, we want to do as much filtering as possible
// before we go into individual trackers and conditions to match.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 2a63073..9cbe6f6 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -23,13 +23,16 @@
#include <limits.h>
#include <stdlib.h>
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::list;
+using std::make_pair;
using std::make_shared;
using std::map;
using std::shared_ptr;
@@ -41,7 +44,7 @@
namespace statsd {
// for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
const int FIELD_ID_START_REPORT_NANOS = 2;
const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_VALUE_METRICS = 7;
@@ -61,13 +64,23 @@
const int FIELD_ID_END_BUCKET_NANOS = 2;
const int FIELD_ID_VALUE = 3;
+static const uint64_t kDefaultBucketSizeMillis = 60 * 60 * 1000L;
+
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId,
- const uint64_t startTimeNs)
- : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric), mPullTagId(pullTagId) {
+ const uint64_t startTimeNs,
+ shared_ptr<StatsPullerManager> statsPullerManager)
+ : MetricProducer(startTimeNs, conditionIndex, wizard),
+ mMetric(metric),
+ mStatsPullerManager(statsPullerManager),
+ mPullTagId(pullTagId) {
// TODO: valuemetric for pushed events may need unlimited bucket length
- mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+ if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
+ mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+ } else {
+ mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000;
+ }
mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
@@ -78,23 +91,35 @@
}
if (!metric.has_condition() && mPullTagId != -1) {
- mStatsPullerManager.RegisterReceiver(mPullTagId, this,
- metric.bucket().bucket_size_millis());
+ VLOG("Setting up periodic pulling for %d", mPullTagId);
+ mStatsPullerManager->RegisterReceiver(mPullTagId, this,
+ metric.bucket().bucket_size_millis());
}
startNewProtoOutputStream(mStartTimeNs);
- VLOG("value metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ VLOG("value metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
+// for testing
+ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int pullTagId,
+ const uint64_t startTimeNs)
+ : ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs,
+ make_shared<StatsPullerManager>()) {
+}
+
ValueMetricProducer::~ValueMetricProducer() {
VLOG("~ValueMetricProducer() called");
+ if (mPullTagId != -1) {
+ mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
+ }
}
void ValueMetricProducer::startNewProtoOutputStream(long long startTime) {
mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
}
@@ -105,11 +130,11 @@
}
void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+ VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
}
std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
- VLOG("metric %lld dump report now...", mMetric.metric_id());
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
@@ -119,11 +144,13 @@
ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
continue;
}
- long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+ long long wrapperToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+ long long dimensionToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -139,7 +166,8 @@
// Then fill bucket_info (ValueBucketInfo).
for (const auto& bucket : pair.second) {
- long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+ long long bucketInfoToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)bucket.mBucketStartNs);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -155,12 +183,11 @@
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
- VLOG("metric %lld dump report now...", mMetric.metric_id());
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
mPastBuckets.clear();
- mByteSize = 0;
return buffer;
@@ -168,42 +195,46 @@
}
void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) {
+ AutoMutex _l(mLock);
mCondition = condition;
if (mPullTagId != -1) {
if (mCondition == true) {
- mStatsPullerManager.RegisterReceiver(mPullTagId, this,
- mMetric.bucket().bucket_size_millis());
- } else if (mCondition == ConditionState::kFalse) {
- mStatsPullerManager.UnRegisterReceiver(mPullTagId, this);
+ mStatsPullerManager->RegisterReceiver(mPullTagId, this,
+ mMetric.bucket().bucket_size_millis());
+ } else if (mCondition == false) {
+ mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
}
vector<shared_ptr<LogEvent>> allData;
- if (mStatsPullerManager.Pull(mPullTagId, &allData)) {
+ if (mStatsPullerManager->Pull(mPullTagId, &allData)) {
if (allData.size() == 0) {
return;
}
- AutoMutex _l(mLock);
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, false);
}
- flush_if_needed(eventTime);
+ flushIfNeeded(eventTime);
}
return;
}
}
void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
- if (mCondition == ConditionState::kTrue || !mMetric.has_condition()) {
- AutoMutex _l(mLock);
+ AutoMutex _l(mLock);
+ if (mCondition == true || !mMetric.has_condition()) {
if (allData.size() == 0) {
return;
}
uint64_t eventTime = allData.at(0)->GetTimestampNs();
+ // alarm is not accurate and might drift.
+ if (eventTime > mCurrentBucketStartTimeNs + mBucketSizeNs * 3 / 2) {
+ flushIfNeeded(eventTime);
+ }
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, true);
}
- flush_if_needed(eventTime);
+ flushIfNeeded(eventTime);
}
}
@@ -222,24 +253,36 @@
long value = get_value(event);
- if (scheduledPull) {
- if (interval.raw.size() > 0) {
- interval.raw.back().second = value;
- } else {
- interval.raw.push_back(std::make_pair(value, value));
- }
- mNextSlicedBucket[eventKey].raw[0].first = value;
- } else {
- if (mCondition == ConditionState::kTrue) {
- interval.raw.push_back(std::make_pair(value, 0));
- } else {
- if (interval.raw.size() != 0) {
+ if (mPullTagId != -1) {
+ if (scheduledPull) {
+ // scheduled pull always sets beginning of current bucket and end
+ // of next bucket
+ if (interval.raw.size() > 0) {
interval.raw.back().second = value;
+ } else {
+ interval.raw.push_back(make_pair(value, value));
+ }
+ Interval& nextInterval = mNextSlicedBucket[eventKey];
+ if (nextInterval.raw.size() == 0) {
+ nextInterval.raw.push_back(make_pair(value, 0));
+ } else {
+ nextInterval.raw.front().first = value;
+ }
+ } else {
+ if (mCondition == true) {
+ interval.raw.push_back(make_pair(value, 0));
+ } else {
+ if (interval.raw.size() != 0) {
+ interval.raw.back().second = value;
+ } else {
+ interval.tainted = true;
+ VLOG("Data on condition true missing!");
+ }
}
}
- }
- if (mPullTagId == -1) {
- flush_if_needed(eventTimeNs);
+ } else {
+ flushIfNeeded(eventTimeNs);
+ interval.raw.push_back(make_pair(value, 0));
}
}
@@ -249,51 +292,65 @@
if (err == NO_ERROR) {
return val;
} else {
- VLOG("Can't find value in message.");
+ VLOG("Can't find value in message. %s", event.ToString().c_str());
return 0;
}
}
-void ValueMetricProducer::flush_if_needed(const uint64_t eventTimeNs) {
+void ValueMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) {
if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) {
VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
(long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
return;
}
-
VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
(int)mCurrentSlicedBucket.size());
ValueBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ info.mBucketNum = mCurrentBucketNum;
+ int tainted = 0;
for (const auto& slice : mCurrentSlicedBucket) {
long value = 0;
- for (const auto& pair : slice.second.raw) {
- value += pair.second - pair.first;
+ if (mPullTagId != -1) {
+ for (const auto& pair : slice.second.raw) {
+ value += (pair.second - pair.first);
+ }
+ } else {
+ for (const auto& pair : slice.second.raw) {
+ value += pair.first;
+ }
}
+ tainted += slice.second.tainted;
info.mValue = value;
- VLOG(" %s, %ld", slice.first.c_str(), value);
+ VLOG(" %s, %ld, %d", slice.first.c_str(), value, tainted);
// it will auto create new vector of ValuebucketInfo if the key is not found.
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
- mByteSize += sizeof(info);
}
// Reset counters
mCurrentSlicedBucket.swap(mNextSlicedBucket);
mNextSlicedBucket.clear();
+
int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
+
if (numBucketsForward > 1) {
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
}
- mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
- VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+ VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
(long long)mCurrentBucketStartTimeNs);
}
size_t ValueMetricProducer::byteSize() {
- return mByteSize;
+ size_t totalSize = 0;
+ for (const auto& pair : mPastBuckets) {
+ totalSize += pair.second.size() * kBucketSize;
+ }
+ return totalSize;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index ef9868b..2b0b0ad 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <gtest/gtest_prod.h>
#include <utils/threads.h>
#include <list>
#include "../condition/ConditionTracker.h"
@@ -32,6 +33,7 @@
int64_t mBucketStartNs;
int64_t mBucketEndNs;
int64_t mValue;
+ uint64_t mBucketNum;
};
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
@@ -45,6 +47,7 @@
void onConditionChanged(const bool condition, const uint64_t eventTime) override;
void finish() override;
+ void flushIfNeeded(const uint64_t eventTimeNs) override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
@@ -71,7 +74,13 @@
private:
const ValueMetric mMetric;
- StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+ std::shared_ptr<StatsPullerManager> mStatsPullerManager;
+
+ // for testing
+ ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int pullTagId,
+ const uint64_t startTimeNs,
+ std::shared_ptr<StatsPullerManager> statsPullerManager);
Mutex mLock;
@@ -81,6 +90,7 @@
// internal state of a bucket.
typedef struct {
std::vector<std::pair<long, long>> raw;
+ bool tainted;
} Interval;
std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
@@ -94,9 +104,11 @@
long get_value(const LogEvent& event);
- void flush_if_needed(const uint64_t eventTimeNs);
+ static const size_t kBucketSize = sizeof(ValueBucket{});
- size_t mByteSize;
+ FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
+ FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 5c76d0e..c91ea0f 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -17,6 +17,7 @@
#ifndef DURATION_TRACKER_H
#define DURATION_TRACKER_H
+#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
#include "stats_util.h"
@@ -34,6 +35,10 @@
// Hold duration information for one atom level duration in current on-going bucket.
struct DurationInfo {
DurationState state;
+
+ // the number of starts seen.
+ int32_t startCount;
+
// most recent start time.
int64_t lastStartTime;
// existing duration in current bucket.
@@ -42,29 +47,37 @@
// cache the HashableDimensionKeys we need to query the condition for this duration event.
ConditionKey conditionKeys;
- DurationInfo() : state(kStopped), lastStartTime(0), lastDuration(0){};
+ DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){};
};
struct DurationBucket {
- int64_t mBucketStartNs;
- int64_t mBucketEndNs;
- int64_t mDuration;
+ uint64_t mBucketStartNs;
+ uint64_t mBucketEndNs;
+ uint64_t mDuration;
+ uint64_t mBucketNum;
};
class DurationTracker {
public:
- DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, uint64_t currentBucketStartNs,
- uint64_t bucketSizeNs, std::vector<DurationBucket>& bucket)
- : mWizard(wizard),
+ DurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
+ std::vector<DurationBucket>& bucket)
+ : mEventKey(eventKey),
+ mWizard(wizard),
mConditionTrackerIndex(conditionIndex),
- mCurrentBucketStartTimeNs(currentBucketStartNs),
mBucketSizeNs(bucketSizeNs),
+ mNested(nesting),
+ mCurrentBucketStartTimeNs(currentBucketStartNs),
mBucket(bucket),
- mDuration(0){};
+ mDuration(0),
+ mCurrentBucketNum(0),
+ mAnomalyTrackers(anomalyTrackers){};
virtual ~DurationTracker(){};
virtual void noteStart(const HashableDimensionKey& key, bool condition,
const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
- virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) = 0;
+ virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ const bool stopAll) = 0;
virtual void noteStopAll(const uint64_t eventTime) = 0;
virtual void onSlicedConditionMayChange(const uint64_t timestamp) = 0;
virtual void onConditionChanged(bool condition, const uint64_t timestamp) = 0;
@@ -72,18 +85,78 @@
// events, so that the owner can safely remove the tracker.
virtual bool flushIfNeeded(uint64_t timestampNs) = 0;
+ // Predict the anomaly timestamp given the current status.
+ virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const uint64_t currentTimestamp) const = 0;
+
protected:
+ // Starts the anomaly alarm.
+ void startAnomalyAlarm(const uint64_t eventTime) {
+ for (auto& anomalyTracker : mAnomalyTrackers) {
+ if (anomalyTracker != nullptr) {
+ anomalyTracker->startAlarm(mEventKey,
+ predictAnomalyTimestampNs(*anomalyTracker, eventTime));
+ }
+ }
+ }
+
+ // Stops the anomaly alarm.
+ void stopAnomalyAlarm() {
+ for (auto& anomalyTracker : mAnomalyTrackers) {
+ if (anomalyTracker != nullptr) {
+ anomalyTracker->stopAlarm(mEventKey);
+ }
+ }
+ }
+
+ void addPastBucketToAnomalyTrackers(const int64_t& bucketValue, const int64_t& bucketNum) {
+ for (auto& anomalyTracker : mAnomalyTrackers) {
+ if (anomalyTracker != nullptr) {
+ anomalyTracker->addPastBucket(mEventKey, bucketValue, bucketNum);
+ }
+ }
+ }
+
+ void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum,
+ const int64_t& currentBucketValue) {
+ for (auto& anomalyTracker : mAnomalyTrackers) {
+ if (anomalyTracker != nullptr) {
+ anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mEventKey,
+ currentBucketValue);
+ }
+ }
+ }
+
+ void declareAnomalyIfAlarmExpired(const uint64_t& timestamp) {
+ for (auto& anomalyTracker : mAnomalyTrackers) {
+ if (anomalyTracker != nullptr) {
+ anomalyTracker->declareAnomalyIfAlarmExpired(mEventKey, timestamp);
+ }
+ }
+ }
+
+ HashableDimensionKey mEventKey;
+
sp<ConditionWizard> mWizard;
- int mConditionTrackerIndex;
+ const int mConditionTrackerIndex;
+
+ const int64_t mBucketSizeNs;
+
+ const bool mNested;
uint64_t mCurrentBucketStartTimeNs;
- int64_t mBucketSizeNs;
-
std::vector<DurationBucket>& mBucket; // where to write output
int64_t mDuration; // current recorded duration result
+
+ uint64_t mCurrentBucketNum;
+
+ std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
+
+ FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
+ FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index a4d3098..06e743d 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -23,14 +23,18 @@
namespace os {
namespace statsd {
-MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+MaxDurationTracker::MaxDurationTracker(const HashableDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
std::vector<DurationBucket>& bucket)
- : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket) {
+ : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
+ anomalyTrackers, bucket) {
}
void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
const uint64_t eventTime, const ConditionKey& conditionKey) {
+ flushIfNeeded(eventTime);
// this will construct a new DurationInfo if this key didn't exist.
DurationInfo& duration = mInfos[key];
duration.conditionKeys = conditionKey;
@@ -38,10 +42,10 @@
switch (duration.state) {
case kStarted:
- // The same event is already started. Because we are not counting nesting, so ignore.
+ duration.startCount++;
break;
case kPaused:
- // Safe to do nothing here. Paused means started but condition is false.
+ duration.startCount++;
break;
case kStopped:
if (!condition) {
@@ -51,11 +55,16 @@
duration.state = DurationState::kStarted;
duration.lastStartTime = eventTime;
}
+ duration.startCount = 1;
break;
}
}
-void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) {
+
+void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ bool forceStop) {
+ flushIfNeeded(eventTime);
+ declareAnomalyIfAlarmExpired(eventTime);
VLOG("MaxDuration: key %s stop", key.c_str());
if (mInfos.find(key) == mInfos.end()) {
// we didn't see a start event before. do nothing.
@@ -68,31 +77,47 @@
// already stopped, do nothing.
break;
case DurationState::kStarted: {
- duration.state = DurationState::kStopped;
- int64_t durationTime = eventTime - duration.lastStartTime;
- VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(), (long long)duration.lastStartTime,
- (long long)eventTime, (long long)durationTime);
- duration.lastDuration = duration.lastDuration + durationTime;
- VLOG(" record duration: %lld ", (long long)duration.lastDuration);
+ duration.startCount--;
+ if (forceStop || !mNested || duration.startCount <= 0) {
+ duration.state = DurationState::kStopped;
+ int64_t durationTime = eventTime - duration.lastStartTime;
+ VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(),
+ (long long)duration.lastStartTime, (long long)eventTime,
+ (long long)durationTime);
+ duration.lastDuration = duration.lastDuration + durationTime;
+ duration.lastStartTime = -1;
+ VLOG(" record duration: %lld ", (long long)duration.lastDuration);
+ }
break;
}
case DurationState::kPaused: {
- duration.state = DurationState::kStopped;
+ duration.startCount--;
+ if (forceStop || !mNested || duration.startCount <= 0) {
+ duration.state = DurationState::kStopped;
+ }
break;
}
}
if (duration.lastDuration > mDuration) {
mDuration = duration.lastDuration;
+ detectAndDeclareAnomaly(eventTime, mCurrentBucketNum, mDuration);
VLOG("Max: new max duration: %lld", (long long)mDuration);
}
// Once an atom duration ends, we erase it. Next time, if we see another atom event with the
// same name, they are still considered as different atom durations.
- mInfos.erase(key);
+ if (duration.state == DurationState::kStopped) {
+ mInfos.erase(key);
+ }
}
+
void MaxDurationTracker::noteStopAll(const uint64_t eventTime) {
- for (auto& pair : mInfos) {
- noteStop(pair.first, eventTime);
+ std::set<HashableDimensionKey> keys;
+ for (const auto& pair : mInfos) {
+ keys.insert(pair.first);
+ }
+ for (auto& key : keys) {
+ noteStop(key, eventTime, true);
}
}
@@ -111,7 +136,7 @@
DurationBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = endTime;
-
+ info.mBucketNum = mCurrentBucketNum;
uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
@@ -154,6 +179,7 @@
if (mDuration != 0) {
info.mDuration = mDuration;
mBucket.push_back(info);
+ addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
VLOG(" final duration for last bucket: %lld", (long long)mDuration);
}
@@ -163,17 +189,20 @@
DurationBucket info;
info.mBucketStartNs = oldBucketStartTimeNs + mBucketSizeNs * i;
info.mBucketEndNs = endTime + mBucketSizeNs * i;
+ info.mBucketNum = mCurrentBucketNum + i;
info.mDuration = mBucketSizeNs;
mBucket.push_back(info);
+ addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
VLOG(" filling gap bucket with duration %lld", (long long)mBucketSizeNs);
}
}
+
+ mCurrentBucketNum += numBucketsForward;
// If this tracker has no pending events, tell owner to remove.
return !hasPendingEvent;
}
void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
- // VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
// Now for each of the on-going event, check if the condition has changed for them.
for (auto& pair : mInfos) {
if (pair.second.state == kStopped) {
@@ -194,6 +223,8 @@
void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
const uint64_t timestamp) {
+ flushIfNeeded(timestamp);
+ declareAnomalyIfAlarmExpired(timestamp);
auto it = mInfos.find(key);
if (it == mInfos.end()) {
return;
@@ -205,7 +236,6 @@
if (!conditionMet) {
it->second.state = DurationState::kPaused;
it->second.lastDuration += (timestamp - it->second.lastStartTime);
-
VLOG("MaxDurationTracker Key: %s Started->Paused ", key.c_str());
}
break;
@@ -222,6 +252,16 @@
}
break;
}
+ if (it->second.lastDuration > mDuration) {
+ mDuration = it->second.lastDuration;
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
+ }
+}
+
+int64_t MaxDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const uint64_t currentTimestamp) const {
+ ALOGE("Max duration producer does not support anomaly timestamp prediction!!!");
+ return currentTimestamp;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index b095884..ca10210 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,22 +28,34 @@
// they stop or bucket expires.
class MaxDurationTracker : public DurationTracker {
public:
- MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
- uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ MaxDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs,
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
std::vector<DurationBucket>& bucket);
void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
const ConditionKey& conditionKey) override;
- void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+ void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ const bool stopAll) override;
void noteStopAll(const uint64_t eventTime) override;
bool flushIfNeeded(uint64_t timestampNs) override;
void onSlicedConditionMayChange(const uint64_t timestamp) override;
void onConditionChanged(bool condition, const uint64_t timestamp) override;
+ int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const uint64_t currentTimestamp) const override;
+
private:
std::map<HashableDimensionKey, DurationInfo> mInfos;
void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
const uint64_t timestamp);
+
+ FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration);
+ FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary);
+ FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition);
+ FRIEND_TEST(MaxDurationTrackerTest, TestStopAll);
+ FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index e4f1d21..29b6c89 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -20,10 +20,17 @@
namespace android {
namespace os {
namespace statsd {
-OringDurationTracker::OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
- uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+
+using std::pair;
+
+OringDurationTracker::OringDurationTracker(const HashableDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ bool nesting, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs,
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
std::vector<DurationBucket>& bucket)
- : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket),
+ : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
+ anomalyTrackers, bucket),
mStarted(),
mPaused() {
mLastStartTime = 0;
@@ -31,14 +38,16 @@
void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
const uint64_t eventTime, const ConditionKey& conditionKey) {
+ flushIfNeeded(eventTime);
if (condition) {
if (mStarted.size() == 0) {
mLastStartTime = eventTime;
VLOG("record first start....");
+ startAnomalyAlarm(eventTime);
}
- mStarted.insert(key);
+ mStarted[key]++;
} else {
- mPaused.insert(key);
+ mPaused[key]++;
}
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
@@ -48,69 +57,98 @@
VLOG("Oring: %s start, condition %d", key.c_str(), condition);
}
-void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp) {
+void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp,
+ const bool stopAll) {
+ flushIfNeeded(timestamp);
+ declareAnomalyIfAlarmExpired(timestamp);
VLOG("Oring: %s stop", key.c_str());
auto it = mStarted.find(key);
if (it != mStarted.end()) {
- mStarted.erase(it);
+ (it->second)--;
+ if (stopAll || !mNested || it->second <= 0) {
+ mStarted.erase(it);
+ mConditionKeyMap.erase(key);
+ }
if (mStarted.empty()) {
mDuration += (timestamp - mLastStartTime);
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
+ mLastStartTime = -1;
VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
(long long)mDuration);
}
}
- mPaused.erase(key);
- mConditionKeyMap.erase(key);
+ auto pausedIt = mPaused.find(key);
+ if (pausedIt != mPaused.end()) {
+ (pausedIt->second)--;
+ if (stopAll || !mNested || pausedIt->second <= 0) {
+ mPaused.erase(pausedIt);
+ mConditionKeyMap.erase(key);
+ }
+ }
+ if (mStarted.empty()) {
+ stopAnomalyAlarm();
+ }
}
+
void OringDurationTracker::noteStopAll(const uint64_t timestamp) {
+ flushIfNeeded(timestamp);
+ declareAnomalyIfAlarmExpired(timestamp);
if (!mStarted.empty()) {
mDuration += (timestamp - mLastStartTime);
VLOG("Oring Stop all: record duration %lld %lld ", (long long)timestamp - mLastStartTime,
(long long)mDuration);
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
}
+ stopAnomalyAlarm();
mStarted.clear();
mPaused.clear();
mConditionKeyMap.clear();
+ mLastStartTime = -1;
}
bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) {
- if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
+ if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) {
return false;
}
VLOG("OringDurationTracker Flushing.............");
// adjust the bucket start time
int numBucketsForward = (eventTime - mCurrentBucketStartTimeNs) / mBucketSizeNs;
- DurationBucket info;
- uint64_t endTime = mCurrentBucketStartTimeNs + mBucketSizeNs;
- info.mBucketStartNs = mCurrentBucketStartTimeNs;
- info.mBucketEndNs = endTime;
-
- uint64_t oldBucketStartTimeNs = mCurrentBucketStartTimeNs;
- mCurrentBucketStartTimeNs += (numBucketsForward)*mBucketSizeNs;
-
+ DurationBucket current_info;
+ current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
+ current_info.mBucketEndNs = current_info.mBucketStartNs + mBucketSizeNs;
+ current_info.mBucketNum = mCurrentBucketNum;
+ // Process the current bucket.
if (mStarted.size() > 0) {
- mDuration += (endTime - mLastStartTime);
+ mDuration += (current_info.mBucketEndNs - mLastStartTime);
+ mLastStartTime = current_info.mBucketEndNs;
}
- if (mDuration != 0) {
- info.mDuration = mDuration;
- // it will auto create new vector of CountbucketInfo if the key is not found.
- mBucket.push_back(info);
- VLOG(" duration: %lld", (long long)mDuration);
+ if (mDuration > 0) {
+ current_info.mDuration = mDuration;
+ mBucket.push_back(current_info);
+ addPastBucketToAnomalyTrackers(current_info.mDuration, current_info.mBucketNum);
+ VLOG(" duration: %lld", (long long)current_info.mDuration);
}
if (mStarted.size() > 0) {
for (int i = 1; i < numBucketsForward; i++) {
DurationBucket info;
- info.mBucketStartNs = oldBucketStartTimeNs + mBucketSizeNs * i;
- info.mBucketEndNs = endTime + mBucketSizeNs * i;
+ info.mBucketStartNs = mCurrentBucketStartTimeNs + mBucketSizeNs * i;
+ info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
+ info.mBucketNum = mCurrentBucketNum + i;
info.mDuration = mBucketSizeNs;
- mBucket.push_back(info);
- VLOG(" add filling bucket with duration %lld", (long long)mBucketSizeNs);
+ mLastStartTime = info.mBucketEndNs;
+ if (info.mDuration > 0) {
+ mBucket.push_back(info);
+ addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
+ VLOG(" add filling bucket with duration %lld", (long long)info.mDuration);
+ }
}
}
- mLastStartTime = mCurrentBucketStartTimeNs;
+ mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
+
mDuration = 0;
// if all stopped, then tell owner it's safe to remove this tracker.
@@ -118,11 +156,13 @@
}
void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
- vector<HashableDimensionKey> startedToPaused;
- vector<HashableDimensionKey> pausedToStarted;
+ flushIfNeeded(timestamp);
+ declareAnomalyIfAlarmExpired(timestamp);
+ vector<pair<HashableDimensionKey, int>> startedToPaused;
+ vector<pair<HashableDimensionKey, int>> pausedToStarted;
if (!mStarted.empty()) {
for (auto it = mStarted.begin(); it != mStarted.end();) {
- auto key = *it;
+ const auto& key = it->first;
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
VLOG("Key %s dont have condition key", key.c_str());
++it;
@@ -130,8 +170,8 @@
}
if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
ConditionState::kTrue) {
+ startedToPaused.push_back(*it);
it = mStarted.erase(it);
- startedToPaused.push_back(key);
VLOG("Key %s started -> paused", key.c_str());
} else {
++it;
@@ -139,15 +179,17 @@
}
if (mStarted.empty()) {
- mDuration += (timestamp - mLastStartTime);
+ mDuration = (timestamp - mLastStartTime);
+ mLastStartTime = -1;
VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime),
(long long)mDuration);
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
}
}
if (!mPaused.empty()) {
for (auto it = mPaused.begin(); it != mPaused.end();) {
- auto key = *it;
+ const auto& key = it->first;
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
VLOG("Key %s dont have condition key", key.c_str());
++it;
@@ -155,8 +197,8 @@
}
if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
ConditionState::kTrue) {
+ pausedToStarted.push_back(*it);
it = mPaused.erase(it);
- pausedToStarted.push_back(key);
VLOG("Key %s paused -> started", key.c_str());
} else {
++it;
@@ -168,26 +210,95 @@
}
}
+ if (mStarted.empty() && !pausedToStarted.empty()) {
+ startAnomalyAlarm(timestamp);
+ }
mStarted.insert(pausedToStarted.begin(), pausedToStarted.end());
mPaused.insert(startedToPaused.begin(), startedToPaused.end());
+
+ if (mStarted.empty()) {
+ stopAnomalyAlarm();
+ }
}
void OringDurationTracker::onConditionChanged(bool condition, const uint64_t timestamp) {
+ flushIfNeeded(timestamp);
+ declareAnomalyIfAlarmExpired(timestamp);
if (condition) {
if (!mPaused.empty()) {
VLOG("Condition true, all started");
if (mStarted.empty()) {
- mLastStartTime = timestamp;
+ mLastStartTime = -1;
+ }
+ if (mStarted.empty() && !mPaused.empty()) {
+ startAnomalyAlarm(timestamp);
}
mStarted.insert(mPaused.begin(), mPaused.end());
+ mPaused.clear();
}
} else {
if (!mStarted.empty()) {
VLOG("Condition false, all paused");
- mDuration += (timestamp - mLastStartTime);
+ mDuration = (timestamp - mLastStartTime);
+ mLastStartTime = -1;
mPaused.insert(mStarted.begin(), mStarted.end());
+ mStarted.clear();
+ detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration);
}
}
+ if (mStarted.empty()) {
+ stopAnomalyAlarm();
+ }
+}
+
+int64_t OringDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const uint64_t eventTimestampNs) const {
+ // TODO: Unit-test this and see if it can be done more efficiently (e.g. use int32).
+ // All variables below represent durations (not timestamps).
+
+ // The time until the current bucket ends. This is how much more 'space' it can hold.
+ const int64_t currRemainingBucketSizeNs =
+ mBucketSizeNs - (eventTimestampNs - mCurrentBucketStartTimeNs);
+ // TODO: This should never be < 0. Document/guard against possible failures if it is.
+
+ const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold();
+
+ // As we move into the future, old buckets get overwritten (so their old data is erased).
+
+ // Sum of past durations. Will change as we overwrite old buckets.
+ int64_t pastNs = mDuration;
+ pastNs += anomalyTracker.getSumOverPastBuckets(mEventKey);
+
+ // How much of the threshold is still unaccounted after considering pastNs.
+ int64_t leftNs = thresholdNs - pastNs;
+
+ // First deal with the remainder of the current bucket.
+ if (leftNs <= currRemainingBucketSizeNs) { // Predict the anomaly will occur in this bucket.
+ return eventTimestampNs + leftNs;
+ }
+ // The remainder of this bucket contributes, but we must then move to the next bucket.
+ pastNs += currRemainingBucketSizeNs;
+
+ // Now deal with the past buckets, starting with the oldest.
+ for (int futBucketIdx = 0; futBucketIdx < anomalyTracker.getNumOfPastPackets();
+ futBucketIdx++) {
+ // We now overwrite the oldest bucket with the previous 'current', and start a new
+ // 'current'.
+ pastNs -= anomalyTracker.getPastBucketValue(
+ mEventKey, mCurrentBucketNum - anomalyTracker.getNumOfPastPackets() + futBucketIdx);
+ leftNs = thresholdNs - pastNs;
+ if (leftNs <= mBucketSizeNs) { // Predict anomaly will occur in this bucket.
+ return eventTimestampNs + currRemainingBucketSizeNs + (futBucketIdx * mBucketSizeNs) +
+ leftNs;
+ } else { // This bucket would be entirely filled, and we'll need to move to the next
+ // bucket.
+ pastNs += mBucketSizeNs;
+ }
+ }
+
+ // If we have reached this point, we even have to overwrite the the original current bucket.
+ // Thus, none of the past data will still be extant - pastNs is now 0.
+ return eventTimestampNs + thresholdNs;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index b54dafa..6f92113 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -27,27 +27,39 @@
// Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
class OringDurationTracker : public DurationTracker {
public:
- OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
- uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ OringDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs,
+ const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
std::vector<DurationBucket>& bucket);
void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
const ConditionKey& conditionKey) override;
- void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+ void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ const bool stopAll) override;
void noteStopAll(const uint64_t eventTime) override;
void onSlicedConditionMayChange(const uint64_t timestamp) override;
void onConditionChanged(bool condition, const uint64_t timestamp) override;
bool flushIfNeeded(uint64_t timestampNs) override;
+ int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+ const uint64_t currentTimestamp) const override;
+
private:
// We don't need to keep track of individual durations. The information that's needed is:
// 1) which keys are started. We record the first start time.
// 2) which keys are paused (started but condition was false)
// 3) whenever a key stops, we remove it from the started set. And if the set becomes empty,
// it means everything has stopped, we then record the end time.
- std::set<HashableDimensionKey> mStarted;
- std::set<HashableDimensionKey> mPaused;
+ std::map<HashableDimensionKey, int> mStarted;
+ std::map<HashableDimensionKey, int> mPaused;
int64_t mLastStartTime;
std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
+
+ FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap);
+ FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary);
+ FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange);
+ FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
+ FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index ca9cdfb..2344cb4 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -190,12 +190,13 @@
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
unordered_map<int, std::vector<int>>& conditionToMetricMap,
- unordered_map<int, std::vector<int>>& trackerToMetricMap) {
+ unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ unordered_map<string, int>& metricMap) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
config.event_metric_size() + config.value_metric_size();
allMetricProducers.reserve(allMetricsCount);
- StatsPullerManager& statsPullerManager = StatsPullerManager::GetInstance();
+ StatsPullerManager statsPullerManager;
uint64_t startTimeNs = time(nullptr) * NS_PER_SEC;
// Build MetricProducers for each metric defined in config.
@@ -203,11 +204,12 @@
for (int i = 0; i < config.count_metric_size(); i++) {
const CountMetric& metric = config.count_metric(i);
if (!metric.has_what()) {
- ALOGW("cannot find what in CountMetric %lld", metric.metric_id());
+ ALOGW("cannot find what in CountMetric %s", metric.name().c_str());
return false;
}
int metricIndex = allMetricProducers.size();
+ metricMap.insert({metric.name(), metricIndex});
int trackerIndex;
if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
@@ -220,6 +222,11 @@
handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap);
+ } else {
+ if (metric.links_size() > 0) {
+ ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+ return false;
+ }
}
sp<MetricProducer> countProducer =
@@ -231,6 +238,7 @@
for (int i = 0; i < config.duration_metric_size(); i++) {
int metricIndex = allMetricProducers.size();
const DurationMetric& metric = config.duration_metric(i);
+ metricMap.insert({metric.name(), metricIndex});
auto what_it = conditionTrackerMap.find(metric.what());
if (what_it == conditionTrackerMap.end()) {
@@ -247,6 +255,8 @@
const auto& simpleCondition = durationWhat.simple_condition();
+ bool nesting = simpleCondition.count_nesting();
+
int trackerIndices[3] = {-1, -1, -1};
if (!simpleCondition.has_start() ||
!handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
@@ -276,15 +286,20 @@
int conditionIndex = -1;
- if (metric.has_predicate()) {
- handleMetricWithConditions(metric.predicate(), metricIndex, conditionTrackerMap,
+ if (metric.has_condition()) {
+ handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap);
+ } else {
+ if (metric.links_size() > 0) {
+ ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+ return false;
+ }
}
sp<MetricProducer> durationMetric = new DurationMetricProducer(
metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
- wizard, internalDimension, startTimeNs);
+ nesting, wizard, internalDimension, startTimeNs);
allMetricProducers.push_back(durationMetric);
}
@@ -293,8 +308,9 @@
for (int i = 0; i < config.event_metric_size(); i++) {
int metricIndex = allMetricProducers.size();
const EventMetric& metric = config.event_metric(i);
- if (!metric.has_metric_id() || !metric.has_what()) {
- ALOGW("cannot find the metric id or what in config");
+ metricMap.insert({metric.name(), metricIndex});
+ if (!metric.has_name() || !metric.has_what()) {
+ ALOGW("cannot find the metric name or what in config");
return false;
}
int trackerIndex;
@@ -308,6 +324,11 @@
handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap);
+ } else {
+ if (metric.links_size() > 0) {
+ ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+ return false;
+ }
}
sp<MetricProducer> eventMetric =
@@ -320,11 +341,12 @@
for (int i = 0; i < config.value_metric_size(); i++) {
const ValueMetric& metric = config.value_metric(i);
if (!metric.has_what()) {
- ALOGW("cannot find what in ValueMetric %lld", metric.metric_id());
+ ALOGW("cannot find what in ValueMetric %s", metric.name().c_str());
return false;
}
int metricIndex = allMetricProducers.size();
+ metricMap.insert({metric.name(), metricIndex});
int trackerIndex;
if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
@@ -349,6 +371,11 @@
handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap);
+ } else {
+ if (metric.links_size() > 0) {
+ ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+ return false;
+ }
}
sp<MetricProducer> valueProducer =
@@ -360,11 +387,12 @@
for (int i = 0; i < config.gauge_metric_size(); i++) {
const GaugeMetric& metric = config.gauge_metric(i);
if (!metric.has_what()) {
- ALOGW("cannot find what in ValueMetric %lld", metric.metric_id());
+ ALOGW("cannot find what in ValueMetric %s", metric.name().c_str());
return false;
}
int metricIndex = allMetricProducers.size();
+ metricMap.insert({metric.name(), metricIndex});
int trackerIndex;
if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
allLogEntryMatchers, logTrackerMap, trackerToMetricMap,
@@ -389,24 +417,51 @@
handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap);
+ } else {
+ if (metric.links_size() > 0) {
+ ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+ return false;
+ }
}
sp<MetricProducer> gaugeProducer =
- new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId);
+ new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
allMetricProducers.push_back(gaugeProducer);
}
return true;
}
+bool initAlerts(const StatsdConfig& config, const unordered_map<string, int>& metricProducerMap,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
+ for (int i = 0; i < config.alert_size(); i++) {
+ const Alert& alert = config.alert(i);
+ const auto& itr = metricProducerMap.find(alert.metric_name());
+ if (itr == metricProducerMap.end()) {
+ ALOGW("alert has unknown metric name: %s %s", alert.name().c_str(),
+ alert.metric_name().c_str());
+ return false;
+ }
+ const int metricIndex = itr->second;
+ sp<AnomalyTracker> anomalyTracker =
+ new AnomalyTracker(alert, allMetricProducers[metricIndex]->getBuckeSizeInNs());
+ allMetricProducers[metricIndex]->addAnomalyTracker(anomalyTracker);
+ allAnomalyTrackers.push_back(anomalyTracker);
+ }
+ return true;
+}
+
bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
+ vector<sp<AnomalyTracker>>& allAnomalyTrackers,
unordered_map<int, std::vector<int>>& conditionToMetricMap,
unordered_map<int, std::vector<int>>& trackerToMetricMap,
unordered_map<int, std::vector<int>>& trackerToConditionMap) {
unordered_map<string, int> logTrackerMap;
unordered_map<string, int> conditionTrackerMap;
+ unordered_map<string, int> metricProducerMap;
if (!initLogTrackers(config, logTrackerMap, allLogEntryMatchers, allTagIds)) {
ALOGE("initLogMatchingTrackers failed");
@@ -422,10 +477,14 @@
if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
allConditionTrackers, allMetricProducers, conditionToMetricMap,
- trackerToMetricMap)) {
+ trackerToMetricMap, metricProducerMap)) {
ALOGE("initMetricProducers failed");
return false;
}
+ if (!initAlerts(config, metricProducerMap, allMetricProducers, allAnomalyTrackers)) {
+ ALOGE("initAlerts failed");
+ return false;
+ }
return true;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index e089d065..7d7e0c3 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -21,7 +21,7 @@
#include <vector>
#include "../condition/ConditionTracker.h"
-#include "../external/StatsPullerManager.h"
+#include "../external/StatsPullerManagerImpl.h"
#include "../matchers/LogMatchingTracker.h"
namespace android {
@@ -88,6 +88,7 @@
std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
+ vector<sp<AnomalyTracker>>& allAnomalyTrackers,
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h
index 13e776f..5aa3db5 100644
--- a/cmds/statsd/src/packages/PackageInfoListener.h
+++ b/cmds/statsd/src/packages/PackageInfoListener.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 7b27642..6c32d3e 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, versionCode 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,7 +52,7 @@
void UidMap::updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
const vector<String16>& packageName) {
- updateMap(time(nullptr) * 1000000000, uid, versionCode, packageName);
+ updateMap(time(nullptr) * NS_PER_SEC, uid, versionCode, packageName);
}
void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
@@ -76,7 +76,7 @@
}
void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) {
- updateApp(time(nullptr) * 1000000000, app_16, uid, versionCode);
+ updateApp(time(nullptr) * NS_PER_SEC, app_16, uid, versionCode);
}
void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
@@ -112,7 +112,7 @@
}
void UidMap::removeApp(const String16& app_16, const int32_t& uid) {
- removeApp(time(nullptr) * 1000000000, app_16, uid);
+ removeApp(time(nullptr) * NS_PER_SEC, app_16, uid);
}
void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) {
lock_guard<mutex> lock(mMutex);
@@ -202,7 +202,7 @@
}
UidMapping UidMap::getOutput(const ConfigKey& key) {
- return getOutput(time(nullptr) * 1000000000, key);
+ return getOutput(time(nullptr) * NS_PER_SEC, key);
}
UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) {
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index de68fbc..24eb966 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/cmds/statsd/src/stats_events_copy.proto b/cmds/statsd/src/stats_events_copy.proto
deleted file mode 100644
index 898856b..0000000
--- a/cmds/statsd/src/stats_events_copy.proto
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// STOPSHIP: this is a duplicate of stats_event.proto with LITE_RUNTIME added
-// to produce device side lite proto. We should move statsd to soong so that
-// we can generate full and lite library from the same proto file.
-syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
-
-// TODO: Not the right package and class name
-package android.os.statsd;
-option java_package = "com.android.os";
-option java_outer_classname = "StatsEventProto";
-
-/**
- * The master event class. This message defines all of the available
- * raw stats log events from the Android system, also known as "atoms."
- *
- * This field contains a single oneof with all of the available messages.
- * The stats-log-api-gen tool runs as part of the Android build and
- * generates the android.util.StatsLog class, which contains the constants
- * and methods that Android uses to log.
- *
- * This StatsEvent class is not actually built into the Android system.
- * Instead, statsd on Android constructs these messages synthetically,
- * in the format defined here and in stats_log.proto.
- */
-message StatsEvent {
- oneof event {
- // For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
- BleScanStateChanged ble_scan_state_changed = 2;
- BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3;
- BleScanResultReceived ble_scan_result_received = 4;
- SensorStateChanged sensor_state_changed = 5;
- GpsScanStateChanged gps_scan_state_changed = 6; // TODO: untested
- SyncStateChanged sync_state_changed = 7;
- ScheduledJobStateChanged scheduled_job_state_changed = 8;
- ScreenBrightnessChanged screen_brightness_changed = 9;
- // 10-20 are temporarily reserved for wakelocks etc.
- UidWakelockStateChanged uid_wakelock_state_changed = 11;
- LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 12;
- BatterySaverModeStateChanged battery_saver_mode_state_changed = 21;
- DeviceIdleModeStateChanged device_idle_mode_state_changed = 22;
- AudioStateChanged audio_state_changed = 23;
- MediaCodecActivityChanged media_codec_activity_changed = 24;
- CameraStateChanged camera_state_changed = 25;
- FlashlightStateChanged flashlight_state_changed = 26;
- UidProcessStateChanged uid_process_state_changed = 27;
- ProcessLifeCycleStateChanged process_life_cycle_state_changed = 28;
- ScreenStateChanged screen_state_changed = 29;
- DeviceTemperatureReported device_temperature_reported = 33;
- // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
- }
-}
-
-/**
- * A WorkSource represents the chained attribution of applications that
- * resulted in a particular bit of work being done.
- */
-message WorkSource {
- // TODO
-}
-
-/*
- * *****************************************************************************
- * Below are all of the individual atoms that are logged by Android via statsd
- * and Westworld.
- *
- * RULES:
- * - The field ids for each atom must start at 1, and count upwards by 1.
- * Skipping field ids is not allowed.
- * - These form an API, so renaming, renumbering or removing fields is
- * not allowed between android releases. (This is not currently enforced,
- * but there will be a tool to enforce this restriction).
- * - The types must be built-in protocol buffer types, namely, no sub-messages
- * are allowed (yet). The bytes type is also not allowed.
- * - The CamelCase name of the message type should match the
- * underscore_separated name as defined in StatsEvent.
- * - If an atom represents work that can be attributed to an app, there can
- * be exactly one WorkSource field. It must be field number 1.
- * - A field that is a uid should be a string field, tagged with the [xxx]
- * annotation. The generated code on android will be represented by UIDs,
- * and those UIDs will be translated in xxx to those strings.
- *
- * CONVENTIONS:
- * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange.
- * - If there is a UID, it goes first. Think in an object-oriented fashion.
- * *****************************************************************************
- */
-
-/**
- * Logs the temperature of the device, in tenths of a degree Celsius.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message DeviceTemperatureReported {
- // Temperature in tenths of a degree C.
- optional int32 temperature = 1;
-}
-
-/**
- * Logs when the screen state changes.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
- */
-message ScreenStateChanged {
- // TODO: Use the real screen state.
- enum State {
- STATE_UNKNOWN = 0;
- STATE_OFF = 1;
- STATE_ON = 2;
- STATE_DOZE = 3;
- STATE_DOZE_SUSPEND = 4;
- STATE_VR = 5;
- }
- // New screen state.
- optional State display_state = 1;
-}
-
-/**
- * Logs that the state of a process state, as per the activity manager, has changed.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
- */
-message UidProcessStateChanged {
- optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
-
- // The state.
- // TODO: Use the real (mapped) process states.
- optional int32 state = 2;
-}
-
-/**
- * Logs that a process started, finished, crashed, or ANRed.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
- */
-message ProcessLifeCycleStateChanged {
- // TODO: Use the real (mapped) process states.
- optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
-
- // TODO: What is this?
- optional string name = 2;
-
- // The state.
- // TODO: Use an enum.
- optional int32 event = 3;
-}
-
-
-
-/**
- * Logs when the ble scan state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message BleScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs when an unoptimized ble scan state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
-message BleUnoptimizedScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs reporting of a ble scan finding results.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
-message BleScanResultReceived {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // Number of ble scan results returned.
- optional int32 num_of_results = 2;
-}
-
-/**
- * Logs when a sensor state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message SensorStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // TODO: Is there a way to get the actual name of the sensor?
- // The id (int) of the sensor.
- optional int32 sensor_id = 2;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 3;
-}
-
-
-/**
- * Logs when GPS state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message GpsScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-
-/**
- * Logs when a sync manager sync state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message SyncStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // Name of the sync (as named in the app)
- optional string name = 2;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 3;
-}
-
-/**
- * Logs when a job scheduler job state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message ScheduledJobStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // Name of the job (as named in the app)
- optional string name = 2;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 3;
-
- // TODO: Consider adding the stopReason (int)
-}
-
-/**
- * Logs when the audio state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message AudioStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs when the video codec state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message MediaCodecActivityChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs when the flashlight state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message FlashlightStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs when the camera state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message CameraStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs that the state of a wakelock (per app and per wakelock name) has changed.
- *
- * Logged from:
- * TODO
- */
-message WakelockChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // Type of wakelock.
- enum Type {
- PARTIAL = 0;
- FULL = 1;
- WINDOW = 2;
- }
- optional int32 type = 2;
-
- // The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
- optional string tag = 3;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 4;
-}
-
-/**
- * Logs when an app is holding a wakelock, regardless of the wakelock's name.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message UidWakelockStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // Type of wakelock.
- enum Type {
- PARTIAL = 0;
- FULL = 1;
- WINDOW = 2;
- }
- optional int32 type = 2;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 3;
-}
-
-/**
- * Logs when a partial wakelock is considered 'long' (over 1 min).
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message LongPartialWakelockStateChanged {
- // TODO: Add attribution instead of uid?
- optional int32 uid = 1;
-
- // The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
- optional string tag = 2;
-
- // TODO: I have no idea what this is.
- optional string history_tag = 3;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 4;
-}
-
-/**
- * Logs Battery Saver state change.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message BatterySaverModeStateChanged {
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 1;
-}
-
-/**
- * Logs Doze mode state change.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
- */
-message DeviceIdleModeStateChanged {
- // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_.
- optional int32 state = 1;
-}
-
-/**
- * Logs screen brightness level.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
- */
-message ScreenBrightnessChanged {
- // Screen brightness level. Should be in [-1, 255] according to PowerManager.java.
- optional int32 level = 1;
-}
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 1e37ff8..4f5df55 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -22,7 +22,7 @@
option java_package = "com.android.os";
option java_outer_classname = "StatsLog";
-import "frameworks/base/cmds/statsd/src/stats_events_copy.proto";
+import "frameworks/base/cmds/statsd/src/atoms_copy.proto";
message KeyValuePair {
optional int32 key = 1;
@@ -38,7 +38,7 @@
message EventMetricData {
optional int64 timestamp_nanos = 1;
- optional StatsEvent stats_events = 2;
+ optional Atom atom = 2;
}
message CountBucketInfo {
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index e1d0aceb..b7d8f97 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -35,15 +35,14 @@
typedef std::map<std::string, HashableDimensionKey> ConditionKey;
-// TODO: For P, change int to int64_t.
-// TODO: Should HashableDimensionKey be marked here as const?
-typedef std::unordered_map<HashableDimensionKey, int> DimToValMap;
+typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
EventMetricData parse(log_msg msg);
int getTagId(log_msg msg);
std::string getHashableKey(std::vector<KeyValuePair> key);
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 3b8eeaf..e75a37f 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -47,6 +47,7 @@
}
enum LogicalOperation {
+ LOGICAL_OPERATION_UNSPECIFIED = 0;
AND = 1;
OR = 2;
NOT = 3;
@@ -83,7 +84,13 @@
optional string stop_all = 4;
- repeated KeyMatcher dimension = 5;
+ enum InitialValue {
+ UNKNOWN = 0;
+ FALSE = 1;
+ }
+ optional InitialValue initial_value = 5 [default = UNKNOWN];
+
+ repeated KeyMatcher dimension = 6;
}
message Condition {
@@ -105,137 +112,129 @@
optional int64 bucket_size_millis = 1;
}
-message Alert {
- message IncidentdDetails {
- optional string alert_name = 1;
- repeated int32 incidentd_sections = 2;
- }
- optional IncidentdDetails incidentd_details = 1;
+message EventConditionLink {
+ optional string condition = 1;
- optional int32 number_of_buckets = 3;
+ repeated KeyMatcher key_in_main = 2;
- optional int32 refractory_period_secs = 4;
-
- optional int64 trigger_if_sum_gt = 5;
-
- optional int32 refractory_period_in_buckets = 6;
+ repeated KeyMatcher key_in_condition = 3;
}
message EventMetric {
- optional int64 metric_id = 1;
+ optional string name = 1;
- optional string what = 2;
+ optional string what = 2;
- optional string condition = 3;
+ optional string condition = 3;
- repeated EventConditionLink links = 4;
+ repeated EventConditionLink links = 4;
}
message CountMetric {
- optional int64 metric_id = 1;
+ optional string name = 1;
- optional string what = 2;
+ optional string what = 2;
- optional string condition = 3;
+ optional string condition = 3;
- repeated KeyMatcher dimension = 4;
+ repeated KeyMatcher dimension = 4;
- optional Bucket bucket = 5;
+ optional Bucket bucket = 5;
- repeated Alert alerts = 6;
-
- optional bool include_in_output = 7;
-
- repeated EventConditionLink links = 8;
+ repeated EventConditionLink links = 6;
}
message DurationMetric {
- optional int64 metric_id = 1;
+ optional string name = 1;
- optional string what = 2;
+ optional string what = 2;
- enum AggregationType {
- DURATION_SUM = 1;
+ optional string condition = 3;
- DURATION_MAX_SPARSE = 2;
- }
- optional AggregationType type = 3;
+ repeated EventConditionLink links = 4;
- optional string predicate = 4;
+ enum AggregationType {
+ SUM = 1;
- repeated KeyMatcher dimension = 5;
+ MAX_SPARSE = 2;
+ }
+ optional AggregationType aggregation_type = 5 [default = SUM];
- optional Bucket bucket = 6;
+ repeated KeyMatcher dimension = 6;
- repeated Alert alerts = 7;
-
- repeated EventConditionLink links = 8;
+ optional Bucket bucket = 7;
}
message GaugeMetric {
- optional int64 metric_id = 1;
+ optional string name = 1;
- optional string what = 2;
+ optional string what = 2;
- optional int32 gauge_field = 3;
+ optional int32 gauge_field = 3;
- optional string condition = 4;
+ optional string condition = 4;
- repeated KeyMatcher dimension = 5;
+ repeated KeyMatcher dimension = 5;
- optional Bucket bucket = 6;
+ optional Bucket bucket = 6;
- repeated Alert alerts = 7;
-
- repeated EventConditionLink links = 8;
+ repeated EventConditionLink links = 7;
}
message ValueMetric {
- optional int64 metric_id = 1;
+ optional string name = 1;
- optional string what = 2;
+ optional string what = 2;
- optional int32 value_field = 3;
+ optional int32 value_field = 3;
- optional string condition = 4;
+ optional string condition = 4;
- repeated KeyMatcher dimension = 5;
+ repeated KeyMatcher dimension = 5;
- optional Bucket bucket = 6;
+ optional Bucket bucket = 6;
- repeated Alert alerts = 7;
+ repeated EventConditionLink links = 7;
- repeated EventConditionLink links = 8;
-
- enum Operation {
- SUM = 1;
- }
- optional Operation operation = 9 [default = SUM];
+ enum AggregationType {
+ SUM = 1;
+ }
+ optional AggregationType aggregation_type = 8 [default = SUM];
}
-message EventConditionLink {
- optional string condition = 1;
+message Alert {
+ optional string name = 1;
- repeated KeyMatcher key_in_main = 2;
- repeated KeyMatcher key_in_condition = 3;
-};
+ optional string metric_name = 2;
+
+ message IncidentdDetails {
+ repeated int32 section = 1;
+ }
+ optional IncidentdDetails incidentd_details = 3;
+
+ optional int32 number_of_buckets = 4;
+
+ optional int32 refractory_period_secs = 5;
+
+ optional int64 trigger_if_sum_gt = 6;
+}
message StatsdConfig {
- optional int64 config_id = 1;
+ optional string name = 1;
- repeated EventMetric event_metric = 2;
+ repeated EventMetric event_metric = 2;
- repeated CountMetric count_metric = 3;
+ repeated CountMetric count_metric = 3;
- repeated ValueMetric value_metric = 4;
+ repeated ValueMetric value_metric = 4;
- repeated DurationMetric duration_metric = 5;
+ repeated GaugeMetric gauge_metric = 5;
- repeated GaugeMetric gauge_metric = 6;
+ repeated DurationMetric duration_metric = 6;
- repeated LogEntryMatcher log_entry_matcher = 7;
+ repeated LogEntryMatcher log_entry_matcher = 7;
- repeated Condition condition = 8;
+ repeated Condition condition = 8;
- repeated Alert alerts = 9;
+ repeated Alert alert = 9;
}
diff --git a/cmds/statsd/tests/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AnomalyMonitor_test.cpp
index 59fa160..920ca08 100644
--- a/cmds/statsd/tests/AnomalyMonitor_test.cpp
+++ b/cmds/statsd/tests/AnomalyMonitor_test.cpp
@@ -20,6 +20,8 @@
#ifdef __ANDROID__
TEST(AnomalyMonitor, popSoonerThan) {
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> set;
AnomalyMonitor am(2);
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index aa896ca..f570522 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -30,7 +30,7 @@
namespace statsd {
static ostream& operator<<(ostream& os, const StatsdConfig& config) {
- return os << "StatsdConfig{id=" << config.config_id() << "}";
+ return os << "StatsdConfig{name=" << config.name().c_str() << "}";
}
} // namespace statsd
@@ -56,8 +56,8 @@
/**
* Validate that the StatsdConfig is the one we wanted.
*/
-MATCHER_P(StatsdConfigEq, configId, "") {
- return arg.config_id() == configId;
+MATCHER_P(StatsdConfigEq, name, "") {
+ return arg.name() == name;
}
/**
@@ -70,13 +70,13 @@
manager->AddListener(listener);
StatsdConfig config91;
- config91.set_config_id(91);
+ config91.set_name("91");
StatsdConfig config92;
- config92.set_config_id(92);
+ config92.set_name("92");
StatsdConfig config93;
- config93.set_config_id(93);
+ config93.set_name("93");
StatsdConfig config94;
- config94.set_config_id(94);
+ config94.set_name("94");
{
InSequence s;
@@ -85,27 +85,27 @@
// TODO: Remove this when we get rid of the fake one, and make this
// test loading one from disk somewhere.
EXPECT_CALL(*(listener.get()),
- OnConfigUpdated(ConfigKeyEq(0, "fake"), StatsdConfigEq(12345)))
+ OnConfigUpdated(ConfigKeyEq(1000, "fake"), StatsdConfigEq("12345")))
.RetiresOnSaturation();
manager->Startup();
// Add another one
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq(91)))
+ EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq("91")))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(1, "zzz"), config91);
// Update It
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq(92)))
+ EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq("92")))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(1, "zzz"), config92);
// Add one with the same uid but a different name
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "yyy"), StatsdConfigEq(93)))
+ EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "yyy"), StatsdConfigEq("93")))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(1, "yyy"), config93);
// Add one with the same name but a different uid
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, "zzz"), StatsdConfigEq(94)))
+ EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, "zzz"), StatsdConfigEq("94")))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(2, "zzz"), config94);
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index d0898b0..40c0e9d 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -27,7 +27,7 @@
using std::unordered_map;
using std::vector;
-const int TAG_ID = 123;
+const int32_t TAG_ID = 123;
const int FIELD_ID_1 = 1;
const int FIELD_ID_2 = 2;
const int FIELD_ID_3 = 2;
@@ -43,8 +43,6 @@
simpleMatcher->set_tag(TAG_ID);
LogEvent event(TAG_ID, 0);
-
- // Convert to a LogEvent
event.init();
// Test
@@ -63,10 +61,8 @@
// Set up the event
LogEvent event(TAG_ID, 0);
- auto list = event.GetAndroidLogEventList();
- *list << true;
- *list << false;
-
+ event.write(true);
+ event.write(false);
// Convert to a LogEvent
event.init();
@@ -99,9 +95,7 @@
// Set up the event
LogEvent event(TAG_ID, 0);
- auto list = event.GetAndroidLogEventList();
- *list << "some value";
-
+ event.write("some value");
// Convert to a LogEvent
event.init();
@@ -109,6 +103,38 @@
EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
}
+TEST(LogEntryMatcherTest, TestMultiFieldsMatcher) {
+ // Set up the matcher
+ LogEntryMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+ simpleMatcher->set_tag(TAG_ID);
+ auto keyValue1 = simpleMatcher->add_key_value_matcher();
+ keyValue1->mutable_key_matcher()->set_key(FIELD_ID_1);
+ auto keyValue2 = simpleMatcher->add_key_value_matcher();
+ keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
+
+ // Set up the event
+ LogEvent event(TAG_ID, 0);
+ event.write(2);
+ event.write(3);
+
+ // Convert to a LogEvent
+ event.init();
+
+ // Test
+ keyValue1->set_eq_int(2);
+ keyValue2->set_eq_int(3);
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+
+ keyValue1->set_eq_int(2);
+ keyValue2->set_eq_int(4);
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+
+ keyValue1->set_eq_int(4);
+ keyValue2->set_eq_int(3);
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+}
+
TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
// Set up the matcher
LogEntryMatcher matcher;
@@ -120,9 +146,7 @@
// Set up the event
LogEvent event(TAG_ID, 0);
- auto list = event.GetAndroidLogEventList();
- *list << 11;
-
+ event.write(11);
event.init();
// Test
@@ -168,8 +192,6 @@
EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
}
-#if 0
-
TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) {
// Set up the matcher
LogEntryMatcher matcher;
@@ -179,22 +201,28 @@
auto keyValue = simpleMatcher->add_key_value_matcher();
keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
- LogEvent event;
- event.tagId = TAG_ID;
-
+ LogEvent event1(TAG_ID, 0);
keyValue->set_lt_float(10.0);
- event.floatMap[FIELD_ID_1] = 10.1;
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
- event.floatMap[FIELD_ID_1] = 9.9;
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ event1.write(10.1f);
+ event1.init();
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, event1));
+ LogEvent event2(TAG_ID, 0);
+ event2.write(9.9f);
+ event2.init();
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, event2));
+
+ LogEvent event3(TAG_ID, 0);
+ event3.write(10.1f);
+ event3.init();
keyValue->set_gt_float(10.0);
- event.floatMap[FIELD_ID_1] = 10.1;
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
- event.floatMap[FIELD_ID_1] = 9.9;
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, event3));
+
+ LogEvent event4(TAG_ID, 0);
+ event4.write(9.9f);
+ event4.init();
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, event4));
}
-#endif
// Helper for the composite matchers.
void addSimpleMatcher(SimpleLogEntryMatcher* simpleMatcher, int tag, int key, int val) {
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 7333785..3dd4e70 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -34,6 +34,7 @@
using std::set;
using std::unordered_map;
using std::vector;
+using android::os::statsd::Condition;
#ifdef __ANDROID__
@@ -41,7 +42,7 @@
StatsdConfig buildGoodConfig() {
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("SCREEN_IS_ON");
@@ -71,12 +72,25 @@
combination->add_matcher("SCREEN_IS_ON");
combination->add_matcher("SCREEN_IS_OFF");
+ CountMetric* metric = config.add_count_metric();
+ metric->set_name("3");
+ metric->set_what("SCREEN_IS_ON");
+ metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ KeyMatcher* keyMatcher = metric->add_dimension();
+ keyMatcher->set_key(1);
+
+ auto alert = config.add_alert();
+ alert->set_name("3");
+ alert->set_metric_name("3");
+ alert->set_number_of_buckets(10);
+ alert->set_refractory_period_secs(100);
+ alert->set_trigger_if_sum_gt(100);
return config;
}
StatsdConfig buildCircleMatchers() {
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("SCREEN_IS_ON");
@@ -100,9 +114,32 @@
return config;
}
+StatsdConfig buildAlertWithUnknownMetric() {
+ StatsdConfig config;
+ config.set_name("12345");
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_name("3");
+ metric->set_what("SCREEN_IS_ON");
+ metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+ KeyMatcher* keyMatcher = metric->add_dimension();
+ keyMatcher->set_key(1);
+
+ auto alert = config.add_alert();
+ alert->set_name("3");
+ alert->set_metric_name("2");
+ alert->set_number_of_buckets(10);
+ alert->set_refractory_period_secs(100);
+ alert->set_trigger_if_sum_gt(100);
+ return config;
+}
+
StatsdConfig buildMissingMatchers() {
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("SCREEN_IS_ON");
@@ -128,7 +165,7 @@
StatsdConfig buildDimensionMetricsWithMultiTags() {
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("BATTERY_VERY_LOW");
@@ -150,18 +187,24 @@
// Count process state changes, slice by uid, while SCREEN_IS_OFF
CountMetric* metric = config.add_count_metric();
- metric->set_metric_id(3);
+ metric->set_name("3");
metric->set_what("BATTERY_LOW");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
KeyMatcher* keyMatcher = metric->add_dimension();
keyMatcher->set_key(1);
+ auto alert = config.add_alert();
+ alert->set_name("3");
+ alert->set_metric_name("3");
+ alert->set_number_of_buckets(10);
+ alert->set_refractory_period_secs(100);
+ alert->set_trigger_if_sum_gt(100);
return config;
}
StatsdConfig buildCircleConditions() {
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("SCREEN_IS_ON");
@@ -183,7 +226,7 @@
simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
- Condition* condition = config.add_condition();
+ auto condition = config.add_condition();
condition->set_name("SCREEN_IS_ON");
SimpleCondition* simpleCondition = condition->mutable_simple_condition();
simpleCondition->set_start("SCREEN_IS_ON");
@@ -206,13 +249,16 @@
vector<sp<LogMatchingTracker>> allLogEntryMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
- allMetricProducers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap));
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
+ EXPECT_EQ(1u, allMetricProducers.size());
+ EXPECT_EQ(1u, allAnomalyTrackers.size());
}
TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
@@ -221,13 +267,14 @@
vector<sp<LogMatchingTracker>> allLogEntryMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
- allMetricProducers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap));
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -236,13 +283,14 @@
vector<sp<LogMatchingTracker>> allLogEntryMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
- allMetricProducers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap));
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -251,13 +299,13 @@
vector<sp<LogMatchingTracker>> allLogEntryMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
-
EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
- allMetricProducers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap));
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, TestCircleConditionDependency) {
@@ -266,13 +314,30 @@
vector<sp<LogMatchingTracker>> allLogEntryMatchers;
vector<sp<ConditionTracker>> allConditionTrackers;
vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
- allMetricProducers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap));
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
+ StatsdConfig config = buildAlertWithUnknownMetric();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
+ trackerToMetricMap, trackerToConditionMap));
}
#else
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index c64719e..0c19468 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -34,12 +34,12 @@
TEST(UidMapTest, TestIsolatedUID) {
sp<UidMap> m = new UidMap();
- StatsLogProcessor p(m, nullptr);
+ sp<AnomalyMonitor> anomalyMonitor;
+ StatsLogProcessor p(m, anomalyMonitor, nullptr);
LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
- android_log_event_list* list = addEvent.GetAndroidLogEventList();
- *list << 100; // parent UID
- *list << 101; // isolated UID
- *list << 1; // Indicates creation.
+ addEvent.write(100); // parent UID
+ addEvent.write(101); // isolated UID
+ addEvent.write(1); // Indicates creation.
addEvent.init();
EXPECT_EQ(101, m->getParentUidOrSelf(101));
@@ -48,10 +48,9 @@
EXPECT_EQ(100, m->getParentUidOrSelf(101));
LogEvent removeEvent(android::util::ISOLATED_UID_CHANGED, 1);
- list = removeEvent.GetAndroidLogEventList();
- *list << 100; // parent UID
- *list << 101; // isolated UID
- *list << 0; // Indicates removal.
+ removeEvent.write(100); // parent UID
+ removeEvent.write(101); // isolated UID
+ removeEvent.write(0); // Indicates removal.
removeEvent.init();
p.OnLogEvent(removeEvent);
EXPECT_EQ(101, m->getParentUidOrSelf(101));
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index b8150d0..e0200f27 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/anomaly/DiscreteAnomalyTracker.h"
+#include "src/anomaly/AnomalyTracker.h"
#include <gtest/gtest.h>
#include <stdio.h>
@@ -37,7 +37,7 @@
}
}
-std::shared_ptr<DimToValMap> MockeBucket(
+std::shared_ptr<DimToValMap> MockBucket(
const std::vector<std::pair<string, long>>& key_value_pair_list) {
std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
AddValueToBucket(key_value_pair_list, bucket);
@@ -45,190 +45,240 @@
}
TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
+ const int64_t bucketSizeNs = 30 * NS_PER_SEC;
Alert alert;
alert.set_number_of_buckets(3);
- alert.set_refractory_period_in_buckets(3);
+ alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
alert.set_trigger_if_sum_gt(2);
- DiscreteAnomalyTracker anomaly_tracker(alert);
+ AnomalyTracker anomalyTracker(alert, bucketSizeNs);
- std::shared_ptr<DimToValMap> bucket0 = MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}});
- // Adds bucket #0
- anomaly_tracker.addOrUpdateBucket(bucket0, 0);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+ std::shared_ptr<DimToValMap> bucket0 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+ int64_t eventTimestamp0 = 10;
+ std::shared_ptr<DimToValMap> bucket1 = MockBucket({{"a", 1}});
+ int64_t eventTimestamp1 = bucketSizeNs + 11;
+ std::shared_ptr<DimToValMap> bucket2 = MockBucket({{"b", 1}});
+ int64_t eventTimestamp2 = 2 * bucketSizeNs + 12;
+ std::shared_ptr<DimToValMap> bucket3 = MockBucket({{"a", 2}});
+ int64_t eventTimestamp3 = 3 * bucketSizeNs + 13;
+ std::shared_ptr<DimToValMap> bucket4 = MockBucket({{"b", 1}});
+ int64_t eventTimestamp4 = 4 * bucketSizeNs + 14;
+ std::shared_ptr<DimToValMap> bucket5 = MockBucket({{"a", 2}});
+ int64_t eventTimestamp5 = 5 * bucketSizeNs + 15;
+ std::shared_ptr<DimToValMap> bucket6 = MockBucket({{"a", 2}});
+ int64_t eventTimestamp6 = 6 * bucketSizeNs + 16;
- // Adds bucket #0 again. The sum does not change.
- anomaly_tracker.addOrUpdateBucket(bucket0, 0);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 0L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(0, *bucket0));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp0, 0, *bucket0);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
- // Adds bucket #1.
- std::shared_ptr<DimToValMap> bucket1 = MockeBucket({{"b", 2}});
- anomaly_tracker.addOrUpdateBucket(bucket1, 1);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- // Alarm.
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+ // Adds past bucket #0
+ anomalyTracker.addPastBucket(bucket0, 0);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 1, *bucket1);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
- // Adds bucket #1 again. The sum does not change.
- anomaly_tracker.addOrUpdateBucket(bucket1, 1);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- // Alarm.
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+ // Adds past bucket #0 again. The sum does not change.
+ anomalyTracker.addPastBucket(bucket0, 0);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1 + 1, 1, *bucket1);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1L);
- // Adds bucket #2.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 2);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 2L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Adds past bucket #1.
+ anomalyTracker.addPastBucket(bucket1, 1);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 2, *bucket2);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+
+ // Adds past bucket #1 again. Nothing changes.
+ anomalyTracker.addPastBucket(bucket1, 1);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2 + 1, 2, *bucket2);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+
+ // Adds past bucket #2.
+ anomalyTracker.addPastBucket(bucket2, 2);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(3, *bucket3));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 3, *bucket3);
// Within refractory period.
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
// Adds bucket #3.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 3);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 3L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomalyTracker.addPastBucket(bucket3, 3L);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(4, *bucket4));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 4, *bucket4);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
- // Adds bucket #3.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 2}}), 4);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 4L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- // Within refractory period.
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+ // Adds bucket #4.
+ anomalyTracker.addPastBucket(bucket4, 4);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(5, *bucket5));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 5, *bucket5);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp5);
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 5);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 5L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Adds bucket #5.
+ anomalyTracker.addPastBucket(bucket5, 5);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(6, *bucket6));
// Within refractory period.
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 5L);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 6, *bucket6);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp5);
}
TEST(AnomalyTrackerTest, TestSparseBuckets) {
+ const int64_t bucketSizeNs = 30 * NS_PER_SEC;
Alert alert;
alert.set_number_of_buckets(3);
- alert.set_refractory_period_in_buckets(3);
+ alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
alert.set_trigger_if_sum_gt(2);
- DiscreteAnomalyTracker anomaly_tracker(alert);
+ AnomalyTracker anomalyTracker(alert, bucketSizeNs);
- // Add bucket #9
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}}), 9);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 9L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+ std::shared_ptr<DimToValMap> bucket9 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+ std::shared_ptr<DimToValMap> bucket16 = MockBucket({{"b", 4}});
+ std::shared_ptr<DimToValMap> bucket18 = MockBucket({{"b", 1}, {"c", 1}});
+ std::shared_ptr<DimToValMap> bucket20 = MockBucket({{"b", 3}, {"c", 1}});
+ std::shared_ptr<DimToValMap> bucket25 = MockBucket({{"d", 1}});
+ std::shared_ptr<DimToValMap> bucket28 = MockBucket({{"e", 2}});
- // Add bucket #16
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 4}}), 16);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 16L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+ int64_t eventTimestamp1 = bucketSizeNs * 8 + 1;
+ int64_t eventTimestamp2 = bucketSizeNs * 15 + 11;
+ int64_t eventTimestamp3 = bucketSizeNs * 17 + 1;
+ int64_t eventTimestamp4 = bucketSizeNs * 19 + 2;
+ int64_t eventTimestamp5 = bucketSizeNs * 24 + 3;
+ int64_t eventTimestamp6 = bucketSizeNs * 27 + 3;
- // Add bucket #18
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(9, *bucket9));
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 9, *bucket9);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, -1);
+
+ // Add past bucket #9
+ anomalyTracker.addPastBucket(bucket9, 9);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(16, *bucket16));
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 16, *bucket16);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
+
+ // Add past bucket #16
+ anomalyTracker.addPastBucket(bucket16, 16);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(18, *bucket18));
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
// Within refractory period.
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 18, *bucket18);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
- // Add bucket #18 again.
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+ // Add past bucket #18
+ anomalyTracker.addPastBucket(bucket18, 18);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 20, *bucket20);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
- // Add bucket #20
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 3}, {"d", 1}}), 20);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 20L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+ // Add bucket #18 again. Nothing changes.
+ anomalyTracker.addPastBucket(bucket18, 18);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4 + 1, 20, *bucket20);
+ // Within refractory period.
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
- // Add bucket #25
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"d", 1}}), 25);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 25L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1L);
- EXPECT_FALSE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+ // Add past bucket #20
+ anomalyTracker.addPastBucket(bucket20, 20);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 3LL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(25, *bucket25));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 25, *bucket25);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
- // Add bucket #28
- anomaly_tracker.addOrUpdateBucket(MockeBucket({{"e", 5}}), 28);
- EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 28L);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("e")->second, 5L);
- EXPECT_TRUE(anomaly_tracker.detectAnomaly());
- anomaly_tracker.declareAndDeclareAnomaly();
- EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 3L);
- EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 28L);
+ // Add past bucket #25
+ anomalyTracker.addPastBucket(bucket25, 25);
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("d"), 1LL);
+ EXPECT_FALSE(anomalyTracker.detectAnomaly(28, *bucket28));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 28, *bucket28);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
+
+ // Updates current bucket #28.
+ (*bucket28)["e"] = 5;
+ EXPECT_TRUE(anomalyTracker.detectAnomaly(28, *bucket28));
+ EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6 + 7, 28, *bucket28);
+ EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp6 + 7);
}
} // namespace statsd
diff --git a/cmds/statsd/tests/ConditionTracker_test.cpp b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
similarity index 98%
rename from cmds/statsd/tests/ConditionTracker_test.cpp
rename to cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
index 2935ac7..23d6926 100644
--- a/cmds/statsd/tests/ConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
@@ -23,7 +23,6 @@
using namespace android::os::statsd;
using std::vector;
-
#ifdef __ANDROID__
TEST(ConditionTrackerTest, TestUnknownCondition) {
LogicalOperation operation = LogicalOperation::AND;
@@ -39,7 +38,7 @@
conditionResults.push_back(ConditionState::kTrue);
EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults),
- ConditionState::kUnknown);
+ ConditionState::kUnknown);
}
TEST(ConditionTrackerTest, TestAndCondition) {
// Set up the matcher
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
new file mode 100644
index 0000000..80a0068
--- /dev/null
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -0,0 +1,480 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "src/condition/SimpleConditionTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using std::map;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
+ bool outputSlicedUid) {
+ SimpleCondition simpleCondition;
+ simpleCondition.set_start("WAKE_LOCK_ACQUIRE");
+ simpleCondition.set_stop("WAKE_LOCK_RELEASE");
+ simpleCondition.set_stop_all("RELEASE_ALL");
+ if (outputSlicedUid) {
+ KeyMatcher* keyMatcher = simpleCondition.add_dimension();
+ keyMatcher->set_key(1);
+ }
+
+ simpleCondition.set_count_nesting(countNesting);
+ simpleCondition.set_initial_value(defaultFalse ? SimpleCondition_InitialValue_FALSE
+ : SimpleCondition_InitialValue_UNKNOWN);
+ return simpleCondition;
+}
+
+void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) {
+ event->write(uid); // uid
+ event->write(wl);
+ event->write(acquire);
+ event->init();
+}
+
+map<string, HashableDimensionKey> getWakeLockQueryKey(int key, int uid,
+ const string& conditionName) {
+ // test query
+ KeyValuePair kv1;
+ kv1.set_key(key);
+ kv1.set_value_int(uid);
+ vector<KeyValuePair> kv_list;
+ kv_list.push_back(kv1);
+ map<string, HashableDimensionKey> queryKey;
+ queryKey[conditionName] = getHashableKey(kv_list);
+ return queryKey;
+}
+
+TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
+ SimpleCondition simpleCondition;
+ simpleCondition.set_start("SCREEN_TURNED_ON");
+ simpleCondition.set_stop("SCREEN_TURNED_OFF");
+ simpleCondition.set_count_nesting(false);
+
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
+ trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
+
+ SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*tracker index*/, simpleCondition,
+ trackerNameIndexMap);
+
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+
+ vector<sp<ConditionTracker>> allConditions;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ // not matched start or stop. condition doesn't change
+ EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
+ EXPECT_FALSE(changedCache[0]);
+
+ // prepare a case for match start.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ // now condition should change to true.
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ EXPECT_TRUE(changedCache[0]);
+
+ // match nothing.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ EXPECT_FALSE(changedCache[0]);
+
+ // the case for match stop.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ // condition changes to false.
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ EXPECT_TRUE(changedCache[0]);
+
+ // match stop again.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ // condition should still be false. not changed.
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ EXPECT_FALSE(changedCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
+ SimpleCondition simpleCondition;
+ simpleCondition.set_start("SCREEN_TURNED_ON");
+ simpleCondition.set_stop("SCREEN_TURNED_OFF");
+ simpleCondition.set_count_nesting(true);
+
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
+ trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
+
+ SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*condition tracker index*/,
+ simpleCondition, trackerNameIndexMap);
+
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+
+ // one matched start
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allConditions;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ EXPECT_TRUE(changedCache[0]);
+
+ // prepare for another matched start.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ EXPECT_FALSE(changedCache[0]);
+
+ // ONE MATCHED STOP
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ // result should still be true
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ EXPECT_FALSE(changedCache[0]);
+
+ // ANOTHER MATCHED STOP
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ // result should still be true
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ EXPECT_TRUE(changedCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
+ SimpleCondition simpleCondition = getWakeLockHeldCondition(
+ true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
+ string conditionName = "WL_HELD_BY_UID2";
+
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+ trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+ trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+ SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+ simpleCondition, trackerNameIndexMap);
+ int uid = 111;
+
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event, uid, "wl1", 1);
+
+ // one matched start
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allConditions;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // Now test query
+ const auto queryKey = getWakeLockQueryKey(1, uid, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+ // another wake lock acquired by this uid
+ LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event2, uid, "wl2", 1);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_FALSE(changedCache[0]);
+
+ // wake lock 1 release
+ LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event3, uid, "wl1", 0); // now release it.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+ changedCache);
+ // nothing changes, because wake lock 2 is still held for this uid
+ EXPECT_FALSE(changedCache[0]);
+
+ LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event4, uid, "wl2", 0); // now release it.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // query again
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
+ SimpleCondition simpleCondition = getWakeLockHeldCondition(
+ true /*nesting*/, true /*default to false*/, false /*slice output by uid*/);
+ string conditionName = "WL_HELD";
+
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+ trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+ trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+ SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+ simpleCondition, trackerNameIndexMap);
+ int uid1 = 111;
+ string uid1_wl1 = "wl1_1";
+ int uid2 = 222;
+ string uid2_wl1 = "wl2_1";
+
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event, uid1, uid1_wl1, 1);
+
+ // one matched start for uid1
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allConditions;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // Now test query
+ map<string, HashableDimensionKey> queryKey;
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+ // another wake lock acquired by this uid
+ LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event2, uid2, uid2_wl1, 1);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_FALSE(changedCache[0]);
+
+ // uid1 wake lock 1 release
+ LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event3, uid1, uid1_wl1, 0); // now release it.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+ changedCache);
+ // nothing changes, because uid2 is still holding wl.
+ EXPECT_FALSE(changedCache[0]);
+
+ LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event4, uid2, uid2_wl1, 0); // now release it.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // query again
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestStopAll) {
+ SimpleCondition simpleCondition = getWakeLockHeldCondition(
+ true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
+ string conditionName = "WL_HELD_BY_UID3";
+
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+ trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+ trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+ SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+ simpleCondition, trackerNameIndexMap);
+ int uid1 = 111;
+ int uid2 = 222;
+
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event, uid1, "wl1", 1);
+
+ // one matched start
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allConditions;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // Now test query
+ const auto queryKey = getWakeLockQueryKey(1, uid1, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+ // another wake lock acquired by uid2
+ LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event2, uid2, "wl2", 1);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // TEST QUERY
+ const auto queryKey2 = getWakeLockQueryKey(1, uid2, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+
+ // stop all event
+ LogEvent event3(2 /*tagId*/, 0 /*timestamp*/);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_TRUE(changedCache[0]);
+ EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+
+ // TEST QUERY
+ const auto queryKey3 = getWakeLockQueryKey(1, uid1, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+
+ // TEST QUERY
+ const auto queryKey4 = getWakeLockQueryKey(1, uid2, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp
index 600b953..d6cd876 100644
--- a/cmds/statsd/tests/indexed_priority_queue_test.cpp
+++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp
@@ -22,10 +22,12 @@
/** struct for template in indexed_priority_queue */
struct AATest : public RefBase {
- AATest(uint32_t val) : val(val) {
+ AATest(uint32_t val, std::string a, std::string b) : val(val), a(a), b(b) {
}
const int val;
+ const std::string a;
+ const std::string b;
struct Smaller {
bool operator()(const sp<const AATest> a, const sp<const AATest> b) const {
@@ -36,9 +38,11 @@
#ifdef __ANDROID__
TEST(indexed_priority_queue, empty_and_size) {
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4 = new AATest{4};
- sp<const AATest> aa8 = new AATest{8};
+ sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId};
EXPECT_EQ(0u, ipq.size());
EXPECT_TRUE(ipq.empty());
@@ -61,13 +65,15 @@
}
TEST(indexed_priority_queue, top) {
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa2 = new AATest{2};
- sp<const AATest> aa4 = new AATest{4};
- sp<const AATest> aa8 = new AATest{8};
- sp<const AATest> aa12 = new AATest{12};
- sp<const AATest> aa16 = new AATest{16};
- sp<const AATest> aa20 = new AATest{20};
+ sp<const AATest> aa2 = new AATest{2, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa12 = new AATest{12, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa16 = new AATest{16, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa20 = new AATest{20, emptyMetricId, emptyDimensionId};
EXPECT_EQ(ipq.top(), nullptr);
@@ -113,9 +119,11 @@
}
TEST(indexed_priority_queue, push_same_aa) {
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4_a = new AATest{4};
- sp<const AATest> aa4_b = new AATest{4};
+ sp<const AATest> aa4_a = new AATest{4, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId};
ipq.push(aa4_a);
EXPECT_EQ(1u, ipq.size());
@@ -134,9 +142,11 @@
}
TEST(indexed_priority_queue, remove_nonexistant) {
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4 = new AATest{4};
- sp<const AATest> aa5 = new AATest{5};
+ sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa5 = new AATest{5, emptyMetricId, emptyDimensionId};
ipq.push(aa4);
ipq.remove(aa5);
@@ -147,8 +157,10 @@
TEST(indexed_priority_queue, remove_same_aa) {
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4_a = new AATest{4};
- sp<const AATest> aa4_b = new AATest{4};
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
+ sp<const AATest> aa4_a = new AATest{4, emptyMetricId, emptyDimensionId};
+ sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId};
ipq.push(aa4_a);
ipq.push(aa4_b);
@@ -184,9 +196,11 @@
TEST(indexed_priority_queue, pop) {
indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> a = new AATest{1};
- sp<const AATest> b = new AATest{2};
- sp<const AATest> c = new AATest{3};
+ std::string emptyMetricId;
+ std::string emptyDimensionId;
+ sp<const AATest> a = new AATest{1, emptyMetricId, emptyDimensionId};
+ sp<const AATest> b = new AATest{2, emptyMetricId, emptyDimensionId};
+ sp<const AATest> c = new AATest{3, emptyMetricId, emptyDimensionId};
ipq.push(c);
ipq.push(b);
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 5a4ee73..35e08af 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -40,7 +40,7 @@
int tagId = 1;
CountMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
LogEvent event1(tagId, bucketStartTimeNs + 1);
@@ -54,21 +54,26 @@
// 2 events in bucket 1.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
- countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+
+ // Flushes at event #2.
+ countProducer.flushIfNeeded(bucketStartTimeNs + 2);
+ EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+ // Flushes.
+ countProducer.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
- const auto& bucketInfo = buckets[0];
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
- EXPECT_EQ(2LL, bucketInfo.mCount);
+ EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
+ EXPECT_EQ(2LL, buckets[0].mCount);
// 1 matched event happens in bucket 2.
LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3, false);
- countProducer.flushCounterIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+ countProducer.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
@@ -79,7 +84,7 @@
EXPECT_EQ(1LL, bucketInfo2.mCount);
// nothing happens in bucket 3. we should not record anything for bucket 3.
- countProducer.flushCounterIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 1);
+ countProducer.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
@@ -92,7 +97,7 @@
int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
CountMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
metric.set_condition("SCREEN_ON");
@@ -108,20 +113,22 @@
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
+ // Upon this match event, the matched event1 is flushed.
countProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
- countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-
+ countProducer.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
- EXPECT_EQ(1UL, buckets.size());
- const auto& bucketInfo = buckets[0];
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
- EXPECT_EQ(1LL, bucketInfo.mCount);
+ {
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ EXPECT_EQ(1UL, buckets.size());
+ const auto& bucketInfo = buckets[0];
+ EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
+ EXPECT_EQ(1LL, bucketInfo.mCount);
+ }
}
TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
@@ -129,7 +136,7 @@
int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
CountMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
EventConditionLink* link = metric.add_links();
@@ -138,15 +145,13 @@
link->add_key_in_condition()->set_key(2);
LogEvent event1(1, bucketStartTimeNs + 1);
- auto list = event1.GetAndroidLogEventList();
- *list << "111"; // uid
+ event1.write("111"); // uid
event1.init();
ConditionKey key1;
key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
LogEvent event2(1, bucketStartTimeNs + 10);
- auto list2 = event2.GetAndroidLogEventList();
- *list2 << "222"; // uid
+ event2.write("222"); // uid
event2.init();
ConditionKey key2;
key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
@@ -160,10 +165,11 @@
bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
+ countProducer.flushIfNeeded(bucketStartTimeNs + 1);
+ EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
-
- countProducer.flushCounterIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-
+ countProducer.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
@@ -175,6 +181,68 @@
EXPECT_EQ(1LL, bucketInfo.mCount);
}
+TEST(CountMetricProducerTest, TestAnomalyDetection) {
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name("1");
+ alert.set_trigger_if_sum_gt(2);
+ alert.set_number_of_buckets(2);
+ alert.set_refractory_period_secs(1);
+
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 30 * NS_PER_SEC;
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+
+ CountMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+ bucketStartTimeNs);
+ countProducer.addAnomalyTracker(anomalyTracker);
+
+ int tagId = 1;
+ LogEvent event1(tagId, bucketStartTimeNs + 1);
+ LogEvent event2(tagId, bucketStartTimeNs + 2);
+ LogEvent event3(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1);
+ LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1);
+ LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2);
+ LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3);
+ LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3 + NS_PER_SEC);
+
+ // Two events in bucket #0.
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
+
+ EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
+
+ // One event in bucket #2. No alarm as bucket #0 is trashed out.
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3, false);
+ EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
+
+ // Two events in bucket #3.
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6, false);
+ EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
+ // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event5.GetTimestampNs());
+
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7, false);
+ EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event7.GetTimestampNs());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 76dbc73..18d177c 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -38,7 +38,7 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
EventMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1);
LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2);
@@ -61,7 +61,7 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
EventMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
metric.set_condition("SCREEN_ON");
LogEvent event1(1, bucketStartTimeNs + 1);
@@ -87,7 +87,7 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
EventMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
EventConditionLink* link = metric.add_links();
link->set_condition("APP_IN_BACKGROUND_PER_UID");
@@ -95,15 +95,13 @@
link->add_key_in_condition()->set_key(2);
LogEvent event1(1, bucketStartTimeNs + 1);
- auto list = event1.GetAndroidLogEventList();
- *list << "111"; // uid
+ event1.write("111"); // uid
event1.init();
ConditionKey key1;
key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
LogEvent event2(1, bucketStartTimeNs + 10);
- auto list2 = event2.GetAndroidLogEventList();
- *list2 << "222"; // uid
+ event2.write("222"); // uid
event2.init();
ConditionKey key2;
key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
new file mode 100644
index 0000000..b9e2b8a
--- /dev/null
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -0,0 +1,201 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "logd/LogEvent.h"
+#include "metrics_test_helper.h"
+#include "src/metrics/GaugeMetricProducer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(GaugeMetricProducerTest, TestWithCondition) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ GaugeMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_gauge_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ GaugeMetricProducer gaugeProducer(metric, 1 /*has condition*/, wizard, -1, bucketStartTimeNs);
+
+ vector<std::shared_ptr<LogEvent>> allData;
+ std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
+ event1->write(1);
+ event1->write(13);
+ event1->init();
+ allData.push_back(event1);
+
+ std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 10);
+ event2->write(1);
+ event2->write(15);
+ event2->init();
+ allData.push_back(event2);
+
+ gaugeProducer.onDataPulled(allData);
+ gaugeProducer.flushIfNeeded(event2->GetTimestampNs() + 1);
+ EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+
+ gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 11);
+ gaugeProducer.onConditionChanged(false, bucketStartTimeNs + 21);
+ gaugeProducer.onConditionChanged(true, bucketStartTimeNs + bucketSizeNs + 11);
+ std::shared_ptr<LogEvent> event3 =
+ std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+ event3->write(1);
+ event3->write(25);
+ event3->init();
+ allData.push_back(event3);
+ gaugeProducer.onDataPulled(allData);
+ gaugeProducer.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 10);
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(25L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ // One dimension.
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.front().mGauge);
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.front().mBucketNum);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ gaugeProducer.mPastBuckets.begin()->second.front().mBucketStartNs);
+}
+
+TEST(GaugeMetricProducerTest, TestNoCondition) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ GaugeMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_gauge_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ GaugeMetricProducer gaugeProducer(metric, -1 /*no condition*/, wizard, -1, bucketStartTimeNs);
+
+ vector<std::shared_ptr<LogEvent>> allData;
+ std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
+ event1->write(1);
+ event1->write(13);
+ event1->init();
+ allData.push_back(event1);
+
+ std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 10);
+ event2->write(1);
+ event2->write(15);
+ event2->init();
+ allData.push_back(event2);
+
+ std::shared_ptr<LogEvent> event3 =
+ std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+ event3->write(1);
+ event3->write(25);
+ event3->init();
+ allData.push_back(event3);
+
+ gaugeProducer.onDataPulled(allData);
+ // Has one slice
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(25L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(13L, gaugeProducer.mPastBuckets.begin()->second.front().mGauge);
+ EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.begin()->second.front().mBucketNum);
+ EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.back().mGauge);
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ gaugeProducer.mPastBuckets.begin()->second.back().mBucketStartNs);
+}
+
+TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ GaugeMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_gauge_field(2);
+ GaugeMetricProducer gaugeProducer(metric, -1 /*no condition*/, wizard, -1, bucketStartTimeNs);
+
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name("1");
+ alert.set_trigger_if_sum_gt(25);
+ alert.set_number_of_buckets(2);
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+ gaugeProducer.addAnomalyTracker(anomalyTracker);
+
+ std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
+ event1->write(1);
+ event1->write(13);
+ event1->init();
+
+ gaugeProducer.onDataPulled({event1});
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
+
+ std::shared_ptr<LogEvent> event2 =
+ std::make_shared<LogEvent>(1, bucketStartTimeNs + bucketSizeNs + 10);
+ event2->write(1);
+ event2->write(15);
+ event2->init();
+
+ gaugeProducer.onDataPulled({event2});
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event2->GetTimestampNs());
+
+ std::shared_ptr<LogEvent> event3 =
+ std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+ event3->write(1);
+ event3->write(24);
+ event3->init();
+
+ gaugeProducer.onDataPulled({event3});
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(24L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event3->GetTimestampNs());
+
+ // The event4 does not have the gauge field. Thus the current bucket value is 0.
+ std::shared_ptr<LogEvent> event4 =
+ std::make_shared<LogEvent>(1, bucketStartTimeNs + 3 * bucketSizeNs + 10);
+ event4->write(1);
+ event4->init();
+ gaugeProducer.onDataPulled({event4});
+ EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event3->GetTimestampNs());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 58bf1b3..9e169bb 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -45,17 +45,49 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+ MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
- tracker.noteStart("", true, bucketStartTimeNs, key1);
- tracker.noteStop("", bucketStartTimeNs + 10);
+ tracker.noteStart("1", true, bucketStartTimeNs, key1);
+ // Event starts again. This would not change anything as it already starts.
+ tracker.noteStart("1", true, bucketStartTimeNs + 3, key1);
+ // Stopped.
+ tracker.noteStop("1", bucketStartTimeNs + 10, false);
- tracker.noteStart("", true, bucketStartTimeNs + 20, key1);
- tracker.noteStop("", bucketStartTimeNs + 40);
+ // Another event starts in this bucket.
+ tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
+ tracker.noteStop("2", bucketStartTimeNs + 40, false /*stop all*/);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1u, buckets.size());
- EXPECT_EQ(20, buckets[0].mDuration);
+ EXPECT_EQ(20ULL, buckets[0].mDuration);
+}
+
+TEST(MaxDurationTrackerTest, TestStopAll) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ vector<DurationBucket> buckets;
+ ConditionKey key1;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ tracker.noteStart("1", true, bucketStartTimeNs + 1, key1);
+
+ // Another event starts in this bucket.
+ tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
+ tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
+ EXPECT_TRUE(tracker.mInfos.empty());
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(40ULL, buckets[1].mDuration);
}
TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
@@ -67,14 +99,67 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+ MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+ // The event starts.
tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
+
+ // Starts again. Does not change anything.
+ tracker.noteStart("", true, bucketStartTimeNs + bucketSizeNs + 1, key1);
+
+ // Flushes at early 2nd bucket. The event still does not stop yet.
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
+
+ // Flushes at the end of the 2nd bucket. The event still does not stop yet.
+ tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs));
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
+ EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration);
+
+ // The event stops at early 4th bucket.
+ tracker.noteStop("", bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/);
+ tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 21);
+ EXPECT_EQ(3u, buckets.size());
+ EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
+ EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration);
+ EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[2].mDuration);
+}
+
+TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ vector<DurationBucket> buckets;
+ ConditionKey key1;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ // 2 starts
+ tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
+ tracker.noteStart("", true, bucketStartTimeNs + 10, key1);
+ // one stop
+ tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/);
+
tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
EXPECT_EQ(2u, buckets.size());
- EXPECT_EQ((long long)(bucketSizeNs - 1), buckets[0].mDuration);
- EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration);
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+
+ // real stop now.
+ tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+ tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1);
+
+ EXPECT_EQ(3u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+ EXPECT_EQ(5ULL, buckets[2].mDuration);
}
TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
@@ -93,17 +178,65 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t durationTimeNs = 2 * 1000;
- MaxDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+ MaxDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+ EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 2 * bucketSizeNs + 5);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
- tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+ tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + durationTimeNs, false);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+ EXPECT_TRUE(tracker.mInfos.empty());
+ EXPECT_EQ(6LL, tracker.mDuration);
- tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+ tracker.noteStart("2:maps", false, eventStartTimeNs + 3 * bucketSizeNs + 10, key1);
+ EXPECT_EQ(1u, tracker.mInfos.size());
+ for (const auto& itr : tracker.mInfos) {
+ EXPECT_EQ(DurationState::kPaused, itr.second.state);
+ EXPECT_EQ(0LL, itr.second.lastDuration);
+ }
+ EXPECT_EQ(3u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+ EXPECT_EQ(6ULL, buckets[2].mDuration);
+}
- tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
- EXPECT_EQ(1u, buckets.size());
- EXPECT_EQ(5, buckets[0].mDuration);
+TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name("1");
+ alert.set_trigger_if_sum_gt(32 * NS_PER_SEC);
+ alert.set_number_of_buckets(2);
+ alert.set_refractory_period_secs(1);
+
+ vector<DurationBucket> buckets;
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+ uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
+ uint64_t bucketSizeNs = 30 * NS_PER_SEC;
+
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+ MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs,
+ {anomalyTracker}, buckets);
+
+ tracker.noteStart("1", true, eventStartTimeNs, key1);
+ tracker.noteStop("1", eventStartTimeNs + 10, false);
+ EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, -1);
+ EXPECT_EQ(10LL, tracker.mDuration);
+
+ tracker.noteStart("2", true, eventStartTimeNs + 20, key1);
+ tracker.noteStop("2", eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false);
+ EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration);
+ EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs,
+ (long long)(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC));
}
} // namespace statsd
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 74a6f11..f4edffd 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -45,18 +45,105 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t durationTimeNs = 2 * 1000;
+ uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+ OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
+ tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
+ EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
+ tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(durationTimeNs, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestDurationNested) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ vector<DurationBucket> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
- tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+ tracker.noteStop("2:maps", eventStartTimeNs + 2000, false);
+ tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1u, buckets.size());
- EXPECT_EQ(durationTimeNs, buckets[0].mDuration);
+ EXPECT_EQ(2003ULL, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestStopAll) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ vector<DurationBucket> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
+
+ tracker.noteStopAll(eventStartTimeNs + 2003);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(2003ULL, buckets[0].mDuration);
+}
+
+TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ vector<DurationBucket> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ uint64_t durationTimeNs = 2 * 1000;
+
+ OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
+ tracker.noteStart("2:maps", true, eventStartTimeNs + 2 * bucketSizeNs, key1);
+ EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
+
+ tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 10, false);
+ tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 12, false);
+ tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
}
TEST(OringDurationTrackerTest, TestDurationConditionChange) {
@@ -73,20 +160,151 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t durationTimeNs = 2 * 1000;
+ uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+ OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
- tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 2 * bucketSizeNs + 5);
+ tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + durationTimeNs, false);
+ tracker.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + durationTimeNs);
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
+ EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+}
- tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ EXPECT_CALL(*wizard, query(_, key1)) // #4
+ .WillOnce(Return(ConditionState::kFalse));
+
+ vector<DurationBucket> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
+ buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + 3, false);
+
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 15);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1u, buckets.size());
- EXPECT_EQ(5, buckets[0].mDuration);
+ EXPECT_EQ(15ULL, buckets[0].mDuration);
}
+
+TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name("1");
+ alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
+ alert.set_number_of_buckets(2);
+ alert.set_refractory_period_secs(1);
+
+ vector<DurationBucket> buckets;
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+ uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
+ uint64_t bucketSizeNs = 30 * NS_PER_SEC;
+
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+ OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs,
+ {anomalyTracker}, buckets);
+
+ // Nothing in the past bucket.
+ tracker.noteStart("", true, eventStartTimeNs, key1);
+ EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
+
+ tracker.noteStop("", eventStartTimeNs + 3, false);
+ EXPECT_EQ(0u, buckets.size());
+
+ uint64_t event1StartTimeNs = eventStartTimeNs + 10;
+ tracker.noteStart("1", true, event1StartTimeNs, key1);
+ // No past buckets. The anomaly will happen in bucket #0.
+ EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
+
+ uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10;
+ tracker.noteStop("1", event1StopTimeNs, false);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10,
+ buckets[0].mDuration);
+
+ const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10;
+ const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs;
+
+ // One past buckets. The anomaly will happen in bucket #1.
+ uint64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15;
+ tracker.noteStart("1", true, event2StartTimeNs, key1);
+ EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration -
+ bucket1Duration),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
+ tracker.noteStop("1", event2StartTimeNs + 1, false);
+
+ // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in
+ // bucket #2.
+ uint64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC;
+ tracker.noteStart("1", true, event3StartTimeNs, key1);
+ EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL),
+ tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
+}
+
+TEST(OringDurationTrackerTest, TestAnomalyDetection) {
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name("1");
+ alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
+ alert.set_number_of_buckets(2);
+ alert.set_refractory_period_secs(1);
+
+ vector<DurationBucket> buckets;
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+ uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
+ uint64_t bucketSizeNs = 30 * NS_PER_SEC;
+
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
+ OringDurationTracker tracker("event", wizard, 1, true /*nesting*/, bucketStartTimeNs,
+ bucketSizeNs, {anomalyTracker}, buckets);
+
+ tracker.noteStart("", true, eventStartTimeNs, key1);
+ tracker.noteStop("", eventStartTimeNs + 10, false);
+ EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, -1);
+ EXPECT_TRUE(tracker.mStarted.empty());
+ EXPECT_EQ(-1LL, tracker.mLastStartTime);
+ EXPECT_EQ(10LL, tracker.mDuration);
+
+ EXPECT_EQ(0u, tracker.mStarted.size());
+
+ tracker.noteStart("", true, eventStartTimeNs + 20, key1);
+ EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+ EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
+ (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
+ tracker.noteStop("", eventStartTimeNs + 2 * bucketSizeNs + 25, false);
+ EXPECT_EQ(anomalyTracker->getSumOverPastBuckets("event"), (long long)(bucketSizeNs));
+ EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25),
+ anomalyTracker->mLastAlarmTimestampNs);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
new file mode 100644
index 0000000..1ed3636
--- /dev/null
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -0,0 +1,291 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "metrics_test_helper.h"
+#include "src/metrics/ValueMetricProducer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+using std::shared_ptr;
+using std::make_shared;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/*
+ * Tests pulled atoms with no conditions
+ */
+TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2*bucketSizeNs;
+
+ ValueMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_value_field(2);
+
+ int tagId = 1;
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ // TODO: pending refactor of StatsPullerManager
+ // For now we still need this so that it doesn't do real pulling.
+ shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+
+ ValueMetricProducer valueProducer(metric, -1 /*-1 meaning no condition*/, wizard,tagId,
+ bucketStartTimeNs, pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+ event->write(1);
+ event->write(11);
+ event->init();
+ allData.push_back(event);
+
+ valueProducer.onDataPulled(allData);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 11, 11
+ EXPECT_EQ(11, curInterval.raw.front().first);
+ EXPECT_EQ(11, curInterval.raw.front().second);
+ ValueMetricProducer::Interval nextInterval = valueProducer.mNextSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, nextInterval.raw.size());
+ // value is 11, 0
+ EXPECT_EQ(11, nextInterval.raw.front().first);
+ EXPECT_EQ(0, nextInterval.raw.front().second);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ allData.clear();
+ event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(1);
+ event->write(22);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 22, 0
+ EXPECT_EQ(22, curInterval.raw.front().first);
+ EXPECT_EQ(0, curInterval.raw.front().second);
+ EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(11, valueProducer.mPastBuckets.begin()->second.back().mValue);
+
+ allData.clear();
+ event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
+ event->write(1);
+ event->write(33);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 33, 0
+ EXPECT_EQ(33, curInterval.raw.front().first);
+ EXPECT_EQ(0, curInterval.raw.front().second);
+ EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(11, valueProducer.mPastBuckets.begin()->second.back().mValue);
+}
+
+/*
+ * Test pulled event with non sliced condition.
+ */
+TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+
+ ValueMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_value_field(2);
+ metric.set_condition("SCREEN_ON");
+
+ int tagId = 1;
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(1);
+ event->write(100);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ .WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
+ event->write(1);
+ event->write(120);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(metric, 1, wizard,tagId,
+ bucketStartTimeNs, pullerManager);
+
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 10);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 100, 0
+ EXPECT_EQ(100, curInterval.raw.front().first);
+ EXPECT_EQ(0, curInterval.raw.front().second);
+ EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(1);
+ event->write(110);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 110, 0
+ EXPECT_EQ(110, curInterval.raw.front().first);
+ EXPECT_EQ(0, curInterval.raw.front().second);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue);
+
+ valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 110, 120
+ EXPECT_EQ(110, curInterval.raw.front().first);
+ EXPECT_EQ(120, curInterval.raw.front().second);
+}
+
+TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+
+ ValueMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_value_field(2);
+
+ int tagId = 1;
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+
+ ValueMetricProducer valueProducer(metric, -1, wizard,-1,
+ bucketStartTimeNs, pullerManager);
+
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event1->write(1);
+ event1->write(10);
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event2->write(1);
+ event2->write(20);
+ event2->init();
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1, false);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 10, 0
+ EXPECT_EQ(10, curInterval.raw.front().first);
+ EXPECT_EQ(0, curInterval.raw.front().second);
+ EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2, false);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(2UL, curInterval.raw.size());
+ // value is 10, 20
+ EXPECT_EQ(10, curInterval.raw.front().first);
+ EXPECT_EQ(20, curInterval.raw.back().first);
+ EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+
+ valueProducer.flushIfNeeded(bucket3StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 5fd7d62..fa221aa 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -14,6 +14,7 @@
#pragma once
#include "src/condition/ConditionWizard.h"
+#include "src/external/StatsPullerManager.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -30,6 +31,13 @@
const std::map<std::string, HashableDimensionKey>& conditionParameters));
};
+class MockStatsPullerManager : public StatsPullerManager {
+public:
+ MOCK_METHOD3(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver, long intervalMs));
+ MOCK_METHOD2(UnRegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver));
+ MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
+};
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/webview_zygote/Android.mk b/cmds/webview_zygote/Android.mk
index 66e762c..955e58e 100644
--- a/cmds/webview_zygote/Android.mk
+++ b/cmds/webview_zygote/Android.mk
@@ -21,6 +21,8 @@
LOCAL_SRC_FILES := webview_zygote.cpp
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
libbinder \
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 42b0f6b..1503445 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -2,3 +2,8 @@
name: "IKeyAttestationApplicationIdProvider.aidl",
srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
}
+
+filegroup {
+ name: "IKeystoreService.aidl",
+ srcs: ["android/security/IKeystoreService.aidl"],
+}
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index ee89ca8..cc95eb6 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -254,6 +254,11 @@
HashMap<String, PropertyValuesHolder> mValuesMap;
/**
+ * If set to non-negative value, this will override {@link #sDurationScale}.
+ */
+ private float mDurationScale = -1f;
+
+ /**
* Public constants
*/
@@ -579,8 +584,23 @@
return this;
}
+ /**
+ * Overrides the global duration scale by a custom value.
+ *
+ * @param durationScale The duration scale to set; or {@code -1f} to use the global duration
+ * scale.
+ * @hide
+ */
+ public void overrideDurationScale(float durationScale) {
+ mDurationScale = durationScale;
+ }
+
+ private float resolveDurationScale() {
+ return mDurationScale >= 0f ? mDurationScale : sDurationScale;
+ }
+
private long getScaledDuration() {
- return (long)(mDuration * sDurationScale);
+ return (long)(mDuration * resolveDurationScale());
}
/**
@@ -735,7 +755,10 @@
if (mSeekFraction >= 0) {
return (long) (mDuration * mSeekFraction);
}
- float durationScale = sDurationScale == 0 ? 1 : sDurationScale;
+ float durationScale = resolveDurationScale();
+ if (durationScale == 0f) {
+ durationScale = 1f;
+ }
return (long) ((AnimationUtils.currentAnimationTimeMillis() - mStartTime) / durationScale);
}
@@ -1397,7 +1420,9 @@
if (mStartTime < 0) {
// First frame. If there is start delay, start delay count down will happen *after* this
// frame.
- mStartTime = mReversing ? frameTime : frameTime + (long) (mStartDelay * sDurationScale);
+ mStartTime = mReversing
+ ? frameTime
+ : frameTime + (long) (mStartDelay * resolveDurationScale());
}
// Handle pause/resume
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a8863bf..03a3631 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -193,10 +193,13 @@
* <a name="Fragments"></a>
* <h3>Fragments</h3>
*
- * <p>Starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB}, Activity
- * implementations can make use of the {@link Fragment} class to better
+ * <p>The {@link android.support.v4.app.FragmentActivity} subclass
+ * can make use of the {@link android.support.v4.app.Fragment} class to better
* modularize their code, build more sophisticated user interfaces for larger
- * screens, and help scale their application between small and large screens.
+ * screens, and help scale their application between small and large screens.</p>
+ *
+ * <p>For more information about using fragments, read the
+ * <a href="{@docRoot}guide/components/fragments.html">Fragments</a> developer guide.</p>
*
* <a name="ActivityLifecycle"></a>
* <h3>Activity Lifecycle</h3>
@@ -915,7 +918,10 @@
/**
* Return the LoaderManager for this activity, creating it if needed.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportLoaderManager()}
*/
+ @Deprecated
public LoaderManager getLoaderManager() {
return mFragments.getLoaderManager();
}
@@ -2395,7 +2401,10 @@
/**
* Return the FragmentManager for interacting with fragments associated
* with this activity.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()}
*/
+ @Deprecated
public FragmentManager getFragmentManager() {
return mFragments.getFragmentManager();
}
@@ -2404,7 +2413,11 @@
* Called when a Fragment is being attached to this activity, immediately
* after the call to its {@link Fragment#onAttach Fragment.onAttach()}
* method and before {@link Fragment#onCreate Fragment.onCreate()}.
+ *
+ * @deprecated Use {@link
+ * android.support.v4.app.FragmentActivity#onAttachFragment(android.support.v4.app.Fragment)}
*/
+ @Deprecated
public void onAttachFragment(Fragment fragment) {
}
@@ -4366,7 +4379,7 @@
throw new IllegalArgumentException("requestCode should be >= 0");
}
if (mHasCurrentPermissionsRequest) {
- Log.w(TAG, "Can reqeust only one set of permissions at a time");
+ Log.w(TAG, "Can request only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
@@ -5106,7 +5119,11 @@
*
* @see Fragment#startActivity
* @see Fragment#startActivityForResult
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentActivity#startActivityFromFragment(
+ * android.support.v4.app.Fragment,Intent,int)}
*/
+ @Deprecated
public void startActivityFromFragment(@NonNull Fragment fragment,
@RequiresPermission Intent intent, int requestCode) {
startActivityFromFragment(fragment, intent, requestCode, null);
@@ -5131,7 +5148,11 @@
*
* @see Fragment#startActivity
* @see Fragment#startActivityForResult
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentActivity#startActivityFromFragment(
+ * android.support.v4.app.Fragment,Intent,int,Bundle)}
*/
+ @Deprecated
public void startActivityFromFragment(@NonNull Fragment fragment,
@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
startActivityForResult(fragment.mWho, intent, requestCode, options);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 21e454f..95c5fb5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5380,7 +5380,7 @@
}
}
- GraphicsEnvironment.chooseDriver(context);
+ GraphicsEnvironment.getInstance().setup(context);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index b7c1f4e..7257044 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -17,9 +17,12 @@
package android.app;
import android.os.Build;
+import android.os.GraphicsEnvironment;
import android.os.Trace;
import android.util.ArrayMap;
+
import com.android.internal.os.ClassLoaderFactory;
+
import dalvik.system.PathClassLoader;
/** @hide */
@@ -72,8 +75,9 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupVulkanLayerPath");
- setupVulkanLayerPath(classloader, librarySearchPath);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setLayerPaths");
+ GraphicsEnvironment.getInstance().setLayerPaths(
+ classloader, librarySearchPath, libraryPermittedPath);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
mLoaders.put(cacheKey, classloader);
@@ -105,8 +109,6 @@
cacheKey, null /* classLoaderName */);
}
- private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);
-
/**
* Adds a new path the classpath of the given loader.
* @throws IllegalStateException if the provided class loader is not a {@link PathClassLoader}.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0eafdec..7a4c00f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -35,7 +35,6 @@
import android.content.pm.IOnPermissionsChangeListener;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
@@ -1681,21 +1680,8 @@
}
@Override
- public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,
- String installerPackageName) {
- installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
- installerPackageName, mContext.getUserId());
- }
-
- @Override
- public void installPackage(Uri packageURI, PackageInstallObserver observer,
- int flags, String installerPackageName) {
- installCommon(packageURI, observer, flags, installerPackageName, mContext.getUserId());
- }
-
- private void installCommon(Uri packageURI,
- PackageInstallObserver observer, int flags, String installerPackageName,
- int userId) {
+ public void installPackage(Uri packageURI,
+ PackageInstallObserver observer, int flags, String installerPackageName) {
if (!"file".equals(packageURI.getScheme())) {
throw new UnsupportedOperationException("Only file:// URIs are supported");
}
@@ -1703,7 +1689,7 @@
final String originPath = packageURI.getPath();
try {
mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,
- userId);
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index f99d1a8..0643414 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -19,7 +19,6 @@
import android.os.FileUtils;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.system.ErrnoException;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -27,8 +26,6 @@
import dalvik.system.BaseDexClassLoader;
import dalvik.system.VMRuntime;
-import libcore.io.Libcore;
-
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -155,23 +152,12 @@
return;
}
- File realDexPath;
- try {
- // Secondary dex profiles are stored in the oat directory, next to the real dex file
- // and have the same name with 'cur.prof' appended. We use the realpath because that
- // is what installd is using when processing the dex file.
- // NOTE: Keep in sync with installd.
- realDexPath = new File(Libcore.os.realpath(dexPath));
- } catch (ErrnoException ex) {
- Slog.e(TAG, "Failed to get the real path of secondary dex " + dexPath
- + ":" + ex.getMessage());
- // Do not continue with registration if we could not retrieve the real path.
- return;
- }
-
+ // Secondary dex profiles are stored in the oat directory, next to dex file
+ // and have the same name with 'cur.prof' appended.
// NOTE: Keep this in sync with installd expectations.
- File secondaryProfileDir = new File(realDexPath.getParent(), "oat");
- File secondaryProfile = new File(secondaryProfileDir, realDexPath.getName() + ".cur.prof");
+ File dexPathFile = new File(dexPath);
+ File secondaryProfileDir = new File(dexPathFile.getParent(), "oat");
+ File secondaryProfile = new File(secondaryProfileDir, dexPathFile.getName() + ".cur.prof");
// Create the profile if not already there.
// Returns true if the file was created, false if the file already exists.
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index 7e0e4d8..a0fb6ee 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -136,7 +136,10 @@
*
* {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
* embed}
+ *
+ * @deprecated Use {@link android.support.v4.app.DialogFragment}
*/
+@Deprecated
public class DialogFragment extends Fragment
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 9377345..a92684b 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -256,7 +256,10 @@
* <p>After each call to this function, a new entry is on the stack, and
* pressing back will pop it to return the user to whatever previous state
* the activity UI was in.
+ *
+ * @deprecated Use {@link android.support.v4.app.Fragment}
*/
+@Deprecated
public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener {
private static final ArrayMap<String, Class<?>> sClassMap =
new ArrayMap<String, Class<?>>();
@@ -414,7 +417,10 @@
* State information that has been retrieved from a fragment instance
* through {@link FragmentManager#saveFragmentInstanceState(Fragment)
* FragmentManager.saveFragmentInstanceState}.
+ *
+ * @deprecated Use {@link android.support.v4.app.Fragment.SavedState}
*/
+ @Deprecated
public static class SavedState implements Parcelable {
final Bundle mState;
@@ -458,7 +464,10 @@
/**
* Thrown by {@link Fragment#instantiate(Context, String, Bundle)} when
* there is an instantiation failure.
+ *
+ * @deprecated Use {@link android.support.v4.app.Fragment.InstantiationException}
*/
+ @Deprecated
static public class InstantiationException extends AndroidRuntimeException {
public InstantiationException(String msg, Exception cause) {
super(msg, cause);
@@ -1031,7 +1040,10 @@
/**
* Return the LoaderManager for this fragment, creating it if needed.
+ *
+ * @deprecated Use {@link android.support.v4.app.Fragment#getLoaderManager()}
*/
+ @Deprecated
public LoaderManager getLoaderManager() {
if (mLoaderManager != null) {
return mLoaderManager;
diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java
index d0aa0fd..e3e47ae 100644
--- a/core/java/android/app/FragmentBreadCrumbs.java
+++ b/core/java/android/app/FragmentBreadCrumbs.java
@@ -65,7 +65,10 @@
/**
* Interface to intercept clicks on the bread crumbs.
+ *
+ * @deprecated This widget is no longer supported.
*/
+ @Deprecated
public interface OnBreadCrumbClickListener {
/**
* Called when a bread crumb is clicked.
diff --git a/core/java/android/app/FragmentContainer.java b/core/java/android/app/FragmentContainer.java
index f8836bc8..a1dd32f 100644
--- a/core/java/android/app/FragmentContainer.java
+++ b/core/java/android/app/FragmentContainer.java
@@ -24,7 +24,10 @@
/**
* Callbacks to a {@link Fragment}'s container.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentContainer}
*/
+@Deprecated
public abstract class FragmentContainer {
/**
* Return the view with the given resource ID. May return {@code null} if the
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index cff94d8..cbb58d4 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -37,7 +37,10 @@
* <p>
* It is the responsibility of the host to take care of the Fragment's lifecycle.
* The methods provided by {@link FragmentController} are for that purpose.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentController}
*/
+@Deprecated
public class FragmentController {
private final FragmentHostCallback<?> mHost;
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index 5ef23e6..1edc68e 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -37,7 +37,10 @@
* Fragments may be hosted by any object; such as an {@link Activity}. In order to
* host fragments, implement {@link FragmentHostCallback}, overriding the methods
* applicable to the host.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentHostCallback}
*/
+@Deprecated
public abstract class FragmentHostCallback<E> extends FragmentContainer {
private final Activity mActivity;
final Context mContext;
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 0d5cd02..12e60b8 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -74,7 +74,10 @@
* {@link android.support.v4.app.FragmentActivity}. See the blog post
* <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html">
* Fragments For All</a> for more details.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManager}
*/
+@Deprecated
public abstract class FragmentManager {
/**
* Representation of an entry on the fragment back stack, as created
@@ -86,7 +89,10 @@
* <p>Note that you should never hold on to a BackStackEntry object;
* the identifier as returned by {@link #getId} is the only thing that
* will be persisted across activity instances.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManager.BackStackEntry}
*/
+ @Deprecated
public interface BackStackEntry {
/**
* Return the unique identifier for the entry. This is the only
@@ -129,7 +135,10 @@
/**
* Interface to watch for changes to the back stack.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManager.OnBackStackChangedListener}
*/
+ @Deprecated
public interface OnBackStackChangedListener {
/**
* Called whenever the contents of the back stack change.
@@ -428,7 +437,10 @@
/**
* Callback interface for listening to fragment state changes that happen
* within a given FragmentManager.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks}
*/
+ @Deprecated
public abstract static class FragmentLifecycleCallbacks {
/**
* Called right before the fragment's {@link Fragment#onAttach(Context)} method is called.
diff --git a/core/java/android/app/FragmentManagerNonConfig.java b/core/java/android/app/FragmentManagerNonConfig.java
index 50d3797..beb1a15 100644
--- a/core/java/android/app/FragmentManagerNonConfig.java
+++ b/core/java/android/app/FragmentManagerNonConfig.java
@@ -27,7 +27,10 @@
* and passed to the state save and restore process for fragments in
* {@link FragmentController#retainNonConfig()} and
* {@link FragmentController#restoreAllState(Parcelable, FragmentManagerNonConfig)}.</p>
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManagerNonConfig}
*/
+@Deprecated
public class FragmentManagerNonConfig {
private final List<Fragment> mFragments;
private final List<FragmentManagerNonConfig> mChildNonConfigs;
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index c910e90..0f4a7fb 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -21,7 +21,10 @@
* <a href="{@docRoot}guide/components/fragments.html">Fragments</a> developer
* guide.</p>
* </div>
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentTransaction}
*/
+@Deprecated
public abstract class FragmentTransaction {
/**
* Calls {@link #add(int, Fragment, String)} with a 0 containerViewId.
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index bbc90c8..4fc649e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -22,6 +22,7 @@
import android.app.IApplicationThread;
import android.app.IActivityController;
import android.app.IAppTask;
+import android.app.IAssistDataReceiver;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
@@ -63,7 +64,6 @@
import android.os.PersistableBundle;
import android.os.StrictMode;
import android.service.voice.IVoiceInteractionSession;
-import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -423,7 +423,7 @@
int startAssistantActivity(in String callingPackage, int callingPid, int callingUid,
in Intent intent, in String resolvedType, in Bundle options, int userId);
int startRecentsActivity(in IAssistDataReceiver assistDataReceiver, in Bundle options,
- int userId);
+ in Bundle activityOptions, int userId);
int startActivityFromRecents(int taskId, in Bundle options);
Bundle getActivityOptions(in IBinder token);
List<IBinder> getAppTasks(in String callingPackage);
diff --git a/core/java/com/android/internal/app/IAssistDataReceiver.aidl b/core/java/android/app/IAssistDataReceiver.aidl
similarity index 95%
rename from core/java/com/android/internal/app/IAssistDataReceiver.aidl
rename to core/java/android/app/IAssistDataReceiver.aidl
index 9c9ffef..2d5daf9 100644
--- a/core/java/com/android/internal/app/IAssistDataReceiver.aidl
+++ b/core/java/android/app/IAssistDataReceiver.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.app;
+package android.app;
import android.graphics.Bitmap;
import android.os.Bundle;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e260967..d49e11f 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -418,22 +419,51 @@
* different process. In addition, if the given Intent resolves to
* multiple activities, instead of displaying a dialog for the user to
* select an activity, an exception will be thrown.
- *
+ *
* <p>The function returns as soon as the activity goes idle following the
* call to its {@link Activity#onCreate}. Generally this means it has gone
* through the full initialization including {@link Activity#onResume} and
* drawn and displayed its initial window.
- *
+ *
* @param intent Description of the activity to start.
- *
+ *
* @see Context#startActivity
+ * @see #startActivitySync(Intent, Bundle)
*/
public Activity startActivitySync(Intent intent) {
+ return startActivitySync(intent, null /* options */);
+ }
+
+ /**
+ * Start a new activity and wait for it to begin running before returning.
+ * In addition to being synchronous, this method as some semantic
+ * differences from the standard {@link Context#startActivity} call: the
+ * activity component is resolved before talking with the activity manager
+ * (its class name is specified in the Intent that this method ultimately
+ * starts), and it does not allow you to start activities that run in a
+ * different process. In addition, if the given Intent resolves to
+ * multiple activities, instead of displaying a dialog for the user to
+ * select an activity, an exception will be thrown.
+ *
+ * <p>The function returns as soon as the activity goes idle following the
+ * call to its {@link Activity#onCreate}. Generally this means it has gone
+ * through the full initialization including {@link Activity#onResume} and
+ * drawn and displayed its initial window.
+ *
+ * @param intent Description of the activity to start.
+ * @param options Additional options for how the Activity should be started.
+ * May be null if there are no options. See {@link android.app.ActivityOptions}
+ * for how to build the Bundle supplied here; there are no supported definitions
+ * for building it manually.
+ *
+ * @see Context#startActivity(Intent, Bundle)
+ */
+ public Activity startActivitySync(Intent intent, @Nullable Bundle options) {
validateNotAppThread();
synchronized (mSync) {
intent = new Intent(intent);
-
+
ActivityInfo ai = intent.resolveActivityInfo(
getTargetContext().getPackageManager(), 0);
if (ai == null) {
@@ -447,7 +477,7 @@
+ myProc + " resolved to different process "
+ ai.processName + ": " + intent);
}
-
+
intent.setComponent(new ComponentName(
ai.applicationInfo.packageName, ai.name));
final ActivityWaiter aw = new ActivityWaiter(intent);
@@ -457,7 +487,7 @@
}
mWaitingActivities.add(aw);
- getTargetContext().startActivity(intent);
+ getTargetContext().startActivity(intent, options);
do {
try {
@@ -465,7 +495,7 @@
} catch (InterruptedException e) {
}
} while (mWaitingActivities.contains(aw));
-
+
return aw.activity;
}
}
diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java
index 0b96d84..90b77b3 100644
--- a/core/java/android/app/ListFragment.java
+++ b/core/java/android/app/ListFragment.java
@@ -144,7 +144,10 @@
*
* @see #setListAdapter
* @see android.widget.ListView
+ *
+ * @deprecated Use {@link android.support.v4.app.ListFragment}
*/
+@Deprecated
public class ListFragment extends Fragment {
final private Handler mHandler = new Handler();
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 56dfc58..7969684 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -54,11 +54,17 @@
* <p>For more information about using loaders, read the
* <a href="{@docRoot}guide/topics/fundamentals/loaders.html">Loaders</a> developer guide.</p>
* </div>
+ *
+ * @deprecated Use {@link android.support.v4.app.LoaderManager}
*/
+@Deprecated
public abstract class LoaderManager {
/**
* Callback interface for a client to interact with the manager.
+ *
+ * @deprecated Use {@link android.support.v4.app.LoaderManager.LoaderCallbacks}
*/
+ @Deprecated
public interface LoaderCallbacks<D> {
/**
* Instantiate and return a new Loader for the given ID.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d5d95fb..42c1347 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -68,6 +68,7 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.Gravity;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -2447,6 +2448,30 @@
notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
}
+ /**
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(NotificationProto.CHANNEL_ID, getChannelId());
+ proto.write(NotificationProto.HAS_TICKER_TEXT, this.tickerText != null);
+ proto.write(NotificationProto.FLAGS, this.flags);
+ proto.write(NotificationProto.COLOR, this.color);
+ proto.write(NotificationProto.CATEGORY, this.category);
+ proto.write(NotificationProto.GROUP_KEY, this.mGroupKey);
+ proto.write(NotificationProto.SORT_KEY, this.mSortKey);
+ if (this.actions != null) {
+ proto.write(NotificationProto.ACTION_LENGTH, this.actions.length);
+ }
+ if (this.visibility >= VISIBILITY_SECRET && this.visibility <= VISIBILITY_PUBLIC) {
+ proto.write(NotificationProto.VISIBILITY, this.visibility);
+ }
+ if (publicVersion != null) {
+ publicVersion.writeToProto(proto, NotificationProto.PUBLIC_VERSION);
+ }
+ proto.end(token);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index a52dc1e..f931589 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -758,10 +758,10 @@
}
/**
- * Checks the ability to read/modify notification do not disturb policy for the calling package.
+ * Checks the ability to modify notification do not disturb policy for the calling package.
*
* <p>
- * Returns true if the calling package can read/modify notification policy.
+ * Returns true if the calling package can modify notification policy.
*
* <p>
* Apps can request policy access by sending the user to the activity that matches the system
@@ -839,8 +839,6 @@
* Gets the current notification policy.
*
* <p>
- * Only available if policy access is granted to this package.
- * See {@link #isNotificationPolicyAccessGranted}.
*/
public Policy getNotificationPolicy() {
INotificationManager service = getService();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 4efc2c7..e48946f 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -41,6 +41,8 @@
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
+import android.content.pm.crossprofile.CrossProfileApps;
+import android.content.pm.crossprofile.ICrossProfileApps;
import android.content.res.Resources;
import android.hardware.ConsumerIrManager;
import android.hardware.ISerialManager;
@@ -135,6 +137,7 @@
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import android.util.Log;
+import android.util.StatsManager;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.WindowManager;
@@ -450,6 +453,13 @@
ctx.mMainThread.getHandler().getLooper());
}});
+ registerService(Context.STATS_MANAGER, StatsManager.class,
+ new StaticServiceFetcher<StatsManager>() {
+ @Override
+ public StatsManager createService() throws ServiceNotFoundException {
+ return new StatsManager();
+ }});
+
registerService(Context.STATUS_BAR_SERVICE, StatusBarManager.class,
new CachedServiceFetcher<StatusBarManager>() {
@Override
@@ -922,6 +932,18 @@
public RulesManager createService(ContextImpl ctx) {
return new RulesManager(ctx.getOuterContext());
}});
+
+ registerService(Context.CROSS_PROFILE_APPS_SERVICE, CrossProfileApps.class,
+ new CachedServiceFetcher<CrossProfileApps>() {
+ @Override
+ public CrossProfileApps createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.CROSS_PROFILE_APPS_SERVICE);
+ return new CrossProfileApps(ctx.getOuterContext(),
+ ICrossProfileApps.Stub.asInterface(b));
+ }
+ });
}
/**
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 0f006b6..8686944 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -152,6 +152,9 @@
public void onClick(View view) {
if (mTimePicker.validateInput()) {
TimePickerDialog.this.onClick(TimePickerDialog.this, BUTTON_POSITIVE);
+ // Clearing focus forces the dialog to commit any pending
+ // changes, e.g. typed text in a NumberPicker.
+ mTimePicker.clearFocus();
dismiss();
}
}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index e491a4f..da5569d 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -359,6 +359,8 @@
if (DEBUG_PARCEL) Log.d(TAG, "Finished reading: at " + mCurParcel.dataPosition()
+ ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
+ ", views=" + mNumReadViews);
+ mCurParcel.recycle();
+ mCurParcel = null; // Parcel cannot be used after recycled.
}
Parcel readParcel(int validateToken, int level) {
@@ -396,20 +398,23 @@
private void fetchData() {
Parcel data = Parcel.obtain();
- data.writeInterfaceToken(DESCRIPTOR);
- data.writeStrongBinder(mTransferToken);
- if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
- if (mCurParcel != null) {
- mCurParcel.recycle();
- }
- mCurParcel = Parcel.obtain();
try {
- mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Failure reading AssistStructure data", e);
- throw new IllegalStateException("Failure reading AssistStructure data: " + e);
+ data.writeInterfaceToken(DESCRIPTOR);
+ data.writeStrongBinder(mTransferToken);
+ if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
+ if (mCurParcel != null) {
+ mCurParcel.recycle();
+ }
+ mCurParcel = Parcel.obtain();
+ try {
+ mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failure reading AssistStructure data", e);
+ throw new IllegalStateException("Failure reading AssistStructure data: " + e);
+ }
+ } finally {
+ data.recycle();
}
- data.recycle();
mNumReadWindows = mNumReadViews = 0;
}
}
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 1ce89d2..a4aeb67 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -21,9 +21,12 @@
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
-import android.app.slice.widget.SliceView;
+import android.content.ContentProvider;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
@@ -59,7 +62,6 @@
* the content should be used in the shortcut representation of the slice (icon, label, action),
* normally this should be indicated by adding the hint on the action containing that content.
*
- * @see SliceView#MODE_SHORTCUT
* @see SliceItem#TYPE_ACTION
*/
public static final String HINT_TITLE = "title";
@@ -106,13 +108,21 @@
*/
public static final String HINT_NO_TINT = "no_tint";
/**
- * Hint to indicate that this content should not be shown in the {@link SliceView#MODE_SMALL}
- * and {@link SliceView#MODE_LARGE} modes of SliceView. This content may be used to populate
- * the {@link SliceView#MODE_SHORTCUT} format of the slice.
+ * Hint to indicate that this content should not be shown in larger renderings
+ * of Slices. This content may be used to populate the shortcut/icon
+ * format of the slice.
* @hide
*/
public static final String HINT_HIDDEN = "hidden";
/**
+ * Hint to indicate that this content has a toggle action associated with it. To indicate that
+ * the toggle is on, use {@link #HINT_SELECTED}. When the toggle state changes, the intent
+ * associated with it will be sent along with an extra {@link #EXTRA_TOGGLE_STATE} which can be
+ * retrieved to see the new state of the toggle.
+ * @hide
+ */
+ public static final String HINT_TOGGLE = "toggle";
+ /**
* Hint to indicate that this slice is incomplete and an update will be sent once
* loading is complete. Slices which contain HINT_PARTIAL will not be cached by the
* OS and should not be cached by apps.
@@ -125,6 +135,11 @@
* @hide
*/
public static final String HINT_ALT = "alt";
+ /**
+ * Key to retrieve an extra added to an intent when a control is changed.
+ * @hide
+ */
+ public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
private final SliceItem[] mItems;
private final @SliceHint String[] mHints;
@@ -411,4 +426,58 @@
resolver.releaseProvider(provider);
}
}
+
+ /**
+ * Turns a slice intent into slice content. Expects an explicit intent. If there is no
+ * {@link ContentProvider} associated with the given intent this will throw
+ * {@link IllegalArgumentException}.
+ *
+ * @param context The context to use.
+ * @param intent The intent associated with a slice.
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ * @see SliceProvider#onMapIntentToUri(Intent)
+ * @see Intent
+ */
+ public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent) {
+ Preconditions.checkNotNull(intent, "intent");
+ Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
+ "Slice intent must be explicit " + intent);
+ ContentResolver resolver = context.getContentResolver();
+
+ // Check if the intent has data for the slice uri on it and use that
+ final Uri intentData = intent.getData();
+ if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+ return bindSlice(resolver, intentData);
+ }
+ // Otherwise ask the app
+ List<ResolveInfo> providers =
+ context.getPackageManager().queryIntentContentProviders(intent, 0);
+ if (providers == null) {
+ throw new IllegalArgumentException("Unable to resolve intent " + intent);
+ }
+ String authority = providers.get(0).providerInfo.authority;
+ Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).build();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ final Bundle res = provider.call(resolver.getPackageName(),
+ SliceProvider.METHOD_MAP_INTENT, null, extras);
+ if (res == null) {
+ return null;
+ }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ }
}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index da718dc..4f9c168 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -16,10 +16,12 @@
package android.app.slice;
import android.Manifest.permission;
+import android.annotation.NonNull;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
+import android.content.IntentFilter;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
@@ -37,29 +39,45 @@
import java.util.concurrent.CountDownLatch;
/**
- * A SliceProvider allows app to provide content to be displayed in system
- * spaces. This content is templated and can contain actions, and the behavior
- * of how it is surfaced is specific to the system surface.
+ * A SliceProvider allows an app to provide content to be displayed in system spaces. This content
+ * is templated and can contain actions, and the behavior of how it is surfaced is specific to the
+ * system surface.
+ * <p>
+ * Slices are not currently live content. They are bound once and shown to the user. If the content
+ * changes due to a callback from user interaction, then
+ * {@link ContentResolver#notifyChange(Uri, ContentObserver)} should be used to notify the system.
+ * </p>
+ * <p>
+ * The provider needs to be declared in the manifest to provide the authority for the app. The
+ * authority for most slices is expected to match the package of the application.
+ * </p>
*
- * <p>Slices are not currently live content. They are bound once and shown to the
- * user. If the content changes due to a callback from user interaction, then
- * {@link ContentResolver#notifyChange(Uri, ContentObserver)}
- * should be used to notify the system.</p>
- *
- * <p>The provider needs to be declared in the manifest to provide the authority
- * for the app. The authority for most slices is expected to match the package
- * of the application.</p>
* <pre class="prettyprint">
* {@literal
* <provider
* android:name="com.android.mypkg.MySliceProvider"
* android:authorities="com.android.mypkg" />}
* </pre>
+ * <p>
+ * Slices can be identified by a Uri or by an Intent. To link an Intent with a slice, the provider
+ * must have an {@link IntentFilter} matching the slice intent. When a slice is being requested via
+ * an intent, {@link #onMapIntentToUri(Intent)} can be called and is expected to return an
+ * appropriate Uri representing the slice.
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * <provider
+ * android:name="com.android.mypkg.MySliceProvider"
+ * android:authorities="com.android.mypkg">
+ * <intent-filter>
+ * <action android:name="android.intent.action.MY_SLICE_INTENT" />
+ * </intent-filter>
+ * </provider>}
+ * </pre>
*
* @see Slice
*/
public abstract class SliceProvider extends ContentProvider {
-
/**
* This is the Android platform's MIME type for a slice: URI
* containing a slice implemented through {@link SliceProvider}.
@@ -78,6 +96,14 @@
/**
* @hide
*/
+ public static final String METHOD_MAP_INTENT = "map_slice";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_INTENT = "slice_intent";
+ /**
+ * @hide
+ */
public static final String EXTRA_SLICE = "slice";
private static final boolean DEBUG = false;
@@ -98,6 +124,19 @@
// TODO: Provide alternate notifyChange that takes in the slice (i.e. notifyChange(Uri, Slice)).
public abstract Slice onBindSlice(Uri sliceUri);
+ /**
+ * This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
+ * In that case, this method can be called and is expected to return a non-null Uri representing
+ * a slice. Otherwise this will throw {@link UnsupportedOperationException}.
+ *
+ * @return Uri representing the slice associated with the provided intent.
+ * @see {@link Slice}
+ */
+ public @NonNull Uri onMapIntentToUri(Intent intent) {
+ throw new UnsupportedOperationException(
+ "This provider has not implemented intent to uri mapping");
+ }
+
@Override
public final int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
@@ -159,6 +198,19 @@
Bundle b = new Bundle();
b.putParcelable(EXTRA_SLICE, s);
return b;
+ } else if (method.equals(METHOD_MAP_INTENT)) {
+ getContext().enforceCallingPermission(permission.BIND_SLICE,
+ "Slice binding requires the permission BIND_SLICE");
+ Intent intent = extras.getParcelable(EXTRA_INTENT);
+ Uri uri = onMapIntentToUri(intent);
+ Bundle b = new Bundle();
+ if (uri != null) {
+ Slice s = handleBindSlice(uri);
+ b.putParcelable(EXTRA_SLICE, s);
+ } else {
+ b.putParcelable(EXTRA_SLICE, null);
+ }
+ return b;
}
return super.call(method, arg, extras);
}
diff --git a/core/java/android/app/slice/widget/ActionRow.java b/core/java/android/app/slice/widget/ActionRow.java
deleted file mode 100644
index c96e6304..0000000
--- a/core/java/android/app/slice/widget/ActionRow.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.RemoteInput;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewParent;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-public class ActionRow extends FrameLayout {
-
- private static final int MAX_ACTIONS = 5;
- private final int mSize;
- private final int mIconPadding;
- private final LinearLayout mActionsGroup;
- private final boolean mFullActions;
- private int mColor = Color.BLACK;
-
- public ActionRow(Context context, boolean fullActions) {
- super(context);
- mFullActions = fullActions;
- mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
- context.getResources().getDisplayMetrics());
- mIconPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12,
- context.getResources().getDisplayMetrics());
- mActionsGroup = new LinearLayout(context);
- mActionsGroup.setOrientation(LinearLayout.HORIZONTAL);
- mActionsGroup.setLayoutParams(
- new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- addView(mActionsGroup);
- }
-
- private void setColor(int color) {
- mColor = color;
- for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
- View view = mActionsGroup.getChildAt(i);
- SliceItem item = (SliceItem) view.getTag();
- boolean tint = !item.hasHint(Slice.HINT_NO_TINT);
- if (tint) {
- ((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor));
- }
- }
- }
-
- private ImageView addAction(Icon icon, boolean allowTint, SliceItem image) {
- ImageView imageView = new ImageView(getContext());
- imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding);
- imageView.setScaleType(ScaleType.FIT_CENTER);
- imageView.setImageIcon(icon);
- if (allowTint) {
- imageView.setImageTintList(ColorStateList.valueOf(mColor));
- }
- imageView.setBackground(SliceViewUtil.getDrawable(getContext(),
- android.R.attr.selectableItemBackground));
- imageView.setTag(image);
- addAction(imageView);
- return imageView;
- }
-
- /**
- * Set the actions and color for this action row.
- */
- public void setActions(SliceItem actionRow, SliceItem defColor) {
- removeAllViews();
- mActionsGroup.removeAllViews();
- addView(mActionsGroup);
-
- SliceItem color = SliceQuery.find(actionRow, SliceItem.TYPE_COLOR);
- if (color == null) {
- color = defColor;
- }
- if (color != null) {
- setColor(color.getColor());
- }
- SliceQuery.findAll(actionRow, SliceItem.TYPE_ACTION).forEach(action -> {
- if (mActionsGroup.getChildCount() >= MAX_ACTIONS) {
- return;
- }
- SliceItem image = SliceQuery.find(action, SliceItem.TYPE_IMAGE);
- if (image == null) {
- return;
- }
- boolean tint = !image.hasHint(Slice.HINT_NO_TINT);
- SliceItem input = SliceQuery.find(action, SliceItem.TYPE_REMOTE_INPUT);
- if (input != null && input.getRemoteInput().getAllowFreeFormInput()) {
- addAction(image.getIcon(), tint, image).setOnClickListener(
- v -> handleRemoteInputClick(v, action.getAction(), input.getRemoteInput()));
- createRemoteInputView(mColor, getContext());
- } else {
- addAction(image.getIcon(), tint, image).setOnClickListener(v -> AsyncTask.execute(
- () -> {
- try {
- action.getAction().send();
- } catch (CanceledException e) {
- e.printStackTrace();
- }
- }));
- }
- });
- setVisibility(getChildCount() != 0 ? View.VISIBLE : View.GONE);
- }
-
- private void addAction(View child) {
- mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1));
- }
-
- private void createRemoteInputView(int color, Context context) {
- View riv = RemoteInputView.inflate(context, this);
- riv.setVisibility(View.INVISIBLE);
- addView(riv, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- riv.setBackgroundColor(color);
- }
-
- private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent,
- RemoteInput input) {
- if (input == null) {
- return false;
- }
-
- ViewParent p = view.getParent().getParent();
- RemoteInputView riv = null;
- while (p != null) {
- if (p instanceof View) {
- View pv = (View) p;
- riv = findRemoteInputView(pv);
- if (riv != null) {
- break;
- }
- }
- p = p.getParent();
- }
- if (riv == null) {
- return false;
- }
-
- int width = view.getWidth();
- if (view instanceof TextView) {
- // Center the reveal on the text which might be off-center from the TextView
- TextView tv = (TextView) view;
- if (tv.getLayout() != null) {
- int innerWidth = (int) tv.getLayout().getLineWidth(0);
- innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
- width = Math.min(width, innerWidth);
- }
- }
- int cx = view.getLeft() + width / 2;
- int cy = view.getTop() + view.getHeight() / 2;
- int w = riv.getWidth();
- int h = riv.getHeight();
- int r = Math.max(
- Math.max(cx + cy, cx + (h - cy)),
- Math.max((w - cx) + cy, (w - cx) + (h - cy)));
-
- riv.setRevealParameters(cx, cy, r);
- riv.setPendingIntent(pendingIntent);
- riv.setRemoteInput(new RemoteInput[] {
- input
- }, input);
- riv.focusAnimated();
- return true;
- }
-
- private RemoteInputView findRemoteInputView(View v) {
- if (v == null) {
- return null;
- }
- return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
- }
-}
diff --git a/core/java/android/app/slice/widget/GridView.java b/core/java/android/app/slice/widget/GridView.java
deleted file mode 100644
index 793abc0..0000000
--- a/core/java/android/app/slice/widget/GridView.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.content.Context;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public class GridView extends LinearLayout implements SliceListView {
-
- private static final String TAG = "GridView";
-
- private static final int MAX_IMAGES = 3;
- private static final int MAX_ALL = 5;
- private boolean mIsAllImages;
-
- public GridView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mIsAllImages) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = width / getChildCount();
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,
- height);
- getLayoutParams().height = height;
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).getLayoutParams().height = height;
- }
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- @Override
- public void setSliceItem(SliceItem slice) {
- mIsAllImages = true;
- removeAllViews();
- int total = 1;
- if (slice.getType() == SliceItem.TYPE_SLICE) {
- List<SliceItem> items = slice.getSlice().getItems();
- total = items.size();
- for (int i = 0; i < total; i++) {
- SliceItem item = items.get(i);
- if (isFull()) {
- continue;
- }
- if (!addItem(item)) {
- mIsAllImages = false;
- }
- }
- } else {
- if (!isFull()) {
- if (!addItem(slice)) {
- mIsAllImages = false;
- }
- }
- }
- if (total > getChildCount() && mIsAllImages) {
- addExtraCount(total - getChildCount());
- }
- }
-
- private void addExtraCount(int numExtra) {
- View last = getChildAt(getChildCount() - 1);
- FrameLayout frame = new FrameLayout(getContext());
- frame.setLayoutParams(last.getLayoutParams());
-
- removeView(last);
- frame.addView(last, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
- TextView v = new TextView(getContext());
- v.setTextColor(Color.WHITE);
- v.setBackgroundColor(0x4d000000);
- v.setText(getResources().getString(R.string.slice_more_content, numExtra));
- v.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
- v.setGravity(Gravity.CENTER);
- frame.addView(v, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
- addView(frame);
- }
-
- private boolean isFull() {
- return getChildCount() >= (mIsAllImages ? MAX_IMAGES : MAX_ALL);
- }
-
- /**
- * Returns true if this item is just an image.
- */
- private boolean addItem(SliceItem item) {
- if (item.hasHint(Slice.HINT_HIDDEN)) {
- return false;
- }
- if (item.getType() == SliceItem.TYPE_IMAGE) {
- ImageView v = new ImageView(getContext());
- v.setImageIcon(item.getIcon());
- v.setScaleType(ScaleType.CENTER_CROP);
- addView(v, new LayoutParams(0, MATCH_PARENT, 1));
- return true;
- } else {
- LinearLayout v = new LinearLayout(getContext());
- int s = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- 12, getContext().getResources().getDisplayMetrics());
- v.setPadding(0, s, 0, 0);
- v.setOrientation(LinearLayout.VERTICAL);
- v.setGravity(Gravity.CENTER_HORIZONTAL);
- // TODO: Unify sporadic inflates that happen throughout the code.
- ArrayList<SliceItem> items = new ArrayList<>();
- if (item.getType() == SliceItem.TYPE_SLICE) {
- items.addAll(item.getSlice().getItems());
- }
- items.forEach(i -> {
- if (i.hasHint(Slice.HINT_HIDDEN)) {
- return;
- }
- Context context = getContext();
- switch (i.getType()) {
- case SliceItem.TYPE_TEXT:
- boolean title = false;
- if ((item.hasAnyHints(new String[] {
- Slice.HINT_LARGE, Slice.HINT_TITLE
- }))) {
- title = true;
- }
- TextView tv = (TextView) LayoutInflater.from(context).inflate(
- title ? R.layout.slice_title : R.layout.slice_secondary_text, null);
- tv.setText(i.getText());
- v.addView(tv);
- break;
- case SliceItem.TYPE_IMAGE:
- ImageView iv = new ImageView(context);
- iv.setImageIcon(i.getIcon());
- if (item.hasHint(Slice.HINT_LARGE)) {
- iv.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- } else {
- int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- 48, context.getResources().getDisplayMetrics());
- iv.setLayoutParams(new LayoutParams(size, size));
- }
- v.addView(iv);
- break;
- case SliceItem.TYPE_REMOTE_VIEW:
- v.addView(i.getRemoteView().apply(context, v));
- break;
- case SliceItem.TYPE_COLOR:
- // TODO: Support color to tint stuff here.
- break;
- }
- });
- addView(v, new LayoutParams(0, WRAP_CONTENT, 1));
- return false;
- }
- }
-}
diff --git a/core/java/android/app/slice/widget/LargeSliceAdapter.java b/core/java/android/app/slice/widget/LargeSliceAdapter.java
deleted file mode 100644
index 267fff6..0000000
--- a/core/java/android/app/slice/widget/LargeSliceAdapter.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceViewHolder;
-import android.content.Context;
-import android.util.ArrayMap;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-import com.android.internal.widget.RecyclerView;
-import com.android.internal.widget.RecyclerView.ViewHolder;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * @hide
- */
-public class LargeSliceAdapter extends RecyclerView.Adapter<SliceViewHolder> {
-
- public static final int TYPE_DEFAULT = 1;
- public static final int TYPE_HEADER = 2;
- public static final int TYPE_GRID = 3;
- public static final int TYPE_MESSAGE = 4;
- public static final int TYPE_MESSAGE_LOCAL = 5;
- public static final int TYPE_REMOTE_VIEWS = 6;
-
- private final IdGenerator mIdGen = new IdGenerator();
- private final Context mContext;
- private List<SliceWrapper> mSlices = new ArrayList<>();
- private SliceItem mColor;
-
- public LargeSliceAdapter(Context context) {
- mContext = context;
- setHasStableIds(true);
- }
-
- /**
- * Set the {@link SliceItem}'s to be displayed in the adapter and the accent color.
- */
- public void setSliceItems(List<SliceItem> slices, SliceItem color) {
- mColor = color;
- mIdGen.resetUsage();
- mSlices = slices.stream().map(s -> new SliceWrapper(s, mIdGen))
- .collect(Collectors.toList());
- notifyDataSetChanged();
- }
-
- @Override
- public SliceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View v = inflateForType(viewType);
- v.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
- return new SliceViewHolder(v);
- }
-
- @Override
- public int getItemViewType(int position) {
- return mSlices.get(position).mType;
- }
-
- @Override
- public long getItemId(int position) {
- return mSlices.get(position).mId;
- }
-
- @Override
- public int getItemCount() {
- return mSlices.size();
- }
-
- @Override
- public void onBindViewHolder(SliceViewHolder holder, int position) {
- SliceWrapper slice = mSlices.get(position);
- if (holder.mSliceView != null) {
- holder.mSliceView.setColor(mColor);
- holder.mSliceView.setSliceItem(slice.mItem);
- } else if (slice.mType == TYPE_REMOTE_VIEWS) {
- FrameLayout frame = (FrameLayout) holder.itemView;
- frame.removeAllViews();
- frame.addView(slice.mItem.getRemoteView().apply(mContext, frame));
- }
- }
-
- private View inflateForType(int viewType) {
- switch (viewType) {
- case TYPE_REMOTE_VIEWS:
- return new FrameLayout(mContext);
- case TYPE_GRID:
- return LayoutInflater.from(mContext).inflate(R.layout.slice_grid, null);
- case TYPE_MESSAGE:
- return LayoutInflater.from(mContext).inflate(R.layout.slice_message, null);
- case TYPE_MESSAGE_LOCAL:
- return LayoutInflater.from(mContext).inflate(R.layout.slice_message_local, null);
- }
- return new SmallTemplateView(mContext);
- }
-
- protected static class SliceWrapper {
- private final SliceItem mItem;
- private final int mType;
- private final long mId;
-
- public SliceWrapper(SliceItem item, IdGenerator idGen) {
- mItem = item;
- mType = getType(item);
- mId = idGen.getId(item);
- }
-
- public static int getType(SliceItem item) {
- if (item.getType() == SliceItem.TYPE_REMOTE_VIEW) {
- return TYPE_REMOTE_VIEWS;
- }
- if (item.hasHint(Slice.HINT_MESSAGE)) {
- // TODO: Better way to determine me or not? Something more like Messaging style.
- if (SliceQuery.find(item, -1, Slice.HINT_SOURCE, null) != null) {
- return TYPE_MESSAGE;
- } else {
- return TYPE_MESSAGE_LOCAL;
- }
- }
- if (item.hasHint(Slice.HINT_HORIZONTAL)) {
- return TYPE_GRID;
- }
- return TYPE_DEFAULT;
- }
- }
-
- /**
- * A {@link ViewHolder} for presenting slices in {@link LargeSliceAdapter}.
- */
- public static class SliceViewHolder extends ViewHolder {
- public final SliceListView mSliceView;
-
- public SliceViewHolder(View itemView) {
- super(itemView);
- mSliceView = itemView instanceof SliceListView ? (SliceListView) itemView : null;
- }
- }
-
- /**
- * View slices being displayed in {@link LargeSliceAdapter}.
- */
- public interface SliceListView {
- /**
- * Set the slice item for this view.
- */
- void setSliceItem(SliceItem slice);
-
- /**
- * Set the color for the items in this view.
- */
- default void setColor(SliceItem color) {
-
- }
- }
-
- private static class IdGenerator {
- private long mNextLong = 0;
- private final ArrayMap<String, Long> mCurrentIds = new ArrayMap<>();
- private final ArrayMap<String, Integer> mUsedIds = new ArrayMap<>();
-
- public long getId(SliceItem item) {
- String str = genString(item);
- if (!mCurrentIds.containsKey(str)) {
- mCurrentIds.put(str, mNextLong++);
- }
- long id = mCurrentIds.get(str);
- int index = mUsedIds.getOrDefault(str, 0);
- mUsedIds.put(str, index + 1);
- return id + index * 10000;
- }
-
- private String genString(SliceItem item) {
- StringBuilder builder = new StringBuilder();
- SliceQuery.stream(item).forEach(i -> {
- builder.append(i.getType());
- i.removeHint(Slice.HINT_SELECTED);
- builder.append(i.getHints());
- switch (i.getType()) {
- case SliceItem.TYPE_REMOTE_VIEW:
- builder.append(i.getRemoteView());
- break;
- case SliceItem.TYPE_IMAGE:
- builder.append(i.getIcon());
- break;
- case SliceItem.TYPE_TEXT:
- builder.append(i.getText());
- break;
- case SliceItem.TYPE_COLOR:
- builder.append(i.getColor());
- break;
- }
- });
- return builder.toString();
- }
-
- public void resetUsage() {
- mUsedIds.clear();
- }
- }
-}
diff --git a/core/java/android/app/slice/widget/LargeTemplateView.java b/core/java/android/app/slice/widget/LargeTemplateView.java
deleted file mode 100644
index 788f6fb..0000000
--- a/core/java/android/app/slice/widget/LargeTemplateView.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.util.TypedValue;
-
-import com.android.internal.widget.LinearLayoutManager;
-import com.android.internal.widget.RecyclerView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public class LargeTemplateView extends SliceModeView {
-
- private final LargeSliceAdapter mAdapter;
- private final RecyclerView mRecyclerView;
- private final int mDefaultHeight;
- private final int mMaxHeight;
- private Slice mSlice;
- private boolean mIsScrollable;
-
- public LargeTemplateView(Context context) {
- super(context);
-
- mRecyclerView = new RecyclerView(getContext());
- mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- mAdapter = new LargeSliceAdapter(context);
- mRecyclerView.setAdapter(mAdapter);
- addView(mRecyclerView);
- mDefaultHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
- getResources().getDisplayMetrics());
- mMaxHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
- getResources().getDisplayMetrics());
- }
-
- @Override
- public String getMode() {
- return SliceView.MODE_LARGE;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mRecyclerView.getLayoutParams().height = WRAP_CONTENT;
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mRecyclerView.getMeasuredHeight() > mMaxHeight
- || (mSlice != null && mSlice.hasHint(Slice.HINT_PARTIAL))) {
- mRecyclerView.getLayoutParams().height = mDefaultHeight;
- } else {
- mRecyclerView.getLayoutParams().height = mRecyclerView.getMeasuredHeight();
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- @Override
- public void setSlice(Slice slice) {
- SliceItem color = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
- mSlice = slice;
- List<SliceItem> items = new ArrayList<>();
- boolean[] hasHeader = new boolean[1];
- if (slice.hasHint(Slice.HINT_LIST)) {
- addList(slice, items);
- } else {
- slice.getItems().forEach(item -> {
- if (item.hasHint(Slice.HINT_HIDDEN)) {
- // If it's hidden we don't show it
- return;
- } else if (item.hasHint(Slice.HINT_ACTIONS)) {
- // Action groups don't show in lists
- return;
- } else if (item.getType() == SliceItem.TYPE_COLOR) {
- // A color is not a list item
- return;
- } else if (item.getType() == SliceItem.TYPE_SLICE
- && item.hasHint(Slice.HINT_LIST)) {
- addList(item.getSlice(), items);
- } else if (item.hasHint(Slice.HINT_LIST_ITEM)) {
- items.add(item);
- } else if (!hasHeader[0]) {
- hasHeader[0] = true;
- items.add(0, item);
- } else {
- item.addHint(Slice.HINT_LIST_ITEM);
- items.add(item);
- }
- });
- }
- mAdapter.setSliceItems(items, color);
- }
-
- private void addList(Slice slice, List<SliceItem> items) {
- List<SliceItem> sliceItems = slice.getItems();
- sliceItems.forEach(i -> {
- if (!i.hasHint(Slice.HINT_HIDDEN) && i.getType() != SliceItem.TYPE_COLOR) {
- i.addHint(Slice.HINT_LIST_ITEM);
- items.add(i);
- }
- });
- }
-
- /**
- * Whether or not the content in this template should be scrollable.
- */
- public void setScrollable(boolean isScrollable) {
- // TODO -- restrict / enable how much this view can show
- mIsScrollable = isScrollable;
- }
-}
diff --git a/core/java/android/app/slice/widget/MessageView.java b/core/java/android/app/slice/widget/MessageView.java
deleted file mode 100644
index 3124398..0000000
--- a/core/java/android/app/slice/widget/MessageView.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.text.SpannableStringBuilder;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-public class MessageView extends LinearLayout implements SliceListView {
-
- private TextView mDetails;
- private ImageView mIcon;
-
- public MessageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mDetails = findViewById(android.R.id.summary);
- mIcon = findViewById(android.R.id.icon);
- }
-
- @Override
- public void setSliceItem(SliceItem slice) {
- SliceItem source = SliceQuery.find(slice, SliceItem.TYPE_IMAGE, Slice.HINT_SOURCE, null);
- if (source != null) {
- final int iconSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- 24, getContext().getResources().getDisplayMetrics());
- // TODO try and turn this into a drawable
- Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
- Canvas iconCanvas = new Canvas(iconBm);
- Drawable d = source.getIcon().loadDrawable(getContext());
- d.setBounds(0, 0, iconSize, iconSize);
- d.draw(iconCanvas);
- mIcon.setImageBitmap(SliceViewUtil.getCircularBitmap(iconBm));
- }
- SpannableStringBuilder builder = new SpannableStringBuilder();
- SliceQuery.findAll(slice, SliceItem.TYPE_TEXT).forEach(text -> {
- if (builder.length() != 0) {
- builder.append('\n');
- }
- builder.append(text.getText());
- });
- mDetails.setText(builder.toString());
- }
-
-}
diff --git a/core/java/android/app/slice/widget/RemoteInputView.java b/core/java/android/app/slice/widget/RemoteInputView.java
deleted file mode 100644
index 6eff5af..0000000
--- a/core/java/android/app/slice/widget/RemoteInputView.java
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.animation.Animator;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutManager;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.internal.R;
-
-/**
- * Host for the remote input.
- *
- * @hide
- */
-// TODO this should be unified with SystemUI RemoteInputView (b/67527720)
-public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
-
- private static final String TAG = "RemoteInput";
-
- /**
- * A marker object that let's us easily find views of this class.
- */
- public static final Object VIEW_TAG = new Object();
-
- private RemoteEditText mEditText;
- private ImageButton mSendButton;
- private ProgressBar mProgressBar;
- private PendingIntent mPendingIntent;
- private RemoteInput[] mRemoteInputs;
- private RemoteInput mRemoteInput;
-
- private int mRevealCx;
- private int mRevealCy;
- private int mRevealR;
- private boolean mResetting;
-
- public RemoteInputView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mProgressBar = findViewById(R.id.remote_input_progress);
- mSendButton = findViewById(R.id.remote_input_send);
- mSendButton.setOnClickListener(this);
-
- mEditText = (RemoteEditText) getChildAt(0);
- mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- final boolean isSoftImeEvent = event == null
- && (actionId == EditorInfo.IME_ACTION_DONE
- || actionId == EditorInfo.IME_ACTION_NEXT
- || actionId == EditorInfo.IME_ACTION_SEND);
- final boolean isKeyboardEnterKey = event != null
- && KeyEvent.isConfirmKey(event.getKeyCode())
- && event.getAction() == KeyEvent.ACTION_DOWN;
-
- if (isSoftImeEvent || isKeyboardEnterKey) {
- if (mEditText.length() > 0) {
- sendRemoteInput();
- }
- // Consume action to prevent IME from closing.
- return true;
- }
- return false;
- }
- });
- mEditText.addTextChangedListener(this);
- mEditText.setInnerFocusable(false);
- mEditText.mRemoteInputView = this;
- }
-
- private void sendRemoteInput() {
- Bundle results = new Bundle();
- results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
- Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
- results);
-
- mEditText.setEnabled(false);
- mSendButton.setVisibility(INVISIBLE);
- mProgressBar.setVisibility(VISIBLE);
- mEditText.mShowImeOnInputConnection = false;
-
- // Tell ShortcutManager that this package has been "activated". ShortcutManager
- // will reset the throttling for this package.
- // Strictly speaking, the intent receiver may be different from the intent creator,
- // but that's an edge case, and also because we can't always know which package will receive
- // an intent, so we just reset for the creator.
- getContext().getSystemService(ShortcutManager.class).onApplicationActive(
- mPendingIntent.getCreatorPackage(),
- getContext().getUserId());
-
- try {
- mPendingIntent.send(mContext, 0, fillInIntent);
- reset();
- } catch (PendingIntent.CanceledException e) {
- Log.i(TAG, "Unable to send remote input result", e);
- Toast.makeText(mContext, "Failure sending pending intent for inline reply :(",
- Toast.LENGTH_SHORT).show();
- reset();
- }
- }
-
- /**
- * Creates a remote input view.
- */
- public static RemoteInputView inflate(Context context, ViewGroup root) {
- RemoteInputView v = (RemoteInputView) LayoutInflater.from(context).inflate(
- R.layout.slice_remote_input, root, false);
- v.setTag(VIEW_TAG);
- return v;
- }
-
- @Override
- public void onClick(View v) {
- if (v == mSendButton) {
- sendRemoteInput();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- super.onTouchEvent(event);
-
- // We never want for a touch to escape to an outer view or one we covered.
- return true;
- }
-
- private void onDefocus() {
- setVisibility(INVISIBLE);
- }
-
- /**
- * Set the pending intent for remote input.
- */
- public void setPendingIntent(PendingIntent pendingIntent) {
- mPendingIntent = pendingIntent;
- }
-
- /**
- * Set the remote inputs for this view.
- */
- public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) {
- mRemoteInputs = remoteInputs;
- mRemoteInput = remoteInput;
- mEditText.setHint(mRemoteInput.getLabel());
- }
-
- /**
- * Focuses the remote input view.
- */
- public void focusAnimated() {
- if (getVisibility() != VISIBLE) {
- Animator animator = ViewAnimationUtils.createCircularReveal(
- this, mRevealCx, mRevealCy, 0, mRevealR);
- animator.setDuration(200);
- animator.start();
- }
- focus();
- }
-
- private void focus() {
- setVisibility(VISIBLE);
- mEditText.setInnerFocusable(true);
- mEditText.mShowImeOnInputConnection = true;
- mEditText.setSelection(mEditText.getText().length());
- mEditText.requestFocus();
- updateSendButton();
- }
-
- private void reset() {
- mResetting = true;
-
- mEditText.getText().clear();
- mEditText.setEnabled(true);
- mSendButton.setVisibility(VISIBLE);
- mProgressBar.setVisibility(INVISIBLE);
- updateSendButton();
- onDefocus();
-
- mResetting = false;
- }
-
- @Override
- public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
- if (mResetting && child == mEditText) {
- // Suppress text events if it happens during resetting. Ideally this would be
- // suppressed by the text view not being shown, but that doesn't work here because it
- // needs to stay visible for the animation.
- return false;
- }
- return super.onRequestSendAccessibilityEvent(child, event);
- }
-
- private void updateSendButton() {
- mSendButton.setEnabled(mEditText.getText().length() != 0);
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- updateSendButton();
- }
-
- /**
- * Tries to find an action that matches the current pending intent of this view and updates its
- * state to that of the found action
- *
- * @return true if a matching action was found, false otherwise
- */
- public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
- if (mPendingIntent == null || actions == null) {
- return false;
- }
- Intent current = mPendingIntent.getIntent();
- if (current == null) {
- return false;
- }
-
- for (Notification.Action a : actions) {
- RemoteInput[] inputs = a.getRemoteInputs();
- if (a.actionIntent == null || inputs == null) {
- continue;
- }
- Intent candidate = a.actionIntent.getIntent();
- if (!current.filterEquals(candidate)) {
- continue;
- }
-
- RemoteInput input = null;
- for (RemoteInput i : inputs) {
- if (i.getAllowFreeFormInput()) {
- input = i;
- }
- }
- if (input == null) {
- continue;
- }
- setPendingIntent(a.actionIntent);
- setRemoteInput(inputs, input);
- return true;
- }
- return false;
- }
-
- /**
- * @hide
- */
- public void setRevealParameters(int cx, int cy, int r) {
- mRevealCx = cx;
- mRevealCy = cy;
- mRevealR = r;
- }
-
- @Override
- public void dispatchStartTemporaryDetach() {
- super.dispatchStartTemporaryDetach();
- // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and
- // won't lose IME focus.
- detachViewFromParent(mEditText);
- }
-
- @Override
- public void dispatchFinishTemporaryDetach() {
- if (isAttachedToWindow()) {
- attachViewToParent(mEditText, 0, mEditText.getLayoutParams());
- } else {
- removeDetachedView(mEditText, false /* animate */);
- }
- super.dispatchFinishTemporaryDetach();
- }
-
- /**
- * An EditText that changes appearance based on whether it's focusable and becomes un-focusable
- * whenever the user navigates away from it or it becomes invisible.
- */
- public static class RemoteEditText extends EditText {
-
- private final Drawable mBackground;
- private RemoteInputView mRemoteInputView;
- boolean mShowImeOnInputConnection;
-
- public RemoteEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- mBackground = getBackground();
- }
-
- private void defocusIfNeeded(boolean animate) {
- if (mRemoteInputView != null || isTemporarilyDetached()) {
- if (isTemporarilyDetached()) {
- // We might get reattached but then the other one of HUN / expanded might steal
- // our focus, so we'll need to save our text here.
- }
- return;
- }
- if (isFocusable() && isEnabled()) {
- setInnerFocusable(false);
- if (mRemoteInputView != null) {
- mRemoteInputView.onDefocus();
- }
- mShowImeOnInputConnection = false;
- }
- }
-
- @Override
- protected void onVisibilityChanged(View changedView, int visibility) {
- super.onVisibilityChanged(changedView, visibility);
-
- if (!isShown()) {
- defocusIfNeeded(false /* animate */);
- }
- }
-
- @Override
- protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
- super.onFocusChanged(focused, direction, previouslyFocusedRect);
- if (!focused) {
- defocusIfNeeded(true /* animate */);
- }
- }
-
- @Override
- public void getFocusedRect(Rect r) {
- super.getFocusedRect(r);
- r.top = mScrollY;
- r.bottom = mScrollY + (mBottom - mTop);
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- // Eat the DOWN event here to prevent any default behavior.
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- defocusIfNeeded(true /* animate */);
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
- public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
- final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
-
- if (mShowImeOnInputConnection && inputConnection != null) {
- final InputMethodManager imm = InputMethodManager.getInstance();
- if (imm != null) {
- // onCreateInputConnection is called by InputMethodManager in the middle of
- // setting up the connection to the IME; wait with requesting the IME until that
- // work has completed.
- post(new Runnable() {
- @Override
- public void run() {
- imm.viewClicked(RemoteEditText.this);
- imm.showSoftInput(RemoteEditText.this, 0);
- }
- });
- }
- }
-
- return inputConnection;
- }
-
- @Override
- public void onCommitCompletion(CompletionInfo text) {
- clearComposingText();
- setText(text.getText());
- setSelection(getText().length());
- }
-
- void setInnerFocusable(boolean focusable) {
- setFocusableInTouchMode(focusable);
- setFocusable(focusable);
- setCursorVisible(focusable);
-
- if (focusable) {
- requestFocus();
- setBackground(mBackground);
- } else {
- setBackground(null);
- }
-
- }
- }
-}
diff --git a/core/java/android/app/slice/widget/ShortcutView.java b/core/java/android/app/slice/widget/ShortcutView.java
deleted file mode 100644
index 0b7ad0d..0000000
--- a/core/java/android/app/slice/widget/ShortcutView.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.OvalShape;
-import android.net.Uri;
-
-import com.android.internal.R;
-
-import java.util.List;
-
-/**
- * @hide
- */
-public class ShortcutView extends SliceModeView {
-
- private static final String TAG = "ShortcutView";
-
- private Uri mUri;
- private PendingIntent mAction;
- private SliceItem mLabel;
- private SliceItem mIcon;
-
- private int mLargeIconSize;
- private int mSmallIconSize;
-
- public ShortcutView(Context context) {
- super(context);
- final Resources res = getResources();
- mSmallIconSize = res.getDimensionPixelSize(R.dimen.slice_icon_size);
- mLargeIconSize = res.getDimensionPixelSize(R.dimen.slice_shortcut_size);
- }
-
- @Override
- public void setSlice(Slice slice) {
- removeAllViews();
- determineShortcutItems(getContext(), slice);
- SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
- if (colorItem == null) {
- colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
- }
- // TODO: pick better default colour
- final int color = colorItem != null ? colorItem.getColor() : Color.GRAY;
- ShapeDrawable circle = new ShapeDrawable(new OvalShape());
- circle.setTint(color);
- setBackground(circle);
- if (mIcon != null) {
- final boolean isLarge = mIcon.hasHint(Slice.HINT_LARGE);
- final int iconSize = isLarge ? mLargeIconSize : mSmallIconSize;
- SliceViewUtil.createCircledIcon(getContext(), color, iconSize, mIcon.getIcon(),
- isLarge, this /* parent */);
- mUri = slice.getUri();
- setClickable(true);
- } else {
- setClickable(false);
- }
- }
-
- @Override
- public String getMode() {
- return SliceView.MODE_SHORTCUT;
- }
-
- @Override
- public boolean performClick() {
- if (!callOnClick()) {
- try {
- if (mAction != null) {
- mAction.send();
- } else {
- Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
- }
- } catch (CanceledException e) {
- e.printStackTrace();
- }
- }
- return true;
- }
-
- /**
- * Looks at the slice and determines which items are best to use to compose the shortcut.
- */
- private void determineShortcutItems(Context context, Slice slice) {
- List<String> h = slice.getHints();
- SliceItem sliceItem = new SliceItem(slice, SliceItem.TYPE_SLICE,
- h.toArray(new String[h.size()]));
- SliceItem titleItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION,
- Slice.HINT_TITLE, null);
-
- if (titleItem != null) {
- // Preferred case: hinted action containing hinted image and text
- mAction = titleItem.getAction();
- mIcon = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
- null);
- mLabel = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
- null);
- } else {
- // No hinted action; just use the first one
- SliceItem actionItem = SliceQuery.find(sliceItem, SliceItem.TYPE_ACTION, (String) null,
- null);
- mAction = (actionItem != null) ? actionItem.getAction() : null;
- }
- // First fallback: any hinted image and text
- if (mIcon == null) {
- mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
- null);
- }
- if (mLabel == null) {
- mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
- null);
- }
- // Second fallback: first image and text
- if (mIcon == null) {
- mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, (String) null,
- null);
- }
- if (mLabel == null) {
- mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, (String) null,
- null);
- }
- // Final fallback: use app info
- if (mIcon == null || mLabel == null || mAction == null) {
- PackageManager pm = context.getPackageManager();
- ProviderInfo providerInfo = pm.resolveContentProvider(
- slice.getUri().getAuthority(), 0);
- ApplicationInfo appInfo = providerInfo.applicationInfo;
- if (appInfo != null) {
- if (mIcon == null) {
- Drawable icon = appInfo.loadDefaultIcon(pm);
- mIcon = new SliceItem(SliceViewUtil.createIconFromDrawable(icon),
- SliceItem.TYPE_IMAGE, new String[] {Slice.HINT_LARGE});
- }
- if (mLabel == null) {
- mLabel = new SliceItem(pm.getApplicationLabel(appInfo),
- SliceItem.TYPE_TEXT, null);
- }
- if (mAction == null) {
- mAction = PendingIntent.getActivity(context, 0,
- pm.getLaunchIntentForPackage(appInfo.packageName), 0);
- }
- }
- }
- }
-}
diff --git a/core/java/android/app/slice/widget/SliceView.java b/core/java/android/app/slice/widget/SliceView.java
deleted file mode 100644
index cc13ba3..0000000
--- a/core/java/android/app/slice/widget/SliceView.java
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.graphics.drawable.ColorDrawable;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.android.internal.R;
-import com.android.internal.util.Preconditions;
-
-import java.util.List;
-
-/**
- * A view for displaying a {@link Slice} which is a piece of app content and actions. SliceView is
- * able to present slice content in a templated format outside of the associated app. The way this
- * content is displayed depends on the structure of the slice, the hints associated with the
- * content, and the mode that SliceView is configured for. The modes that SliceView supports are:
- * <ul>
- * <li><b>Shortcut</b>: A shortcut is presented as an icon and a text label representing the main
- * content or action associated with the slice.</li>
- * <li><b>Small</b>: The small format has a restricted height and can present a single
- * {@link SliceItem} or a limited collection of items.</li>
- * <li><b>Large</b>: The large format displays multiple small templates in a list, if scrolling is
- * not enabled (see {@link #setScrollable(boolean)}) the view will show as many items as it can
- * comfortably fit.</li>
- * </ul>
- * <p>
- * When constructing a slice, the contents of it can be annotated with hints, these provide the OS
- * with some information on how the content should be displayed. For example, text annotated with
- * {@link Slice#HINT_TITLE} would be placed in the title position of a template. A slice annotated
- * with {@link Slice#HINT_LIST} would present the child items of that slice in a list.
- * <p>
- * SliceView can be provided a slice via a uri {@link #setSlice(Uri)} in which case a content
- * observer will be set for that uri and the view will update if there are any changes to the slice.
- * To use this the app must have a special permission to bind to the slice (see
- * {@link android.Manifest.permission#BIND_SLICE}).
- * <p>
- * Example usage:
- *
- * <pre class="prettyprint">
- * SliceView v = new SliceView(getContext());
- * v.setMode(desiredMode);
- * v.setSlice(sliceUri);
- * </pre>
- */
-public class SliceView extends ViewGroup {
-
- private static final String TAG = "SliceView";
-
- /**
- * @hide
- */
- public abstract static class SliceModeView extends FrameLayout {
-
- public SliceModeView(Context context) {
- super(context);
- }
-
- /**
- * @return the mode of the slice being presented.
- */
- public abstract String getMode();
-
- /**
- * @param slice the slice to show in this view.
- */
- public abstract void setSlice(Slice slice);
- }
-
- /**
- * @hide
- */
- @StringDef({
- MODE_SMALL, MODE_LARGE, MODE_SHORTCUT
- })
- public @interface SliceMode {}
-
- /**
- * Mode indicating this slice should be presented in small template format.
- */
- public static final String MODE_SMALL = "SLICE_SMALL";
- /**
- * Mode indicating this slice should be presented in large template format.
- */
- public static final String MODE_LARGE = "SLICE_LARGE";
- /**
- * Mode indicating this slice should be presented as an icon. A shortcut requires an intent,
- * icon, and label. This can be indicated by using {@link Slice#HINT_TITLE} on an action in a
- * slice.
- */
- public static final String MODE_SHORTCUT = "SLICE_ICON";
-
- /**
- * Will select the type of slice binding based on size of the View. TODO: Put in some info about
- * that selection.
- */
- private static final String MODE_AUTO = "auto";
-
- private String mMode = MODE_AUTO;
- private SliceModeView mCurrentView;
- private final ActionRow mActions;
- private Slice mCurrentSlice;
- private boolean mShowActions = true;
- private boolean mIsScrollable;
- private SliceObserver mObserver;
- private final int mShortcutSize;
-
- public SliceView(Context context) {
- this(context, null);
- }
-
- public SliceView(Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SliceView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public SliceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mObserver = new SliceObserver(new Handler(Looper.getMainLooper()));
- mActions = new ActionRow(mContext, true);
- mActions.setBackground(new ColorDrawable(0xffeeeeee));
- mCurrentView = new LargeTemplateView(mContext);
- addView(mCurrentView, getChildLp(mCurrentView));
- addView(mActions, getChildLp(mActions));
- mShortcutSize = getContext().getResources()
- .getDimensionPixelSize(R.dimen.slice_shortcut_size);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- measureChildren(widthMeasureSpec, heightMeasureSpec);
- int actionHeight = mActions.getVisibility() != View.GONE
- ? mActions.getMeasuredHeight()
- : 0;
- int newHeightSpec = MeasureSpec.makeMeasureSpec(
- mCurrentView.getMeasuredHeight() + actionHeight, MeasureSpec.EXACTLY);
- int width = MeasureSpec.getSize(widthMeasureSpec);
- setMeasuredDimension(width, newHeightSpec);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- mCurrentView.layout(l, t, l + mCurrentView.getMeasuredWidth(),
- t + mCurrentView.getMeasuredHeight());
- if (mActions.getVisibility() != View.GONE) {
- mActions.layout(l, mCurrentView.getMeasuredHeight(), l + mActions.getMeasuredWidth(),
- mCurrentView.getMeasuredHeight() + mActions.getMeasuredHeight());
- }
- }
-
- /**
- * @hide
- */
- public void showSlice(Intent intent) {
- // TODO
- }
-
- /**
- * Populates this view with the {@link Slice} associated with the provided {@link Uri}. To use
- * this method your app must have the permission
- * {@link android.Manifest.permission#BIND_SLICE}).
- * <p>
- * Setting a slice differs from {@link #showSlice(Slice)} because it will ensure the view is
- * updated when the slice identified by the provided URI changes. The lifecycle of this observer
- * is handled by SliceView in {@link #onAttachedToWindow()} and {@link #onDetachedFromWindow()}.
- * To unregister this observer outside of that you can call {@link #clearSlice}.
- *
- * @return true if the a slice was found for the provided uri.
- * @see #clearSlice
- */
- public boolean setSlice(@NonNull Uri sliceUri) {
- Preconditions.checkNotNull(sliceUri,
- "Uri cannot be null, to remove the slice use clearSlice()");
- if (sliceUri == null) {
- clearSlice();
- return false;
- }
- validate(sliceUri);
- Slice s = Slice.bindSlice(mContext.getContentResolver(), sliceUri);
- if (s != null) {
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- }
- mObserver = new SliceObserver(new Handler(Looper.getMainLooper()));
- if (isAttachedToWindow()) {
- registerSlice(sliceUri);
- }
- mCurrentSlice = s;
- reinflate();
- }
- return s != null;
- }
-
- /**
- * Populates this view to the provided {@link Slice}.
- * <p>
- * This does not register a content observer on the URI that the slice is backed by so it will
- * not update if the content changes. To have the view update when the content changes use
- * {@link #setSlice(Uri)} instead. Unlike {@link #setSlice(Uri)}, this method does not require
- * any special permissions.
- */
- public void showSlice(@NonNull Slice slice) {
- Preconditions.checkNotNull(slice,
- "Slice cannot be null, to remove the slice use clearSlice()");
- clearSlice();
- mCurrentSlice = slice;
- reinflate();
- }
-
- /**
- * Unregisters the change observer that is set when using {@link #setSlice}. Normally this is
- * done automatically during {@link #onDetachedFromWindow()}.
- * <p>
- * It is safe to call this method multiple times.
- */
- public void clearSlice() {
- mCurrentSlice = null;
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
- }
-
- /**
- * Set the mode this view should present in.
- */
- public void setMode(@SliceMode String mode) {
- setMode(mode, false /* animate */);
- }
-
- /**
- * Set whether this view should allow scrollable content when presenting in {@link #MODE_LARGE}.
- */
- public void setScrollable(boolean isScrollable) {
- mIsScrollable = isScrollable;
- reinflate();
- }
-
- /**
- * @hide
- */
- public void setMode(@SliceMode String mode, boolean animate) {
- if (animate) {
- Log.e(TAG, "Animation not supported yet");
- }
- mMode = mode;
- reinflate();
- }
-
- /**
- * @return the mode this view is presenting in.
- */
- public @SliceMode String getMode() {
- if (mMode.equals(MODE_AUTO)) {
- return MODE_LARGE;
- }
- return mMode;
- }
-
- /**
- * @hide
- *
- * Whether this view should show a row of actions with it.
- */
- public void setShowActionRow(boolean show) {
- mShowActions = show;
- reinflate();
- }
-
- private SliceModeView createView(String mode) {
- switch (mode) {
- case MODE_SHORTCUT:
- return new ShortcutView(getContext());
- case MODE_SMALL:
- return new SmallTemplateView(getContext());
- }
- return new LargeTemplateView(getContext());
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- registerSlice(mCurrentSlice != null ? mCurrentSlice.getUri() : null);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
- }
-
- private void registerSlice(Uri sliceUri) {
- if (sliceUri == null || mObserver == null) {
- return;
- }
- mContext.getContentResolver().registerContentObserver(sliceUri,
- false /* notifyForDescendants */, mObserver);
- }
-
- private void reinflate() {
- if (mCurrentSlice == null) {
- return;
- }
- // TODO: Smarter mapping here from one state to the next.
- SliceItem color = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_COLOR);
- List<SliceItem> items = mCurrentSlice.getItems();
- SliceItem actionRow = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_SLICE,
- Slice.HINT_ACTIONS,
- Slice.HINT_ALT);
- String mode = getMode();
- if (!mode.equals(mCurrentView.getMode())) {
- removeAllViews();
- mCurrentView = createView(mode);
- addView(mCurrentView, getChildLp(mCurrentView));
- addView(mActions, getChildLp(mActions));
- }
- if (mode.equals(MODE_LARGE)) {
- ((LargeTemplateView) mCurrentView).setScrollable(mIsScrollable);
- }
- if (items.size() > 1 || (items.size() != 0 && items.get(0) != actionRow)) {
- mCurrentView.setVisibility(View.VISIBLE);
- mCurrentView.setSlice(mCurrentSlice);
- } else {
- mCurrentView.setVisibility(View.GONE);
- }
-
- boolean showActions = mShowActions && actionRow != null
- && !mode.equals(MODE_SHORTCUT);
- if (showActions) {
- mActions.setActions(actionRow, color);
- mActions.setVisibility(View.VISIBLE);
- } else {
- mActions.setVisibility(View.GONE);
- }
- }
-
- private LayoutParams getChildLp(View child) {
- if (child instanceof ShortcutView) {
- return new LayoutParams(mShortcutSize, mShortcutSize);
- } else {
- return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
- }
- }
-
- private static void validate(Uri sliceUri) {
- if (!ContentResolver.SCHEME_CONTENT.equals(sliceUri.getScheme())) {
- throw new RuntimeException("Invalid uri " + sliceUri);
- }
- if (sliceUri.getPathSegments().size() == 0) {
- throw new RuntimeException("Invalid uri " + sliceUri);
- }
- }
-
- private class SliceObserver extends ContentObserver {
- SliceObserver(Handler handler) {
- super(handler);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- this.onChange(selfChange, null);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- Slice s = Slice.bindSlice(mContext.getContentResolver(), uri);
- mCurrentSlice = s;
- reinflate();
- }
- }
-}
diff --git a/core/java/android/app/slice/widget/SliceViewUtil.java b/core/java/android/app/slice/widget/SliceViewUtil.java
deleted file mode 100644
index 1cf0055..0000000
--- a/core/java/android/app/slice/widget/SliceViewUtil.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.annotation.ColorInt;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.view.Gravity;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-/**
- * A bunch of utilities for slice UI.
- *
- * @hide
- */
-public class SliceViewUtil {
-
- /**
- * @hide
- */
- @ColorInt
- public static int getColorAccent(Context context) {
- return getColorAttr(context, android.R.attr.colorAccent);
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int getColorError(Context context) {
- return getColorAttr(context, android.R.attr.colorError);
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int getDefaultColor(Context context, int resId) {
- final ColorStateList list = context.getResources().getColorStateList(resId,
- context.getTheme());
-
- return list.getDefaultColor();
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int getDisabled(Context context, int inputColor) {
- return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor);
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int applyAlphaAttr(Context context, int attr, int inputColor) {
- TypedArray ta = context.obtainStyledAttributes(new int[] {
- attr
- });
- float alpha = ta.getFloat(0, 0);
- ta.recycle();
- return applyAlpha(alpha, inputColor);
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int applyAlpha(float alpha, int inputColor) {
- alpha *= Color.alpha(inputColor);
- return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor),
- Color.blue(inputColor));
- }
-
- /**
- * @hide
- */
- @ColorInt
- public static int getColorAttr(Context context, int attr) {
- TypedArray ta = context.obtainStyledAttributes(new int[] {
- attr
- });
- @ColorInt
- int colorAccent = ta.getColor(0, 0);
- ta.recycle();
- return colorAccent;
- }
-
- /**
- * @hide
- */
- public static int getThemeAttr(Context context, int attr) {
- TypedArray ta = context.obtainStyledAttributes(new int[] {
- attr
- });
- int theme = ta.getResourceId(0, 0);
- ta.recycle();
- return theme;
- }
-
- /**
- * @hide
- */
- public static Drawable getDrawable(Context context, int attr) {
- TypedArray ta = context.obtainStyledAttributes(new int[] {
- attr
- });
- Drawable drawable = ta.getDrawable(0);
- ta.recycle();
- return drawable;
- }
-
- /**
- * @hide
- */
- public static Icon createIconFromDrawable(Drawable d) {
- if (d instanceof BitmapDrawable) {
- return Icon.createWithBitmap(((BitmapDrawable) d).getBitmap());
- }
- Bitmap b = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(b);
- d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- d.draw(canvas);
- return Icon.createWithBitmap(b);
- }
-
- /**
- * @hide
- */
- public static void createCircledIcon(Context context, int color, int iconSize, Icon icon,
- boolean isLarge, ViewGroup parent) {
- ImageView v = new ImageView(context);
- v.setImageIcon(icon);
- parent.addView(v);
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
- if (isLarge) {
- // XXX better way to convert from icon -> bitmap or crop an icon (?)
- Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
- Canvas iconCanvas = new Canvas(iconBm);
- v.layout(0, 0, iconSize, iconSize);
- v.draw(iconCanvas);
- v.setImageBitmap(getCircularBitmap(iconBm));
- } else {
- v.setColorFilter(Color.WHITE);
- }
- lp.width = iconSize;
- lp.height = iconSize;
- lp.gravity = Gravity.CENTER;
- }
-
- /**
- * @hide
- */
- public static Bitmap getCircularBitmap(Bitmap bitmap) {
- Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
- bitmap.getHeight(), Config.ARGB_8888);
- Canvas canvas = new Canvas(output);
- final Paint paint = new Paint();
- final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
- paint.setAntiAlias(true);
- canvas.drawARGB(0, 0, 0, 0);
- canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
- bitmap.getWidth() / 2, paint);
- paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
- canvas.drawBitmap(bitmap, rect, rect, paint);
- return output;
- }
-}
diff --git a/core/java/android/app/slice/widget/SmallTemplateView.java b/core/java/android/app/slice/widget/SmallTemplateView.java
deleted file mode 100644
index 1c4c5df..0000000
--- a/core/java/android/app/slice/widget/SmallTemplateView.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.slice.widget;
-
-import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.app.slice.SliceQuery;
-import android.app.slice.widget.LargeSliceAdapter.SliceListView;
-import android.app.slice.widget.SliceView.SliceModeView;
-import android.content.Context;
-import android.os.AsyncTask;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.text.Format;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-/**
- * Small template is also used to construct list items for use with {@link LargeTemplateView}.
- *
- * @hide
- */
-public class SmallTemplateView extends SliceModeView implements SliceListView {
-
- private static final String TAG = "SmallTemplateView";
-
- private int mIconSize;
- private int mPadding;
-
- private LinearLayout mStartContainer;
- private TextView mTitleText;
- private TextView mSecondaryText;
- private LinearLayout mEndContainer;
-
- public SmallTemplateView(Context context) {
- super(context);
- inflate(context, R.layout.slice_small_template, this);
- mIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size);
- mPadding = getContext().getResources().getDimensionPixelSize(R.dimen.slice_padding);
-
- mStartContainer = (LinearLayout) findViewById(android.R.id.icon_frame);
- mTitleText = (TextView) findViewById(android.R.id.title);
- mSecondaryText = (TextView) findViewById(android.R.id.summary);
- mEndContainer = (LinearLayout) findViewById(android.R.id.widget_frame);
- }
-
- @Override
- public String getMode() {
- return SliceView.MODE_SMALL;
- }
-
- @Override
- public void setSliceItem(SliceItem slice) {
- resetViews();
- SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
- int color = colorItem != null ? colorItem.getColor() : -1;
-
- // Look for any title elements
- List<SliceItem> titleItems = SliceQuery.findAll(slice, -1, Slice.HINT_TITLE,
- null);
- boolean hasTitleText = false;
- boolean hasTitleItem = false;
- for (int i = 0; i < titleItems.size(); i++) {
- SliceItem item = titleItems.get(i);
- if (!hasTitleItem) {
- // icon, action icon, or timestamp
- if (item.getType() == SliceItem.TYPE_ACTION) {
- hasTitleItem = addIcon(item, color, mStartContainer);
- } else if (item.getType() == SliceItem.TYPE_IMAGE) {
- addIcon(item, color, mStartContainer);
- hasTitleItem = true;
- } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
- TextView tv = new TextView(getContext());
- tv.setText(convertTimeToString(item.getTimestamp()));
- hasTitleItem = true;
- }
- }
- if (!hasTitleText && item.getType() == SliceItem.TYPE_TEXT) {
- mTitleText.setText(item.getText());
- hasTitleText = true;
- }
- if (hasTitleText && hasTitleItem) {
- break;
- }
- }
- mTitleText.setVisibility(hasTitleText ? View.VISIBLE : View.GONE);
- mStartContainer.setVisibility(hasTitleItem ? View.VISIBLE : View.GONE);
-
- if (slice.getType() != SliceItem.TYPE_SLICE) {
- return;
- }
-
- // Deal with remaining items
- int itemCount = 0;
- boolean hasSummary = false;
- ArrayList<SliceItem> sliceItems = new ArrayList<SliceItem>(
- slice.getSlice().getItems());
- for (int i = 0; i < sliceItems.size(); i++) {
- SliceItem item = sliceItems.get(i);
- if (!hasSummary && item.getType() == SliceItem.TYPE_TEXT
- && !item.hasHint(Slice.HINT_TITLE)) {
- // TODO -- Should combine all text items?
- mSecondaryText.setText(item.getText());
- hasSummary = true;
- }
- if (itemCount <= 3) {
- if (item.getType() == SliceItem.TYPE_ACTION) {
- if (addIcon(item, color, mEndContainer)) {
- itemCount++;
- }
- } else if (item.getType() == SliceItem.TYPE_IMAGE) {
- addIcon(item, color, mEndContainer);
- itemCount++;
- } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
- TextView tv = new TextView(getContext());
- tv.setText(convertTimeToString(item.getTimestamp()));
- mEndContainer.addView(tv);
- itemCount++;
- } else if (item.getType() == SliceItem.TYPE_SLICE) {
- List<SliceItem> subItems = item.getSlice().getItems();
- for (int j = 0; j < subItems.size(); j++) {
- sliceItems.add(subItems.get(j));
- }
- }
- }
- }
- }
-
- @Override
- public void setSlice(Slice slice) {
- setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE,
- slice.getHints().toArray(new String[slice.getHints().size()])));
- }
-
- /**
- * @return Whether an icon was added.
- */
- private boolean addIcon(SliceItem sliceItem, int color, LinearLayout container) {
- SliceItem image = null;
- SliceItem action = null;
- if (sliceItem.getType() == SliceItem.TYPE_ACTION) {
- image = SliceQuery.find(sliceItem.getSlice(), SliceItem.TYPE_IMAGE);
- action = sliceItem;
- } else if (sliceItem.getType() == SliceItem.TYPE_IMAGE) {
- image = sliceItem;
- }
- if (image != null) {
- ImageView iv = new ImageView(getContext());
- iv.setImageIcon(image.getIcon());
- if (action != null) {
- final SliceItem sliceAction = action;
- iv.setOnClickListener(v -> AsyncTask.execute(
- () -> {
- try {
- sliceAction.getAction().send();
- } catch (CanceledException e) {
- e.printStackTrace();
- }
- }));
- iv.setBackground(SliceViewUtil.getDrawable(getContext(),
- android.R.attr.selectableItemBackground));
- }
- if (color != -1 && !sliceItem.hasHint(Slice.HINT_NO_TINT)) {
- iv.setColorFilter(color);
- }
- container.addView(iv);
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) iv.getLayoutParams();
- lp.width = mIconSize;
- lp.height = mIconSize;
- lp.setMarginStart(mPadding);
- return true;
- }
- return false;
- }
-
- private String convertTimeToString(long time) {
- // TODO -- figure out what format(s) we support
- Date date = new Date(time);
- Format format = new SimpleDateFormat("MM dd yyyy HH:mm:ss");
- return format.format(date);
- }
-
- private void resetViews() {
- mStartContainer.removeAllViews();
- mEndContainer.removeAllViews();
- mTitleText.setText(null);
- mSecondaryText.setText(null);
- }
-}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0d7a941..8200414 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -100,6 +100,12 @@
*/
public static final int CHOOSER_ACTION = 9;
+ /**
+ * An event type denoting that a notification was viewed by the user.
+ * @hide
+ */
+ public static final int NOTIFICATION_SEEN = 10;
+
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index e3d763a..6692e13 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -350,13 +350,22 @@
* application can be registered at time. When no longer used, application
* should be unregistered using
* {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}.
+ * The registration status should be tracked by the application by handling callback from
+ * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related
+ * to the return value of this method.
*
* @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record.
+ * The HID Device SDP record is required.
* @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings.
+ * The Incoming QoS Settings is not required. Use null or default
+ * BluetoothHidDeviceAppQosSettings.Builder for default values.
* @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings.
+ * The Outgoing QoS Settings is not required. Use null or default
+ * BluetoothHidDeviceAppQosSettings.Builder for default values.
* @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be
* sent.
- * @return
+ * The BluetoothHidDeviceCallback object is required.
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
@@ -394,12 +403,15 @@
* {@link #registerApp
* (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
* BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)}
+ * The registration status should be tracked by the application by handling callback from
+ * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related
+ * to the return value of this method.
*
* @param config {@link BluetoothHidDeviceAppConfiguration} object as obtained from {@link
* BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice,
* BluetoothHidDeviceAppConfiguration,
* boolean)}
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) {
Log.v(TAG, "unregisterApp()");
@@ -426,7 +438,7 @@
* @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in
* descriptor.
* @param data Report data, not including Report Id.
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
boolean result = false;
@@ -452,7 +464,7 @@
* @param type Report Type, as in request.
* @param id Report Id, as in request.
* @param data Report data, not including Report Id.
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
@@ -478,7 +490,7 @@
* from {@link BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
*
* @param error Error to be sent for SET_REPORT via HANDSHAKE.
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean reportError(BluetoothDevice device, byte error) {
Log.v(TAG, "reportError(): device=" + device + " error=" + error);
@@ -524,10 +536,13 @@
}
/**
- * Initiates connection to host which currently has Virtual Cable
- * established with device.
+ * Initiates connection to host which is currently paired with this device.
+ * If the application is not registered, #connect(BluetoothDevice) will fail.
+ * The connection state should be tracked by the application by handling callback from
+ * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related
+ * to the return value of this method.
*
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean connect(BluetoothDevice device) {
Log.v(TAG, "connect(): device=" + device);
@@ -550,8 +565,11 @@
/**
* Disconnects from currently connected host.
+ * The connection state should be tracked by the application by handling callback from
+ * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related
+ * to the return value of this method.
*
- * @return
+ * @return true if the command is successfully sent; otherwise false.
*/
public boolean disconnect(BluetoothDevice device) {
Log.v(TAG, "disconnect(): device=" + device);
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
index ccc3ef4..881ae98 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
@@ -45,6 +45,21 @@
public static final int MAX = (int) 0xffffffff;
+ /**
+ * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel.
+ * The QoS Settings is optional.
+ * Recommended to use BluetoothHidDeviceAppQosSettings.Builder.
+ * {@see <a href="https://www.bluetooth.com/specifications/profiles-overview">
+ * https://www.bluetooth.com/specifications/profiles-overview
+ * </a>
+ * Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D }
+ * @param serviceType L2CAP service type
+ * @param tokenRate L2CAP token rate
+ * @param tokenBucketSize L2CAP token bucket size
+ * @param peakBandwidth L2CAP peak bandwidth
+ * @param latency L2CAP latency
+ * @param delayVariation L2CAP delay variation
+ */
public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize,
int peakBandwidth, int latency, int delayVariation) {
this.serviceType = serviceType;
@@ -59,7 +74,12 @@
public boolean equals(Object o) {
if (o instanceof BluetoothHidDeviceAppQosSettings) {
BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o;
- return false;
+ return this.serviceType == qos.serviceType
+ && this.tokenRate == qos.tokenRate
+ && this.tokenBucketSize == qos.tokenBucketSize
+ && this.peakBandwidth == qos.peakBandwidth
+ && this.latency == qos.latency
+ && this.delayVariation == qos.delayVariation;
}
return false;
}
@@ -106,4 +126,85 @@
serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation
};
}
+
+ /**
+ * A helper to build the BluetoothHidDeviceAppQosSettings object.
+ */
+ public static class Builder {
+ // Optional parameters - initialized to default values
+ private int mServiceType = SERVICE_BEST_EFFORT;
+ private int mTokenRate = 0;
+ private int mTokenBucketSize = 0;
+ private int mPeakBandwidth = 0;
+ private int mLatency = MAX;
+ private int mDelayVariation = MAX;
+
+ /**
+ * Set the service type.
+ * @param val service type. Should be one of {SERVICE_NO_TRAFFIC, SERVICE_BEST_EFFORT,
+ * SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one.
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified service type.
+ */
+ public Builder serviceType(int val) {
+ mServiceType = val;
+ return this;
+ }
+ /**
+ * Set the token rate.
+ * @param val token rate
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified token rate.
+ */
+ public Builder tokenRate(int val) {
+ mTokenRate = val;
+ return this;
+ }
+
+ /**
+ * Set the bucket size.
+ * @param val bucket size
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified bucket size.
+ */
+ public Builder tokenBucketSize(int val) {
+ mTokenBucketSize = val;
+ return this;
+ }
+
+ /**
+ * Set the peak bandwidth.
+ * @param val peak bandwidth
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified peak bandwidth.
+ */
+ public Builder peakBandwidth(int val) {
+ mPeakBandwidth = val;
+ return this;
+ }
+ /**
+ * Set the latency.
+ * @param val latency
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified latency.
+ */
+ public Builder latency(int val) {
+ mLatency = val;
+ return this;
+ }
+
+ /**
+ * Set the delay variation.
+ * @param val delay variation
+ * @return BluetoothHidDeviceAppQosSettings Builder with specified delay variation.
+ */
+ public Builder delayVariation(int val) {
+ mDelayVariation = val;
+ return this;
+ }
+
+ /**
+ * Build the BluetoothHidDeviceAppQosSettings object.
+ * @return BluetoothHidDeviceAppQosSettings object with current settings.
+ */
+ public BluetoothHidDeviceAppQosSettings build() {
+ return new BluetoothHidDeviceAppQosSettings(mServiceType, mTokenRate, mTokenBucketSize,
+ mPeakBandwidth, mLatency, mDelayVariation);
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index f01c493..4669637 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+
/**
* Represents the Service Discovery Protocol (SDP) settings for a Bluetooth
* HID Device application.
@@ -39,6 +41,18 @@
public final byte subclass;
public final byte[] descriptors;
+ /**
+ * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record.
+ * @param name Name of this Bluetooth HID device. Maximum length is 50 bytes.
+ * @param description Description for this Bluetooth HID device. Maximum length is 50 bytes.
+ * @param provider Provider of this Bluetooth HID device. Maximum length is 50 bytes.
+ * @param subclass Subclass of this Bluetooth HID device.
+ * See <a href="www.usb.org/developers/hidpage/HID1_11.pdf">
+ * www.usb.org/developers/hidpage/HID1_11.pdf Section 4.2</a>
+ * @param descriptors Descriptors of this Bluetooth HID device.
+ * See <a href="www.usb.org/developers/hidpage/HID1_11.pdf">
+ * www.usb.org/developers/hidpage/HID1_11.pdf Chapter 6</a> Maximum length is 2048 bytes.
+ */
public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider,
byte subclass, byte[] descriptors) {
this.name = name;
@@ -52,7 +66,11 @@
public boolean equals(Object o) {
if (o instanceof BluetoothHidDeviceAppSdpSettings) {
BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o;
- return false;
+ return this.name.equals(sdp.name)
+ && this.description.equals(sdp.description)
+ && this.provider.equals(sdp.provider)
+ && this.subclass == sdp.subclass
+ && Arrays.equals(this.descriptors, sdp.descriptors);
}
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 23956e1..0569913 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -375,7 +375,7 @@
IBluetooth bluetoothProxy =
BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
- mPfd = bluetoothProxy.connectSocket(mDevice, mType,
+ mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType,
mUuid, mPort, getSecurityFlags());
synchronized (this) {
if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java
index b7545bf..6e9f09c 100644
--- a/core/java/android/content/AsyncTaskLoader.java
+++ b/core/java/android/content/AsyncTaskLoader.java
@@ -49,7 +49,10 @@
* fragment}
*
* @param <D> the data type to be loaded.
+ *
+ * @deprecated Use {@link android.support.v4.content.AsyncTaskLoader}
*/
+@Deprecated
public abstract class AsyncTaskLoader<D> extends Loader<D> {
static final String TAG = "AsyncTaskLoader";
static final boolean DEBUG = false;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 01ad3ad..19e24ad 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4044,6 +4044,13 @@
public static final String STATS_COMPANION_SERVICE = "statscompanion";
/**
+ * Use with {@link #getSystemService} to retrieve an {@link android.stats.StatsManager}.
+ * @hide
+ */
+ @SystemApi
+ public static final String STATS_MANAGER = "stats";
+
+ /**
* Use with {@link #getSystemService} to retrieve a {@link
* android.content.om.OverlayManager} for managing overlay packages.
*
@@ -4073,6 +4080,14 @@
public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.content.pm.crossprofile.CrossProfileApps} for cross profile operations.
+ *
+ * @see #getSystemService
+ */
+ public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index c78871c..33386e5 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -38,7 +38,10 @@
* in the desired paramters with {@link #setUri(Uri)}, {@link #setSelection(String)},
* {@link #setSelectionArgs(String[])}, {@link #setSortOrder(String)},
* and {@link #setProjection(String[])}.
+ *
+ * @deprecated Use {@link android.support.v4.content.CursorLoader}
*/
+@Deprecated
public class CursorLoader extends AsyncTaskLoader<Cursor> {
final ForceLoadContentObserver mObserver;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index dd729a3..bad452c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1728,6 +1728,9 @@
* <p>
* Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the install
* succeeded.
+ * <p>
+ * Requires {@link android.Manifest.permission#REQUEST_DELETE_PACKAGES}
+ * since {@link Build.VERSION_CODES#P}.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE";
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 3faf13b..80f9a14 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -48,7 +48,10 @@
* </div>
*
* @param <D> The result returned when the load is complete
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader}
*/
+@Deprecated
public class Loader<D> {
int mId;
OnLoadCompleteListener<D> mListener;
@@ -66,7 +69,10 @@
* is told it has changed. You do not normally need to use this yourself;
* it is used for you by {@link CursorLoader} to take care of executing
* an update when the cursor's backing data changes.
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader.ForceLoadContentObserver}
*/
+ @Deprecated
public final class ForceLoadContentObserver extends ContentObserver {
public ForceLoadContentObserver() {
super(new Handler());
@@ -90,7 +96,10 @@
* to find out when a Loader it is managing has completed so that this can
* be reported to its client. This interface should only be used if a
* Loader is not being used in conjunction with LoaderManager.
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader.OnLoadCompleteListener}
*/
+ @Deprecated
public interface OnLoadCompleteListener<D> {
/**
* Called on the thread that created the Loader when the load is complete.
@@ -108,7 +117,10 @@
* to find out when a Loader it is managing has been canceled so that it
* can schedule the next Loader. This interface should only be used if a
* Loader is not being used in conjunction with LoaderManager.
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader.OnLoadCanceledListener}
*/
+ @Deprecated
public interface OnLoadCanceledListener<D> {
/**
* Called on the thread that created the Loader when the load is canceled.
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 4b09fed..d3652e8 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -30,6 +30,11 @@
* when they are committed to storage. Objects that are returned from the
* various <code>get</code> methods must be treated as immutable by the application.
*
+ * <p>Note: This class provides strong consistency guarantees. It is using expensive operations
+ * which might slow down an app. Frequently changing properties or properties where loss can be
+ * tolerated should use other mechanisms. For more details read the comments on
+ * {@link Editor#commit()} and {@link Editor#apply()}.
+ *
* <p><em>Note: This class does not support use across multiple processes.</em>
*
* <div class="special reference">
diff --git a/core/java/android/content/pm/IPackageInstallObserver.aidl b/core/java/android/content/pm/IPackageInstallObserver.aidl
deleted file mode 100644
index 6133365..0000000
--- a/core/java/android/content/pm/IPackageInstallObserver.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.content.pm;
-
-/**
- * API for installation callbacks from the Package Manager.
- * @hide
- */
-oneway interface IPackageInstallObserver {
- void packageInstalled(in String packageName, int returnCode);
-}
-
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1352bc2..64d33d5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -543,9 +543,10 @@
void forceDexOpt(String packageName);
/**
- * Execute the background dexopt job immediately.
+ * Execute the background dexopt job immediately on packages in packageNames.
+ * If null, then execute on all packages.
*/
- boolean runBackgroundDexoptJob();
+ boolean runBackgroundDexoptJob(in List<String> packageNames);
/**
* Reconcile the information we have about the secondary dex files belonging to
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 05c5556..9e54e23 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -282,13 +282,13 @@
public static final int FLAG_GET_MANIFEST = FLAG_MATCH_MANIFEST;
/**
- * @hide include all pinned shortcuts by any launchers, not just by the caller,
+ * Include all pinned shortcuts by any launchers, not just by the caller,
* in the result.
- * If the caller doesn't havve the {@link android.Manifest.permission#ACCESS_SHORTCUTS}
- * permission, this flag will be ignored.
+ *
+ * The caller must be the selected assistant app to use this flag, or have the system
+ * {@code ACCESS_SHORTCUTS} permission.
*/
- @TestApi
- public static final int FLAG_MATCH_ALL_PINNED = 1 << 10;
+ public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1 << 10;
/**
* FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST
@@ -302,7 +302,7 @@
* @hide
*/
public static final int FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED =
- FLAG_MATCH_ALL_KINDS | FLAG_MATCH_ALL_PINNED;
+ FLAG_MATCH_ALL_KINDS | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
/** @hide kept for unit tests */
@Deprecated
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 5673361..77c5743 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -81,6 +81,9 @@
* <li>All APKs must have unique split names.
* <li>All installations must contain a single base APK.
* </ul>
+ * <p>
+ * The ApiDemos project contains examples of using this API:
+ * <code>ApiDemos/src/com/example/android/apis/content/InstallApk*.java</code>.
*/
public class PackageInstaller {
private static final String TAG = "PackageInstaller";
@@ -444,6 +447,9 @@
* @param packageName The package to uninstall.
* @param statusReceiver Where to deliver the result.
*/
+ @RequiresPermission(anyOf = {
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.REQUEST_DELETE_PACKAGES})
public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
uninstall(packageName, 0 /*flags*/, statusReceiver);
}
@@ -476,6 +482,9 @@
* @param versionedPackage The versioned package to uninstall.
* @param statusReceiver Where to deliver the result.
*/
+ @RequiresPermission(anyOf = {
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.REQUEST_DELETE_PACKAGES})
public void uninstall(@NonNull VersionedPackage versionedPackage,
@NonNull IntentSender statusReceiver) {
uninstall(versionedPackage, 0 /*flags*/, statusReceiver);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 31ca198..15ea4b5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -871,8 +871,8 @@
public static final int INSTALL_REASON_USER = 4;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} on success.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * on success.
*
* @hide
*/
@@ -880,8 +880,8 @@
public static final int INSTALL_SUCCEEDED = 1;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package is already installed.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package is already installed.
*
* @hide
*/
@@ -889,8 +889,8 @@
public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package archive file is invalid.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package archive file is invalid.
*
* @hide
*/
@@ -898,8 +898,8 @@
public static final int INSTALL_FAILED_INVALID_APK = -2;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the URI passed in is invalid.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the URI passed in is invalid.
*
* @hide
*/
@@ -907,9 +907,9 @@
public static final int INSTALL_FAILED_INVALID_URI = -3;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package manager service found that
- * the device didn't have enough storage space to install the app.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package manager service found that the device didn't have enough storage space to
+ * install the app.
*
* @hide
*/
@@ -917,9 +917,8 @@
public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if a package is already installed with
- * the same name.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if a package is already installed with the same name.
*
* @hide
*/
@@ -927,9 +926,8 @@
public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the requested shared user does not
- * exist.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the requested shared user does not exist.
*
* @hide
*/
@@ -937,10 +935,9 @@
public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if a previously installed package of the
- * same name has a different signature than the new package (and the old
- * package's data was not removed).
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if a previously installed package of the same name has a different signature than the new
+ * package (and the old package's data was not removed).
*
* @hide
*/
@@ -948,10 +945,9 @@
public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package is requested a shared
- * user which is already installed on the device and does not have matching
- * signature.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package is requested a shared user which is already installed on the device and
+ * does not have matching signature.
*
* @hide
*/
@@ -959,9 +955,8 @@
public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package uses a shared library
- * that is not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package uses a shared library that is not available.
*
* @hide
*/
@@ -969,9 +964,8 @@
public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package uses a shared library
- * that is not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package uses a shared library that is not available.
*
* @hide
*/
@@ -979,10 +973,9 @@
public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed while
- * optimizing and validating its dex files, either because there was not
- * enough storage or the validation failed.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed while optimizing and validating its dex files, either because there
+ * was not enough storage or the validation failed.
*
* @hide
*/
@@ -990,9 +983,9 @@
public static final int INSTALL_FAILED_DEXOPT = -11;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because the
- * current SDK version is older than that required by the package.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because the current SDK version is older than that required by the
+ * package.
*
* @hide
*/
@@ -1000,10 +993,9 @@
public static final int INSTALL_FAILED_OLDER_SDK = -12;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because it
- * contains a content provider with the same authority as a provider already
- * installed in the system.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because it contains a content provider with the same authority as a
+ * provider already installed in the system.
*
* @hide
*/
@@ -1011,9 +1003,9 @@
public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because the
- * current SDK version is newer than that required by the package.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because the current SDK version is newer than that required by the
+ * package.
*
* @hide
*/
@@ -1021,10 +1013,9 @@
public static final int INSTALL_FAILED_NEWER_SDK = -14;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package failed because it has
- * specified that it is a test-only package and the caller has not supplied
- * the {@link #INSTALL_ALLOW_TEST} flag.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because it has specified that it is a test-only package and the
+ * caller has not supplied the {@link #INSTALL_ALLOW_TEST} flag.
*
* @hide
*/
@@ -1032,9 +1023,9 @@
public static final int INSTALL_FAILED_TEST_ONLY = -15;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package being installed contains
- * native code, but none that is compatible with the device's CPU_ABI.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package being installed contains native code, but none that is compatible with the
+ * device's CPU_ABI.
*
* @hide
*/
@@ -1042,9 +1033,8 @@
public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package uses a feature that is
- * not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package uses a feature that is not available.
*
* @hide
*/
@@ -1053,9 +1043,9 @@
// ------ Errors related to sdcard
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if a secure container mount point
- * couldn't be accessed on external media.
+ * Installation return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if a secure container mount point couldn't be
+ * accessed on external media.
*
* @hide
*/
@@ -1063,9 +1053,8 @@
public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * in the specified install location.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed in the specified install location.
*
* @hide
*/
@@ -1073,9 +1062,9 @@
public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * in the specified install location because the media is not available.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed in the specified install location because the media
+ * is not available.
*
* @hide
*/
@@ -1083,9 +1072,8 @@
public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * because the verification timed out.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed because the verification timed out.
*
* @hide
*/
@@ -1093,9 +1081,8 @@
public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package couldn't be installed
- * because the verification did not succeed.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package couldn't be installed because the verification did not succeed.
*
* @hide
*/
@@ -1103,9 +1090,8 @@
public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the package changed from what the
- * calling program expected.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the package changed from what the calling program expected.
*
* @hide
*/
@@ -1113,28 +1099,25 @@
public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package is assigned a
- * different UID than it previously held.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package is assigned a different UID than it previously held.
*
* @hide
*/
public static final int INSTALL_FAILED_UID_CHANGED = -24;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package has an older version
- * code than the currently installed package.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package has an older version code than the currently installed package.
*
* @hide
*/
public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the old package has target SDK high
- * enough to support runtime permission and the new package has target SDK
- * low enough to not support runtime permissions.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the old package has target SDK high enough to support runtime permission and the new
+ * package has target SDK low enough to not support runtime permissions.
*
* @hide
*/
@@ -1142,9 +1125,8 @@
public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26;
/**
- * Installation return code: this is passed to the
- * {@link IPackageInstallObserver} if the new package attempts to downgrade the
- * target sandbox version of the app.
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package attempts to downgrade the target sandbox version of the app.
*
* @hide
*/
@@ -1152,9 +1134,9 @@
public static final int INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE = -27;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser was given a path that is
- * not a file, or does not end with the expected '.apk' extension.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was given a path that is not a
+ * file, or does not end with the expected '.apk' extension.
*
* @hide
*/
@@ -1162,8 +1144,8 @@
public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser was unable to retrieve the
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was unable to retrieve the
* AndroidManifest.xml file.
*
* @hide
@@ -1172,8 +1154,8 @@
public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered an unexpected
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered an unexpected
* exception.
*
* @hide
@@ -1182,9 +1164,9 @@
public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser did not find any
- * certificates in the .apk.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser did not find any certificates in
+ * the .apk.
*
* @hide
*/
@@ -1192,9 +1174,9 @@
public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser found inconsistent
- * certificates on the files in the .apk.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser found inconsistent certificates on
+ * the files in the .apk.
*
* @hide
*/
@@ -1202,8 +1184,8 @@
public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered a
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a
* CertificateEncodingException in one of the files in the .apk.
*
* @hide
@@ -1212,9 +1194,9 @@
public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered a bad or
- * missing package name in the manifest.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a bad or missing
+ * package name in the manifest.
*
* @hide
*/
@@ -1222,9 +1204,9 @@
public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered a bad shared
- * user id name in the manifest.
+ * Installation parse return code: tthis is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered a bad shared user id
+ * name in the manifest.
*
* @hide
*/
@@ -1232,8 +1214,8 @@
public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser encountered some structural
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser encountered some structural
* problem in the manifest.
*
* @hide
@@ -1242,9 +1224,9 @@
public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
/**
- * Installation parse return code: this is passed to the
- * {@link IPackageInstallObserver} if the parser did not find any actionable
- * tags (instrumentation or application) in the manifest.
+ * Installation parse return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser did not find any actionable tags
+ * (instrumentation or application) in the manifest.
*
* @hide
*/
@@ -1252,9 +1234,9 @@
public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because of system issues.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because of system issues.
*
* @hide
*/
@@ -1262,24 +1244,23 @@
public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because the user is restricted from installing apps.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because the user is restricted from installing apps.
*
* @hide
*/
public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because it is attempting to define a permission that is already
- * defined by some existing package.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because it is attempting to define a permission that is already defined by some existing
+ * package.
* <p>
- * The package name of the app which has already defined the permission is
- * passed to a {@link PackageInstallObserver}, if any, as the
- * {@link #EXTRA_FAILURE_EXISTING_PACKAGE} string extra; and the name of the
- * permission being redefined is passed in the
+ * The package name of the app which has already defined the permission is passed to a
+ * {@link PackageInstallObserver}, if any, as the {@link #EXTRA_FAILURE_EXISTING_PACKAGE} string
+ * extra; and the name of the permission being redefined is passed in the
* {@link #EXTRA_FAILURE_EXISTING_PERMISSION} string extra.
*
* @hide
@@ -1287,10 +1268,9 @@
public static final int INSTALL_FAILED_DUPLICATE_PERMISSION = -112;
/**
- * Installation failed return code: this is passed to the
- * {@link IPackageInstallObserver} if the system failed to install the
- * package because its packaged native code did not match any of the ABIs
- * supported by the system.
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because its packaged native code did not match any of the ABIs supported by the system.
*
* @hide
*/
@@ -4718,16 +4698,6 @@
@Deprecated
public abstract void installPackage(
Uri packageURI,
- IPackageInstallObserver observer,
- @InstallFlags int flags,
- String installerPackageName);
- /**
- * @deprecated replaced by {@link PackageInstaller}
- * @hide
- */
- @Deprecated
- public abstract void installPackage(
- Uri packageURI,
PackageInstallObserver observer,
@InstallFlags int flags,
String installerPackageName);
@@ -5743,25 +5713,6 @@
}
/** {@hide} */
- public static class LegacyPackageInstallObserver extends PackageInstallObserver {
- private final IPackageInstallObserver mLegacy;
-
- public LegacyPackageInstallObserver(IPackageInstallObserver legacy) {
- mLegacy = legacy;
- }
-
- @Override
- public void onPackageInstalled(String basePackageName, int returnCode, String msg,
- Bundle extras) {
- if (mLegacy == null) return;
- try {
- mLegacy.packageInstalled(basePackageName, returnCode);
- } catch (RemoteException ignored) {
- }
- }
- }
-
- /** {@hide} */
public static class LegacyPackageDeleteObserver extends PackageDeleteObserver {
private final IPackageDeleteObserver mLegacy;
diff --git a/core/java/android/content/pm/crossprofile/CrossProfileApps.java b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
new file mode 100644
index 0000000..c441b5f
--- /dev/null
+++ b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.crossprofile;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.List;
+
+/**
+ * Class for handling cross profile operations. Apps can use this class to interact with its
+ * instance in any profile that is in {@link #getTargetUserProfiles()}. For example, app can
+ * use this class to start its main activity in managed profile.
+ */
+public class CrossProfileApps {
+ private final Context mContext;
+ private final ICrossProfileApps mService;
+
+ /** @hide */
+ public CrossProfileApps(Context context, ICrossProfileApps service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Starts the specified main activity of the caller package in the specified profile.
+ *
+ * @param component The ComponentName of the activity to launch, it must be exported and has
+ * action {@link android.content.Intent#ACTION_MAIN}, category
+ * {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
+ * be thrown.
+ * @param user The UserHandle of the profile, must be one of the users returned by
+ * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+ * be thrown.
+ * @param sourceBounds The Rect containing the source bounds of the clicked icon, see
+ * {@link android.content.Intent#setSourceBounds(Rect)}.
+ * @param startActivityOptions Options to pass to startActivity
+ */
+ public void startMainActivity(@NonNull ComponentName component, @NonNull UserHandle user,
+ @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
+ try {
+ mService.startActivityAsUser(mContext.getPackageName(),
+ component, sourceBounds, startActivityOptions, user);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return a list of user profiles that that the caller can use when calling other APIs in this
+ * class.
+ * <p>
+ * A user profile would be considered as a valid target user profile, provided that:
+ * <ul>
+ * <li>It gets caller app installed</li>
+ * <li>It is not equal to the calling user</li>
+ * <li>It is in the same profile group of calling user profile</li>
+ * <li>It is enabled</li>
+ * </ul>
+ *
+ * @see UserManager#getUserProfiles()
+ */
+ public @NonNull List<UserHandle> getTargetUserProfiles() {
+ try {
+ return mService.getTargetUserProfiles(mContext.getPackageName());
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl b/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
new file mode 100644
index 0000000..dd8d04f
--- /dev/null
+++ b/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.crossprofile;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * @hide
+ */
+interface ICrossProfileApps {
+ void startActivityAsUser(in String callingPackage, in ComponentName component, in Rect sourceBounds, in Bundle startActivityOptions, in UserHandle user);
+ List<UserHandle> getTargetUserProfiles(in String callingPackage);
+}
\ No newline at end of file
diff --git a/core/java/android/database/MergeCursor.java b/core/java/android/database/MergeCursor.java
index 2c25db7..272cfa2 100644
--- a/core/java/android/database/MergeCursor.java
+++ b/core/java/android/database/MergeCursor.java
@@ -17,7 +17,7 @@
package android.database;
/**
- * A convience class that lets you present an array of Cursors as a single linear Cursor.
+ * A convenience class that lets you present an array of Cursors as a single linear Cursor.
* The schema of the cursors presented is entirely up to the creator of the MergeCursor, and
* may be different if that is desired. Calls to getColumns, getColumnIndex, etc will return the
* value for the row that the MergeCursor is currently pointing at.
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index b692039..2c93a7f 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -289,14 +289,19 @@
private void setWalModeFromConfiguration() {
if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- boolean walEnabled =
+ final boolean walEnabled =
(mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
- if (walEnabled || mConfiguration.useCompatibilityWal) {
+ // Use compatibility WAL unless an app explicitly set journal/synchronous mode
+ final boolean useCompatibilityWal = mConfiguration.journalMode == null
+ && mConfiguration.syncMode == null && mConfiguration.useCompatibilityWal;
+ if (walEnabled || useCompatibilityWal) {
setJournalMode("WAL");
setSyncMode(SQLiteGlobal.getWALSyncMode());
} else {
- setJournalMode(SQLiteGlobal.getDefaultJournalMode());
- setSyncMode(SQLiteGlobal.getDefaultSyncMode());
+ setJournalMode(mConfiguration.journalMode == null
+ ? SQLiteGlobal.getDefaultJournalMode() : mConfiguration.journalMode);
+ setSyncMode(mConfiguration.syncMode == null
+ ? SQLiteGlobal.getDefaultSyncMode() : mConfiguration.syncMode);
}
}
}
@@ -310,12 +315,10 @@
}
private static String canonicalizeSyncMode(String value) {
- if (value.equals("0")) {
- return "OFF";
- } else if (value.equals("1")) {
- return "NORMAL";
- } else if (value.equals("2")) {
- return "FULL";
+ switch (value) {
+ case "0": return "OFF";
+ case "1": return "NORMAL";
+ case "2": return "FULL";
}
return value;
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 83b8dc7..863fb19 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -262,7 +262,8 @@
private SQLiteDatabase(final String path, final int openFlags,
CursorFactory cursorFactory, DatabaseErrorHandler errorHandler,
- int lookasideSlotSize, int lookasideSlotCount, long idleConnectionTimeoutMs) {
+ int lookasideSlotSize, int lookasideSlotCount, long idleConnectionTimeoutMs,
+ String journalMode, String syncMode) {
mCursorFactory = cursorFactory;
mErrorHandler = errorHandler != null ? errorHandler : new DefaultDatabaseErrorHandler();
mConfigurationLocked = new SQLiteDatabaseConfiguration(path, openFlags);
@@ -285,6 +286,8 @@
}
}
mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs;
+ mConfigurationLocked.journalMode = journalMode;
+ mConfigurationLocked.syncMode = syncMode;
mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported();
}
@@ -721,7 +724,7 @@
SQLiteDatabase db = new SQLiteDatabase(path, openParams.mOpenFlags,
openParams.mCursorFactory, openParams.mErrorHandler,
openParams.mLookasideSlotSize, openParams.mLookasideSlotCount,
- openParams.mIdleConnectionTimeout);
+ openParams.mIdleConnectionTimeout, openParams.mJournalMode, openParams.mSyncMode);
db.open();
return db;
}
@@ -747,7 +750,8 @@
*/
public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory,
@DatabaseOpenFlags int flags, @Nullable DatabaseErrorHandler errorHandler) {
- SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler, -1, -1, -1);
+ SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler, -1, -1, -1, null,
+ null);
db.open();
return db;
}
@@ -2302,17 +2306,21 @@
private final DatabaseErrorHandler mErrorHandler;
private final int mLookasideSlotSize;
private final int mLookasideSlotCount;
- private long mIdleConnectionTimeout;
+ private final long mIdleConnectionTimeout;
+ private final String mJournalMode;
+ private final String mSyncMode;
private OpenParams(int openFlags, CursorFactory cursorFactory,
DatabaseErrorHandler errorHandler, int lookasideSlotSize, int lookasideSlotCount,
- long idleConnectionTimeout) {
+ long idleConnectionTimeout, String journalMode, String syncMode) {
mOpenFlags = openFlags;
mCursorFactory = cursorFactory;
mErrorHandler = errorHandler;
mLookasideSlotSize = lookasideSlotSize;
mLookasideSlotCount = lookasideSlotCount;
mIdleConnectionTimeout = idleConnectionTimeout;
+ mJournalMode = journalMode;
+ mSyncMode = syncMode;
}
/**
@@ -2379,6 +2387,28 @@
}
/**
+ * Returns <a href="https://sqlite.org/pragma.html#pragma_journal_mode">journal mode</a>.
+ * This journal mode will only be used if {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING}
+ * flag is not set, otherwise a platform will use "WAL" journal mode.
+ * @see Builder#setJournalMode(String)
+ */
+ @Nullable
+ public String getJournalMode() {
+ return mJournalMode;
+ }
+
+ /**
+ * Returns <a href="https://sqlite.org/pragma.html#pragma_synchronous">synchronous mode</a>.
+ * This value will only be used when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} flag
+ * is not set, otherwise a system wide default will be used.
+ * @see Builder#setSynchronousMode(String)
+ */
+ @Nullable
+ public String getSynchronousMode() {
+ return mSyncMode;
+ }
+
+ /**
* Creates a new instance of builder {@link Builder#Builder(OpenParams) initialized} with
* {@code this} parameters.
* @hide
@@ -2398,6 +2428,8 @@
private int mOpenFlags;
private CursorFactory mCursorFactory;
private DatabaseErrorHandler mErrorHandler;
+ private String mJournalMode;
+ private String mSyncMode;
public Builder() {
}
@@ -2408,6 +2440,8 @@
mOpenFlags = params.mOpenFlags;
mCursorFactory = params.mCursorFactory;
mErrorHandler = params.mErrorHandler;
+ mJournalMode = params.mJournalMode;
+ mSyncMode = params.mSyncMode;
}
/**
@@ -2539,6 +2573,30 @@
return this;
}
+
+ /**
+ * Sets <a href="https://sqlite.org/pragma.html#pragma_journal_mode">journal mode</a>
+ * to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} flag is not set.
+ */
+ @NonNull
+ public Builder setJournalMode(@NonNull String journalMode) {
+ Preconditions.checkNotNull(journalMode);
+ mJournalMode = journalMode;
+ return this;
+ }
+
+ /**
+ * Sets <a href="https://sqlite.org/pragma.html#pragma_synchronous">synchronous mode</a>
+ * to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} flag is not set.
+ * @return
+ */
+ @NonNull
+ public Builder setSynchronousMode(@NonNull String syncMode) {
+ Preconditions.checkNotNull(syncMode);
+ mSyncMode = syncMode;
+ return this;
+ }
+
/**
* Creates an instance of {@link OpenParams} with the options that were previously set
* on this builder
@@ -2546,7 +2604,7 @@
@NonNull
public OpenParams build() {
return new OpenParams(mOpenFlags, mCursorFactory, mErrorHandler, mLookasideSlotSize,
- mLookasideSlotCount, mIdleConnectionTimeout);
+ mLookasideSlotCount, mIdleConnectionTimeout, mJournalMode, mSyncMode);
}
}
}
@@ -2561,4 +2619,6 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface DatabaseOpenFlags {}
+
}
+
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 905da724..a14df1e 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -120,6 +120,18 @@
public boolean useCompatibilityWal;
/**
+ * Journal mode to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} is not set.
+ * <p>Default is returned by {@link SQLiteGlobal#getDefaultJournalMode()}
+ */
+ public String journalMode;
+
+ /**
+ * Synchronous mode to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} is not set.
+ * <p>Default is returned by {@link SQLiteGlobal#getDefaultSyncMode()}
+ */
+ public String syncMode;
+
+ /**
* Creates a database configuration with the required parameters for opening a
* database and default values for all other parameters.
*
@@ -180,6 +192,8 @@
lookasideSlotCount = other.lookasideSlotCount;
idleConnectionTimeoutMs = other.idleConnectionTimeoutMs;
useCompatibilityWal = other.useCompatibilityWal;
+ journalMode = other.journalMode;
+ syncMode = other.syncMode;
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index cc9e0f4..49f357e 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -17,6 +17,8 @@
package android.database.sqlite;
import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.SQLException;
@@ -24,6 +26,8 @@
import android.os.FileUtils;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.io.File;
/**
@@ -69,7 +73,8 @@
* {@link #onUpgrade} will be used to upgrade the database; if the database is
* newer, {@link #onDowngrade} will be used to downgrade the database
*/
- public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
+ public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
+ @Nullable CursorFactory factory, int version) {
this(context, name, factory, version, null);
}
@@ -90,12 +95,33 @@
* @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
* corruption, or null to use the default error handler.
*/
- public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
- DatabaseErrorHandler errorHandler) {
+ public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
+ @Nullable CursorFactory factory, int version,
+ @Nullable DatabaseErrorHandler errorHandler) {
this(context, name, factory, version, 0, errorHandler);
}
/**
+ * Create a helper object to create, open, and/or manage a database.
+ * This method always returns very quickly. The database is not actually
+ * created or opened until one of {@link #getWritableDatabase} or
+ * {@link #getReadableDatabase} is called.
+ *
+ * @param context to use to open or create the database
+ * @param name of the database file, or null for an in-memory database
+ * @param version number of the database (starting at 1); if the database is older,
+ * {@link #onUpgrade} will be used to upgrade the database; if the database is
+ * newer, {@link #onDowngrade} will be used to downgrade the database
+ * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
+ * Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be
+ * set when the helper opens the database
+ */
+ public SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
+ @NonNull SQLiteDatabase.OpenParams openParams) {
+ this(context, name, version, 0, openParams.toBuilder());
+ }
+
+ /**
* Same as {@link #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)}
* but also accepts an integer minimumSupportedVersion as a convenience for upgrading very old
* versions of this database that are no longer supported. If a database with older version that
@@ -118,17 +144,26 @@
* @see #onUpgrade(SQLiteDatabase, int, int)
* @hide
*/
- public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
- int minimumSupportedVersion, DatabaseErrorHandler errorHandler) {
+ public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
+ @Nullable CursorFactory factory, int version,
+ int minimumSupportedVersion, @Nullable DatabaseErrorHandler errorHandler) {
+ this(context, name, version, minimumSupportedVersion,
+ new SQLiteDatabase.OpenParams.Builder());
+ mOpenParamsBuilder.setCursorFactory(factory);
+ mOpenParamsBuilder.setErrorHandler(errorHandler);
+ }
+
+ private SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
+ int minimumSupportedVersion,
+ @NonNull SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
+ Preconditions.checkNotNull(openParamsBuilder);
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
mContext = context;
mName = name;
mNewVersion = version;
mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
- mOpenParamsBuilder = new SQLiteDatabase.OpenParams.Builder();
- mOpenParamsBuilder.setCursorFactory(factory);
- mOpenParamsBuilder.setErrorHandler(errorHandler);
+ mOpenParamsBuilder = openParamsBuilder;
mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
}
diff --git a/core/java/android/database/sqlite/package.html b/core/java/android/database/sqlite/package.html
index 864a9bb..4d6ba28 100644
--- a/core/java/android/database/sqlite/package.html
+++ b/core/java/android/database/sqlite/package.html
@@ -20,6 +20,8 @@
<p>The version of SQLite depends on the version of Android. See the following table:
<table style="width:auto;">
<tr><th>Android API</th><th>SQLite Version</th></tr>
+ <tr><td>API 27</td><td>3.19</td></tr>
+ <tr><td>API 26</td><td>3.18</td></tr>
<tr><td>API 24</td><td>3.9</td></tr>
<tr><td>API 21</td><td>3.8</td></tr>
<tr><td>API 11</td><td>3.7</td></tr>
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index da771e4..ff69bd8 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -249,7 +249,7 @@
* <p>This function can also be called in case where multiple surfaces share the same
* OutputConfiguration, and one of the surfaces becomes available after the {@link
* CameraCaptureSession} is created. In that case, the application must first create the
- * OutputConfiguration with the available Surface, then enable furture surface sharing via
+ * OutputConfiguration with the available Surface, then enable further surface sharing via
* {@link OutputConfiguration#enableSurfaceSharing}, before creating the CameraCaptureSession.
* After the CameraCaptureSession is created, and once the extra Surface becomes available, the
* application must then call {@link OutputConfiguration#addSurface} before finalizing the
@@ -645,6 +645,44 @@
public abstract Surface getInputSurface();
/**
+ * Update {@link OutputConfiguration} after configuration finalization see
+ * {@link #finalizeOutputConfigurations}.
+ *
+ * <p>Any {@link OutputConfiguration} that has been modified via calls to
+ * {@link OutputConfiguration#addSurface} or {@link OutputConfiguration#removeSurface} must be
+ * updated. After the update call returns without throwing exceptions any newly added surfaces
+ * can be referenced in subsequent capture requests.</p>
+ *
+ * <p>Surfaces that get removed must not be part of any active repeating or single/burst
+ * request or have any pending results. Consider updating any repeating requests first via
+ * {@link #setRepeatingRequest} or {@link #setRepeatingBurst} and then wait for the last frame
+ * number when the sequence completes {@link CaptureCallback#onCaptureSequenceCompleted}
+ * before calling updateOutputConfiguration to remove a previously active Surface.</p>
+ *
+ * <p>Surfaces that get added must not be part of any other registered
+ * {@link OutputConfiguration}.</p>
+ *
+ * @param config Modified output configuration.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error.
+ * @throws IllegalArgumentException if an attempt was made to add a {@link Surface} already
+ * in use by another buffer-producing API, such as MediaCodec or
+ * a different camera device or {@link OutputConfiguration}; or
+ * new surfaces are not compatible (see
+ * {@link OutputConfiguration#enableSurfaceSharing}); or a
+ * {@link Surface} that was removed from the modified
+ * {@link OutputConfiguration} still has pending requests.
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
+ */
+ public void updateOutputConfiguration(OutputConfiguration config)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* Close this capture session asynchronously.
*
* <p>Closing a session frees up the target output Surfaces of the session for reuse with either
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 46ad3f0..3a3048e 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1280,11 +1280,11 @@
* <ul>
* <li>Processed (but stalling): any non-RAW format with a stallDurations > 0.
* Typically {@link android.graphics.ImageFormat#JPEG JPEG format}.</li>
- * <li>Raw formats: {@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}, {@link android.graphics.ImageFormat#RAW10 RAW10}, or {@link android.graphics.ImageFormat#RAW12 RAW12}.</li>
- * <li>Processed (but not-stalling): any non-RAW format without a stall duration.
- * Typically {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888},
- * {@link android.graphics.ImageFormat#NV21 NV21}, or
- * {@link android.graphics.ImageFormat#YV12 YV12}.</li>
+ * <li>Raw formats: {@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}, {@link android.graphics.ImageFormat#RAW10 RAW10}, or
+ * {@link android.graphics.ImageFormat#RAW12 RAW12}.</li>
+ * <li>Processed (but not-stalling): any non-RAW format without a stall duration. Typically
+ * {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888},
+ * {@link android.graphics.ImageFormat#NV21 NV21}, or {@link android.graphics.ImageFormat#YV12 YV12}.</li>
* </ul>
* <p><b>Range of valid values:</b><br></p>
* <p>For processed (and stalling) format streams, >= 1.</p>
@@ -1376,8 +1376,7 @@
* CPU resources that will consume more power. The image format for this kind of an output stream can
* be any non-<code>RAW</code> and supported format provided by {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.</p>
* <p>A processed and stalling format is defined as any non-RAW format with a stallDurations
- * > 0. Typically only the {@link android.graphics.ImageFormat#JPEG JPEG format} is a
- * stalling format.</p>
+ * > 0. Typically only the {@link android.graphics.ImageFormat#JPEG JPEG format} is a stalling format.</p>
* <p>For full guarantees, query {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration } with a
* processed format -- it will return a non-0 value for a stalling stream.</p>
* <p>LEGACY devices will support up to 1 processing/stalling stream.</p>
@@ -1535,8 +1534,7 @@
new Key<int[]>("android.request.availableRequestKeys", int[].class);
/**
- * <p>A list of all keys that the camera device has available
- * to use with {@link android.hardware.camera2.CaptureResult }.</p>
+ * <p>A list of all keys that the camera device has available to use with {@link android.hardware.camera2.CaptureResult }.</p>
* <p>Attempting to get a key from a CaptureResult that is not
* listed here will always return a <code>null</code> value. Getting a key from
* a CaptureResult that is listed here will generally never return a <code>null</code>
@@ -1561,8 +1559,7 @@
new Key<int[]>("android.request.availableResultKeys", int[].class);
/**
- * <p>A list of all keys that the camera device has available
- * to use with {@link android.hardware.camera2.CameraCharacteristics }.</p>
+ * <p>A list of all keys that the camera device has available to use with {@link android.hardware.camera2.CameraCharacteristics }.</p>
* <p>This entry follows the same rules as
* android.request.availableResultKeys (except that it applies for
* CameraCharacteristics instead of CaptureResult). See above for more
@@ -1843,8 +1840,6 @@
* <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
* android.scaler.availableStallDurations for more details about
* calculating the max frame rate.</p>
- * <p>(Keep in sync with
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration })</p>
* <p><b>Units</b>: (format, width, height, ns) x n</p>
* <p>This key is available on all devices.</p>
*
@@ -1905,14 +1900,13 @@
* <ul>
* <li>{@link android.graphics.ImageFormat#YUV_420_888 }</li>
* <li>{@link android.graphics.ImageFormat#RAW10 }</li>
+ * <li>{@link android.graphics.ImageFormat#RAW12 }</li>
* </ul>
* <p>All other formats may or may not have an allowed stall duration on
* a per-capability basis; refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
* for more details.</p>
* <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} for more information about
* calculating the max frame rate (absent stalls).</p>
- * <p>(Keep up to date with
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration } )</p>
* <p><b>Units</b>: (format, width, height, ns) x n</p>
* <p>This key is available on all devices.</p>
*
@@ -2195,9 +2189,9 @@
* the raw buffers produced by this sensor.</p>
* <p>If a camera device supports raw sensor formats, either this or
* {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} is the maximum dimensions for the raw
- * output formats listed in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} (this depends on
- * whether or not the image sensor returns buffers containing pixels that are not
- * part of the active array region for blacklevel calibration or other purposes).</p>
+ * output formats listed in {@link android.hardware.camera2.params.StreamConfigurationMap }
+ * (this depends on whether or not the image sensor returns buffers containing pixels that
+ * are not part of the active array region for blacklevel calibration or other purposes).</p>
* <p>Some parts of the full pixel array may not receive light from the scene,
* or be otherwise inactive. The {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} key
* defines the rectangle of active pixels that will be included in processed image
@@ -2205,7 +2199,6 @@
* <p><b>Units</b>: Pixels</p>
* <p>This key is available on all devices.</p>
*
- * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
* @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@@ -2838,7 +2831,7 @@
* <p>See the individual level enums for full descriptions of the supported capabilities. The
* {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} entry describes the device's capabilities at a
* finer-grain level, if needed. In addition, many controls have their available settings or
- * ranges defined in individual {@link android.hardware.camera2.CameraCharacteristics } entries.</p>
+ * ranges defined in individual entries from {@link android.hardware.camera2.CameraCharacteristics }.</p>
* <p>Some features are not part of any particular hardware level or capability and must be
* queried separately. These include:</p>
* <ul>
@@ -2973,7 +2966,6 @@
* <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
* android.scaler.availableStallDurations for more details about
* calculating the max frame rate.</p>
- * <p>(Keep in sync with {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration })</p>
* <p><b>Units</b>: (format, width, height, ns) x n</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Limited capability</b> -
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 8c8c49f..4b57018 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -587,8 +587,8 @@
* then the list of resolutions for YUV_420_888 from {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } contains at
* least one resolution >= 8 megapixels, with a minimum frame duration of <= 1/20
* s.</p>
- * <p>If the device supports the {@link android.graphics.ImageFormat#RAW10 }, {@link android.graphics.ImageFormat#RAW12 }, then those can also be captured at the same rate
- * as the maximum-size YUV_420_888 resolution is.</p>
+ * <p>If the device supports the {@link android.graphics.ImageFormat#RAW10 }, {@link android.graphics.ImageFormat#RAW12 }, then those can also be
+ * captured at the same rate as the maximum-size YUV_420_888 resolution is.</p>
* <p>If the device supports the PRIVATE_REPROCESSING capability, then the same guarantees
* as for the YUV_420_888 format also apply to the {@link android.graphics.ImageFormat#PRIVATE } format.</p>
* <p>In addition, the {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} field is guaranted to have a value between 0
@@ -610,25 +610,22 @@
* following:</p>
* <ul>
* <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li>
- * <li>{@link android.graphics.ImageFormat#YUV_420_888 } is supported as an output/input format, that is,
- * YUV_420_888 is included in the lists of formats returned by
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats } and
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputFormats }.</li>
+ * <li>{@link android.graphics.ImageFormat#YUV_420_888 } is supported as an output/input
+ * format, that is, YUV_420_888 is included in the lists of formats returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats } and {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputFormats }.</li>
* <li>{@link android.hardware.camera2.params.StreamConfigurationMap#getValidOutputFormatsForInput }
* returns non-empty int[] for each supported input format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }.</li>
* <li>Each size returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputSizes getInputSizes(YUV_420_888)} is also included in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes getOutputSizes(YUV_420_888)}</li>
- * <li>Using {@link android.graphics.ImageFormat#YUV_420_888 } does not cause a frame rate drop
- * relative to the sensor's maximum capture rate (at that resolution).</li>
+ * <li>Using {@link android.graphics.ImageFormat#YUV_420_888 } does not cause a frame rate
+ * drop relative to the sensor's maximum capture rate (at that resolution).</li>
* <li>{@link android.graphics.ImageFormat#YUV_420_888 } will be reprocessable into both
* {@link android.graphics.ImageFormat#YUV_420_888 } and {@link android.graphics.ImageFormat#JPEG } formats.</li>
* <li>The maximum available resolution for {@link android.graphics.ImageFormat#YUV_420_888 } streams (both input/output) will match the
* maximum available resolution of {@link android.graphics.ImageFormat#JPEG } streams.</li>
* <li>Static metadata {@link CameraCharacteristics#REPROCESS_MAX_CAPTURE_STALL android.reprocess.maxCaptureStall}.</li>
* <li>Only the below controls are effective for reprocessing requests and will be present
- * in capture results. The reprocess requests are from the original capture results that
- * are associated with the intermediate {@link android.graphics.ImageFormat#YUV_420_888 }
- * output buffers. All other controls in the reprocess requests will be ignored by the
- * camera device.<ul>
+ * in capture results. The reprocess requests are from the original capture results
+ * that are associated with the intermediate {@link android.graphics.ImageFormat#YUV_420_888 } output buffers. All other controls in the
+ * reprocess requests will be ignored by the camera device.<ul>
* <li>android.jpeg.*</li>
* <li>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}</li>
* <li>{@link CaptureRequest#EDGE_MODE android.edge.mode}</li>
@@ -654,13 +651,13 @@
* <p>The camera device can produce depth measurements from its field of view.</p>
* <p>This capability requires the camera device to support the following:</p>
* <ul>
- * <li>{@link android.graphics.ImageFormat#DEPTH16 } is supported as an output format.</li>
- * <li>{@link android.graphics.ImageFormat#DEPTH_POINT_CLOUD } is optionally supported as an
- * output format.</li>
- * <li>This camera device, and all camera devices with the same {@link CameraCharacteristics#LENS_FACING android.lens.facing},
- * will list the following calibration entries in both
- * {@link android.hardware.camera2.CameraCharacteristics } and
- * {@link android.hardware.camera2.CaptureResult }:<ul>
+ * <li>{@link android.graphics.ImageFormat#DEPTH16 } is supported as
+ * an output format.</li>
+ * <li>{@link android.graphics.ImageFormat#DEPTH_POINT_CLOUD } is
+ * optionally supported as an output format.</li>
+ * <li>This camera device, and all camera devices with the same {@link CameraCharacteristics#LENS_FACING android.lens.facing}, will
+ * list the following calibration metadata entries in both {@link android.hardware.camera2.CameraCharacteristics }
+ * and {@link android.hardware.camera2.CaptureResult }:<ul>
* <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
* <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
* <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
@@ -674,8 +671,7 @@
* </ul>
* <p>Generally, depth output operates at a slower frame rate than standard color capture,
* so the DEPTH16 and DEPTH_POINT_CLOUD formats will commonly have a stall duration that
- * should be accounted for (see
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }).
+ * should be accounted for (see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }).
* On a device that supports both depth and color-based output, to enable smooth preview,
* using a repeating burst is recommended, where a depth-output target is only included
* once every N frames, where N is the ratio between preview output rate and depth output
@@ -692,23 +688,19 @@
public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8;
/**
- * <p>The device supports constrained high speed video recording (frame rate >=120fps)
- * use case. The camera device will support high speed capture session created by
- * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }, which
- * only accepts high speed request lists created by
- * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList }.</p>
- * <p>A camera device can still support high speed video streaming by advertising the high speed
- * FPS ranges in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}. For this case, all normal
- * capture request per frame control and synchronization requirements will apply to
- * the high speed fps ranges, the same as all other fps ranges. This capability describes
- * the capability of a specialized operating mode with many limitations (see below), which
- * is only targeted at high speed video recording.</p>
- * <p>The supported high speed video sizes and fps ranges are specified in
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.
- * To get desired output frame rates, the application is only allowed to select video size
- * and FPS range combinations provided by
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }.
- * The fps range can be controlled via {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}.</p>
+ * <p>The device supports constrained high speed video recording (frame rate >=120fps) use
+ * case. The camera device will support high speed capture session created by {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }, which
+ * only accepts high speed request lists created by {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList }.</p>
+ * <p>A camera device can still support high speed video streaming by advertising the high
+ * speed FPS ranges in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}. For this case, all
+ * normal capture request per frame control and synchronization requirements will apply
+ * to the high speed fps ranges, the same as all other fps ranges. This capability
+ * describes the capability of a specialized operating mode with many limitations (see
+ * below), which is only targeted at high speed video recording.</p>
+ * <p>The supported high speed video sizes and fps ranges are specified in {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.
+ * To get desired output frame rates, the application is only allowed to select video
+ * size and FPS range combinations provided by {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }. The
+ * fps range can be controlled via {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}.</p>
* <p>In this capability, the camera device will override aeMode, awbMode, and afMode to
* ON, AUTO, and CONTINUOUS_VIDEO, respectively. All post-processing block mode
* controls will be overridden to be FAST. Therefore, no manual control of capture
@@ -743,19 +735,16 @@
* frame rate. If the destination surface is from preview window, the actual preview frame
* rate will be bounded by the screen refresh rate.</p>
* <p>The camera device will only support up to 2 high speed simultaneous output surfaces
- * (preview and recording surfaces)
- * in this mode. Above controls will be effective only if all of below conditions are true:</p>
+ * (preview and recording surfaces) in this mode. Above controls will be effective only
+ * if all of below conditions are true:</p>
* <ul>
* <li>The application creates a camera capture session with no more than 2 surfaces via
* {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }. The
- * targeted surfaces must be preview surface (either from
- * {@link android.view.SurfaceView } or {@link android.graphics.SurfaceTexture }) or
- * recording surface(either from {@link android.media.MediaRecorder#getSurface } or
- * {@link android.media.MediaCodec#createInputSurface }).</li>
+ * targeted surfaces must be preview surface (either from {@link android.view.SurfaceView } or {@link android.graphics.SurfaceTexture }) or recording
+ * surface(either from {@link android.media.MediaRecorder#getSurface } or {@link android.media.MediaCodec#createInputSurface }).</li>
* <li>The stream sizes are selected from the sizes reported by
* {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }.</li>
- * <li>The FPS ranges are selected from
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.</li>
+ * <li>The FPS ranges are selected from {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.</li>
* </ul>
* <p>When above conditions are NOT satistied,
* {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }
@@ -1038,8 +1027,7 @@
/**
* <p>This camera device is running in backward compatibility mode.</p>
- * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession}
- * documentation are supported.</p>
+ * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are supported.</p>
* <p>A <code>LEGACY</code> device does not support per-frame control, manual sensor control, manual
* post-processing, arbitrary cropping regions, and has relaxed performance constraints.
* No additional capabilities beyond <code>BACKWARD_COMPATIBLE</code> will ever be listed by a
@@ -1061,8 +1049,7 @@
* <p>This camera device is capable of YUV reprocessing and RAW data capture, in addition to
* FULL-level capabilities.</p>
* <p>The stream configurations listed in the <code>LEVEL_3</code>, <code>RAW</code>, <code>FULL</code>, <code>LEGACY</code> and
- * <code>LIMITED</code> tables in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession}
- * documentation are guaranteed to be supported.</p>
+ * <code>LIMITED</code> tables in the {@link android.hardware.camera2.CameraDevice#createCaptureSession createCaptureSession} documentation are guaranteed to be supported.</p>
* <p>The following additional capabilities are guaranteed to be supported:</p>
* <ul>
* <li><code>YUV_REPROCESSING</code> capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains
@@ -2155,12 +2142,13 @@
public static final int EDGE_MODE_HIGH_QUALITY = 2;
/**
- * <p>Edge enhancement is applied at different levels for different output streams,
- * based on resolution. Streams at maximum recording resolution (see {@link android.hardware.camera2.CameraDevice#createCaptureSession }) or below have
- * edge enhancement applied, while higher-resolution streams have no edge enhancement
- * applied. The level of edge enhancement for low-resolution streams is tuned so that
- * frame rate is not impacted, and the quality is equal to or better than FAST (since it
- * is only applied to lower-resolution outputs, quality may improve from FAST).</p>
+ * <p>Edge enhancement is applied at different
+ * levels for different output streams, based on resolution. Streams at maximum recording
+ * resolution (see {@link android.hardware.camera2.CameraDevice#createCaptureSession })
+ * or below have edge enhancement applied, while higher-resolution streams have no edge
+ * enhancement applied. The level of edge enhancement for low-resolution streams is tuned
+ * so that frame rate is not impacted, and the quality is equal to or better than FAST
+ * (since it is only applied to lower-resolution outputs, quality may improve from FAST).</p>
* <p>This mode is intended to be used by applications operating in a zero-shutter-lag mode
* with YUV or PRIVATE reprocessing, where the application continuously captures
* high-resolution intermediate buffers into a circular buffer, from which a final image is
@@ -2287,12 +2275,12 @@
/**
* <p>Noise reduction is applied at different levels for different output streams,
- * based on resolution. Streams at maximum recording resolution (see {@link android.hardware.camera2.CameraDevice#createCaptureSession }) or below have noise
- * reduction applied, while higher-resolution streams have MINIMAL (if supported) or no
- * noise reduction applied (if MINIMAL is not supported.) The degree of noise reduction
- * for low-resolution streams is tuned so that frame rate is not impacted, and the quality
- * is equal to or better than FAST (since it is only applied to lower-resolution outputs,
- * quality may improve from FAST).</p>
+ * based on resolution. Streams at maximum recording resolution (see {@link android.hardware.camera2.CameraDevice#createCaptureSession })
+ * or below have noise reduction applied, while higher-resolution streams have MINIMAL (if
+ * supported) or no noise reduction applied (if MINIMAL is not supported.) The degree of
+ * noise reduction for low-resolution streams is tuned so that frame rate is not impacted,
+ * and the quality is equal to or better than FAST (since it is only applied to
+ * lower-resolution outputs, quality may improve from FAST).</p>
* <p>This mode is intended to be used by applications operating in a zero-shutter-lag mode
* with YUV or PRIVATE reprocessing, where the application continuously captures
* high-resolution intermediate buffers into a circular buffer, from which a final image is
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index c41fc02..0262ecb 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -680,7 +680,7 @@
* FAST or HIGH_QUALITY will yield a picture with the same white point
* as what was produced by the camera device in the earlier frame.</p>
* <p>The expected processing pipeline is as follows:</p>
- * <p><img alt="White balance processing pipeline" src="../../../../images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png" /></p>
+ * <p><img alt="White balance processing pipeline" src="/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png" /></p>
* <p>The white balance is encoded by two values, a 4-channel white-balance
* gain vector (applied in the Bayer domain), and a 3x3 color transform
* matrix (applied after demosaic).</p>
@@ -1470,10 +1470,10 @@
* <p>When set to AUTO, the individual algorithm controls in
* android.control.* are in effect, such as {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}.</p>
* <p>When set to USE_SCENE_MODE, the individual controls in
- * android.control.* are mostly disabled, and the camera device implements
- * one of the scene mode settings (such as ACTION, SUNSET, or PARTY)
- * as it wishes. The camera device scene mode 3A settings are provided by
- * {@link android.hardware.camera2.CaptureResult capture results}.</p>
+ * android.control.* are mostly disabled, and the camera device
+ * implements one of the scene mode settings (such as ACTION,
+ * SUNSET, or PARTY) as it wishes. The camera device scene mode
+ * 3A settings are provided by {@link android.hardware.camera2.CaptureResult capture results}.</p>
* <p>When set to OFF_KEEP_STATE, it is similar to OFF mode, the only difference
* is that this frame will not be used by camera device background 3A statistics
* update, as if this frame is never captured. This mode can be used in the scenario
@@ -2268,45 +2268,35 @@
* can run concurrently to the rest of the camera pipeline, but
* cannot process more than 1 capture at a time.</li>
* </ul>
- * <p>The necessary information for the application, given the model above,
- * is provided via the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} field using
+ * <p>The necessary information for the application, given the model above, is provided via
* {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }.
- * These are used to determine the maximum frame rate / minimum frame
- * duration that is possible for a given stream configuration.</p>
+ * These are used to determine the maximum frame rate / minimum frame duration that is
+ * possible for a given stream configuration.</p>
* <p>Specifically, the application can use the following rules to
* determine the minimum frame duration it can request from the camera
* device:</p>
* <ol>
- * <li>Let the set of currently configured input/output streams
- * be called <code>S</code>.</li>
- * <li>Find the minimum frame durations for each stream in <code>S</code>, by looking
- * it up in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} using {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
- * (with its respective size/format). Let this set of frame durations be
- * called <code>F</code>.</li>
- * <li>For any given request <code>R</code>, the minimum frame duration allowed
- * for <code>R</code> is the maximum out of all values in <code>F</code>. Let the streams
- * used in <code>R</code> be called <code>S_r</code>.</li>
+ * <li>Let the set of currently configured input/output streams be called <code>S</code>.</li>
+ * <li>Find the minimum frame durations for each stream in <code>S</code>, by looking it up in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
+ * (with its respective size/format). Let this set of frame durations be called <code>F</code>.</li>
+ * <li>For any given request <code>R</code>, the minimum frame duration allowed for <code>R</code> is the maximum
+ * out of all values in <code>F</code>. Let the streams used in <code>R</code> be called <code>S_r</code>.</li>
* </ol>
* <p>If none of the streams in <code>S_r</code> have a stall time (listed in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }
- * using its respective size/format), then the frame duration in <code>F</code>
- * determines the steady state frame rate that the application will get
- * if it uses <code>R</code> as a repeating request. Let this special kind of
- * request be called <code>Rsimple</code>.</p>
- * <p>A repeating request <code>Rsimple</code> can be <em>occasionally</em> interleaved
- * by a single capture of a new request <code>Rstall</code> (which has at least
- * one in-use stream with a non-0 stall time) and if <code>Rstall</code> has the
- * same minimum frame duration this will not cause a frame rate loss
- * if all buffers from the previous <code>Rstall</code> have already been
- * delivered.</p>
- * <p>For more details about stalling, see
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
+ * using its respective size/format), then the frame duration in <code>F</code> determines the steady
+ * state frame rate that the application will get if it uses <code>R</code> as a repeating request. Let
+ * this special kind of request be called <code>Rsimple</code>.</p>
+ * <p>A repeating request <code>Rsimple</code> can be <em>occasionally</em> interleaved by a single capture of a
+ * new request <code>Rstall</code> (which has at least one in-use stream with a non-0 stall time) and if
+ * <code>Rstall</code> has the same minimum frame duration this will not cause a frame rate loss if all
+ * buffers from the previous <code>Rstall</code> have already been delivered.</p>
+ * <p>For more details about stalling, see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
- * See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration},
- * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}. The duration
- * is capped to <code>max(duration, exposureTime + overhead)</code>.</p>
+ * See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}, {@link android.hardware.camera2.params.StreamConfigurationMap }.
+ * The duration is capped to <code>max(duration, exposureTime + overhead)</code>.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
* Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
@@ -2315,7 +2305,6 @@
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureRequest#CONTROL_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
- * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
* @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
*/
@PublicKey
@@ -2584,11 +2573,11 @@
* <p>Linear mapping:</p>
* <pre><code>android.tonemap.curveRed = [ 0, 0, 1.0, 1.0 ]
* </code></pre>
- * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+ * <p><img alt="Linear mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
* <p>Invert mapping:</p>
* <pre><code>android.tonemap.curveRed = [ 0, 1.0, 1.0, 0 ]
* </code></pre>
- * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+ * <p><img alt="Inverting mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
* <p>Gamma 1/2.2 mapping, with 16 control points:</p>
* <pre><code>android.tonemap.curveRed = [
* 0.0000, 0.0000, 0.0667, 0.2920, 0.1333, 0.4002, 0.2000, 0.4812,
@@ -2596,7 +2585,7 @@
* 0.5333, 0.7515, 0.6000, 0.7928, 0.6667, 0.8317, 0.7333, 0.8685,
* 0.8000, 0.9035, 0.8667, 0.9370, 0.9333, 0.9691, 1.0000, 1.0000 ]
* </code></pre>
- * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+ * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
* <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
* <pre><code>android.tonemap.curveRed = [
* 0.0000, 0.0000, 0.0667, 0.2864, 0.1333, 0.4007, 0.2000, 0.4845,
@@ -2604,7 +2593,7 @@
* 0.5333, 0.7569, 0.6000, 0.7977, 0.6667, 0.8360, 0.7333, 0.8721,
* 0.8000, 0.9063, 0.8667, 0.9389, 0.9333, 0.9701, 1.0000, 1.0000 ]
* </code></pre>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p><b>Range of valid values:</b><br>
* 0-1 on both input and output coordinates, normalized
* as a floating-point value such that 0 == black and 1 == white.</p>
@@ -2646,11 +2635,11 @@
* <p>Linear mapping:</p>
* <pre><code>curveRed = [ (0, 0), (1.0, 1.0) ]
* </code></pre>
- * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+ * <p><img alt="Linear mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
* <p>Invert mapping:</p>
* <pre><code>curveRed = [ (0, 1.0), (1.0, 0) ]
* </code></pre>
- * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+ * <p><img alt="Inverting mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
* <p>Gamma 1/2.2 mapping, with 16 control points:</p>
* <pre><code>curveRed = [
* (0.0000, 0.0000), (0.0667, 0.2920), (0.1333, 0.4002), (0.2000, 0.4812),
@@ -2658,7 +2647,7 @@
* (0.5333, 0.7515), (0.6000, 0.7928), (0.6667, 0.8317), (0.7333, 0.8685),
* (0.8000, 0.9035), (0.8667, 0.9370), (0.9333, 0.9691), (1.0000, 1.0000) ]
* </code></pre>
- * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+ * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
* <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
* <pre><code>curveRed = [
* (0.0000, 0.0000), (0.0667, 0.2864), (0.1333, 0.4007), (0.2000, 0.4845),
@@ -2666,7 +2655,7 @@
* (0.5333, 0.7569), (0.6000, 0.7977), (0.6667, 0.8360), (0.7333, 0.8721),
* (0.8000, 0.9063), (0.8667, 0.9389), (0.9333, 0.9701), (1.0000, 1.0000) ]
* </code></pre>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
* Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
@@ -2756,9 +2745,9 @@
* PRESET_CURVE</p>
* <p>The tonemap curve will be defined by specified standard.</p>
* <p>sRGB (approximated by 16 control points):</p>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p>Rec. 709 (approximated by 16 control points):</p>
- * <p><img alt="Rec. 709 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png" /></p>
+ * <p><img alt="Rec. 709 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png" /></p>
* <p>Note that above figures show a 16 control points approximation of preset
* curves. Camera devices may apply a different approximation to the curve.</p>
* <p><b>Possible values:</b>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 6d80c20..cfad098 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -390,7 +390,7 @@
* FAST or HIGH_QUALITY will yield a picture with the same white point
* as what was produced by the camera device in the earlier frame.</p>
* <p>The expected processing pipeline is as follows:</p>
- * <p><img alt="White balance processing pipeline" src="../../../../images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png" /></p>
+ * <p><img alt="White balance processing pipeline" src="/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png" /></p>
* <p>The white balance is encoded by two values, a 4-channel white-balance
* gain vector (applied in the Bayer domain), and a 3x3 color transform
* matrix (applied after demosaic).</p>
@@ -1975,10 +1975,10 @@
* <p>When set to AUTO, the individual algorithm controls in
* android.control.* are in effect, such as {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}.</p>
* <p>When set to USE_SCENE_MODE, the individual controls in
- * android.control.* are mostly disabled, and the camera device implements
- * one of the scene mode settings (such as ACTION, SUNSET, or PARTY)
- * as it wishes. The camera device scene mode 3A settings are provided by
- * {@link android.hardware.camera2.CaptureResult capture results}.</p>
+ * android.control.* are mostly disabled, and the camera device
+ * implements one of the scene mode settings (such as ACTION,
+ * SUNSET, or PARTY) as it wishes. The camera device scene mode
+ * 3A settings are provided by {@link android.hardware.camera2.CaptureResult capture results}.</p>
* <p>When set to OFF_KEEP_STATE, it is similar to OFF mode, the only difference
* is that this frame will not be used by camera device background 3A statistics
* update, as if this frame is never captured. This mode can be used in the scenario
@@ -3108,45 +3108,35 @@
* can run concurrently to the rest of the camera pipeline, but
* cannot process more than 1 capture at a time.</li>
* </ul>
- * <p>The necessary information for the application, given the model above,
- * is provided via the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} field using
+ * <p>The necessary information for the application, given the model above, is provided via
* {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }.
- * These are used to determine the maximum frame rate / minimum frame
- * duration that is possible for a given stream configuration.</p>
+ * These are used to determine the maximum frame rate / minimum frame duration that is
+ * possible for a given stream configuration.</p>
* <p>Specifically, the application can use the following rules to
* determine the minimum frame duration it can request from the camera
* device:</p>
* <ol>
- * <li>Let the set of currently configured input/output streams
- * be called <code>S</code>.</li>
- * <li>Find the minimum frame durations for each stream in <code>S</code>, by looking
- * it up in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} using {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
- * (with its respective size/format). Let this set of frame durations be
- * called <code>F</code>.</li>
- * <li>For any given request <code>R</code>, the minimum frame duration allowed
- * for <code>R</code> is the maximum out of all values in <code>F</code>. Let the streams
- * used in <code>R</code> be called <code>S_r</code>.</li>
+ * <li>Let the set of currently configured input/output streams be called <code>S</code>.</li>
+ * <li>Find the minimum frame durations for each stream in <code>S</code>, by looking it up in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
+ * (with its respective size/format). Let this set of frame durations be called <code>F</code>.</li>
+ * <li>For any given request <code>R</code>, the minimum frame duration allowed for <code>R</code> is the maximum
+ * out of all values in <code>F</code>. Let the streams used in <code>R</code> be called <code>S_r</code>.</li>
* </ol>
* <p>If none of the streams in <code>S_r</code> have a stall time (listed in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }
- * using its respective size/format), then the frame duration in <code>F</code>
- * determines the steady state frame rate that the application will get
- * if it uses <code>R</code> as a repeating request. Let this special kind of
- * request be called <code>Rsimple</code>.</p>
- * <p>A repeating request <code>Rsimple</code> can be <em>occasionally</em> interleaved
- * by a single capture of a new request <code>Rstall</code> (which has at least
- * one in-use stream with a non-0 stall time) and if <code>Rstall</code> has the
- * same minimum frame duration this will not cause a frame rate loss
- * if all buffers from the previous <code>Rstall</code> have already been
- * delivered.</p>
- * <p>For more details about stalling, see
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
+ * using its respective size/format), then the frame duration in <code>F</code> determines the steady
+ * state frame rate that the application will get if it uses <code>R</code> as a repeating request. Let
+ * this special kind of request be called <code>Rsimple</code>.</p>
+ * <p>A repeating request <code>Rsimple</code> can be <em>occasionally</em> interleaved by a single capture of a
+ * new request <code>Rstall</code> (which has at least one in-use stream with a non-0 stall time) and if
+ * <code>Rstall</code> has the same minimum frame duration this will not cause a frame rate loss if all
+ * buffers from the previous <code>Rstall</code> have already been delivered.</p>
+ * <p>For more details about stalling, see {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
- * See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration},
- * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}. The duration
- * is capped to <code>max(duration, exposureTime + overhead)</code>.</p>
+ * See {@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}, {@link android.hardware.camera2.params.StreamConfigurationMap }.
+ * The duration is capped to <code>max(duration, exposureTime + overhead)</code>.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
* Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
@@ -3155,7 +3145,6 @@
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureRequest#CONTROL_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
- * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
* @see CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION
*/
@PublicKey
@@ -3408,9 +3397,8 @@
* layout key (see {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}), i.e. the
* nth value given corresponds to the black level offset for the nth
* color channel listed in the CFA.</p>
- * <p>This key will be available if {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions} is
- * available or the camera device advertises this key via
- * {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p>
+ * <p>This key will be available if {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions} is available or the
+ * camera device advertises this key via {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p>
* <p><b>Range of valid values:</b><br>
* >= 0 for each.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -3640,13 +3628,13 @@
* </code></pre>
* <p>The low-resolution scaling map images for each channel are
* (displayed using nearest-neighbor interpolation):</p>
- * <p><img alt="Red lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png" />
- * <img alt="Green (even rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png" />
- * <img alt="Green (odd rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
- * <img alt="Blue lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
+ * <p><img alt="Red lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png" />
+ * <img alt="Green (even rows) lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png" />
+ * <img alt="Green (odd rows) lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
+ * <img alt="Blue lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
* <p>As a visualization only, inverting the full-color map to recover an
* image of a gray wall (using bicubic interpolation for visual quality) as captured by the sensor gives:</p>
- * <p><img alt="Image of a uniform white wall (inverse shading map)" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+ * <p><img alt="Image of a uniform white wall (inverse shading map)" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
* <p><b>Range of valid values:</b><br>
* Each gain factor is >= 1</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -3707,14 +3695,14 @@
* </code></pre>
* <p>The low-resolution scaling map images for each channel are
* (displayed using nearest-neighbor interpolation):</p>
- * <p><img alt="Red lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png" />
- * <img alt="Green (even rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png" />
- * <img alt="Green (odd rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
- * <img alt="Blue lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
+ * <p><img alt="Red lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png" />
+ * <img alt="Green (even rows) lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png" />
+ * <img alt="Green (odd rows) lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
+ * <img alt="Blue lens shading map" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
* <p>As a visualization only, inverting the full-color map to recover an
* image of a gray wall (using bicubic interpolation for visual quality)
* as captured by the sensor gives:</p>
- * <p><img alt="Image of a uniform white wall (inverse shading map)" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+ * <p><img alt="Image of a uniform white wall (inverse shading map)" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
* <p>Note that the RAW image data might be subject to lens shading
* correction not reported on this map. Query
* {@link CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED android.sensor.info.lensShadingApplied} to see if RAW image data has subject
@@ -3947,11 +3935,11 @@
* <p>Linear mapping:</p>
* <pre><code>android.tonemap.curveRed = [ 0, 0, 1.0, 1.0 ]
* </code></pre>
- * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+ * <p><img alt="Linear mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
* <p>Invert mapping:</p>
* <pre><code>android.tonemap.curveRed = [ 0, 1.0, 1.0, 0 ]
* </code></pre>
- * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+ * <p><img alt="Inverting mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
* <p>Gamma 1/2.2 mapping, with 16 control points:</p>
* <pre><code>android.tonemap.curveRed = [
* 0.0000, 0.0000, 0.0667, 0.2920, 0.1333, 0.4002, 0.2000, 0.4812,
@@ -3959,7 +3947,7 @@
* 0.5333, 0.7515, 0.6000, 0.7928, 0.6667, 0.8317, 0.7333, 0.8685,
* 0.8000, 0.9035, 0.8667, 0.9370, 0.9333, 0.9691, 1.0000, 1.0000 ]
* </code></pre>
- * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+ * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
* <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
* <pre><code>android.tonemap.curveRed = [
* 0.0000, 0.0000, 0.0667, 0.2864, 0.1333, 0.4007, 0.2000, 0.4845,
@@ -3967,7 +3955,7 @@
* 0.5333, 0.7569, 0.6000, 0.7977, 0.6667, 0.8360, 0.7333, 0.8721,
* 0.8000, 0.9063, 0.8667, 0.9389, 0.9333, 0.9701, 1.0000, 1.0000 ]
* </code></pre>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p><b>Range of valid values:</b><br>
* 0-1 on both input and output coordinates, normalized
* as a floating-point value such that 0 == black and 1 == white.</p>
@@ -4009,11 +3997,11 @@
* <p>Linear mapping:</p>
* <pre><code>curveRed = [ (0, 0), (1.0, 1.0) ]
* </code></pre>
- * <p><img alt="Linear mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
+ * <p><img alt="Linear mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png" /></p>
* <p>Invert mapping:</p>
* <pre><code>curveRed = [ (0, 1.0), (1.0, 0) ]
* </code></pre>
- * <p><img alt="Inverting mapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
+ * <p><img alt="Inverting mapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png" /></p>
* <p>Gamma 1/2.2 mapping, with 16 control points:</p>
* <pre><code>curveRed = [
* (0.0000, 0.0000), (0.0667, 0.2920), (0.1333, 0.4002), (0.2000, 0.4812),
@@ -4021,7 +4009,7 @@
* (0.5333, 0.7515), (0.6000, 0.7928), (0.6667, 0.8317), (0.7333, 0.8685),
* (0.8000, 0.9035), (0.8667, 0.9370), (0.9333, 0.9691), (1.0000, 1.0000) ]
* </code></pre>
- * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
+ * <p><img alt="Gamma = 1/2.2 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png" /></p>
* <p>Standard sRGB gamma mapping, per IEC 61966-2-1:1999, with 16 control points:</p>
* <pre><code>curveRed = [
* (0.0000, 0.0000), (0.0667, 0.2864), (0.1333, 0.4007), (0.2000, 0.4845),
@@ -4029,7 +4017,7 @@
* (0.5333, 0.7569), (0.6000, 0.7977), (0.6667, 0.8360), (0.7333, 0.8721),
* (0.8000, 0.9063), (0.8667, 0.9389), (0.9333, 0.9701), (1.0000, 1.0000) ]
* </code></pre>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
* Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
@@ -4119,9 +4107,9 @@
* PRESET_CURVE</p>
* <p>The tonemap curve will be defined by specified standard.</p>
* <p>sRGB (approximated by 16 control points):</p>
- * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+ * <p><img alt="sRGB tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
* <p>Rec. 709 (approximated by 16 control points):</p>
- * <p><img alt="Rec. 709 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png" /></p>
+ * <p><img alt="Rec. 709 tonemapping curve" src="/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png" /></p>
* <p>Note that above figures show a 16 control points approximation of preset
* curves. Camera devices may apply a different approximation to the curve.</p>
* <p><b>Possible values:</b>
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index c7654c9..374789c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -314,6 +314,20 @@
}
@Override
+ public void updateOutputConfiguration(OutputConfiguration config)
+ throws CameraAccessException {
+ synchronized (mDeviceImpl.mInterfaceLock) {
+ checkNotClosed();
+
+ if (DEBUG) {
+ Log.v(TAG, mIdString + "updateOutputConfiguration");
+ }
+
+ mDeviceImpl.updateOutputConfiguration(config);
+ }
+ }
+
+ @Override
public boolean isReprocessable() {
return mInput != null;
}
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index fec7fd9..8c4dbfa 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -235,6 +235,13 @@
}
@Override
+ public void updateOutputConfiguration(OutputConfiguration config)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+ + " this method");
+ }
+
+ @Override
public void close() {
mSessionImpl.close();
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index bfeb14d..6787d84 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -764,6 +764,24 @@
}
}
+ public void updateOutputConfiguration(OutputConfiguration config)
+ throws CameraAccessException {
+ synchronized(mInterfaceLock) {
+ int streamId = -1;
+ for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+ if (config.getSurface() == mConfiguredOutputs.valueAt(i).getSurface()) {
+ streamId = mConfiguredOutputs.keyAt(i);
+ break;
+ }
+ }
+ if (streamId == -1) {
+ throw new IllegalArgumentException("Invalid output configuration");
+ }
+
+ mRemoteDevice.updateOutputConfiguration(streamId, config);
+ }
+ }
+
public void tearDown(Surface surface) throws CameraAccessException {
if (surface == null) throw new IllegalArgumentException("Surface is null");
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index 27087a2..0978ff8 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -215,6 +215,16 @@
}
}
+ public void updateOutputConfiguration(int streamId, OutputConfiguration config)
+ throws CameraAccessException {
+ try {
+ mRemoteDevice.updateOutputConfiguration(streamId, config);
+ } catch (Throwable t) {
+ CameraManager.throwAsPublicException(t);
+ throw new UnsupportedOperationException("Unexpected exception", t);
+ }
+ }
+
public void finalizeOutputConfigurations(int streamId, OutputConfiguration deferredConfig)
throws CameraAccessException {
try {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 49d4096..119cca8 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -646,6 +646,11 @@
}
@Override
+ public void updateOutputConfiguration(int streamId, OutputConfiguration config) {
+ // TODO: b/63912484 implement updateOutputConfiguration.
+ }
+
+ @Override
public void waitUntilIdle() throws RemoteException {
if (DEBUG) {
Log.d(TAG, "waitUntilIdle called.");
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 2b317d6..7409671 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -42,6 +42,53 @@
* A class for describing camera output, which contains a {@link Surface} and its specific
* configuration for creating capture session.
*
+ * <p>There are several ways to instantiate, modify and use OutputConfigurations. The most common
+ * and recommended usage patterns are summarized in the following list:</p>
+ *<ul>
+ * <li>Passing a {@link Surface} to the constructor and using the OutputConfiguration instance as
+ * argument to {@link CameraDevice#createCaptureSessionByOutputConfigurations}. This is the most
+ * frequent usage and clients should consider it first before other more complicated alternatives.
+ * </li>
+ *
+ * <li>Passing only a surface source class as an argument to the constructor. This is usually
+ * followed by a call to create a capture session
+ * (see {@link CameraDevice#createCaptureSessionByOutputConfigurations} and a {@link Surface} add
+ * call {@link #addSurface} with a valid {@link Surface}. The sequence completes with
+ * {@link CameraCaptureSession#finalizeOutputConfigurations}. This is the deferred usage case which
+ * aims to enhance performance by allowing the resource-intensive capture session create call to
+ * execute in parallel with any {@link Surface} initialization, such as waiting for a
+ * {@link android.view.SurfaceView} to be ready as part of the UI initialization.</li>
+ *
+ * <li>The third and most complex usage pattern inlvolves surface sharing. Once instantiated an
+ * OutputConfiguration can be enabled for surface sharing via {@link #enableSurfaceSharing}. This
+ * must be done before creating a new capture session and enables calls to
+ * {@link CameraCaptureSession#updateOutputConfiguration}. An OutputConfiguration with enabled
+ * surface sharing can be modified via {@link #addSurface} or {@link #removeSurface}. The updates
+ * to this OutputConfiguration will only come into effect after
+ * {@link CameraCaptureSession#updateOutputConfiguration} returns without throwing exceptions.
+ * Such updates can be done as long as the session is active. Clients should always consider the
+ * additional requirements and limitations placed on the output surfaces (for more details see
+ * {@link #enableSurfaceSharing}, {@link #addSurface}, {@link #removeSurface},
+ * {@link CameraCaptureSession#updateOutputConfiguration}). A trade-off exists between additional
+ * complexity and flexibility. If exercised correctly surface sharing can switch between different
+ * output surfaces without interrupting any ongoing repeating capture requests. This saves time and
+ * can significantly improve the user experience.</li>
+ *
+ * <li>Surface sharing can be used in combination with deferred surfaces. The rules from both cases
+ * are combined and clients must call {@link #enableSurfaceSharing} before creating a capture
+ * session. Attach and/or remove output surfaces via {@link #addSurface}/{@link #removeSurface} and
+ * finalize the configuration using {@link CameraCaptureSession#finalizeOutputConfigurations}.
+ * {@link CameraCaptureSession#updateOutputConfiguration} can be called after the configuration
+ * finalize method returns without exceptions.</li>
+ *
+ * </ul>
+ *
+ * <p>Please note that surface sharing is currently only enabled for outputs that use the
+ * {@link ImageFormat#PRIVATE} format. This includes surface sources like
+ * {@link android.view.SurfaceView}, {@link android.media.MediaRecorder},
+ * {@link android.graphics.SurfaceTexture} and {@link android.media.ImageReader}, configured using
+ * the aforementioned format.</p>
+ *
* @see CameraDevice#createCaptureSessionByOutputConfigurations
*
*/
@@ -123,7 +170,7 @@
* {@link OutputConfiguration#addSurface} should not exceed this value.</p>
*
*/
- private static final int MAX_SURFACES_COUNT = 2;
+ private static final int MAX_SURFACES_COUNT = 4;
/**
* Create a new {@link OutputConfiguration} instance with a {@link Surface},
@@ -280,7 +327,10 @@
* <p>For advanced use cases, a camera application may require more streams than the combination
* guaranteed by {@link CameraDevice#createCaptureSession}. In this case, more than one
* compatible surface can be attached to an OutputConfiguration so that they map to one
- * camera stream, and the outputs share memory buffers when possible. </p>
+ * camera stream, and the outputs share memory buffers when possible. Due to buffer sharing
+ * clients should be careful when adding surface outputs that modify their input data. If such
+ * case exists, camera clients should have an additional mechanism to synchronize read and write
+ * access between individual consumers.</p>
*
* <p>Two surfaces are compatible in the below cases:</p>
*
@@ -301,9 +351,9 @@
* CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after {@link
* CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
*
- * <p>Up to 2 surfaces can be shared for an OutputConfiguration. The supported surfaces for
- * sharing must be of type SurfaceTexture, SurfaceView, MediaRecorder, MediaCodec, or
- * implementation defined ImageReader.</p>
+ * <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration.
+ * The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
+ * MediaRecorder, MediaCodec, or implementation defined ImageReader.</p>
*/
public void enableSurfaceSharing() {
mIsShared = true;
@@ -329,8 +379,10 @@
* <p> This function can be called before or after {@link
* CameraDevice#createCaptureSessionByOutputConfigurations}. If it's called after,
* the application must finalize the capture session with
- * {@link CameraCaptureSession#finalizeOutputConfigurations}.
- * </p>
+ * {@link CameraCaptureSession#finalizeOutputConfigurations}. It is possible to call this method
+ * after the output configurations have been finalized only in cases of enabled surface sharing
+ * see {@link #enableSurfaceSharing}. The modified output configuration must be updated with
+ * {@link CameraCaptureSession#updateOutputConfiguration}.</p>
*
* <p> If the OutputConfiguration was constructed with a deferred surface by {@link
* OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained
@@ -388,6 +440,31 @@
}
/**
+ * Remove a surface from this OutputConfiguration.
+ *
+ * <p> Surfaces added via calls to {@link #addSurface} can also be removed from the
+ * OutputConfiguration. The only notable exception is the surface associated with
+ * the OutputConfigration see {@link #getSurface} which was passed as part of the constructor
+ * or was added first in the deferred case
+ * {@link OutputConfiguration#OutputConfiguration(Size, Class)}.</p>
+ *
+ * @param surface The surface to be removed.
+ *
+ * @throws IllegalArgumentException If the surface is associated with this OutputConfiguration
+ * (see {@link #getSurface}) or the surface didn't get added
+ * with {@link #addSurface}.
+ */
+ public void removeSurface(@NonNull Surface surface) {
+ if (getSurface() == surface) {
+ throw new IllegalArgumentException(
+ "Cannot remove surface associated with this output configuration");
+ }
+ if (!mSurfaces.remove(surface)) {
+ throw new IllegalArgumentException("Surface is not part of this output configuration");
+ }
+ }
+
+ /**
* Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
* instance.
*
@@ -447,6 +524,17 @@
}
/**
+ * Get the maximum supported shared {@link Surface} count.
+ *
+ * @return the maximum number of surfaces that can be added per each OutputConfiguration.
+ *
+ * @see #enableSurfaceSharing
+ */
+ public static int getMaxSharedSurfaceCount() {
+ return MAX_SURFACES_COUNT;
+ }
+
+ /**
* Get the {@link Surface} associated with this {@link OutputConfiguration}.
*
* If more than one surface is associated with this {@link OutputConfiguration}, return the
diff --git a/core/java/com/android/internal/app/IAssistDataReceiver.aidl b/core/java/android/hardware/display/BrightnessChangeEvent.aidl
similarity index 70%
copy from core/java/com/android/internal/app/IAssistDataReceiver.aidl
copy to core/java/android/hardware/display/BrightnessChangeEvent.aidl
index 9c9ffef..942e0db 100644
--- a/core/java/com/android/internal/app/IAssistDataReceiver.aidl
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.aidl
@@ -14,13 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.app;
+package android.hardware.display;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-
-/** @hide */
-oneway interface IAssistDataReceiver {
- void onHandleAssistData(in Bundle resultData);
- void onHandleAssistScreenshot(in Bitmap screenshot);
-}
+parcelable BrightnessChangeEvent;
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
new file mode 100644
index 0000000..fe24e32
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data about a brightness settings change.
+ * TODO make this SystemAPI
+ * @hide
+ */
+public final class BrightnessChangeEvent implements Parcelable {
+ /** Brightness in nits */
+ public int brightness;
+
+ /** Timestamp of the change {@see System.currentTimeMillis()} */
+ public long timeStamp;
+
+ /** Package name of focused activity when brightness was changed. */
+ public String packageName;
+
+ /** User id of of the user running when brightness was changed.
+ * @hide */
+ public int userId;
+
+ /** Lux values of recent sensor data */
+ public float[] luxValues;
+
+ /** Timestamps of the lux sensor readings {@see System.currentTimeMillis()} */
+ public long[] luxTimestamps;
+
+ /** Most recent battery level when brightness was changed or Float.NaN */
+ public float batteryLevel;
+
+ /** Color filter active to provide night mode */
+ public boolean nightMode;
+
+ /** If night mode color filter is active this will be the temperature in kelvin */
+ public int colorTemperature;
+
+ /** Brightness level before slider adjustment */
+ public int lastBrightness;
+
+ public BrightnessChangeEvent() {
+ }
+
+ private BrightnessChangeEvent(Parcel source) {
+ brightness = source.readInt();
+ timeStamp = source.readLong();
+ packageName = source.readString();
+ userId = source.readInt();
+ luxValues = source.createFloatArray();
+ luxTimestamps = source.createLongArray();
+ batteryLevel = source.readFloat();
+ nightMode = source.readBoolean();
+ colorTemperature = source.readInt();
+ lastBrightness = source.readInt();
+ }
+
+ public static final Creator<BrightnessChangeEvent> CREATOR =
+ new Creator<BrightnessChangeEvent>() {
+ public BrightnessChangeEvent createFromParcel(Parcel source) {
+ return new BrightnessChangeEvent(source);
+ }
+ public BrightnessChangeEvent[] newArray(int size) {
+ return new BrightnessChangeEvent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(brightness);
+ dest.writeLong(timeStamp);
+ dest.writeString(packageName);
+ dest.writeInt(userId);
+ dest.writeFloatArray(luxValues);
+ dest.writeLongArray(luxTimestamps);
+ dest.writeFloat(batteryLevel);
+ dest.writeBoolean(nightMode);
+ dest.writeInt(colorTemperature);
+ dest.writeInt(lastBrightness);
+ }
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b2af44e..ef77d6e 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -30,6 +30,7 @@
import android.view.WindowManagerPolicy;
import java.util.ArrayList;
+import java.util.List;
/**
* Manages the properties of attached displays.
@@ -615,6 +616,21 @@
}
/**
+ * Fetch {@link BrightnessChangeEvent}s.
+ * @hide until we make it a system api.
+ */
+ public List<BrightnessChangeEvent> getBrightnessEvents() {
+ return mGlobal.getBrightnessEvents();
+ }
+
+ /**
+ * @hide STOPSHIP - remove when adaptive brightness accepts curves.
+ */
+ public void setBrightness(int brightness) {
+ mGlobal.setBrightness(brightness);
+ }
+
+ /**
* Listens for changes in available display devices.
*/
public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a8a4eb6..d93d0e4 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -17,6 +17,7 @@
package android.hardware.display;
import android.content.Context;
+import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Point;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -37,6 +38,8 @@
import android.view.Surface;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Manager communication with the display manager service on behalf of
@@ -456,6 +459,33 @@
}
}
+ /**
+ * Retrieves brightness change events.
+ */
+ public List<BrightnessChangeEvent> getBrightnessEvents() {
+ try {
+ ParceledListSlice<BrightnessChangeEvent> events = mDm.getBrightnessEvents();
+ if (events == null) {
+ return Collections.emptyList();
+ }
+ return events.getList();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set brightness but don't add a BrightnessChangeEvent
+ * STOPSHIP remove when adaptive brightness accepts curves.
+ */
+ public void setBrightness(int brightness) {
+ try {
+ mDm.setBrightness(brightness);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, int event) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 5053884..b796cf9 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -16,6 +16,7 @@
package android.hardware.display;
+import android.content.pm.ParceledListSlice;
import android.graphics.Point;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
@@ -81,4 +82,11 @@
// Get a stable metric for the device's display size. No permissions required.
Point getStableDisplaySize();
+
+ // Requires BRIGHTNESS_SLIDER_USAGE permission.
+ ParceledListSlice getBrightnessEvents();
+
+ // STOPSHIP remove when adaptive brightness code is updated to accept curves.
+ // Requires BRIGHTNESS_SLIDER_USAGE permission.
+ void setBrightness(int brightness);
}
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index aaf6c57..e1137aa 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -16,6 +16,7 @@
package android.hardware.location;
import android.annotation.SystemApi;
+import android.hardware.contexthub.V1_0.ContextHub;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,22 +32,52 @@
private String mVendor;
private String mToolchain;
private int mPlatformVersion;
- private int mStaticSwVersion;
private int mToolchainVersion;
private float mPeakMips;
private float mStoppedPowerDrawMw;
private float mSleepPowerDrawMw;
private float mPeakPowerDrawMw;
private int mMaxPacketLengthBytes;
+ private byte mChreApiMajorVersion;
+ private byte mChreApiMinorVersion;
+ private short mChrePatchVersion;
+ private long mChrePlatformId;
private int[] mSupportedSensors;
private MemoryRegion[] mMemoryRegions;
+ /*
+ * TODO(b/67734082): Deprecate this constructor and mark private fields as final.
+ */
public ContextHubInfo() {
}
/**
+ * @hide
+ */
+ public ContextHubInfo(ContextHub contextHub) {
+ mId = contextHub.hubId;
+ mName = contextHub.name;
+ mVendor = contextHub.vendor;
+ mToolchain = contextHub.toolchain;
+ mPlatformVersion = contextHub.platformVersion;
+ mToolchainVersion = contextHub.toolchainVersion;
+ mPeakMips = contextHub.peakMips;
+ mStoppedPowerDrawMw = contextHub.stoppedPowerDrawMw;
+ mSleepPowerDrawMw = contextHub.sleepPowerDrawMw;
+ mPeakPowerDrawMw = contextHub.peakPowerDrawMw;
+ mMaxPacketLengthBytes = contextHub.maxSupportedMsgLen;
+ mChrePlatformId = contextHub.chrePlatformId;
+ mChreApiMajorVersion = contextHub.chreApiMajorVersion;
+ mChreApiMinorVersion = contextHub.chreApiMinorVersion;
+ mChrePatchVersion = contextHub.chrePatchVersion;
+
+ mSupportedSensors = new int[0];
+ mMemoryRegions = new MemoryRegion[0];
+ }
+
+ /**
* returns the maximum number of bytes that can be sent per message to the hub
*
* @return int - maximum bytes that can be transmitted in a
@@ -57,17 +88,6 @@
}
/**
- * set the context hub unique identifer
- *
- * @param bytes - Maximum number of bytes per message
- *
- * @hide
- */
- public void setMaxPacketLenBytes(int bytes) {
- mMaxPacketLengthBytes = bytes;
- }
-
- /**
* get the context hub unique identifer
*
* @return int - unique system wide identifier
@@ -77,17 +97,6 @@
}
/**
- * set the context hub unique identifer
- *
- * @param id - unique system wide identifier for the hub
- *
- * @hide
- */
- public void setId(int id) {
- mId = id;
- }
-
- /**
* get a string as a hub name
*
* @return String - a name for the hub
@@ -97,17 +106,6 @@
}
/**
- * set a string as the hub name
- *
- * @param name - the name for the hub
- *
- * @hide
- */
- public void setName(String name) {
- mName = name;
- }
-
- /**
* get a string as the vendor name
*
* @return String - a name for the vendor
@@ -117,17 +115,6 @@
}
/**
- * set a string as the vendor name
- *
- * @param vendor - a name for the vendor
- *
- * @hide
- */
- public void setVendor(String vendor) {
- mVendor = vendor;
- }
-
- /**
* get tool chain string
*
* @return String - description of the tool chain
@@ -137,17 +124,6 @@
}
/**
- * set tool chain string
- *
- * @param toolchain - description of the tool chain
- *
- * @hide
- */
- public void setToolchain(String toolchain) {
- mToolchain = toolchain;
- }
-
- /**
* get platform version
*
* @return int - platform version number
@@ -157,34 +133,12 @@
}
/**
- * set platform version
- *
- * @param platformVersion - platform version number
- *
- * @hide
- */
- public void setPlatformVersion(int platformVersion) {
- mPlatformVersion = platformVersion;
- }
-
- /**
* get static platform version number
*
* @return int - platform version number
*/
public int getStaticSwVersion() {
- return mStaticSwVersion;
- }
-
- /**
- * set platform software version
- *
- * @param staticSwVersion - platform static s/w version number
- *
- * @hide
- */
- public void setStaticSwVersion(int staticSwVersion) {
- mStaticSwVersion = staticSwVersion;
+ return (mChreApiMajorVersion << 24) | (mChreApiMinorVersion << 16) | (mChrePatchVersion);
}
/**
@@ -197,17 +151,6 @@
}
/**
- * set the tool chain version number
- *
- * @param toolchainVersion - tool chain version number
- *
- * @hide
- */
- public void setToolchainVersion(int toolchainVersion) {
- mToolchainVersion = toolchainVersion;
- }
-
- /**
* get the peak processing mips the hub can support
*
* @return float - peak MIPS that this hub can deliver
@@ -217,17 +160,6 @@
}
/**
- * set the peak mips that this hub can support
- *
- * @param peakMips - peak mips this hub can deliver
- *
- * @hide
- */
- public void setPeakMips(float peakMips) {
- mPeakMips = peakMips;
- }
-
- /**
* get the stopped power draw in milliwatts
* This assumes that the hub enter a stopped state - which is
* different from the sleep state. Latencies on exiting the
@@ -241,17 +173,6 @@
}
/**
- * Set the power consumed by the hub in stopped state
- *
- * @param stoppedPowerDrawMw - stopped power in milli watts
- *
- * @hide
- */
- public void setStoppedPowerDrawMw(float stoppedPowerDrawMw) {
- mStoppedPowerDrawMw = stoppedPowerDrawMw;
- }
-
- /**
* get the power draw of the hub in sleep mode. This assumes
* that the hub supports a sleep mode in which the power draw is
* lower than the power consumed when the hub is actively
@@ -267,17 +188,6 @@
}
/**
- * Set the sleep power draw in milliwatts
- *
- * @param sleepPowerDrawMw - sleep power draw in milliwatts.
- *
- * @hide
- */
- public void setSleepPowerDrawMw(float sleepPowerDrawMw) {
- mSleepPowerDrawMw = sleepPowerDrawMw;
- }
-
- /**
* get the peak powe draw of the hub. This is the power consumed
* by the hub at maximum load.
*
@@ -288,18 +198,6 @@
}
/**
- * set the peak power draw of the hub
- *
- * @param peakPowerDrawMw - peak power draw of the hub in
- * milliwatts.
- *
- * @hide
- */
- public void setPeakPowerDrawMw(float peakPowerDrawMw) {
- mPeakPowerDrawMw = peakPowerDrawMw;
- }
-
- /**
* get the sensors supported by this hub
*
* @return int[] - all the supported sensors on this hub
@@ -322,46 +220,65 @@
}
/**
- * set the supported sensors on this hub
+ * @return the CHRE platform ID as defined in chre/version.h
*
- * @param supportedSensors - supported sensors on this hub
- *
+ * TODO(b/67734082): Expose as public API
* @hide
*/
- public void setSupportedSensors(int[] supportedSensors) {
- mSupportedSensors = Arrays.copyOf(supportedSensors, supportedSensors.length);
+ public long getChrePlatformId() {
+ return mChrePlatformId;
}
/**
- * set memory regions for this hub
+ * @return the CHRE API's major version as defined in chre/version.h
*
- * @param memoryRegions - memory regions information
- *
- * @see MemoryRegion
- *
+ * TODO(b/67734082): Expose as public API
* @hide
*/
- public void setMemoryRegions(MemoryRegion[] memoryRegions) {
- mMemoryRegions = Arrays.copyOf(memoryRegions, memoryRegions.length);
+ public byte getChreApiMajorVersion() {
+ return mChreApiMajorVersion;
+ }
+
+ /**
+ * @return the CHRE API's minor version as defined in chre/version.h
+ *
+ * TODO(b/67734082): Expose as public API
+ * @hide
+ */
+ public byte getChreApiMinorVersion() {
+ return mChreApiMinorVersion;
+ }
+
+ /**
+ * @return the CHRE patch version as defined in chre/version.h
+ *
+ * TODO(b/67734082): Expose as public API
+ * @hide
+ */
+ public short getChrePatchVersion() {
+ return mChrePatchVersion;
}
@Override
public String toString() {
- String retVal = "";
- retVal += "Id : " + mId;
- retVal += ", Name : " + mName;
- retVal += "\n\tVendor : " + mVendor;
- retVal += ", ToolChain : " + mToolchain;
- retVal += "\n\tPlatformVersion : " + mPlatformVersion;
- retVal += ", StaticSwVersion : " + mStaticSwVersion;
- retVal += "\n\tPeakMips : " + mPeakMips;
- retVal += ", StoppedPowerDraw : " + mStoppedPowerDrawMw + " mW";
- retVal += ", PeakPowerDraw : " + mPeakPowerDrawMw + " mW";
- retVal += ", MaxPacketLength : " + mMaxPacketLengthBytes + " Bytes";
- retVal += "\n\tSupported sensors : " + Arrays.toString(mSupportedSensors);
- retVal += "\n\tMemory Regions : " + Arrays.toString(mMemoryRegions);
+ String retVal = "";
+ retVal += "Id : " + mId;
+ retVal += ", Name : " + mName;
+ retVal += "\n\tVendor : " + mVendor;
+ retVal += ", Toolchain : " + mToolchain;
+ retVal += ", Toolchain version: 0x" + Integer.toHexString(mToolchainVersion);
+ retVal += "\n\tPlatformVersion : 0x" + Integer.toHexString(mPlatformVersion);
+ retVal += ", SwVersion : "
+ + mChreApiMajorVersion + "." + mChreApiMinorVersion + "." + mChrePatchVersion;
+ retVal += ", CHRE platform ID: 0x" + Long.toHexString(mChrePlatformId);
+ retVal += "\n\tPeakMips : " + mPeakMips;
+ retVal += ", StoppedPowerDraw : " + mStoppedPowerDrawMw + " mW";
+ retVal += ", PeakPowerDraw : " + mPeakPowerDrawMw + " mW";
+ retVal += ", MaxPacketLength : " + mMaxPacketLengthBytes + " Bytes";
+ retVal += "\n\tSupported sensors : " + Arrays.toString(mSupportedSensors);
+ retVal += "\n\tMemory Regions : " + Arrays.toString(mMemoryRegions);
- return retVal;
+ return retVal;
}
private ContextHubInfo(Parcel in) {
@@ -371,12 +288,15 @@
mToolchain = in.readString();
mPlatformVersion = in.readInt();
mToolchainVersion = in.readInt();
- mStaticSwVersion = in.readInt();
mPeakMips = in.readFloat();
mStoppedPowerDrawMw = in.readFloat();
mSleepPowerDrawMw = in.readFloat();
mPeakPowerDrawMw = in.readFloat();
mMaxPacketLengthBytes = in.readInt();
+ mChrePlatformId = in.readLong();
+ mChreApiMajorVersion = in.readByte();
+ mChreApiMinorVersion = in.readByte();
+ mChrePatchVersion = (short) in.readInt();
int numSupportedSensors = in.readInt();
mSupportedSensors = new int[numSupportedSensors];
@@ -395,12 +315,15 @@
out.writeString(mToolchain);
out.writeInt(mPlatformVersion);
out.writeInt(mToolchainVersion);
- out.writeInt(mStaticSwVersion);
out.writeFloat(mPeakMips);
out.writeFloat(mStoppedPowerDrawMw);
out.writeFloat(mSleepPowerDrawMw);
out.writeFloat(mPeakPowerDrawMw);
out.writeInt(mMaxPacketLengthBytes);
+ out.writeLong(mChrePlatformId);
+ out.writeByte(mChreApiMajorVersion);
+ out.writeByte(mChreApiMinorVersion);
+ out.writeInt(mChrePatchVersion);
out.writeInt(mSupportedSensors.length);
out.writeIntArray(mSupportedSensors);
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 3aaeb50..18287fa 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -94,5 +94,17 @@
*/
void setAnalogForced(boolean isForced);
+ /**
+ * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
+ * @return Vendor-specific key-value pairs, must be Map<String, String>
+ */
+ Map setParameters(in Map parameters);
+
+ /**
+ * @param keys Parameter keys to fetch
+ * @return Vendor-specific key-value pairs, must be Map<String, String>
+ */
+ Map getParameters(in List<String> keys);
+
boolean isAntennaConnected();
}
diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl
index 6ed171b..775e25c 100644
--- a/core/java/android/hardware/radio/ITunerCallback.aidl
+++ b/core/java/android/hardware/radio/ITunerCallback.aidl
@@ -30,4 +30,9 @@
void onBackgroundScanAvailabilityChange(boolean isAvailable);
void onBackgroundScanComplete();
void onProgramListChanged();
+
+ /**
+ * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
+ */
+ void onParametersUpdated(in Map parameters);
}
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 6e8991a..e93fd5f 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -309,6 +309,58 @@
public abstract void setAnalogForced(boolean isForced);
/**
+ * Generic method for setting vendor-specific parameter values.
+ * The framework does not interpret the parameters, they are passed
+ * in an opaque manner between a vendor application and HAL.
+ *
+ * Framework does not make any assumptions on the keys or values, other than
+ * ones stated in VendorKeyValue documentation (a requirement of key
+ * prefixes).
+ *
+ * For each pair in the result map, the key will be one of the keys
+ * contained in the input (possibly with wildcards expanded), and the value
+ * will be a vendor-specific result status (such as "OK" or an error code).
+ * The implementation may choose to return an empty map, or only return
+ * a status for a subset of the provided inputs, at its discretion.
+ *
+ * Application and HAL must not use keys with unknown prefix. In particular,
+ * it must not place a key-value pair in results vector for unknown key from
+ * parameters vector - instead, an unknown key should simply be ignored.
+ * In other words, results vector may contain a subset of parameter keys
+ * (however, the framework doesn't enforce a strict subset - the only
+ * formal requirement is vendor domain prefix for keys).
+ *
+ * @param parameters Vendor-specific key-value pairs.
+ * @return Operation completion status for parameters being set.
+ * @hide FutureFeature
+ */
+ public abstract @NonNull Map<String, String>
+ setParameters(@NonNull Map<String, String> parameters);
+
+ /**
+ * Generic method for retrieving vendor-specific parameter values.
+ * The framework does not interpret the parameters, they are passed
+ * in an opaque manner between a vendor application and HAL.
+ *
+ * Framework does not cache set/get requests, so it's possible for
+ * getParameter to return a different value than previous setParameter call.
+ *
+ * The syntax and semantics of keys are up to the vendor (as long as prefix
+ * rules are obeyed). For instance, vendors may include some form of
+ * wildcard support. In such case, result vector may be of different size
+ * than requested keys vector. However, wildcards are not recognized by
+ * framework and they are passed as-is to the HAL implementation.
+ *
+ * Unknown keys must be ignored and not placed into results vector.
+ *
+ * @param keys Parameter keys to fetch.
+ * @return Vendor-specific key-value pairs.
+ * @hide FutureFeature
+ */
+ public abstract @NonNull Map<String, String>
+ getParameters(@NonNull List<String> keys);
+
+ /**
* Get current antenna connection state for current configuration.
* Only valid if a configuration has been applied.
* @return {@code true} if the antenna is connected, {@code false} otherwise.
@@ -429,6 +481,22 @@
* Use {@link RadioTuner#getProgramList(String)} to get an actual list.
*/
public void onProgramListChanged() {}
+
+ /**
+ * Generic callback for passing updates to vendor-specific parameter values.
+ * The framework does not interpret the parameters, they are passed
+ * in an opaque manner between a vendor application and HAL.
+ *
+ * It's up to the HAL implementation if and how to implement this callback,
+ * as long as it obeys the prefix rule. In particular, only selected keys
+ * may be notified this way. However, setParameters must not trigger
+ * this callback, while an internal event can change parameters
+ * asynchronously.
+ *
+ * @param parameters Vendor-specific key-value pairs.
+ * @hide FutureFeature
+ */
+ public void onParametersUpdated(@NonNull Map<String, String> parameters) {}
}
}
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index b621969..864d17c 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -24,6 +24,7 @@
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Implements the RadioTuner interface by forwarding calls to radio service.
@@ -251,6 +252,24 @@
}
@Override
+ public @NonNull Map<String, String> setParameters(@NonNull Map<String, String> parameters) {
+ try {
+ return mTuner.setParameters(Objects.requireNonNull(parameters));
+ } catch (RemoteException e) {
+ throw new RuntimeException("service died", e);
+ }
+ }
+
+ @Override
+ public @NonNull Map<String, String> getParameters(@NonNull List<String> keys) {
+ try {
+ return mTuner.getParameters(Objects.requireNonNull(keys));
+ } catch (RemoteException e) {
+ throw new RuntimeException("service died", e);
+ }
+ }
+
+ @Override
public boolean isAntennaConnected() {
try {
return mTuner.isAntennaConnected();
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index ffd5b30f..a01f658 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -22,6 +22,8 @@
import android.os.Looper;
import android.util.Log;
+import java.util.Map;
+
/**
* Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
*/
@@ -94,4 +96,9 @@
public void onProgramListChanged() {
mHandler.post(() -> mCallback.onProgramListChanged());
}
+
+ @Override
+ public void onParametersUpdated(Map parameters) {
+ mHandler.post(() -> mCallback.onParametersUpdated(parameters));
+ }
}
diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java
new file mode 100644
index 0000000..d9b7c5b
--- /dev/null
+++ b/core/java/android/hardware/usb/AccessoryFilter.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * This class is used to describe a USB accessory.
+ * When used in HashMaps all values must be specified,
+ * but wildcards can be used for any of the fields in
+ * the package meta-data.
+ *
+ * @hide
+ */
+public class AccessoryFilter {
+ // USB accessory manufacturer (or null for unspecified)
+ public final String mManufacturer;
+ // USB accessory model (or null for unspecified)
+ public final String mModel;
+ // USB accessory version (or null for unspecified)
+ public final String mVersion;
+
+ public AccessoryFilter(String manufacturer, String model, String version) {
+ mManufacturer = manufacturer;
+ mModel = model;
+ mVersion = version;
+ }
+
+ public AccessoryFilter(UsbAccessory accessory) {
+ mManufacturer = accessory.getManufacturer();
+ mModel = accessory.getModel();
+ mVersion = accessory.getVersion();
+ }
+
+ public static AccessoryFilter read(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ String manufacturer = null;
+ String model = null;
+ String version = null;
+
+ int count = parser.getAttributeCount();
+ for (int i = 0; i < count; i++) {
+ String name = parser.getAttributeName(i);
+ String value = parser.getAttributeValue(i);
+
+ if ("manufacturer".equals(name)) {
+ manufacturer = value;
+ } else if ("model".equals(name)) {
+ model = value;
+ } else if ("version".equals(name)) {
+ version = value;
+ }
+ }
+ return new AccessoryFilter(manufacturer, model, version);
+ }
+
+ public void write(XmlSerializer serializer)throws IOException {
+ serializer.startTag(null, "usb-accessory");
+ if (mManufacturer != null) {
+ serializer.attribute(null, "manufacturer", mManufacturer);
+ }
+ if (mModel != null) {
+ serializer.attribute(null, "model", mModel);
+ }
+ if (mVersion != null) {
+ serializer.attribute(null, "version", mVersion);
+ }
+ serializer.endTag(null, "usb-accessory");
+ }
+
+ public boolean matches(UsbAccessory acc) {
+ if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
+ if (mModel != null && !acc.getModel().equals(mModel)) return false;
+ return !(mVersion != null && !acc.getVersion().equals(mVersion));
+ }
+
+ /**
+ * Is the accessories described {@code accessory} covered by this filter?
+ *
+ * @param accessory A filter describing the accessory
+ *
+ * @return {@code true} iff this the filter covers the accessory
+ */
+ public boolean contains(AccessoryFilter accessory) {
+ if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) {
+ return false;
+ }
+ if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false;
+ return !(mVersion != null && !Objects.equals(accessory.mVersion, mVersion));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // can't compare if we have wildcard strings
+ if (mManufacturer == null || mModel == null || mVersion == null) {
+ return false;
+ }
+ if (obj instanceof AccessoryFilter) {
+ AccessoryFilter filter = (AccessoryFilter)obj;
+ return (mManufacturer.equals(filter.mManufacturer) &&
+ mModel.equals(filter.mModel) &&
+ mVersion.equals(filter.mVersion));
+ }
+ if (obj instanceof UsbAccessory) {
+ UsbAccessory accessory = (UsbAccessory)obj;
+ return (mManufacturer.equals(accessory.getManufacturer()) &&
+ mModel.equals(accessory.getModel()) &&
+ mVersion.equals(accessory.getVersion()));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
+ (mModel == null ? 0 : mModel.hashCode()) ^
+ (mVersion == null ? 0 : mVersion.hashCode()));
+ }
+
+ @Override
+ public String toString() {
+ return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
+ "\", mModel=\"" + mModel +
+ "\", mVersion=\"" + mVersion + "\"]";
+ }
+}
diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java
new file mode 100644
index 0000000..439c629
--- /dev/null
+++ b/core/java/android/hardware/usb/DeviceFilter.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import android.util.Slog;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * This class is used to describe a USB device.
+ * When used in HashMaps all values must be specified,
+ * but wildcards can be used for any of the fields in
+ * the package meta-data.
+ *
+ * @hide
+ */
+public class DeviceFilter {
+ private static final String TAG = DeviceFilter.class.getSimpleName();
+
+ // USB Vendor ID (or -1 for unspecified)
+ public final int mVendorId;
+ // USB Product ID (or -1 for unspecified)
+ public final int mProductId;
+ // USB device or interface class (or -1 for unspecified)
+ public final int mClass;
+ // USB device subclass (or -1 for unspecified)
+ public final int mSubclass;
+ // USB device protocol (or -1 for unspecified)
+ public final int mProtocol;
+ // USB device manufacturer name string (or null for unspecified)
+ public final String mManufacturerName;
+ // USB device product name string (or null for unspecified)
+ public final String mProductName;
+ // USB device serial number string (or null for unspecified)
+ public final String mSerialNumber;
+
+ public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
+ String manufacturer, String product, String serialnum) {
+ mVendorId = vid;
+ mProductId = pid;
+ mClass = clasz;
+ mSubclass = subclass;
+ mProtocol = protocol;
+ mManufacturerName = manufacturer;
+ mProductName = product;
+ mSerialNumber = serialnum;
+ }
+
+ public DeviceFilter(UsbDevice device) {
+ mVendorId = device.getVendorId();
+ mProductId = device.getProductId();
+ mClass = device.getDeviceClass();
+ mSubclass = device.getDeviceSubclass();
+ mProtocol = device.getDeviceProtocol();
+ mManufacturerName = device.getManufacturerName();
+ mProductName = device.getProductName();
+ mSerialNumber = device.getSerialNumber();
+ }
+
+ public static DeviceFilter read(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int vendorId = -1;
+ int productId = -1;
+ int deviceClass = -1;
+ int deviceSubclass = -1;
+ int deviceProtocol = -1;
+ String manufacturerName = null;
+ String productName = null;
+ String serialNumber = null;
+
+ int count = parser.getAttributeCount();
+ for (int i = 0; i < count; i++) {
+ String name = parser.getAttributeName(i);
+ String value = parser.getAttributeValue(i);
+ // Attribute values are ints or strings
+ if ("manufacturer-name".equals(name)) {
+ manufacturerName = value;
+ } else if ("product-name".equals(name)) {
+ productName = value;
+ } else if ("serial-number".equals(name)) {
+ serialNumber = value;
+ } else {
+ int intValue;
+ int radix = 10;
+ if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
+ (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
+ // allow hex values starting with 0x or 0X
+ radix = 16;
+ value = value.substring(2);
+ }
+ try {
+ intValue = Integer.parseInt(value, radix);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "invalid number for field " + name, e);
+ continue;
+ }
+ if ("vendor-id".equals(name)) {
+ vendorId = intValue;
+ } else if ("product-id".equals(name)) {
+ productId = intValue;
+ } else if ("class".equals(name)) {
+ deviceClass = intValue;
+ } else if ("subclass".equals(name)) {
+ deviceSubclass = intValue;
+ } else if ("protocol".equals(name)) {
+ deviceProtocol = intValue;
+ }
+ }
+ }
+ return new DeviceFilter(vendorId, productId,
+ deviceClass, deviceSubclass, deviceProtocol,
+ manufacturerName, productName, serialNumber);
+ }
+
+ public void write(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, "usb-device");
+ if (mVendorId != -1) {
+ serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
+ }
+ if (mProductId != -1) {
+ serializer.attribute(null, "product-id", Integer.toString(mProductId));
+ }
+ if (mClass != -1) {
+ serializer.attribute(null, "class", Integer.toString(mClass));
+ }
+ if (mSubclass != -1) {
+ serializer.attribute(null, "subclass", Integer.toString(mSubclass));
+ }
+ if (mProtocol != -1) {
+ serializer.attribute(null, "protocol", Integer.toString(mProtocol));
+ }
+ if (mManufacturerName != null) {
+ serializer.attribute(null, "manufacturer-name", mManufacturerName);
+ }
+ if (mProductName != null) {
+ serializer.attribute(null, "product-name", mProductName);
+ }
+ if (mSerialNumber != null) {
+ serializer.attribute(null, "serial-number", mSerialNumber);
+ }
+ serializer.endTag(null, "usb-device");
+ }
+
+ private boolean matches(int clasz, int subclass, int protocol) {
+ return ((mClass == -1 || clasz == mClass) &&
+ (mSubclass == -1 || subclass == mSubclass) &&
+ (mProtocol == -1 || protocol == mProtocol));
+ }
+
+ public boolean matches(UsbDevice device) {
+ if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
+ if (mProductId != -1 && device.getProductId() != mProductId) return false;
+ if (mManufacturerName != null && device.getManufacturerName() == null) return false;
+ if (mProductName != null && device.getProductName() == null) return false;
+ if (mSerialNumber != null && device.getSerialNumber() == null) return false;
+ if (mManufacturerName != null && device.getManufacturerName() != null &&
+ !mManufacturerName.equals(device.getManufacturerName())) return false;
+ if (mProductName != null && device.getProductName() != null &&
+ !mProductName.equals(device.getProductName())) return false;
+ if (mSerialNumber != null && device.getSerialNumber() != null &&
+ !mSerialNumber.equals(device.getSerialNumber())) return false;
+
+ // check device class/subclass/protocol
+ if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
+ device.getDeviceProtocol())) return true;
+
+ // if device doesn't match, check the interfaces
+ int count = device.getInterfaceCount();
+ for (int i = 0; i < count; i++) {
+ UsbInterface intf = device.getInterface(i);
+ if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
+ intf.getInterfaceProtocol())) return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * If the device described by {@code device} covered by this filter?
+ *
+ * @param device The device
+ *
+ * @return {@code true} iff this filter covers the {@code device}
+ */
+ public boolean contains(DeviceFilter device) {
+ // -1 and null means "match anything"
+
+ if (mVendorId != -1 && device.mVendorId != mVendorId) return false;
+ if (mProductId != -1 && device.mProductId != mProductId) return false;
+ if (mManufacturerName != null && !Objects.equals(mManufacturerName,
+ device.mManufacturerName)) {
+ return false;
+ }
+ if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) {
+ return false;
+ }
+ if (mSerialNumber != null
+ && !Objects.equals(mSerialNumber, device.mSerialNumber)) {
+ return false;
+ }
+
+ // check device class/subclass/protocol
+ return matches(device.mClass, device.mSubclass, device.mProtocol);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // can't compare if we have wildcard strings
+ if (mVendorId == -1 || mProductId == -1 ||
+ mClass == -1 || mSubclass == -1 || mProtocol == -1) {
+ return false;
+ }
+ if (obj instanceof DeviceFilter) {
+ DeviceFilter filter = (DeviceFilter)obj;
+
+ if (filter.mVendorId != mVendorId ||
+ filter.mProductId != mProductId ||
+ filter.mClass != mClass ||
+ filter.mSubclass != mSubclass ||
+ filter.mProtocol != mProtocol) {
+ return(false);
+ }
+ if ((filter.mManufacturerName != null &&
+ mManufacturerName == null) ||
+ (filter.mManufacturerName == null &&
+ mManufacturerName != null) ||
+ (filter.mProductName != null &&
+ mProductName == null) ||
+ (filter.mProductName == null &&
+ mProductName != null) ||
+ (filter.mSerialNumber != null &&
+ mSerialNumber == null) ||
+ (filter.mSerialNumber == null &&
+ mSerialNumber != null)) {
+ return(false);
+ }
+ if ((filter.mManufacturerName != null &&
+ mManufacturerName != null &&
+ !mManufacturerName.equals(filter.mManufacturerName)) ||
+ (filter.mProductName != null &&
+ mProductName != null &&
+ !mProductName.equals(filter.mProductName)) ||
+ (filter.mSerialNumber != null &&
+ mSerialNumber != null &&
+ !mSerialNumber.equals(filter.mSerialNumber))) {
+ return false;
+ }
+ return true;
+ }
+ if (obj instanceof UsbDevice) {
+ UsbDevice device = (UsbDevice)obj;
+ if (device.getVendorId() != mVendorId ||
+ device.getProductId() != mProductId ||
+ device.getDeviceClass() != mClass ||
+ device.getDeviceSubclass() != mSubclass ||
+ device.getDeviceProtocol() != mProtocol) {
+ return(false);
+ }
+ if ((mManufacturerName != null && device.getManufacturerName() == null) ||
+ (mManufacturerName == null && device.getManufacturerName() != null) ||
+ (mProductName != null && device.getProductName() == null) ||
+ (mProductName == null && device.getProductName() != null) ||
+ (mSerialNumber != null && device.getSerialNumber() == null) ||
+ (mSerialNumber == null && device.getSerialNumber() != null)) {
+ return(false);
+ }
+ if ((device.getManufacturerName() != null &&
+ !mManufacturerName.equals(device.getManufacturerName())) ||
+ (device.getProductName() != null &&
+ !mProductName.equals(device.getProductName())) ||
+ (device.getSerialNumber() != null &&
+ !mSerialNumber.equals(device.getSerialNumber()))) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return (((mVendorId << 16) | mProductId) ^
+ ((mClass << 16) | (mSubclass << 8) | mProtocol));
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
+ ",mClass=" + mClass + ",mSubclass=" + mSubclass +
+ ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
+ ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
+ "]";
+ }
+}
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 4df3bf0..1999e78 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -60,29 +60,25 @@
@Retention(RetentionPolicy.SOURCE)
public @interface EventType {}
- public final int netId;
public final @EventType int eventType;
public final long durationMs;
- public NetworkEvent(int netId, @EventType int eventType, long durationMs) {
- this.netId = netId;
+ public NetworkEvent(@EventType int eventType, long durationMs) {
this.eventType = eventType;
this.durationMs = durationMs;
}
- public NetworkEvent(int netId, @EventType int eventType) {
- this(netId, eventType, 0);
+ public NetworkEvent(@EventType int eventType) {
+ this(eventType, 0);
}
private NetworkEvent(Parcel in) {
- netId = in.readInt();
eventType = in.readInt();
durationMs = in.readLong();
}
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(netId);
out.writeInt(eventType);
out.writeLong(durationMs);
}
@@ -105,8 +101,8 @@
@Override
public String toString() {
- return String.format("NetworkEvent(%d, %s, %dms)",
- netId, Decoder.constants.get(eventType), durationMs);
+ return String.format("NetworkEvent(%s, %dms)",
+ Decoder.constants.get(eventType), durationMs);
}
final static class Decoder {
diff --git a/core/java/android/net/metrics/WakeupEvent.java b/core/java/android/net/metrics/WakeupEvent.java
index cbf3fc8..af9a73c 100644
--- a/core/java/android/net/metrics/WakeupEvent.java
+++ b/core/java/android/net/metrics/WakeupEvent.java
@@ -16,6 +16,10 @@
package android.net.metrics;
+import android.net.MacAddress;
+
+import java.util.StringJoiner;
+
/**
* An event logged when NFLOG notifies userspace of a wakeup packet for
* watched interfaces.
@@ -23,12 +27,35 @@
*/
public class WakeupEvent {
public String iface;
- public long timestampMs;
public int uid;
+ public int ethertype;
+ public MacAddress dstHwAddr;
+ public String srcIp;
+ public String dstIp;
+ public int ipNextHeader;
+ public int srcPort;
+ public int dstPort;
+ public long timestampMs;
@Override
public String toString() {
- return String.format("WakeupEvent(%tT.%tL, %s, uid: %d)",
- timestampMs, timestampMs, iface, uid);
+ StringJoiner j = new StringJoiner(", ", "WakeupEvent(", ")");
+ j.add(String.format("%tT.%tL", timestampMs, timestampMs));
+ j.add(iface);
+ j.add("uid: " + Integer.toString(uid));
+ j.add("eth=0x" + Integer.toHexString(ethertype));
+ j.add("dstHw=" + dstHwAddr);
+ if (ipNextHeader > 0) {
+ j.add("ipNxtHdr=" + ipNextHeader);
+ j.add("srcIp=" + srcIp);
+ j.add("dstIp=" + dstIp);
+ if (srcPort > -1) {
+ j.add("srcPort=" + srcPort);
+ }
+ if (dstPort > -1) {
+ j.add("dstPort=" + dstPort);
+ }
+ }
+ return j.toString();
}
}
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
index 97e83f9..23c1f20 100644
--- a/core/java/android/net/metrics/WakeupStats.java
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -18,6 +18,9 @@
import android.os.Process;
import android.os.SystemClock;
+import android.util.SparseIntArray;
+
+import java.util.StringJoiner;
/**
* An event logged per interface and that aggregates WakeupEvents for that interface.
@@ -38,6 +41,13 @@
public long noUidWakeups = 0;
public long durationSec = 0;
+ public long l2UnicastCount = 0;
+ public long l2MulticastCount = 0;
+ public long l2BroadcastCount = 0;
+
+ public final SparseIntArray ethertypes = new SparseIntArray();
+ public final SparseIntArray ipNextHeaders = new SparseIntArray();
+
public WakeupStats(String iface) {
this.iface = iface;
}
@@ -68,20 +78,56 @@
}
break;
}
+
+ switch (ev.dstHwAddr.addressType()) {
+ case UNICAST:
+ l2UnicastCount++;
+ break;
+ case MULTICAST:
+ l2MulticastCount++;
+ break;
+ case BROADCAST:
+ l2BroadcastCount++;
+ break;
+ default:
+ break;
+ }
+
+ increment(ethertypes, ev.ethertype);
+ if (ev.ipNextHeader >= 0) {
+ increment(ipNextHeaders, ev.ipNextHeader);
+ }
}
@Override
public String toString() {
updateDuration();
- return new StringBuilder()
- .append("WakeupStats(").append(iface)
- .append(", total: ").append(totalWakeups)
- .append(", root: ").append(rootWakeups)
- .append(", system: ").append(systemWakeups)
- .append(", apps: ").append(applicationWakeups)
- .append(", non-apps: ").append(nonApplicationWakeups)
- .append(", no uid: ").append(noUidWakeups)
- .append(", ").append(durationSec).append("s)")
- .toString();
+ StringJoiner j = new StringJoiner(", ", "WakeupStats(", ")");
+ j.add(iface);
+ j.add("" + durationSec + "s");
+ j.add("total: " + totalWakeups);
+ j.add("root: " + rootWakeups);
+ j.add("system: " + systemWakeups);
+ j.add("apps: " + applicationWakeups);
+ j.add("non-apps: " + nonApplicationWakeups);
+ j.add("no uid: " + noUidWakeups);
+ j.add(String.format("l2 unicast/multicast/broadcast: %d/%d/%d",
+ l2UnicastCount, l2MulticastCount, l2BroadcastCount));
+ for (int i = 0; i < ethertypes.size(); i++) {
+ int eth = ethertypes.keyAt(i);
+ int count = ethertypes.valueAt(i);
+ j.add(String.format("ethertype 0x%x: %d", eth, count));
+ }
+ for (int i = 0; i < ipNextHeaders.size(); i++) {
+ int proto = ipNextHeaders.keyAt(i);
+ int count = ipNextHeaders.valueAt(i);
+ j.add(String.format("ipNxtHdr %d: %d", proto, count));
+ }
+ return j.toString();
+ }
+
+ private static void increment(SparseIntArray counters, int key) {
+ int newcount = counters.get(key, 0) + 1;
+ counters.put(key, newcount);
}
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 935f5f3..6ebf3b9 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -103,7 +103,7 @@
/**
* A hardware serial number, if available. Alphanumeric only, case-insensitive.
- * For apps targeting SDK higher than {@link Build.VERSION_CODES#N_MR1} this
+ * For apps targeting SDK higher than {@link Build.VERSION_CODES#O_MR1} this
* field is set to {@link Build#UNKNOWN}.
*
* @deprecated Use {@link #getSerial()} instead.
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 07c6055..f2e0bdd 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -22,6 +22,7 @@
import android.opengl.EGL14;
import android.os.Build;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.util.Log;
import dalvik.system.VMRuntime;
@@ -29,18 +30,110 @@
import java.io.File;
/** @hide */
-public final class GraphicsEnvironment {
+public class GraphicsEnvironment {
+
+ private static final GraphicsEnvironment sInstance = new GraphicsEnvironment();
+
+ /**
+ * Returns the shared {@link GraphicsEnvironment} instance.
+ */
+ public static GraphicsEnvironment getInstance() {
+ return sInstance;
+ }
private static final boolean DEBUG = false;
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+ private ClassLoader mClassLoader;
+ private String mLayerPath;
+ private String mDebugLayerPath;
+
+ /**
+ * Set up GraphicsEnvironment
+ */
+ public void setup(Context context) {
+ setupGpuLayers(context);
+ chooseDriver(context);
+ }
+
+ /**
+ * Check whether application is debuggable
+ */
+ private static boolean isDebuggable(Context context) {
+ return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) > 0;
+ }
+
+ /**
+ * Store the layer paths available to the loader.
+ */
+ public void setLayerPaths(ClassLoader classLoader,
+ String layerPath,
+ String debugLayerPath) {
+ // We have to store these in the class because they are set up before we
+ // have access to the Context to properly set up GraphicsEnvironment
+ mClassLoader = classLoader;
+ mLayerPath = layerPath;
+ mDebugLayerPath = debugLayerPath;
+ }
+
+ /**
+ * Set up layer search paths for all apps
+ * If debuggable, check for additional debug settings
+ */
+ private void setupGpuLayers(Context context) {
+
+ String layerPaths = "";
+
+ // Only enable additional debug functionality if the following conditions are met:
+ // 1. App is debuggable
+ // 2. ENABLE_GPU_DEBUG_LAYERS is true
+ // 3. Package name is equal to GPU_DEBUG_APP
+
+ if (isDebuggable(context)) {
+
+ int enable = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+
+ if (enable != 0) {
+
+ String gpuDebugApp = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.GPU_DEBUG_APP);
+
+ String packageName = context.getPackageName();
+
+ if ((gpuDebugApp != null && packageName != null)
+ && (!gpuDebugApp.isEmpty() && !packageName.isEmpty())
+ && gpuDebugApp.equals(packageName)) {
+ Log.i(TAG, "GPU debug layers enabled for " + packageName);
+
+ // Prepend the debug layer path as a searchable path.
+ // This will ensure debug layers added will take precedence over
+ // the layers specified by the app.
+ layerPaths = mDebugLayerPath + ":";
+
+ String layers = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.GPU_DEBUG_LAYERS);
+
+ Log.i(TAG, "Debug layer list: " + layers);
+ if (layers != null && !layers.isEmpty()) {
+ setDebugLayers(layers);
+ }
+ }
+ }
+
+ }
+
+ // Include the app's lib directory in all cases
+ layerPaths += mLayerPath;
+
+ setLayerPaths(mClassLoader, layerPaths);
+ }
+
/**
* Choose whether the current process should use the builtin or an updated driver.
- *
- * @hide
*/
- public static void chooseDriver(Context context) {
+ private static void chooseDriver(Context context) {
String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
if (driverPackageName == null || driverPackageName.isEmpty()) {
return;
@@ -99,8 +192,6 @@
* on a separate thread, it can usually be finished well before the UI is ready to be drawn.
*
* Should only be called after chooseDriver().
- *
- * @hide
*/
public static void earlyInitEGL() {
Thread eglInitThread = new Thread(
@@ -124,6 +215,7 @@
return null;
}
+ private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
+ private static native void setDebugLayers(String layers);
private static native void setDriverPath(String path);
-
}
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index c0a95cc..3314f60 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -53,4 +53,7 @@
/** Pull the specified data. Results will be sent to statsd when complete. */
StatsLogEventWrapper[] pullData(int pullCode);
+
+ /** Send a broadcast to the specified pkg and class that it should getData now. */
+ oneway void sendBroadcast(String pkg, String cls);
}
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 480296c..8e33b65 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -16,8 +16,6 @@
package android.os;
-import android.os.IStatsCallbacks;
-
/**
* Binder interface to communicate with the statistics management service.
* {@hide}
@@ -65,13 +63,26 @@
oneway void informOnePackageRemoved(in String app, in int uid);
/**
- * Trigger pushLog to force push stats log entries from statsd on client side.
+ * Fetches data for the specified configuration key. Returns a byte array representing proto
+ * wire-encoded of ConfigMetricsReport.
*/
- void requestPush();
+ byte[] getData(in String key);
/**
- * Listen to statsd to send stats log entries.
- * TODO: Limit callbacks with specific configurations.
+ * Sets a configuration with the specified config key and subscribes to updates for this
+ * configuration key. Broadcasts will be sent if this configuration needs to be collected.
+ * The configuration must be a wire-encoded StatsDConfig. The caller specifies the name of the
+ * package and class that should receive these broadcasts.
+ *
+ * Returns if this configuration was correctly registered.
*/
- void subscribeStatsLog(IStatsCallbacks callbacks);
+ boolean addConfiguration(in String configKey, in byte[] config, in String pkg, in String cls);
+
+ /**
+ * Removes the configuration with the matching config key. No-op if this config key does not
+ * exist.
+ *
+ * Returns if this configuration key was removed.
+ */
+ boolean removeConfiguration(in String configKey);
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e426356..0620fa3 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -35,6 +35,10 @@
* DO NOT MOVE - UserManager.h depends on the ordering of this function.
*/
int getCredentialOwnerProfile(int userHandle);
+ int getProfileParentId(int userHandle);
+ /*
+ * END OF DO NOT MOVE
+ */
UserInfo createUser(in String name, int flags);
UserInfo createProfileForUser(in String name, int flags, int userHandle,
@@ -94,4 +98,5 @@
boolean isUserUnlocked(int userId);
boolean isUserRunning(int userId);
boolean isUserNameSet(int userHandle);
+ boolean hasRestrictedProfiles();
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 10adb5a..d092018 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -191,6 +191,7 @@
* {@link #readSparseArray(ClassLoader)}.
*/
public final class Parcel {
+
private static final boolean DEBUG_RECYCLE = false;
private static final boolean DEBUG_ARRAY_MAP = false;
private static final String TAG = "Parcel";
@@ -209,6 +210,12 @@
private RuntimeException mStack;
+ /**
+ * Whether or not to parcel the stack trace of an exception. This has a performance
+ * impact, so should only be included in specific processes and only on debug builds.
+ */
+ private static boolean sParcelExceptionStackTrace;
+
private static final int POOL_SIZE = 6;
private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
@@ -325,6 +332,11 @@
private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
private static native void nativeEnforceInterface(long nativePtr, String interfaceName);
+ /** Last time exception with a stack trace was written */
+ private static volatile long sLastWriteExceptionStackTrace;
+ /** Used for throttling of writing stack trace, which is costly */
+ private static final int WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS = 1000;
+
@CriticalNative
private static native long nativeGetBlobAshmemSize(long nativePtr);
@@ -1696,6 +1708,11 @@
}
}
+ /** @hide For debugging purposes */
+ public static void setStackTraceParceling(boolean enabled) {
+ sParcelExceptionStackTrace = enabled;
+ }
+
/**
* Special function for writing an exception result at the header of
* a parcel, to be used when returning an exception from a transaction.
@@ -1753,6 +1770,27 @@
throw new RuntimeException(e);
}
writeString(e.getMessage());
+ final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0;
+ if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace
+ > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) {
+ sLastWriteExceptionStackTrace = timeNow;
+ final int sizePosition = dataPosition();
+ writeInt(0); // Header size will be filled in later
+ StackTraceElement[] stackTrace = e.getStackTrace();
+ final int truncatedSize = Math.min(stackTrace.length, 5);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < truncatedSize; i++) {
+ sb.append("\tat ").append(stackTrace[i]);
+ }
+ writeString(sb.toString());
+ final int payloadPosition = dataPosition();
+ setDataPosition(sizePosition);
+ // Write stack trace header size. Used in native side to skip the header
+ writeInt(payloadPosition - sizePosition);
+ setDataPosition(payloadPosition);
+ } else {
+ writeInt(0);
+ }
switch (code) {
case EX_SERVICE_SPECIFIC:
writeInt(((ServiceSpecificException) e).errorCode);
@@ -1818,7 +1856,19 @@
int code = readExceptionCode();
if (code != 0) {
String msg = readString();
- readException(code, msg);
+ String remoteStackTrace = null;
+ final int remoteStackPayloadSize = readInt();
+ if (remoteStackPayloadSize > 0) {
+ remoteStackTrace = readString();
+ }
+ Exception e = createException(code, msg);
+ // Attach remote stack trace if availalble
+ if (remoteStackTrace != null) {
+ RemoteException cause = new RemoteException(
+ "Remote stack trace:\n" + remoteStackTrace, null, false, false);
+ e.initCause(cause);
+ }
+ SneakyThrow.sneakyThrow(e);
}
}
@@ -1863,32 +1913,41 @@
* @param msg The exception message.
*/
public final void readException(int code, String msg) {
+ SneakyThrow.sneakyThrow(createException(code, msg));
+ }
+
+ /**
+ * Creates an exception with the given message.
+ *
+ * @param code Used to determine which exception class to throw.
+ * @param msg The exception message.
+ */
+ private Exception createException(int code, String msg) {
switch (code) {
case EX_PARCELABLE:
if (readInt() > 0) {
- SneakyThrow.sneakyThrow(
- (Exception) readParcelable(Parcelable.class.getClassLoader()));
+ return (Exception) readParcelable(Parcelable.class.getClassLoader());
} else {
- throw new RuntimeException(msg + " [missing Parcelable]");
+ return new RuntimeException(msg + " [missing Parcelable]");
}
case EX_SECURITY:
- throw new SecurityException(msg);
+ return new SecurityException(msg);
case EX_BAD_PARCELABLE:
- throw new BadParcelableException(msg);
+ return new BadParcelableException(msg);
case EX_ILLEGAL_ARGUMENT:
- throw new IllegalArgumentException(msg);
+ return new IllegalArgumentException(msg);
case EX_NULL_POINTER:
- throw new NullPointerException(msg);
+ return new NullPointerException(msg);
case EX_ILLEGAL_STATE:
- throw new IllegalStateException(msg);
+ return new IllegalStateException(msg);
case EX_NETWORK_MAIN_THREAD:
- throw new NetworkOnMainThreadException();
+ return new NetworkOnMainThreadException();
case EX_UNSUPPORTED_OPERATION:
- throw new UnsupportedOperationException(msg);
+ return new UnsupportedOperationException(msg);
case EX_SERVICE_SPECIFIC:
- throw new ServiceSpecificException(readInt(), msg);
+ return new ServiceSpecificException(readInt(), msg);
}
- throw new RuntimeException("Unknown exception code: " + code
+ return new RuntimeException("Unknown exception code: " + code
+ " msg " + msg);
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index dd4825e..0fce7a4 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -527,8 +527,7 @@
ServiceType.SOUND,
ServiceType.BATTERY_STATS,
ServiceType.DATA_SAVER,
- ServiceType.FORCE_ALL_APPS_STANDBY_JOBS,
- ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS,
+ ServiceType.FORCE_ALL_APPS_STANDBY,
ServiceType.OPTIONAL_SENSORS,
})
public @interface ServiceType {
@@ -545,14 +544,9 @@
int DATA_SAVER = 10;
/**
- * Whether the job scheduler should force app standby on all apps on battery saver or not.
+ * Whether to enable force-app-standby on all apps or not.
*/
- int FORCE_ALL_APPS_STANDBY_JOBS = 11;
-
- /**
- * Whether the alarm manager should force app standby on all apps on battery saver or not.
- */
- int FORCE_ALL_APPS_STANDBY_ALARMS = 12;
+ int FORCE_ALL_APPS_STANDBY = 11;
/**
* Whether to disable non-essential sensors. (e.g. edge sensors.)
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b5d62e5..0874d93 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -151,6 +151,9 @@
*/
public static final int OTA_UPDATE_UID = 1061;
+ /** {@hide} */
+ public static final int NOBODY_UID = 9999;
+
/**
* Defines the start of a range of UIDs (and GIDs), going from this
* number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 2281fb6..b9b9a18 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -19,6 +19,7 @@
import android.util.ArrayMap;
import android.util.Slog;
+import java.io.PrintWriter;
import java.util.function.Consumer;
/**
@@ -399,6 +400,13 @@
}
}
+ /** @hide */
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
+ pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
+ pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
+ }
+
private void logExcessiveCallbacks() {
final long size = mCallbacks.size();
final long TOO_MANY = 3000;
diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java
index 6d25fc1..4e8b971 100644
--- a/core/java/android/os/RemoteException.java
+++ b/core/java/android/os/RemoteException.java
@@ -30,6 +30,12 @@
super(message);
}
+ /** @hide */
+ public RemoteException(String message, Throwable cause, boolean enableSuppression,
+ boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
/** {@hide} */
public RuntimeException rethrowAsRuntimeException() {
throw new RuntimeException(this);
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index e8ebf63..6381b56 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -27,6 +27,8 @@
* Representation of a user on the device.
*/
public final class UserHandle implements Parcelable {
+ // NOTE: keep logic in sync with system/core/libcutils/multiuser.c
+
/**
* @hide Range of uids allocated for a user.
*/
@@ -88,6 +90,19 @@
*/
public static final boolean MU_ENABLED = true;
+ /** @hide */
+ public static final int ERR_GID = -1;
+ /** @hide */
+ public static final int AID_ROOT = android.os.Process.ROOT_UID;
+ /** @hide */
+ public static final int AID_APP_START = android.os.Process.FIRST_APPLICATION_UID;
+ /** @hide */
+ public static final int AID_APP_END = android.os.Process.LAST_APPLICATION_UID;
+ /** @hide */
+ public static final int AID_SHARED_GID_START = android.os.Process.FIRST_SHARED_APPLICATION_GID;
+ /** @hide */
+ public static final int AID_CACHE_GID_START = android.os.Process.FIRST_APPLICATION_CACHE_GID;
+
final int mHandle;
/**
@@ -197,13 +212,20 @@
return getUid(userId, Process.SHARED_USER_GID);
}
- /**
- * Returns the shared app gid for a given uid or appId.
- * @hide
- */
- public static int getSharedAppGid(int id) {
- return Process.FIRST_SHARED_APPLICATION_GID + (id % PER_USER_RANGE)
- - Process.FIRST_APPLICATION_UID;
+ /** @hide */
+ public static int getSharedAppGid(int uid) {
+ return getSharedAppGid(getUserId(uid), getAppId(uid));
+ }
+
+ /** @hide */
+ public static int getSharedAppGid(int userId, int appId) {
+ if (appId >= AID_APP_START && appId <= AID_APP_END) {
+ return (appId - AID_APP_START) + AID_SHARED_GID_START;
+ } else if (appId >= AID_ROOT && appId <= AID_APP_START) {
+ return appId;
+ } else {
+ return -1;
+ }
}
/**
@@ -219,13 +241,18 @@
return appId;
}
- /**
- * Returns the cache GID for a given UID or appId.
- * @hide
- */
- public static int getCacheAppGid(int id) {
- return Process.FIRST_APPLICATION_CACHE_GID + (id % PER_USER_RANGE)
- - Process.FIRST_APPLICATION_UID;
+ /** @hide */
+ public static int getCacheAppGid(int uid) {
+ return getCacheAppGid(getUserId(uid), getAppId(uid));
+ }
+
+ /** @hide */
+ public static int getCacheAppGid(int userId, int appId) {
+ if (appId >= AID_APP_START && appId <= AID_APP_END) {
+ return getUid(userId, (appId - AID_APP_START) + AID_CACHE_GID_START);
+ } else {
+ return -1;
+ }
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index de52736..22967af 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1049,12 +1049,22 @@
}
/**
- * Used to check if the user making this call is linked to another user. Linked users may have
+ * @hide
+ * @deprecated Use {@link #isRestrictedProfile()}
+ */
+ @Deprecated
+ public boolean isLinkedUser() {
+ return isRestrictedProfile();
+ }
+
+ /**
+ * Returns whether the caller is running as restricted profile. Restricted profile may have
* a reduced number of available apps, app restrictions and account restrictions.
* @return whether the user making this call is a linked user
* @hide
*/
- public boolean isLinkedUser() {
+ @SystemApi
+ public boolean isRestrictedProfile() {
try {
return mService.isRestricted();
} catch (RemoteException re) {
@@ -1075,6 +1085,20 @@
}
/**
+ * Returns whether the calling user has at least one restricted profile associated with it.
+ * @return
+ * @hide
+ */
+ @SystemApi
+ public boolean hasRestrictedProfiles() {
+ try {
+ return mService.hasRestrictedProfiles();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Checks if a user is a guest user.
* @return whether user is a guest user.
* @hide
@@ -1094,6 +1118,7 @@
return user != null && user.isGuest();
}
+
/**
* Checks if the calling app is running in a demo user. When running in a demo user,
* apps can be more helpful to the user, or explain their features in more detail.
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 0b76eec..2ffc7b0 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -186,4 +186,6 @@
long getAllocatableBytes(String volumeUuid, int flags, String callingPackage) = 77;
void allocateBytes(String volumeUuid, long bytes, int flags, String callingPackage) = 78;
void secdiscard(in String path) = 79;
+ void runIdleMaintenance() = 80;
+ void abortIdleMaintenance() = 81;
}
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 73fa01e..4c556ef 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -23,7 +23,6 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -105,7 +104,10 @@
*
* @see Preference
* @see PreferenceScreen
+ *
+ * @deprecated Use {@link android.support.v7.preference.PreferenceFragmentCompat}
*/
+@Deprecated
public abstract class PreferenceFragment extends Fragment implements
PreferenceManager.OnPreferenceTreeClickListener {
@@ -146,7 +148,11 @@
* Interface that PreferenceFragment's containing activity should
* implement to be able to process preference items that wish to
* switch to a new fragment.
+ *
+ * @deprecated Use {@link
+ * android.support.v7.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback}
*/
+ @Deprecated
public interface OnPreferenceStartFragmentCallback {
/**
* Called when the user has clicked on a Preference that has
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 13e1e26..32d68cd 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -81,6 +81,13 @@
public static final String UNHIDE_CALL = "unhide";
/**
+ * The method name used by the media scanner service to reload all localized ringtone titles due
+ * to a locale change.
+ * @hide
+ */
+ public static final String RETRANSLATE_CALL = "update_titles";
+
+ /**
* This is for internal use by the media scanner only.
* Name of the (optional) Uri parameter that determines whether to skip deleting
* the file pointed to by the _data column, when deleting the database entry.
@@ -1358,6 +1365,18 @@
* @hide
*/
public static final String GENRE = "genre";
+
+ /**
+ * The resource URI of a localized title, if any
+ * <P>Type: TEXT</P>
+ * Conforms to this pattern:
+ * Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
+ * Authority: Package Name of ringtone title provider
+ * First Path Segment: Type of resource (must be "string")
+ * Second Path Segment: Resource ID of title
+ * @hide
+ */
+ public static final String TITLE_RESOURCE_URI = "title_resource_uri";
}
/**
diff --git a/core/java/android/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
index 5e0a76d..a60be53 100644
--- a/core/java/android/provider/SearchIndexableData.java
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -56,6 +56,8 @@
/**
* The key for the data. This is application specific. Should be unique per data as the data
* should be able to be retrieved by the key.
+ * <p/>
+ * This is required for indexing to work.
*/
public String key;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
old mode 100755
new mode 100644
index 1bef2b3..9945755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -66,6 +66,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.speech.tts.TextToSpeech;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.AndroidException;
import android.util.ArrayMap;
@@ -2113,6 +2114,9 @@
* functions for accessing individual settings entries.
*/
public static final class System extends NameValueTable {
+ // NOTE: If you add new settings here, be sure to add them to
+ // com.android.providers.settings.SettingsProtoDumpUtil#dumpProtoSystemSettingsLocked.
+
private static final float DEFAULT_FONT_SCALE = 1.0f;
/** @hide */
@@ -4562,6 +4566,9 @@
* APIs for those values, not modified directly by applications.
*/
public static final class Secure extends NameValueTable {
+ // NOTE: If you add new settings here, be sure to add them to
+ // com.android.providers.settings.SettingsProtoDumpUtil#dumpProtoSecureSettingsLocked.
+
/**
* The content:// style URL for this table
*/
@@ -7564,6 +7571,9 @@
* explicitly modify through the system UI or specialized APIs for those values.
*/
public static final class Global extends NameValueTable {
+ // NOTE: If you add new settings here, be sure to add them to
+ // com.android.providers.settings.SettingsProtoDumpUtil#dumpProtoGlobalSettingsLocked.
+
/**
* The content:// style URL for global secure settings items. Not public.
*/
@@ -9468,6 +9478,16 @@
public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
/**
+ * Battery Saver device specific settings
+ * This is encoded as a key=value list, separated by commas.
+ * See {@link com.android.server.power.BatterySaverPolicy} for the details.
+ *
+ * @hide
+ */
+ public static final String BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS =
+ "battery_saver_device_specific_constants";
+
+ /**
* Battery anomaly detection specific settings
* This is encoded as a key=value list, separated by commas.
* wakeup_blacklisted_tags is a string, encoded as a set of tags, encoded via
@@ -9834,6 +9854,27 @@
public static final String WAIT_FOR_DEBUGGER = "wait_for_debugger";
/**
+ * Allow GPU debug layers?
+ * 0 = no
+ * 1 = yes
+ * @hide
+ */
+ public static final String ENABLE_GPU_DEBUG_LAYERS = "enable_gpu_debug_layers";
+
+ /**
+ * App allowed to load GPU debug layers
+ * @hide
+ */
+ public static final String GPU_DEBUG_APP = "gpu_debug_app";
+
+ /**
+ * Ordered GPU debug layer list
+ * i.e. <layer1>:<layer2>:...:<layerN>
+ * @hide
+ */
+ public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers";
+
+ /**
* Control whether the process CPU usage meter should be shown.
*
* @deprecated This functionality is no longer available as of
@@ -10144,12 +10185,17 @@
public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
/**
- * Whether the Volte is enabled
+ * Whether the Volte is enabled. If this setting is not set then we use the Carrier Config
+ * value {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
* <p>
* Type: int (0 for false, 1 for true)
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#ENHANCED_4G_MODE_ENABLED}
+ * instead.
*/
- public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+ @Deprecated
+ public static final String ENHANCED_4G_MODE_ENABLED =
+ SubscriptionManager.ENHANCED_4G_MODE_ENABLED;
/**
* Whether VT (Video Telephony over IMS) is enabled
@@ -10157,8 +10203,10 @@
* Type: int (0 for false, 1 for true)
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#VT_IMS_ENABLED} instead.
*/
- public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+ @Deprecated
+ public static final String VT_IMS_ENABLED = SubscriptionManager.VT_IMS_ENABLED;
/**
* Whether WFC is enabled
@@ -10166,8 +10214,10 @@
* Type: int (0 for false, 1 for true)
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ENABLED} instead.
*/
- public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+ @Deprecated
+ public static final String WFC_IMS_ENABLED = SubscriptionManager.WFC_IMS_ENABLED;
/**
* WFC mode on home/non-roaming network.
@@ -10175,8 +10225,10 @@
* Type: int - 2=Wi-Fi preferred, 1=Cellular preferred, 0=Wi-Fi only
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_MODE} instead.
*/
- public static final String WFC_IMS_MODE = "wfc_ims_mode";
+ @Deprecated
+ public static final String WFC_IMS_MODE = SubscriptionManager.WFC_IMS_MODE;
/**
* WFC mode on roaming network.
@@ -10184,8 +10236,11 @@
* Type: int - see {@link #WFC_IMS_MODE} for values
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_MODE}
+ * instead.
*/
- public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+ @Deprecated
+ public static final String WFC_IMS_ROAMING_MODE = SubscriptionManager.WFC_IMS_ROAMING_MODE;
/**
* Whether WFC roaming is enabled
@@ -10193,8 +10248,12 @@
* Type: int (0 for false, 1 for true)
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_ENABLED}
+ * instead
*/
- public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+ @Deprecated
+ public static final String WFC_IMS_ROAMING_ENABLED =
+ SubscriptionManager.WFC_IMS_ROAMING_ENABLED;
/**
* Whether user can enable/disable LTE as a preferred network. A carrier might control
@@ -10875,7 +10934,7 @@
/** User preferred subscriptions setting.
* This holds the details of the user selected subscription from the card and
- * the activation status. Each settings string have the coma separated values
+ * the activation status. Each settings string have the comma separated values
* iccId,appType,appId,activationStatus,3gppIndex,3gpp2Index
* @hide
*/
diff --git a/core/java/android/security/KeystoreArguments.aidl b/core/java/android/security/KeystoreArguments.aidl
index d636414..dc8ed50 100644
--- a/core/java/android/security/KeystoreArguments.aidl
+++ b/core/java/android/security/KeystoreArguments.aidl
@@ -17,4 +17,4 @@
package android.security;
/* @hide */
-parcelable KeystoreArguments;
+parcelable KeystoreArguments cpp_header "keystore/KeystoreArguments.h";
diff --git a/core/java/android/security/keymaster/ExportResult.aidl b/core/java/android/security/keymaster/ExportResult.aidl
index 4d9b2de..1748653 100644
--- a/core/java/android/security/keymaster/ExportResult.aidl
+++ b/core/java/android/security/keymaster/ExportResult.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable ExportResult;
+parcelable ExportResult cpp_header "keystore/ExportResult.h";
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.aidl b/core/java/android/security/keymaster/KeyCharacteristics.aidl
index be739d3..32e75ad 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.aidl
+++ b/core/java/android/security/keymaster/KeyCharacteristics.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable KeyCharacteristics;
+parcelable KeyCharacteristics cpp_header "keystore/KeyCharacteristics.h";
diff --git a/core/java/android/security/keymaster/KeymasterArguments.aidl b/core/java/android/security/keymaster/KeymasterArguments.aidl
index 1a73206..44d9f09 100644
--- a/core/java/android/security/keymaster/KeymasterArguments.aidl
+++ b/core/java/android/security/keymaster/KeymasterArguments.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable KeymasterArguments;
+parcelable KeymasterArguments cpp_header "keystore/KeymasterArguments.h";
diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl
index b7cd1c9..5c5db9e 100644
--- a/core/java/android/security/keymaster/KeymasterBlob.aidl
+++ b/core/java/android/security/keymaster/KeymasterBlob.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable KeymasterBlob;
+parcelable KeymasterBlob cpp_header "keystore/KeymasterBlob.h";
diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
index dc1876a..ddb5cae 100644
--- a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
+++ b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable KeymasterCertificateChain;
+parcelable KeymasterCertificateChain cpp_header "keystore/KeymasterCertificateChain.h";
diff --git a/core/java/android/security/keymaster/OperationResult.aidl b/core/java/android/security/keymaster/OperationResult.aidl
index ed26c8d..db689d4 100644
--- a/core/java/android/security/keymaster/OperationResult.aidl
+++ b/core/java/android/security/keymaster/OperationResult.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable OperationResult;
+parcelable OperationResult cpp_header "keystore/OperationResult.h";
diff --git a/core/java/android/service/autofill/InternalSanitizer.java b/core/java/android/service/autofill/InternalSanitizer.java
index 95d2f66..d77e41e 100644
--- a/core/java/android/service/autofill/InternalSanitizer.java
+++ b/core/java/android/service/autofill/InternalSanitizer.java
@@ -16,6 +16,7 @@
package android.service.autofill;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Parcelable;
import android.view.autofill.AutofillValue;
@@ -32,7 +33,11 @@
/**
* Sanitizes an {@link AutofillValue}.
*
+ * @return sanitized value or {@code null} if value could not be sanitized (for example: didn't
+ * match regex, it's an invalid type, regex failed, etc).
+ *
* @hide
*/
+ @Nullable
public abstract AutofillValue sanitize(@NonNull AutofillValue value);
}
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 9a1dcbb..bc4b3fc 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -613,6 +613,11 @@
* usernameId, passwordId);
* </pre>
*
+ * <p>The sanitizer can also be used as an alternative for a
+ * {@link #setValidator(Validator) validator}&mdashif any of the {@code ids} is a
+ * {@link #SaveInfo.Builder(int, AutofillId[]) required id} and the {@code sanitizer} fail
+ * for it, then the save UI is not shown.
+ *
* @param sanitizer an implementation provided by the Android System.
* @param ids id of fields whose value will be sanitized.
* @return this builder.
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
index 12e85b1..a3a98d8 100644
--- a/core/java/android/service/autofill/TextValueSanitizer.java
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -19,6 +19,7 @@
import static android.view.autofill.Helper.sDebug;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -62,24 +63,31 @@
/** @hide */
@Override
@TestApi
+ @Nullable
public AutofillValue sanitize(@NonNull AutofillValue value) {
if (value == null) {
Slog.w(TAG, "sanitize() called with null value");
return null;
}
- if (!value.isText()) return value;
+ if (!value.isText()) {
+ if (sDebug) Slog.d(TAG, "sanitize() called with non-text value: " + value);
+ return null;
+ }
final CharSequence text = value.getTextValue();
try {
final Matcher matcher = mRegex.matcher(text);
- if (!matcher.matches()) return value;
+ if (!matcher.matches()) {
+ if (sDebug) Slog.d(TAG, "sanitize(): " + mRegex + " failed for " + value);
+ return null;
+ }
final CharSequence sanitized = matcher.replaceAll(mSubst);
return AutofillValue.forText(sanitized);
} catch (Exception e) {
Slog.w(TAG, "Exception evaluating " + mRegex + "/" + mSubst + ": " + e);
- return value;
+ return null;
}
}
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 080482b..6fc689a 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.Service;
@@ -200,7 +201,11 @@
return mProvider;
}
- private boolean isBound() {
+ /**
+ * @hide
+ */
+ @TestApi
+ public boolean isBound() {
if (mProvider == null) {
Log.w(TAG, "Condition provider service not yet bound.");
return false;
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 2c83fc4..8c90156 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -355,21 +355,21 @@
final Locale locale = localeFromContext(context);
final MeasureFormat measureFormat = MeasureFormat.getInstance(
locale, MeasureFormat.FormatWidth.SHORT);
- if (days >= 2) {
+ if (days >= 2 || (days > 0 && hours == 0)) {
days += (hours+12)/24;
return measureFormat.format(new Measure(days, MeasureUnit.DAY));
} else if (days > 0) {
return measureFormat.formatMeasures(
new Measure(days, MeasureUnit.DAY),
new Measure(hours, MeasureUnit.HOUR));
- } else if (hours >= 2) {
+ } else if (hours >= 2 || (hours > 0 && minutes == 0)) {
hours += (minutes+30)/60;
return measureFormat.format(new Measure(hours, MeasureUnit.HOUR));
} else if (hours > 0) {
return measureFormat.formatMeasures(
new Measure(hours, MeasureUnit.HOUR),
new Measure(minutes, MeasureUnit.MINUTE));
- } else if (minutes >= 2) {
+ } else if (minutes >= 2 || (minutes > 0 && seconds == 0)) {
minutes += (seconds+30)/60;
return measureFormat.format(new Measure(minutes, MeasureUnit.MINUTE));
} else if (minutes > 0) {
diff --git a/core/java/android/util/AndroidException.java b/core/java/android/util/AndroidException.java
index dfe00c9b..1345ddf 100644
--- a/core/java/android/util/AndroidException.java
+++ b/core/java/android/util/AndroidException.java
@@ -34,5 +34,11 @@
public AndroidException(Exception cause) {
super(cause);
}
+
+ /** @hide */
+ protected AndroidException(String message, Throwable cause, boolean enableSuppression,
+ boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
};
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index fc1d487..29baea1 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -16,7 +16,9 @@
package android.util;
+import android.content.Context;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.text.TextUtils;
import java.util.Map;
@@ -37,14 +39,25 @@
* @param feature the flag name
* @return true if the flag is enabled (either by default in system, or override by user)
*/
- public static boolean isEnabled(String feature) {
- // Tries to get feature flag from system property.
- // Step 1: check if feature flag has any override. Flag name: sys.fflag.override.<feature>
- String value = SystemProperties.get(FFLAG_OVERRIDE_PREFIX + feature);
+ public static boolean isEnabled(Context context, String feature) {
+ // Override precedence:
+ // Settings.Global -> sys.fflag.override.* -> sys.fflag.*
+
+ // Step 1: check if feature flag is set in Settings.Global.
+ String value;
+ if (context != null) {
+ value = Settings.Global.getString(context.getContentResolver(), feature);
+ if (!TextUtils.isEmpty(value)) {
+ return Boolean.parseBoolean(value);
+ }
+ }
+
+ // Step 2: check if feature flag has any override. Flag name: sys.fflag.override.<feature>
+ value = SystemProperties.get(FFLAG_OVERRIDE_PREFIX + feature);
if (!TextUtils.isEmpty(value)) {
return Boolean.parseBoolean(value);
}
- // Step 2: check if feature flag has any default value. Flag name: sys.fflag.<feature>
+ // Step 3: check if feature flag has any default value. Flag name: sys.fflag.<feature>
value = SystemProperties.get(FFLAG_PREFIX + feature);
return Boolean.parseBoolean(value);
}
@@ -52,7 +65,7 @@
/**
* Override feature flag to new state.
*/
- public static void setEnabled(String feature, boolean enabled) {
+ public static void setEnabled(Context context, String feature, boolean enabled) {
SystemProperties.set(FFLAG_OVERRIDE_PREFIX + feature, enabled ? "true" : "false");
}
diff --git a/core/java/android/util/KeyValueListParser.java b/core/java/android/util/KeyValueListParser.java
index be531ff..d50395e 100644
--- a/core/java/android/util/KeyValueListParser.java
+++ b/core/java/android/util/KeyValueListParser.java
@@ -147,4 +147,18 @@
}
return def;
}
+
+ /**
+ * @return the number of keys.
+ */
+ public int size() {
+ return mValues.size();
+ }
+
+ /**
+ * @return the key at {@code index}. Use with {@link #size()} to enumerate all key-value pairs.
+ */
+ public String keyAt(int index) {
+ return mValues.keyAt(index);
+ }
}
diff --git a/core/java/android/os/IStatsCallbacks.aidl b/core/java/android/util/MutableInt.java
similarity index 65%
rename from core/java/android/os/IStatsCallbacks.aidl
rename to core/java/android/util/MutableInt.java
index 02e7cd3..a3d8606 100644
--- a/core/java/android/os/IStatsCallbacks.aidl
+++ b/core/java/android/util/MutableInt.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * Copyright (C) 2011 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
+ * 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,
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-package android.os;
+package android.util;
/**
- * Callback for Statsd to allow binder calls to clients.
- * {@hide}
- */
-interface IStatsCallbacks {
- void onReceiveLogs(out byte[] log);
+ */
+public final class MutableInt {
+ public int value;
+
+ public MutableInt(int value) {
+ this.value = value;
+ }
}
diff --git a/core/java/android/os/IStatsCallbacks.aidl b/core/java/android/util/MutableLong.java
similarity index 65%
copy from core/java/android/os/IStatsCallbacks.aidl
copy to core/java/android/util/MutableLong.java
index 02e7cd3..575068e 100644
--- a/core/java/android/os/IStatsCallbacks.aidl
+++ b/core/java/android/util/MutableLong.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * Copyright (C) 2011 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
+ * 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,
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-package android.os;
+package android.util;
/**
- * Callback for Statsd to allow binder calls to clients.
- * {@hide}
- */
-interface IStatsCallbacks {
- void onReceiveLogs(out byte[] log);
+ */
+public final class MutableLong {
+ public long value;
+
+ public MutableLong(long value) {
+ this.value = value;
+ }
}
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
new file mode 100644
index 0000000..2bcd863
--- /dev/null
+++ b/core/java/android/util/StatsManager.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.os.IBinder;
+import android.os.IStatsManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * API for StatsD clients to send configurations and retrieve data.
+ *
+ * @hide
+ */
+@SystemApi
+public final class StatsManager {
+ IStatsManager mService;
+ private static final String TAG = "StatsManager";
+
+ /**
+ * Constructor for StatsManagerClient.
+ *
+ * @hide
+ */
+ public StatsManager() {
+ }
+
+ /**
+ * Clients can send a configuration and simultaneously registers the name of a broadcast
+ * receiver that listens for when it should request data.
+ *
+ * @param configKey An arbitrary string that allows clients to track the configuration.
+ * @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
+ * dependencies eg, conditions and matchers).
+ * @param pkg The package name to receive the broadcast.
+ * @param cls The name of the class that receives the broadcast.
+ * @return true if successful
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) {
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ Slog.d(TAG, "Failed to find statsd when adding configuration");
+ return false;
+ }
+ return service.addConfiguration(configKey, config, pkg, cls);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to connect to statsd when adding configuration");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Remove a configuration from logging.
+ *
+ * @param configKey Configuration key to remove.
+ * @return true if successful
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean removeConfiguration(String configKey) {
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ Slog.d(TAG, "Failed to find statsd when removing configuration");
+ return false;
+ }
+ return service.removeConfiguration(configKey);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to connect to statsd when removing configuration");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Clients can request data with a binder call.
+ *
+ * @param configKey Configuration key to retrieve data from.
+ * @return Serialized ConfigMetricsReport proto. Returns null on failure.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public byte[] getData(String configKey) {
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ Slog.d(TAG, "Failed to find statsd when getting data");
+ return null;
+ }
+ return service.getData(configKey);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to connecto statsd when getting data");
+ return null;
+ }
+ }
+ }
+
+ private class StatsdDeathRecipient implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ synchronized (this) {
+ mService = null;
+ }
+ }
+ }
+
+ private IStatsManager getIStatsManagerLocked() throws RemoteException {
+ if (mService != null) {
+ return mService;
+ }
+ mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
+ if (mService != null) {
+ mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
+ }
+ return mService;
+ }
+}
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
index 449baca..85b7ec8 100644
--- a/core/java/android/util/proto/ProtoUtils.java
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -17,6 +17,7 @@
package android.util.proto;
import android.util.AggStats;
+import android.util.Duration;
/**
* This class contains a list of helper functions to write common proto in
@@ -36,4 +37,15 @@
proto.write(AggStats.MAX, max);
proto.end(aggStatsToken);
}
+
+ /**
+ * Dump Duration to ProtoOutputStream
+ * @hide
+ */
+ public static void toDuration(ProtoOutputStream proto, long fieldId, long startMs, long endMs) {
+ final long token = proto.start(fieldId);
+ proto.write(Duration.START_MS, startMs);
+ proto.write(Duration.END_MS, endMs);
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/DisplayFrames.java b/core/java/android/view/DisplayFrames.java
new file mode 100644
index 0000000..e6861d8
--- /dev/null
+++ b/core/java/android/view/DisplayFrames.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS;
+
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+
+/**
+ * Container class for all the display frames that affect how we do window layout on a display.
+ * @hide
+ */
+public class DisplayFrames {
+ public final int mDisplayId;
+
+ /**
+ * The current size of the screen; really; extends into the overscan area of the screen and
+ * doesn't account for any system elements like the status bar.
+ */
+ public final Rect mOverscan = new Rect();
+
+ /**
+ * The current visible size of the screen; really; (ir)regardless of whether the status bar can
+ * be hidden but not extending into the overscan area.
+ */
+ public final Rect mUnrestricted = new Rect();
+
+ /** Like mOverscan*, but allowed to move into the overscan region where appropriate. */
+ public final Rect mRestrictedOverscan = new Rect();
+
+ /**
+ * The current size of the screen; these may be different than (0,0)-(dw,dh) if the status bar
+ * can't be hidden; in that case it effectively carves out that area of the display from all
+ * other windows.
+ */
+ public final Rect mRestricted = new Rect();
+
+ /**
+ * During layout, the current screen borders accounting for any currently visible system UI
+ * elements.
+ */
+ public final Rect mSystem = new Rect();
+
+ /** For applications requesting stable content insets, these are them. */
+ public final Rect mStable = new Rect();
+
+ /**
+ * For applications requesting stable content insets but have also set the fullscreen window
+ * flag, these are the stable dimensions without the status bar.
+ */
+ public final Rect mStableFullscreen = new Rect();
+
+ /**
+ * During layout, the current screen borders with all outer decoration (status bar, input method
+ * dock) accounted for.
+ */
+ public final Rect mCurrent = new Rect();
+
+ /**
+ * During layout, the frame in which content should be displayed to the user, accounting for all
+ * screen decoration except for any space they deem as available for other content. This is
+ * usually the same as mCurrent*, but may be larger if the screen decor has supplied content
+ * insets.
+ */
+ public final Rect mContent = new Rect();
+
+ /**
+ * During layout, the frame in which voice content should be displayed to the user, accounting
+ * for all screen decoration except for any space they deem as available for other content.
+ */
+ public final Rect mVoiceContent = new Rect();
+
+ /** During layout, the current screen borders along which input method windows are placed. */
+ public final Rect mDock = new Rect();
+
+ private final Rect mDisplayInfoOverscan = new Rect();
+ private final Rect mRotatedDisplayInfoOverscan = new Rect();
+ public int mDisplayWidth;
+ public int mDisplayHeight;
+
+ public int mRotation;
+
+ public DisplayFrames(int displayId, DisplayInfo info) {
+ mDisplayId = displayId;
+ onDisplayInfoUpdated(info);
+ }
+
+ public void onDisplayInfoUpdated(DisplayInfo info) {
+ mDisplayWidth = info.logicalWidth;
+ mDisplayHeight = info.logicalHeight;
+ mRotation = info.rotation;
+ mDisplayInfoOverscan.set(
+ info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom);
+ }
+
+ public void onBeginLayout() {
+ switch (mRotation) {
+ case ROTATION_90:
+ mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top;
+ mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.right;
+ mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.bottom;
+ mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.left;
+ break;
+ case ROTATION_180:
+ mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.right;
+ mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.bottom;
+ mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.left;
+ mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.top;
+ break;
+ case ROTATION_270:
+ mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.bottom;
+ mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.left;
+ mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.top;
+ mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.right;
+ break;
+ default:
+ mRotatedDisplayInfoOverscan.set(mDisplayInfoOverscan);
+ break;
+ }
+
+ mRestrictedOverscan.set(0, 0, mDisplayWidth, mDisplayHeight);
+ mOverscan.set(mRestrictedOverscan);
+ mSystem.set(mRestrictedOverscan);
+ mUnrestricted.set(mRotatedDisplayInfoOverscan);
+ mUnrestricted.right = mDisplayWidth - mUnrestricted.right;
+ mUnrestricted.bottom = mDisplayHeight - mUnrestricted.bottom;
+ mRestricted.set(mUnrestricted);
+ mDock.set(mUnrestricted);
+ mContent.set(mUnrestricted);
+ mVoiceContent.set(mUnrestricted);
+ mStable.set(mUnrestricted);
+ mStableFullscreen.set(mUnrestricted);
+ mCurrent.set(mUnrestricted);
+
+ }
+
+ public int getInputMethodWindowVisibleHeight() {
+ return mDock.bottom - mCurrent.bottom;
+ }
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ mStable.writeToProto(proto, STABLE_BOUNDS);
+ proto.end(token);
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight
+ + " r=" + mRotation);
+ final String myPrefix = prefix + " ";
+ dumpFrame(mStable, "mStable", myPrefix, pw);
+ dumpFrame(mStableFullscreen, "mStableFullscreen", myPrefix, pw);
+ dumpFrame(mDock, "mDock", myPrefix, pw);
+ dumpFrame(mCurrent, "mCurrent", myPrefix, pw);
+ dumpFrame(mSystem, "mSystem", myPrefix, pw);
+ dumpFrame(mContent, "mContent", myPrefix, pw);
+ dumpFrame(mVoiceContent, "mVoiceContent", myPrefix, pw);
+ dumpFrame(mOverscan, "mOverscan", myPrefix, pw);
+ dumpFrame(mRestrictedOverscan, "mRestrictedOverscan", myPrefix, pw);
+ dumpFrame(mRestricted, "mRestricted", myPrefix, pw);
+ dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw);
+ dumpFrame(mDisplayInfoOverscan, "mDisplayInfoOverscan", myPrefix, pw);
+ dumpFrame(mRotatedDisplayInfoOverscan, "mRotatedDisplayInfoOverscan", myPrefix, pw);
+ }
+
+ private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) {
+ pw.print(prefix + name + "="); frame.printShortString(pw); pw.println();
+ }
+}
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 74555de..713cfb4 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -530,7 +530,7 @@
* axis distances. Warning: this fudge factor is finely tuned, be sure to
* run all focus tests if you dare tweak it.
*/
- int getWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance) {
+ long getWeightedDistanceFor(long majorAxisDistance, long minorAxisDistance) {
return 13 * majorAxisDistance * majorAxisDistance
+ minorAxisDistance * minorAxisDistance;
}
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index 232ff25..defa58e 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -446,49 +446,52 @@
*/
public static String toString(int gravity) {
final StringBuilder result = new StringBuilder();
- if ((gravity & FILL) != 0) {
+ if ((gravity & FILL) == FILL) {
result.append("FILL").append(' ');
} else {
- if ((gravity & FILL_VERTICAL) != 0) {
+ if ((gravity & FILL_VERTICAL) == FILL_VERTICAL) {
result.append("FILL_VERTICAL").append(' ');
} else {
- if ((gravity & TOP) != 0) {
+ if ((gravity & TOP) == TOP) {
result.append("TOP").append(' ');
}
- if ((gravity & BOTTOM) != 0) {
+ if ((gravity & BOTTOM) == BOTTOM) {
result.append("BOTTOM").append(' ');
}
}
- if ((gravity & FILL_HORIZONTAL) != 0) {
+ if ((gravity & FILL_HORIZONTAL) == FILL_HORIZONTAL) {
result.append("FILL_HORIZONTAL").append(' ');
} else {
- if ((gravity & START) != 0) {
+ if ((gravity & START) == START) {
result.append("START").append(' ');
- } else if ((gravity & LEFT) != 0) {
+ } else if ((gravity & LEFT) == LEFT) {
result.append("LEFT").append(' ');
}
- if ((gravity & END) != 0) {
+ if ((gravity & END) == END) {
result.append("END").append(' ');
- } else if ((gravity & RIGHT) != 0) {
+ } else if ((gravity & RIGHT) == RIGHT) {
result.append("RIGHT").append(' ');
}
}
}
- if ((gravity & CENTER) != 0) {
+ if ((gravity & CENTER) == CENTER) {
result.append("CENTER").append(' ');
} else {
- if ((gravity & CENTER_VERTICAL) != 0) {
+ if ((gravity & CENTER_VERTICAL) == CENTER_VERTICAL) {
result.append("CENTER_VERTICAL").append(' ');
}
- if ((gravity & CENTER_HORIZONTAL) != 0) {
+ if ((gravity & CENTER_HORIZONTAL) == CENTER_HORIZONTAL) {
result.append("CENTER_HORIZONTAL").append(' ');
}
}
- if ((gravity & DISPLAY_CLIP_VERTICAL) != 0) {
+ if (result.length() == 0) {
+ result.append("NO GRAVITY").append(' ');
+ }
+ if ((gravity & DISPLAY_CLIP_VERTICAL) == DISPLAY_CLIP_VERTICAL) {
result.append("DISPLAY_CLIP_VERTICAL").append(' ');
}
- if ((gravity & DISPLAY_CLIP_VERTICAL) != 0) {
- result.append("DISPLAY_CLIP_VERTICAL").append(' ');
+ if ((gravity & DISPLAY_CLIP_HORIZONTAL) == DISPLAY_CLIP_HORIZONTAL) {
+ result.append("DISPLAY_CLIP_HORIZONTAL").append(' ');
}
result.deleteCharAt(result.length() - 1);
return result.toString();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 65bde49..29032a7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -16,13 +16,13 @@
package android.view;
-import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.os.IResultReceiver;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
+import android.app.IAssistDataReceiver;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index ea6e63c..5070151 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -353,6 +353,11 @@
return nHasShadow(mNativeRenderNode);
}
+ /** setShadowColor */
+ public boolean setShadowColor(int color) {
+ return nSetShadowColor(mNativeRenderNode, color);
+ }
+
/**
* Enables or disables clipping to the outline.
*
@@ -910,6 +915,8 @@
@CriticalNative
private static native boolean nHasShadow(long renderNode);
@CriticalNative
+ private static native boolean nSetShadowColor(long renderNode, int color);
+ @CriticalNative
private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
@CriticalNative
private static native boolean nSetRevealClip(long renderNode,
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 9515040..c4a7160 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -19,7 +19,6 @@
import android.animation.Animator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-import android.graphics.Canvas;
import android.graphics.CanvasProperty;
import android.graphics.Paint;
import android.util.SparseIntArray;
@@ -281,12 +280,9 @@
setTarget(mViewTarget.mRenderNode);
}
- public void setTarget(Canvas canvas) {
- if (!(canvas instanceof DisplayListCanvas)) {
- throw new IllegalArgumentException("Not a GLES20RecordingCanvas");
- }
- final DisplayListCanvas recordingCanvas = (DisplayListCanvas) canvas;
- setTarget(recordingCanvas.mNode);
+ /** Sets the animation target to the owning view of the DisplayListCanvas */
+ public void setTarget(DisplayListCanvas canvas) {
+ setTarget(canvas.mNode);
}
private void setTarget(RenderNode node) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5641009..aa2f1c1 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -61,6 +61,8 @@
private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
private static native void nativeApplyTransaction(long transactionObj, boolean sync);
+ private static native void nativeMergeTransaction(long transactionObj,
+ long otherTransactionObj);
private static native void nativeSetAnimationTransaction(long transactionObj);
private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
@@ -654,6 +656,19 @@
}
}
+ /**
+ * Merge the supplied transaction in to the deprecated "global" transaction.
+ * This clears the supplied transaction in an identical fashion to {@link Transaction#merge}.
+ * <p>
+ * This is a utility for interop with legacy-code and will go away with the Global Transaction.
+ */
+ @Deprecated
+ public static void mergeToGlobalTransaction(Transaction t) {
+ synchronized(sGlobalTransaction) {
+ sGlobalTransaction.merge(t);
+ }
+ }
+
/** end a transaction */
public static void closeTransaction() {
closeTransaction(false);
@@ -1368,7 +1383,7 @@
* Sets the security of the surface. Setting the flag is equivalent to creating the
* Surface with the {@link #SECURE} flag.
*/
- Transaction setSecure(SurfaceControl sc, boolean isSecure) {
+ public Transaction setSecure(SurfaceControl sc, boolean isSecure) {
sc.checkNotReleased();
if (isSecure) {
nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE);
@@ -1449,5 +1464,14 @@
nativeSetAnimationTransaction(mNativeObject);
return this;
}
+
+ /**
+ * Merge the other transaction into this transaction, clearing the
+ * other transaction as if it had been applied.
+ */
+ public Transaction merge(Transaction other) {
+ nativeMergeTransaction(mNativeObject, other.mNativeObject);
+ return this;
+ }
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e12c0b0..19ce43f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4163,6 +4163,11 @@
*/
private static boolean sUseDefaultFocusHighlight;
+ /**
+ * True if zero-sized views can be focused.
+ */
+ private static boolean sCanFocusZeroSized;
+
private String mTransitionName;
static class TintInfo {
@@ -4245,6 +4250,8 @@
OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
OnCapturedPointerListener mOnCapturedPointerListener;
+
+ private ArrayList<OnKeyFallbackListener> mKeyFallbackListeners;
}
ListenerInfo mListenerInfo;
@@ -4743,6 +4750,8 @@
sUseDefaultFocusHighlight = context.getResources().getBoolean(
com.android.internal.R.bool.config_useDefaultFocusHighlight);
+ sCanFocusZeroSized = targetSdkVersion < Build.VERSION_CODES.P;
+
sCompatibilityDone = true;
}
}
@@ -6950,6 +6959,7 @@
void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
mPrivateFlags &= ~PFLAG_FOCUSED;
+ clearParentsWantFocus();
if (propagate && mParent != null) {
mParent.clearChildFocus(this);
@@ -10931,8 +10941,9 @@
* descendants.
*
* A view will not actually take focus if it is not focusable ({@link #isFocusable} returns
- * false), or if it is focusable and it is not focusable in touch mode
- * ({@link #isFocusableInTouchMode}) while the device is in touch mode.
+ * false), or if it can't be focused due to other conditions (not focusable in touch mode
+ * ({@link #isFocusableInTouchMode}) while the device is in touch mode, not visible, not
+ * enabled, or has no size).
*
* See also {@link #focusSearch(int)}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
@@ -10970,17 +10981,17 @@
*/
@TestApi
public boolean restoreFocusNotInCluster() {
- return requestFocus(View.FOCUS_DOWN);
+ return requestFocus();
}
/**
* Gives focus to the default-focus view in the view hierarchy that has this view as a root.
- * If the default-focus view cannot be found, falls back to calling {@link #requestFocus(int)}.
+ * If the default-focus view cannot be found, falls back to calling {@link #requestFocus()}.
*
* @return Whether this view or one of its descendants actually took focus
*/
public boolean restoreDefaultFocus() {
- return requestFocus(View.FOCUS_DOWN);
+ return requestFocus();
}
/**
@@ -11039,9 +11050,7 @@
private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
// need to be focusable
- if ((mViewFlags & FOCUSABLE) != FOCUSABLE
- || (mViewFlags & VISIBILITY_MASK) != VISIBLE
- || (mViewFlags & ENABLED_MASK) != ENABLED) {
+ if (!canTakeFocus()) {
return false;
}
@@ -11056,10 +11065,21 @@
return false;
}
+ if (!isLaidOut()) {
+ mPrivateFlags |= PFLAG_WANTS_FOCUS;
+ }
+
handleFocusGainInternal(direction, previouslyFocusedRect);
return true;
}
+ void clearParentsWantFocus() {
+ if (mParent instanceof View) {
+ ((View) mParent).mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
+ ((View) mParent).clearParentsWantFocus();
+ }
+ }
+
/**
* Call this to try to give focus to a specific view or to one of its descendants. This is a
* special variant of {@link #requestFocus() } that will allow views that are not focusable in
@@ -13431,6 +13451,13 @@
mAttachInfo.mUnbufferedDispatchRequested = true;
}
+ private boolean canTakeFocus() {
+ return ((mViewFlags & VISIBILITY_MASK) == VISIBLE)
+ && ((mViewFlags & FOCUSABLE) == FOCUSABLE)
+ && ((mViewFlags & ENABLED_MASK) == ENABLED)
+ && (sCanFocusZeroSized || !isLaidOut() || (mBottom > mTop) && (mRight > mLeft));
+ }
+
/**
* Set flags controlling behavior of this view.
*
@@ -13450,6 +13477,7 @@
return;
}
int privateFlags = mPrivateFlags;
+ boolean shouldNotifyFocusableAvailable = false;
// If focusable is auto, update the FOCUSABLE bit.
int focusableChangedByAuto = 0;
@@ -13488,7 +13516,7 @@
|| focusableChangedByAuto == 0
|| viewRootImpl == null
|| viewRootImpl.mThread == Thread.currentThread()) {
- mParent.focusableViewAvailable(this);
+ shouldNotifyFocusableAvailable = true;
}
}
}
@@ -13511,10 +13539,7 @@
// about in case nothing has focus. even if this specific view
// isn't focusable, it may contain something that is, so let
// the root view try to give this focus if nothing else does.
- if ((mParent != null) && ((mViewFlags & ENABLED_MASK) == ENABLED)
- && (mBottom > mTop) && (mRight > mLeft)) {
- mParent.focusableViewAvailable(this);
- }
+ shouldNotifyFocusableAvailable = true;
}
}
@@ -13523,17 +13548,18 @@
// a view becoming enabled should notify the parent as long as the view is also
// visible and the parent wasn't already notified by becoming visible during this
// setFlags invocation.
- if ((mViewFlags & VISIBILITY_MASK) == VISIBLE
- && ((changed & VISIBILITY_MASK) == 0)) {
- if ((mParent != null) && (mViewFlags & ENABLED_MASK) == ENABLED) {
- mParent.focusableViewAvailable(this);
- }
- }
+ shouldNotifyFocusableAvailable = true;
} else {
if (hasFocus()) clearFocus();
}
}
+ if (shouldNotifyFocusableAvailable) {
+ if (mParent != null && canTakeFocus()) {
+ mParent.focusableViewAvailable(this);
+ }
+ }
+
/* Check if the GONE bit has changed */
if ((changed & GONE) != 0) {
needGlobalAttributesUpdate(false);
@@ -15182,6 +15208,15 @@
return mRenderNode.hasShadow();
}
+ /**
+ * @hide
+ */
+ public void setShadowColor(@ColorInt int color) {
+ if (mRenderNode.setShadowColor(color)) {
+ invalidateViewProperty(true, true);
+ }
+ }
+
/** @hide */
public void setRevealClip(boolean shouldClip, float x, float y, float radius) {
@@ -20023,15 +20058,58 @@
}
}
+ final boolean wasLaidOut = isLaidOut();
+
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
+ if (!wasLaidOut && isFocused()) {
+ mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
+ if (canTakeFocus()) {
+ // We have a robust focus, so parents should no longer be wanting focus.
+ clearParentsWantFocus();
+ } else if (!getViewRootImpl().isInLayout()) {
+ // This is a weird case. Most-likely the user, rather than ViewRootImpl, called
+ // layout. In this case, there's no guarantee that parent layouts will be evaluated
+ // and thus the safest action is to clear focus here.
+ clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
+ clearParentsWantFocus();
+ } else if (!hasParentWantsFocus()) {
+ // original requestFocus was likely on this view directly, so just clear focus
+ clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
+ }
+ // otherwise, we let parents handle re-assigning focus during their layout passes.
+ } else if ((mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) {
+ mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
+ View focused = findFocus();
+ if (focused != null) {
+ // Try to restore focus as close as possible to our starting focus.
+ if (!restoreDefaultFocus() && !hasParentWantsFocus()) {
+ // Give up and clear focus once we've reached the top-most parent which wants
+ // focus.
+ focused.clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
+ }
+ }
+ }
+
if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
notifyEnterOrExitForAutoFillIfNeeded(true);
}
}
+ private boolean hasParentWantsFocus() {
+ ViewParent parent = mParent;
+ while (parent instanceof ViewGroup) {
+ ViewGroup pv = (ViewGroup) parent;
+ if ((pv.mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) {
+ return true;
+ }
+ parent = pv.mParent;
+ }
+ return false;
+ }
+
/**
* Called from layout when this view should
* assign a size and position to each of its children.
@@ -20138,6 +20216,23 @@
mOverlay.getOverlayView().setRight(newWidth);
mOverlay.getOverlayView().setBottom(newHeight);
}
+ // If this isn't laid out yet, focus assignment will be handled during the "deferment/
+ // backtracking" of requestFocus during layout, so don't touch focus here.
+ if (!sCanFocusZeroSized && isLaidOut()) {
+ if (newWidth <= 0 || newHeight <= 0) {
+ if (hasFocus()) {
+ clearFocus();
+ if (mParent instanceof ViewGroup) {
+ ((ViewGroup) mParent).clearFocusedInCluster();
+ }
+ }
+ clearAccessibilityFocus();
+ } else if (oldWidth <= 0 || oldHeight <= 0) {
+ if (mParent != null && canTakeFocus()) {
+ mParent.focusableViewAvailable(this);
+ }
+ }
+ }
rebuildOutline();
}
@@ -25185,6 +25280,29 @@
}
/**
+ * Interface definition for a callback to be invoked when a hardware key event is
+ * dispatched to this view during the fallback phase. This means no view in the hierarchy
+ * has handled this event.
+ */
+ public interface OnKeyFallbackListener {
+ /**
+ * Called when a hardware key is dispatched to a view in the fallback phase. This allows
+ * listeners to respond to events after the view hierarchy has had a chance to respond.
+ * <p>Key presses in software keyboards will generally NOT trigger this method,
+ * although some may elect to do so in some situations. Do not assume a
+ * software input method has to be key-based; even if it is, it may use key presses
+ * in a different way than you expect, so there is no way to reliably catch soft
+ * input key presses.
+ *
+ * @param v The view the key has been dispatched to.
+ * @param event The KeyEvent object containing full information about
+ * the event.
+ * @return True if the listener has consumed the event, false otherwise.
+ */
+ boolean onKeyFallback(View v, KeyEvent event);
+ }
+
+ /**
* Interface definition for a callback to be invoked when a touch event is
* dispatched to this view. The callback will be invoked before the touch
* event is given to the view.
@@ -26857,4 +26975,56 @@
}
return mTooltipInfo.mTooltipPopup.getContentView();
}
+
+ /**
+ * Allows this view to handle {@link KeyEvent}s which weren't handled by normal dispatch. This
+ * occurs after the normal view hierarchy dispatch, but before the window callback. By default,
+ * this will dispatch into all the listeners registered via
+ * {@link #addKeyFallbackListener(OnKeyFallbackListener)} in last-in-first-out order (most
+ * recently added will receive events first).
+ *
+ * @param event A not-previously-handled event.
+ * @return {@code true} if the event was handled, {@code false} otherwise.
+ * @see #addKeyFallbackListener
+ */
+ public boolean onKeyFallback(@NonNull KeyEvent event) {
+ if (mListenerInfo != null && mListenerInfo.mKeyFallbackListeners != null) {
+ for (int i = mListenerInfo.mKeyFallbackListeners.size() - 1; i >= 0; --i) {
+ if (mListenerInfo.mKeyFallbackListeners.get(i).onKeyFallback(this, event)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adds a listener which will receive unhandled {@link KeyEvent}s.
+ * @param listener the receiver of fallback {@link KeyEvent}s.
+ * @see #onKeyFallback(KeyEvent)
+ */
+ public void addKeyFallbackListener(OnKeyFallbackListener listener) {
+ ArrayList<OnKeyFallbackListener> fallbacks = getListenerInfo().mKeyFallbackListeners;
+ if (fallbacks == null) {
+ fallbacks = new ArrayList<>();
+ getListenerInfo().mKeyFallbackListeners = fallbacks;
+ }
+ fallbacks.add(listener);
+ }
+
+ /**
+ * Removes a listener which will receive unhandled {@link KeyEvent}s.
+ * @param listener the receiver of fallback {@link KeyEvent}s.
+ * @see #onKeyFallback(KeyEvent)
+ */
+ public void removeKeyFallbackListener(OnKeyFallbackListener listener) {
+ if (mListenerInfo != null) {
+ if (mListenerInfo.mKeyFallbackListeners != null) {
+ mListenerInfo.mKeyFallbackListeners.remove(listener);
+ if (mListenerInfo.mKeyFallbackListeners.isEmpty()) {
+ mListenerInfo.mKeyFallbackListeners = null;
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 929beae..d0fcc5d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3215,22 +3215,31 @@
}
int descendantFocusability = getDescendantFocusability();
+ boolean result;
switch (descendantFocusability) {
case FOCUS_BLOCK_DESCENDANTS:
- return super.requestFocus(direction, previouslyFocusedRect);
+ result = super.requestFocus(direction, previouslyFocusedRect);
+ break;
case FOCUS_BEFORE_DESCENDANTS: {
final boolean took = super.requestFocus(direction, previouslyFocusedRect);
- return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
+ result = took ? took : onRequestFocusInDescendants(direction,
+ previouslyFocusedRect);
+ break;
}
case FOCUS_AFTER_DESCENDANTS: {
final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
- return took ? took : super.requestFocus(direction, previouslyFocusedRect);
+ result = took ? took : super.requestFocus(direction, previouslyFocusedRect);
+ break;
}
default:
throw new IllegalStateException("descendant focusability must be "
+ "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
+ "but is " + descendantFocusability);
}
+ if (result && !isLaidOut() && ((mPrivateFlags & PFLAG_WANTS_FOCUS) == 0)) {
+ mPrivateFlags |= PFLAG_WANTS_FOCUS;
+ }
+ return result;
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 37829f0..e9509b7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -73,6 +73,7 @@
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.TypedValue;
import android.view.Surface.OutOfResourcesException;
@@ -362,6 +363,8 @@
InputStage mFirstPostImeInputStage;
InputStage mSyntheticInputStage;
+ private final KeyFallbackManager mKeyFallbackManager = new KeyFallbackManager();
+
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -513,7 +516,7 @@
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
if (!sCompatibilityDone) {
- sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
+ sAlwaysAssignFocus = true;
sCompatibilityDone = true;
}
@@ -4764,6 +4767,13 @@
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
+ mKeyFallbackManager.mDispatched = false;
+
+ if (mKeyFallbackManager.hasFocus()
+ && mKeyFallbackManager.dispatchUnique(mView, event)) {
+ return FINISH_HANDLED;
+ }
+
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
@@ -4773,6 +4783,10 @@
return FINISH_NOT_HANDLED;
}
+ if (mKeyFallbackManager.dispatchUnique(mView, event)) {
+ return FINISH_HANDLED;
+ }
+
int groupNavigationDirection = 0;
if (event.getAction() == KeyEvent.ACTION_DOWN
@@ -7529,6 +7543,16 @@
}
}
+ /**
+ * Dispatches a KeyEvent to all registered key fallback handlers.
+ *
+ * @param event
+ * @return {@code true} if the event was handled, {@code false} otherwise.
+ */
+ public boolean dispatchKeyFallbackEvent(KeyEvent event) {
+ return mKeyFallbackManager.dispatch(mView, event);
+ }
+
class TakenSurfaceHolder extends BaseSurfaceHolder {
@Override
public boolean onAllowLockCanvas() {
@@ -8093,4 +8117,92 @@
run();
}
}
+
+ private static class KeyFallbackManager {
+
+ // This is used to ensure that key-fallback events are only dispatched once. We attempt
+ // to dispatch more than once in order to achieve a certain order. Specifically, if we
+ // are in an Activity or Dialog (and have a Window.Callback), the keyfallback events should
+ // be dispatched after the view hierarchy, but before the Activity. However, if we aren't
+ // in an activity, we still want key fallbacks to be dispatched.
+ boolean mDispatched = false;
+
+ SparseBooleanArray mCapturedKeys = new SparseBooleanArray();
+ WeakReference<View> mFallbackReceiver = null;
+ int mVisitCount = 0;
+
+ private void updateCaptureState(KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ mCapturedKeys.append(event.getKeyCode(), true);
+ }
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ mCapturedKeys.delete(event.getKeyCode());
+ }
+ }
+
+ boolean dispatch(View root, KeyEvent event) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "KeyFallback dispatch");
+ mDispatched = true;
+
+ updateCaptureState(event);
+
+ if (mFallbackReceiver != null) {
+ View target = mFallbackReceiver.get();
+ if (mCapturedKeys.size() == 0) {
+ mFallbackReceiver = null;
+ }
+ if (target != null && target.isAttachedToWindow()) {
+ return target.onKeyFallback(event);
+ }
+ // consume anyways so that we don't feed uncaptured key events to other views
+ return true;
+ }
+
+ boolean result = dispatchInZOrder(root, event);
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ return result;
+ }
+
+ private boolean dispatchInZOrder(View view, KeyEvent evt) {
+ if (view instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) view;
+ ArrayList<View> orderedViews = vg.buildOrderedChildList();
+ if (orderedViews != null) {
+ try {
+ for (int i = orderedViews.size() - 1; i >= 0; --i) {
+ View v = orderedViews.get(i);
+ if (dispatchInZOrder(v, evt)) {
+ return true;
+ }
+ }
+ } finally {
+ orderedViews.clear();
+ }
+ } else {
+ for (int i = vg.getChildCount() - 1; i >= 0; --i) {
+ View v = vg.getChildAt(i);
+ if (dispatchInZOrder(v, evt)) {
+ return true;
+ }
+ }
+ }
+ }
+ if (view.onKeyFallback(evt)) {
+ mFallbackReceiver = new WeakReference<>(view);
+ return true;
+ }
+ return false;
+ }
+
+ boolean hasFocus() {
+ return mFallbackReceiver != null;
+ }
+
+ boolean dispatchUnique(View root, KeyEvent event) {
+ if (mDispatched) {
+ return false;
+ }
+ return dispatch(root, event);
+ }
+ }
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index c7e8dee..cca66d6 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -605,9 +605,10 @@
public void setStoppedState(IBinder token, boolean stopped) {
synchronized (mLock) {
int count = mViews.size();
- for (int i = 0; i < count; i++) {
+ for (int i = count - 1; i >= 0; i--) {
if (token == null || mParams.get(i).token == token) {
ViewRootImpl root = mRoots.get(i);
+ // Client might remove the view by "stopped" event.
root.setWindowStopped(stopped);
}
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index ebe3633..c192f5c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -66,7 +66,6 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.app.ActivityManager.StackId;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
@@ -722,12 +721,6 @@
public void setInitialDisplaySize(Display display, int width, int height, int density);
/**
- * Called by window manager to set the overscan region that should be used for the
- * given display.
- */
- public void setDisplayOverscan(Display display, int left, int top, int right, int bottom);
-
- /**
* Check permissions when adding a window.
*
* @param attrs The window's LayoutParams.
@@ -1173,14 +1166,10 @@
/**
* Called when layout of the windows is about to start.
*
- * @param displayId Id of the display we are doing layout on.
- * @param displayWidth The current full width of the screen.
- * @param displayHeight The current full height of the screen.
- * @param displayRotation The current rotation being applied to the base window.
+ * @param displayFrames frames of the display we are doing layout on.
* @param uiMode The current uiMode in configuration.
*/
- public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
- int displayRotation, int uiMode);
+ default void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {}
/**
* Returns the bottom-most layer of the system decor, above which no policy decor should
@@ -1189,37 +1178,28 @@
public int getSystemDecorLayerLw();
/**
- * Return the rectangle of the screen that is available for applications to run in.
- * This will be called immediately after {@link #beginLayoutLw}.
- *
- * @param r The rectangle to be filled with the boundaries available to applications.
- */
- public void getContentRectLw(Rect r);
-
- /**
- * Called for each window attached to the window manager as layout is
- * proceeding. The implementation of this function must take care of
- * setting the window's frame, either here or in finishLayout().
+ * Called for each window attached to the window manager as layout is proceeding. The
+ * implementation of this function must take care of setting the window's frame, either here or
+ * in finishLayout().
*
* @param win The window being positioned.
* @param attached For sub-windows, the window it is attached to; this
* window will already have had layoutWindow() called on it
* so you can use its Rect. Otherwise null.
+ * @param displayFrames The display frames.
*/
- public void layoutWindowLw(WindowState win, WindowState attached);
+ default void layoutWindowLw(
+ WindowState win, WindowState attached, DisplayFrames displayFrames) {}
/**
- * Return the insets for the areas covered by system windows. These values
- * are computed on the most recent layout, so they are not guaranteed to
- * be correct.
+ * Return the insets for the areas covered by system windows. These values are computed on the
+ * most recent layout, so they are not guaranteed to be correct.
*
* @param attrs The LayoutParams of the window.
* @param taskBounds The bounds of the task this window is on or {@code null} if no task is
* associated with the window.
- * @param displayRotation Rotation of the display.
- * @param displayWidth The width of the display.
- * @param displayHeight The height of the display.
+ * @param displayFrames display frames.
* @param outContentInsets The areas covered by system windows, expressed as positive insets.
* @param outStableInsets The areas covered by stable system windows irrespective of their
* current visibility. Expressed as positive insets.
@@ -1227,16 +1207,11 @@
* @return Whether to always consume the navigation bar.
* See {@link #isNavBarForcedShownLw(WindowState)}.
*/
- public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
- int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets,
- Rect outStableInsets, Rect outOutsets);
-
- /**
- * Called when layout of the windows is finished. After this function has
- * returned, all windows given to layoutWindow() <em>must</em> have had a
- * frame assigned.
- */
- public void finishLayoutLw();
+ default boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
+ DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
+ Rect outOutsets) {
+ return false;
+ }
/** Layout state may have changed (so another layout will be performed) */
static final int FINISH_LAYOUT_REDO_LAYOUT = 0x0001;
@@ -1653,11 +1628,6 @@
public void showGlobalActions();
/**
- * @return The current height of the input method window.
- */
- public int getInputMethodWindowVisibleHeightLw();
-
- /**
* Called when the current user changes. Guaranteed to be called before the broadcast
* of the new user id is made to all listeners.
*
@@ -1693,14 +1663,6 @@
void writeToProto(ProtoOutputStream proto, long fieldId);
/**
- * Returns whether a given window type can be magnified.
- *
- * @param windowType The window type.
- * @return True if the window can be magnified.
- */
- public boolean canMagnifyWindow(int windowType);
-
- /**
* Returns whether a given window type is considered a top level one.
* A top level window does not have a container, i.e. attached window,
* or if it has a container it is laid out as a top-level window, not
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index a56f86d..9241ec0 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -54,6 +54,9 @@
import java.util.List;
import java.util.Objects;
+// TODO: use java.lang.ref.Cleaner once Android supports Java 9
+import sun.misc.Cleaner;
+
/**
* The {@link AutofillManager} provides ways for apps and custom views to integrate with the
* Autofill Framework lifecycle.
@@ -303,6 +306,9 @@
private IAutoFillManagerClient mServiceClient;
@GuardedBy("mLock")
+ private Cleaner mServiceClientCleaner;
+
+ @GuardedBy("mLock")
private AutofillCallback mCallback;
private final Context mContext;
@@ -1172,10 +1178,19 @@
if (mServiceClient == null) {
mServiceClient = new AutofillManagerClient(this);
try {
- final int flags = mService.addClient(mServiceClient, mContext.getUserId());
+ final int userId = mContext.getUserId();
+ final int flags = mService.addClient(mServiceClient, userId);
mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
+ final IAutoFillManager service = mService;
+ final IAutoFillManagerClient serviceClient = mServiceClient;
+ mServiceClientCleaner = Cleaner.create(this, () -> {
+ try {
+ service.removeClient(serviceClient, userId);
+ } catch (RemoteException e) {
+ }
+ });
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1294,6 +1309,10 @@
if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
// Reset connection to system
mServiceClient = null;
+ if (mServiceClientCleaner != null) {
+ mServiceClientCleaner.clean();
+ mServiceClientCleaner = null;
+ }
}
}
sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 9329c4d..d6db3fe 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -33,6 +33,7 @@
interface IAutoFillManager {
// Returns flags: FLAG_ADD_CLIENT_ENABLED | FLAG_ADD_CLIENT_DEBUG | FLAG_ADD_CLIENT_VERBOSE
int addClient(in IAutoFillManagerClient client, int userId);
+ void removeClient(in IAutoFillManagerClient client, int userId);
int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
in ComponentName componentName);
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 107013a..2ad6e02 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -235,7 +235,9 @@
if (mSmartSelection == null || !Objects.equals(mLocale, locale)) {
destroySmartSelectionIfExistsLocked();
final ParcelFileDescriptor fd = getFdLocked(locale);
- mSmartSelection = new SmartSelection(fd.getFd());
+ final int modelFd = fd.getFd();
+ mVersion = SmartSelection.getVersion(modelFd);
+ mSmartSelection = new SmartSelection(modelFd);
closeAndLogError(fd);
mLocale = locale;
}
@@ -256,18 +258,26 @@
@GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
private ParcelFileDescriptor getFdLocked(Locale locale) throws FileNotFoundException {
ParcelFileDescriptor updateFd;
+ int updateVersion = -1;
try {
updateFd = ParcelFileDescriptor.open(
new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY);
+ if (updateFd != null) {
+ updateVersion = SmartSelection.getVersion(updateFd.getFd());
+ }
} catch (FileNotFoundException e) {
updateFd = null;
}
ParcelFileDescriptor factoryFd;
+ int factoryVersion = -1;
try {
final String factoryModelFilePath = getFactoryModelFilePathsLocked().get(locale);
if (factoryModelFilePath != null) {
factoryFd = ParcelFileDescriptor.open(
new File(factoryModelFilePath), ParcelFileDescriptor.MODE_READ_ONLY);
+ if (factoryFd != null) {
+ factoryVersion = SmartSelection.getVersion(factoryFd.getFd());
+ }
} else {
factoryFd = null;
}
@@ -303,15 +313,11 @@
return factoryFd;
}
- final int updateVersion = SmartSelection.getVersion(updateFdInt);
- final int factoryVersion = SmartSelection.getVersion(factoryFd.getFd());
if (updateVersion > factoryVersion) {
closeAndLogError(factoryFd);
- mVersion = updateVersion;
return updateFd;
} else {
closeAndLogError(updateFd);
- mVersion = factoryVersion;
return factoryFd;
}
}
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
index 83af19b..2833564 100644
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -48,31 +48,45 @@
private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
- private static final int VERSION_TAG = MetricsEvent.FIELD_SELECTION_VERSION_TAG;
- private static final int SMART_INDICES = MetricsEvent.FIELD_SELECTION_SMART_RANGE;
- private static final int EVENT_INDICES = MetricsEvent.FIELD_SELECTION_RANGE;
+ private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
+ private static final int WIDGET_VERSION = MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
+ private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
+ private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
+ private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
+ private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
+ private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
+ private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
private static final String ZERO = "0";
private static final String TEXTVIEW = "textview";
private static final String EDITTEXT = "edittext";
+ private static final String UNSELECTABLE_TEXTVIEW = "nosel-textview";
private static final String WEBVIEW = "webview";
private static final String EDIT_WEBVIEW = "edit-webview";
+ private static final String CUSTOM_TEXTVIEW = "customview";
+ private static final String CUSTOM_EDITTEXT = "customedit";
+ private static final String CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
private static final String UNKNOWN = "unknown";
@Retention(RetentionPolicy.SOURCE)
@IntDef({WidgetType.UNSPECIFIED, WidgetType.TEXTVIEW, WidgetType.WEBVIEW,
WidgetType.EDITTEXT, WidgetType.EDIT_WEBVIEW})
public @interface WidgetType {
- int UNSPECIFIED = 0;
- int TEXTVIEW = 1;
- int WEBVIEW = 2;
- int EDITTEXT = 3;
- int EDIT_WEBVIEW = 4;
+ int UNSPECIFIED = 0;
+ int TEXTVIEW = 1;
+ int WEBVIEW = 2;
+ int EDITTEXT = 3;
+ int EDIT_WEBVIEW = 4;
+ int UNSELECTABLE_TEXTVIEW = 5;
+ int CUSTOM_TEXTVIEW = 6;
+ int CUSTOM_EDITTEXT = 7;
+ int CUSTOM_UNSELECTABLE_TEXTVIEW = 8;
}
private final MetricsLogger mMetricsLogger = new MetricsLogger();
private final int mWidgetType;
+ @Nullable private final String mWidgetVersion;
private final Context mContext;
@Nullable private String mSessionId;
@@ -83,10 +97,18 @@
private long mSessionStartTime;
private long mLastEventTime;
private boolean mSmartSelectionTriggered;
- private String mVersionTag;
+ private String mModelName;
public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) {
mWidgetType = widgetType;
+ mWidgetVersion = null;
+ mContext = Preconditions.checkNotNull(context);
+ }
+
+ public SmartSelectionEventTracker(
+ @NonNull Context context, @WidgetType int widgetType, @Nullable String widgetVersion) {
+ mWidgetType = widgetType;
+ mWidgetVersion = widgetVersion;
mContext = Preconditions.checkNotNull(context);
}
@@ -115,7 +137,7 @@
case SelectionEvent.EventType.SMART_SELECTION_SINGLE: // fall through
case SelectionEvent.EventType.SMART_SELECTION_MULTI:
mSmartSelectionTriggered = true;
- mVersionTag = getVersionTag(event);
+ mModelName = getModelName(event);
mSmartIndices[0] = event.mStart;
mSmartIndices[1] = event.mEnd;
break;
@@ -137,14 +159,19 @@
final long prevEventDelta = mLastEventTime == 0 ? 0 : now - mLastEventTime;
final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
.setType(getLogType(event))
- .setSubtype(getLogSubType(event))
+ .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL)
.setPackageName(mContext.getPackageName())
.addTaggedData(START_EVENT_DELTA, now - mSessionStartTime)
.addTaggedData(PREV_EVENT_DELTA, prevEventDelta)
.addTaggedData(INDEX, mIndex)
- .addTaggedData(VERSION_TAG, mVersionTag)
- .addTaggedData(SMART_INDICES, getSmartDelta())
- .addTaggedData(EVENT_INDICES, getEventDelta(event))
+ .addTaggedData(WIDGET_TYPE, getWidgetTypeName())
+ .addTaggedData(WIDGET_VERSION, mWidgetVersion)
+ .addTaggedData(MODEL_NAME, mModelName)
+ .addTaggedData(ENTITY_TYPE, event.mEntityType)
+ .addTaggedData(SMART_START, getSmartRangeDelta(mSmartIndices[0]))
+ .addTaggedData(SMART_END, getSmartRangeDelta(mSmartIndices[1]))
+ .addTaggedData(EVENT_START, getRangeDelta(event.mStart))
+ .addTaggedData(EVENT_END, getRangeDelta(event.mEnd))
.addTaggedData(SESSION_ID, mSessionId);
mMetricsLogger.write(log);
debugLog(log);
@@ -169,7 +196,7 @@
mSessionStartTime = 0;
mLastEventTime = 0;
mSmartSelectionTriggered = false;
- mVersionTag = getVersionTag(null);
+ mModelName = getModelName(null);
mSessionId = null;
}
@@ -251,113 +278,75 @@
}
}
- private static int getLogSubType(SelectionEvent event) {
- switch (event.mEntityType) {
- case TextClassifier.TYPE_OTHER:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER;
- case TextClassifier.TYPE_EMAIL:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL;
- case TextClassifier.TYPE_PHONE:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE;
- case TextClassifier.TYPE_ADDRESS:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS;
- case TextClassifier.TYPE_URL:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_URL;
- default:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_UNKNOWN;
- }
+ private int getRangeDelta(int offset) {
+ return offset - mOrigStart;
}
- private static String getLogSubTypeString(int logSubType) {
- switch (logSubType) {
- case MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER:
- return TextClassifier.TYPE_OTHER;
- case MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL:
- return TextClassifier.TYPE_EMAIL;
- case MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE:
- return TextClassifier.TYPE_PHONE;
- case MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS:
- return TextClassifier.TYPE_ADDRESS;
- case MetricsEvent.TEXT_CLASSIFIER_TYPE_URL:
- return TextClassifier.TYPE_URL;
- default:
- return TextClassifier.TYPE_UNKNOWN;
- }
+ private int getSmartRangeDelta(int offset) {
+ return mSmartSelectionTriggered ? getRangeDelta(offset) : 0;
}
- private int getSmartDelta() {
- if (mSmartSelectionTriggered) {
- return (clamp(mSmartIndices[0] - mOrigStart) << 16)
- | (clamp(mSmartIndices[1] - mOrigStart) & 0xffff);
- }
- // If the smart selection model was not run, return invalid selection indices [0,0]. This
- // allows us to tell from the terminal event alone whether the model was run.
- return 0;
- }
-
- private int getEventDelta(SelectionEvent event) {
- return (clamp(event.mStart - mOrigStart) << 16)
- | (clamp(event.mEnd - mOrigStart) & 0xffff);
- }
-
- private String getVersionTag(@Nullable SelectionEvent event) {
- final String widgetType;
+ private String getWidgetTypeName() {
switch (mWidgetType) {
case WidgetType.TEXTVIEW:
- widgetType = TEXTVIEW;
- break;
+ return TEXTVIEW;
case WidgetType.WEBVIEW:
- widgetType = WEBVIEW;
- break;
+ return WEBVIEW;
case WidgetType.EDITTEXT:
- widgetType = EDITTEXT;
- break;
+ return EDITTEXT;
case WidgetType.EDIT_WEBVIEW:
- widgetType = EDIT_WEBVIEW;
- break;
+ return EDIT_WEBVIEW;
+ case WidgetType.UNSELECTABLE_TEXTVIEW:
+ return UNSELECTABLE_TEXTVIEW;
+ case WidgetType.CUSTOM_TEXTVIEW:
+ return CUSTOM_TEXTVIEW;
+ case WidgetType.CUSTOM_EDITTEXT:
+ return CUSTOM_EDITTEXT;
+ case WidgetType.CUSTOM_UNSELECTABLE_TEXTVIEW:
+ return CUSTOM_UNSELECTABLE_TEXTVIEW;
default:
- widgetType = UNKNOWN;
+ return UNKNOWN;
}
- final String version = event == null
+ }
+
+ private String getModelName(@Nullable SelectionEvent event) {
+ return event == null
? SelectionEvent.NO_VERSION_TAG
: Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG);
- return String.format("%s/%s", widgetType, version);
}
private static String createSessionId() {
return UUID.randomUUID().toString();
}
- private static int clamp(int val) {
- return Math.max(Math.min(val, Short.MAX_VALUE), Short.MIN_VALUE);
- }
-
private static void debugLog(LogMaker log) {
if (!DEBUG_LOG_ENABLED) return;
- final String tag = Objects.toString(log.getTaggedData(VERSION_TAG), "tag");
+ final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
+ final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
+ final String widget = widgetVersion.isEmpty()
+ ? widgetType : widgetType + "-" + widgetVersion;
final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
- Log.d(LOG_TAG, String.format("New selection session: %s(%s)", tag, sessionId));
+ Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
}
+ final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
+ final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
final String type = getLogTypeString(log.getType());
- final String subType = getLogSubTypeString(log.getSubtype());
+ final int smartStart = Integer.parseInt(
+ Objects.toString(log.getTaggedData(SMART_START), ZERO));
+ final int smartEnd = Integer.parseInt(
+ Objects.toString(log.getTaggedData(SMART_END), ZERO));
+ final int eventStart = Integer.parseInt(
+ Objects.toString(log.getTaggedData(EVENT_START), ZERO));
+ final int eventEnd = Integer.parseInt(
+ Objects.toString(log.getTaggedData(EVENT_END), ZERO));
- final int smartIndices = Integer.parseInt(
- Objects.toString(log.getTaggedData(SMART_INDICES), ZERO));
- final int smartStart = (short) ((smartIndices & 0xffff0000) >> 16);
- final int smartEnd = (short) (smartIndices & 0xffff);
-
- final int eventIndices = Integer.parseInt(
- Objects.toString(log.getTaggedData(EVENT_INDICES), ZERO));
- final int eventStart = (short) ((eventIndices & 0xffff0000) >> 16);
- final int eventEnd = (short) (eventIndices & 0xffff);
-
- Log.d(LOG_TAG, String.format("%2d: %s/%s, context=%d,%d - old=%d,%d (%s)",
- index, type, subType, eventStart, eventEnd, smartStart, smartEnd, tag));
+ Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
+ index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model));
}
/**
@@ -369,12 +358,12 @@
/**
* Use this to specify an indeterminate positive index.
*/
- public static final int OUT_OF_BOUNDS = Short.MAX_VALUE;
+ public static final int OUT_OF_BOUNDS = Integer.MAX_VALUE;
/**
* Use this to specify an indeterminate negative index.
*/
- public static final int OUT_OF_BOUNDS_NEGATIVE = Short.MIN_VALUE;
+ public static final int OUT_OF_BOUNDS_NEGATIVE = Integer.MIN_VALUE;
private static final String NO_VERSION_TAG = "";
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 203de9c..e493739 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -29,10 +29,10 @@
/**
* Manages settings state for a WebView. When a WebView is first created, it
* obtains a set of default settings. These default settings will be returned
- * from any getter call. A WebSettings object obtained from
- * WebView.getSettings() is tied to the life of the WebView. If a WebView has
- * been destroyed, any method call on WebSettings will throw an
- * IllegalStateException.
+ * from any getter call. A {@code WebSettings} object obtained from
+ * {@link WebView#getSettings()} is tied to the life of the WebView. If a WebView has
+ * been destroyed, any method call on {@code WebSettings} will throw an
+ * {@link IllegalStateException}.
*/
// This is an abstract base class: concrete WebViewProviders must
// create a class derived from this, and return an instance of it in the
@@ -41,13 +41,13 @@
/**
* Enum for controlling the layout of html.
* <ul>
- * <li>NORMAL means no rendering changes. This is the recommended choice for maximum
+ * <li>{@code NORMAL} means no rendering changes. This is the recommended choice for maximum
* compatibility across different platforms and Android versions.</li>
- * <li>SINGLE_COLUMN moves all content into one column that is the width of the
+ * <li>{@code SINGLE_COLUMN} moves all content into one column that is the width of the
* view.</li>
- * <li>NARROW_COLUMNS makes all columns no wider than the screen if possible. Only use
+ * <li>{@code NARROW_COLUMNS} makes all columns no wider than the screen if possible. Only use
* this for API levels prior to {@link android.os.Build.VERSION_CODES#KITKAT}.</li>
- * <li>TEXT_AUTOSIZING boosts font size of paragraphs based on heuristics to make
+ * <li>{@code TEXT_AUTOSIZING} boosts font size of paragraphs based on heuristics to make
* the text readable when viewing a wide-viewport layout in the overview mode.
* It is recommended to enable zoom support {@link #setSupportZoom} when
* using this mode. Supported from API level
@@ -98,9 +98,9 @@
/**
* Enum for specifying the WebView's desired density.
* <ul>
- * <li>FAR makes 100% looking like in 240dpi</li>
- * <li>MEDIUM makes 100% looking like in 160dpi</li>
- * <li>CLOSE makes 100% looking like in 120dpi</li>
+ * <li>{@code FAR} makes 100% looking like in 240dpi</li>
+ * <li>{@code MEDIUM} makes 100% looking like in 160dpi</li>
+ * <li>{@code CLOSE} makes 100% looking like in 120dpi</li>
* </ul>
*/
public enum ZoomDensity {
@@ -652,7 +652,7 @@
* true, {@link WebChromeClient#onCreateWindow} must be implemented by the
* host application. The default is {@code false}.
*
- * @param support whether to suport multiple windows
+ * @param support whether to support multiple windows
*/
public abstract void setSupportMultipleWindows(boolean support);
@@ -665,7 +665,7 @@
public abstract boolean supportMultipleWindows();
/**
- * Sets the underlying layout algorithm. This will cause a relayout of the
+ * Sets the underlying layout algorithm. This will cause a re-layout of the
* WebView. The default is {@link LayoutAlgorithm#NARROW_COLUMNS}.
*
* @param l the layout algorithm to use, as a {@link LayoutAlgorithm} value
@@ -1198,7 +1198,7 @@
/**
* Tells JavaScript to open windows automatically. This applies to the
- * JavaScript function window.open(). The default is {@code false}.
+ * JavaScript function {@code window.open()}. The default is {@code false}.
*
* @param flag {@code true} if JavaScript can open windows automatically
*/
@@ -1208,7 +1208,7 @@
* Gets whether JavaScript can open windows automatically.
*
* @return {@code true} if JavaScript can open windows automatically during
- * window.open()
+ * {@code window.open()}
* @see #setJavaScriptCanOpenWindowsAutomatically
*/
public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
diff --git a/core/java/android/webkit/WebViewFragment.java b/core/java/android/webkit/WebViewFragment.java
index d803f62d..e5b7c8d 100644
--- a/core/java/android/webkit/WebViewFragment.java
+++ b/core/java/android/webkit/WebViewFragment.java
@@ -27,7 +27,10 @@
* A fragment that displays a WebView.
* <p>
* The WebView is automically paused or resumed when the Fragment is paused or resumed.
+ *
+ * @deprecated Manually call {@link WebView#onPause()} and {@link WebView#onResume()}
*/
+@Deprecated
public class WebViewFragment extends Fragment {
private WebView mWebView;
private boolean mIsWebViewAvailable;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 170582b..e0c897d 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3866,6 +3866,7 @@
private void onTouchDown(MotionEvent ev) {
mHasPerformedLongPress = false;
mActivePointerId = ev.getPointerId(0);
+ hideSelector();
if (mTouchMode == TOUCH_MODE_OVERFLING) {
// Stopped the fling. It is a scroll.
@@ -5226,17 +5227,21 @@
}
mRecycler.fullyDetachScrapViews();
+ boolean selectorOnScreen = false;
if (!inTouchMode && mSelectedPosition != INVALID_POSITION) {
final int childIndex = mSelectedPosition - mFirstPosition;
if (childIndex >= 0 && childIndex < getChildCount()) {
positionSelector(mSelectedPosition, getChildAt(childIndex));
+ selectorOnScreen = true;
}
} else if (mSelectorPosition != INVALID_POSITION) {
final int childIndex = mSelectorPosition - mFirstPosition;
if (childIndex >= 0 && childIndex < getChildCount()) {
- positionSelector(INVALID_POSITION, getChildAt(childIndex));
+ positionSelector(mSelectorPosition, getChildAt(childIndex));
+ selectorOnScreen = true;
}
- } else {
+ }
+ if (!selectorOnScreen) {
mSelectorRect.setEmpty();
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 7451d31..d0c2d86 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -195,6 +195,27 @@
private final boolean mHapticTextHandleEnabled;
private final Magnifier mMagnifier;
+ private final Runnable mUpdateMagnifierRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mMagnifier.update();
+ }
+ };
+ // Update the magnifier contents whenever anything in the view hierarchy is updated.
+ // Note: this only captures UI thread-visible changes, so it's a known issue that an animating
+ // VectorDrawable or Ripple animation will not trigger capture, since they're owned by
+ // RenderThread.
+ private final ViewTreeObserver.OnDrawListener mMagnifierOnDrawListener =
+ new ViewTreeObserver.OnDrawListener() {
+ @Override
+ public void onDraw() {
+ if (mMagnifier != null) {
+ // Posting the method will ensure that updating the magnifier contents will
+ // happen right after the rendering of the current frame.
+ mTextView.post(mUpdateMagnifierRunnable);
+ }
+ }
+ };
// Used to highlight a word when it is corrected by the IME
private CorrectionHighlighter mCorrectionHighlighter;
@@ -415,15 +436,21 @@
}
final ViewTreeObserver observer = mTextView.getViewTreeObserver();
- // No need to create the controller.
- // The get method will add the listener on controller creation.
- if (mInsertionPointCursorController != null) {
- observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+ if (observer.isAlive()) {
+ // No need to create the controller.
+ // The get method will add the listener on controller creation.
+ if (mInsertionPointCursorController != null) {
+ observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+ }
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.resetTouchOffsets();
+ observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
+ }
+ if (FLAG_USE_MAGNIFIER) {
+ observer.addOnDrawListener(mMagnifierOnDrawListener);
+ }
}
- if (mSelectionModifierCursorController != null) {
- mSelectionModifierCursorController.resetTouchOffsets();
- observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
- }
+
updateSpellCheckSpans(0, mTextView.getText().length(),
true /* create the spell checker if needed */);
@@ -472,6 +499,13 @@
mSpellChecker = null;
}
+ if (FLAG_USE_MAGNIFIER) {
+ final ViewTreeObserver observer = mTextView.getViewTreeObserver();
+ if (observer.isAlive()) {
+ observer.removeOnDrawListener(mMagnifierOnDrawListener);
+ }
+ }
+
hideCursorAndSpanControllers();
stopTextActionModeWithPreservingSelection();
}
@@ -3842,14 +3876,10 @@
mProcessTextIntentActionsHandler.onInitializeMenu(menu);
}
- if (menu.hasVisibleItems() || mode.getCustomView() != null) {
- if (mHasSelection && !mTextView.hasTransientState()) {
- mTextView.setHasTransientState(true);
- }
- return true;
- } else {
- return false;
+ if (mHasSelection && !mTextView.hasTransientState()) {
+ mTextView.setHasTransientState(true);
}
+ return true;
}
private Callback getCustomCallback() {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 71854ae..d0ad27a 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -591,7 +591,9 @@
Preconditions.checkNotNull(textView);
final @SmartSelectionEventTracker.WidgetType int widgetType = textView.isTextEditable()
? SmartSelectionEventTracker.WidgetType.EDITTEXT
- : SmartSelectionEventTracker.WidgetType.TEXTVIEW;
+ : (textView.isTextSelectable()
+ ? SmartSelectionEventTracker.WidgetType.TEXTVIEW
+ : SmartSelectionEventTracker.WidgetType.UNSELECTABLE_TEXTVIEW);
mDelegate = new SmartSelectionEventTracker(textView.getContext(), widgetType);
mEditTextLogger = textView.isTextEditable();
mWordIterator = BreakIterator.getWordInstance(textView.getTextLocale());
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 85251d4..5fddfba 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -431,7 +431,11 @@
}
}
- return super.dispatchKeyEvent(event);
+ if (super.dispatchKeyEvent(event)) {
+ return true;
+ }
+
+ return (getViewRootImpl() != null) && getViewRootImpl().dispatchKeyFallbackEvent(event);
}
public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 91bc681..22bfcc3 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -30,6 +30,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -134,6 +135,13 @@
}
/**
+ * Checks if given map is null or has zero elements.
+ */
+ public static boolean isEmpty(@Nullable Map<?, ?> map) {
+ return map == null || map.isEmpty();
+ }
+
+ /**
* Checks if given array is null or has zero elements.
*/
public static <T> boolean isEmpty(@Nullable T[] array) {
diff --git a/core/java/com/android/internal/util/RingBuffer.java b/core/java/com/android/internal/util/RingBuffer.java
index ad84353..c22be2c 100644
--- a/core/java/com/android/internal/util/RingBuffer.java
+++ b/core/java/com/android/internal/util/RingBuffer.java
@@ -45,6 +45,17 @@
return (int) Math.min(mBuffer.length, (long) mCursor);
}
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ public void clear() {
+ for (int i = 0; i < size(); ++i) {
+ mBuffer[i] = null;
+ }
+ mCursor = 0;
+ }
+
public void append(T t) {
mBuffer[indexOf(mCursor++)] = t;
}
diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java
index f6741c3..f79a7f5 100644
--- a/core/java/com/android/internal/widget/Magnifier.java
+++ b/core/java/com/android/internal/widget/Magnifier.java
@@ -18,34 +18,33 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UiThread;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
-import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.PixelCopy;
+import android.view.Surface;
+import android.view.SurfaceView;
import android.view.View;
-import android.view.ViewRootImpl;
import android.widget.ImageView;
import android.widget.PopupWindow;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
-import java.util.Timer;
-import java.util.TimerTask;
-
/**
* Android magnifier widget. Can be used by any view which is attached to window.
*/
public final class Magnifier {
- private static final String LOG_TAG = "magnifier";
- private static final int MAGNIFIER_REFRESH_RATE_MS = 33; // ~30fps
- // The view for which this magnifier is attached.
+ // Use this to specify that a previous configuration value does not exist.
+ private static final int NONEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
+ // The view to which this magnifier is attached.
private final View mView;
// The window containing the magnifier.
private final PopupWindow mWindow;
@@ -64,8 +63,12 @@
private final Handler mPixelCopyHandler = Handler.getMain();
// Current magnification scale.
private final float mZoomScale;
- // Timer used to schedule the copy task.
- private Timer mTimer;
+ // Variables holding previous states, used for detecting redundant calls and invalidation.
+ private final Point mPrevStartCoordsInSurface = new Point(
+ NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
+ private final PointF mPrevPosInView = new PointF(
+ NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
+ private final Rect mPixelCopyRequestRect = new Rect();
/**
* Initializes a magnifier.
@@ -91,8 +94,8 @@
mWindow.setTouchable(false);
mWindow.setBackgroundDrawable(null);
- final int bitmapWidth = (int) (mWindowWidth / mZoomScale);
- final int bitmapHeight = (int) (mWindowHeight / mZoomScale);
+ final int bitmapWidth = Math.round(mWindowWidth / mZoomScale);
+ final int bitmapHeight = Math.round(mWindowHeight / mZoomScale);
mBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
getImageView().setImageBitmap(mBitmap);
}
@@ -106,32 +109,29 @@
* relative to the view. The lower end is clamped to 0
*/
public void show(@FloatRange(from=0) float xPosInView, @FloatRange(from=0) float yPosInView) {
- if (xPosInView < 0) {
- xPosInView = 0;
- }
-
- if (yPosInView < 0) {
- yPosInView = 0;
- }
+ xPosInView = Math.max(0, xPosInView);
+ yPosInView = Math.max(0, yPosInView);
configureCoordinates(xPosInView, yPosInView);
- if (mTimer == null) {
- mTimer = new Timer();
- mTimer.schedule(new TimerTask() {
- @Override
- public void run() {
- performPixelCopy();
- }
- }, 0 /* delay */, MAGNIFIER_REFRESH_RATE_MS);
- }
+ // Clamp startX value to avoid distorting the rendering of the magnifier content.
+ final int startX = Math.max(0, Math.min(
+ mCenterZoomCoords.x - mBitmap.getWidth() / 2,
+ mView.getWidth() - mBitmap.getWidth()));
+ final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
- if (mWindow.isShowing()) {
- mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
- mWindow.getHeight());
- } else {
- mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY,
- mWindowCoords.x, mWindowCoords.y);
+ if (startX != mPrevStartCoordsInSurface.x || startY != mPrevStartCoordsInSurface.y) {
+ performPixelCopy(startX, startY);
+
+ mPrevPosInView.x = xPosInView;
+ mPrevPosInView.y = yPosInView;
+
+ if (mWindow.isShowing()) {
+ mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
+ mWindow.getHeight());
+ } else {
+ mWindow.showAtLocation(mView, Gravity.NO_GRAVITY, mWindowCoords.x, mWindowCoords.y);
+ }
}
}
@@ -140,11 +140,18 @@
*/
public void dismiss() {
mWindow.dismiss();
+ }
- if (mTimer != null) {
- mTimer.cancel();
- mTimer.purge();
- mTimer = null;
+ /**
+ * Forces the magnifier to update its content. It uses the previous coordinates passed to
+ * {@link #show(float, float)}. This only happens if the magnifier is currently showing.
+ *
+ * @hide
+ */
+ public void update() {
+ if (mWindow.isShowing()) {
+ // Update the contents shown in the magnifier.
+ performPixelCopy(mPrevStartCoordsInSurface.x, mPrevStartCoordsInSurface.y);
}
}
@@ -170,13 +177,22 @@
}
private void configureCoordinates(float xPosInView, float yPosInView) {
- final int[] coordinatesOnScreen = new int[2];
- mView.getLocationOnScreen(coordinatesOnScreen);
- final float posXOnScreen = xPosInView + coordinatesOnScreen[0];
- final float posYOnScreen = yPosInView + coordinatesOnScreen[1];
+ final float posX;
+ final float posY;
- mCenterZoomCoords.x = (int) posXOnScreen;
- mCenterZoomCoords.y = (int) posYOnScreen;
+ if (mView instanceof SurfaceView) {
+ // No offset required if the backing Surface matches the size of the SurfaceView.
+ posX = xPosInView;
+ posY = yPosInView;
+ } else {
+ final int[] coordinatesInSurface = new int[2];
+ mView.getLocationInSurface(coordinatesInSurface);
+ posX = xPosInView + coordinatesInSurface[0];
+ posY = yPosInView + coordinatesInSurface[1];
+ }
+
+ mCenterZoomCoords.x = Math.round(posX);
+ mCenterZoomCoords.y = Math.round(posY);
final int verticalMagnifierOffset = mView.getContext().getResources().getDimensionPixelSize(
R.dimen.magnifier_offset);
@@ -184,34 +200,36 @@
mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset;
}
- private void performPixelCopy() {
- final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
- int rawStartX = mCenterZoomCoords.x - mBitmap.getWidth() / 2;
+ private void performPixelCopy(final int startXInSurface, final int startYInSurface) {
+ final Surface surface = getValidViewSurface();
+ if (surface != null) {
+ mPixelCopyRequestRect.set(startXInSurface, startYInSurface,
+ startXInSurface + mBitmap.getWidth(), startYInSurface + mBitmap.getHeight());
- // Clamp startX value to avoid distorting the rendering of the magnifier content.
- if (rawStartX < 0) {
- rawStartX = 0;
- } else if (rawStartX + mBitmap.getWidth() > mView.getWidth()) {
- rawStartX = mView.getWidth() - mBitmap.getWidth();
- }
-
- final int startX = rawStartX;
- final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
-
- if (viewRootImpl != null && viewRootImpl.mSurface != null
- && viewRootImpl.mSurface.isValid()) {
- PixelCopy.request(
- viewRootImpl.mSurface,
- new Rect(startX, startY, startX + mBitmap.getWidth(),
- startY + mBitmap.getHeight()),
- mBitmap,
- result -> getImageView().invalidate(),
+ PixelCopy.request(surface, mPixelCopyRequestRect, mBitmap,
+ result -> {
+ getImageView().invalidate();
+ mPrevStartCoordsInSurface.x = startXInSurface;
+ mPrevStartCoordsInSurface.y = startYInSurface;
+ },
mPixelCopyHandler);
- } else {
- Log.d(LOG_TAG, "Could not perform PixelCopy request");
}
}
+ @Nullable
+ private Surface getValidViewSurface() {
+ final Surface surface;
+ if (mView instanceof SurfaceView) {
+ surface = ((SurfaceView) mView).getHolder().getSurface();
+ } else if (mView.getViewRootImpl() != null) {
+ surface = mView.getViewRootImpl().mSurface;
+ } else {
+ surface = null;
+ }
+
+ return (surface != null && surface.isValid()) ? surface : null;
+ }
+
private ImageView getImageView() {
return mWindow.getContentView().findViewById(R.id.magnifier_image);
}
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 7abc76a..408a4e9 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -9556,7 +9556,7 @@
if (vScroll == 0 && hScroll == 0) {
return false;
}
- mRecyclerView.scrollBy(hScroll, vScroll);
+ mRecyclerView.smoothScrollBy(hScroll, vScroll);
return true;
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index fb8b9f7..9d036dc 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -28,10 +28,6 @@
"-Wunused",
"-Wunreachable-code",
- // necessary for Clang as the GL bindings need to turn
- // off a GCC warning that Clang doesn't know.
- "-Wno-unknown-pragmas",
-
// TODO: Linear blending should be enabled by default, but we are
// TODO: making it an opt-in while it's a work in progress
//"-DANDROID_ENABLE_LINEAR_BLENDING",
@@ -46,7 +42,6 @@
"com_google_android_gles_jni_EGLImpl.cpp",
"com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
"android_app_Activity.cpp",
- "android_app_ApplicationLoaders.cpp",
"android_app_NativeActivity.cpp",
"android_app_admin_SecurityLog.cpp",
"android_opengl_EGL14.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8977891..2a65cde 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -187,7 +187,6 @@
extern int register_android_app_backup_FullBackup(JNIEnv *env);
extern int register_android_app_Activity(JNIEnv *env);
extern int register_android_app_ActivityThread(JNIEnv *env);
-extern int register_android_app_ApplicationLoaders(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
extern int register_android_media_RemoteDisplay(JNIEnv *env);
extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
@@ -1456,7 +1455,6 @@
REG_JNI(register_android_app_backup_FullBackup),
REG_JNI(register_android_app_Activity),
REG_JNI(register_android_app_ActivityThread),
- REG_JNI(register_android_app_ApplicationLoaders),
REG_JNI(register_android_app_NativeActivity),
REG_JNI(register_android_util_jar_StrictJarFile),
REG_JNI(register_android_view_InputChannel),
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 1a19a40..dd3e6f0 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -44,10 +44,10 @@
struct NativeFamilyBuilder {
NativeFamilyBuilder(uint32_t langId, int variant)
- : langId(langId), variant(static_cast<minikin::FontVariant>(variant)),
+ : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)),
allowUnsupportedFont(false) {}
uint32_t langId;
- minikin::FontVariant variant;
+ minikin::FontFamily::Variant variant;
bool allowUnsupportedFont;
std::vector<minikin::Font> fonts;
std::vector<minikin::FontVariation> axes;
@@ -141,7 +141,7 @@
}
builder->fonts.push_back(minikin::Font(minikinFont,
- minikin::FontStyle(weight, static_cast<minikin::FontSlant>(italic))));
+ minikin::FontStyle(weight, static_cast<minikin::FontStyle::Slant>(italic))));
builder->axes.clear();
return true;
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 5f32d37..1e7f5f5 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -581,7 +581,7 @@
// restore the original settings.
paint->setTextSkewX(saveSkewX);
paint->setFakeBoldText(savefakeBold);
- if (paint->getFontVariant() == minikin::FontVariant::ELEGANT) {
+ if (paint->getFamilyVariant() == minikin::FontFamily::Variant::ELEGANT) {
SkScalar size = paint->getTextSize();
metrics->fTop = -size * kElegantTop / 2048;
metrics->fBottom = -size * kElegantBottom / 2048;
@@ -880,12 +880,13 @@
static jboolean isElegantTextHeight(jlong paintHandle) {
Paint* obj = reinterpret_cast<Paint*>(paintHandle);
- return obj->getFontVariant() == minikin::FontVariant::ELEGANT;
+ return obj->getFamilyVariant() == minikin::FontFamily::Variant::ELEGANT;
}
static void setElegantTextHeight(jlong paintHandle, jboolean aa) {
Paint* obj = reinterpret_cast<Paint*>(paintHandle);
- obj->setFontVariant(aa ? minikin::FontVariant::ELEGANT : minikin::FontVariant::DEFAULT);
+ obj->setFamilyVariant(
+ aa ? minikin::FontFamily::Variant::ELEGANT : minikin::FontFamily::Variant::DEFAULT);
}
static jfloat getTextSize(jlong paintHandle) {
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 3e4073f..d67c0b0 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -83,7 +83,7 @@
static jint Typeface_getWeight(JNIEnv* env, jobject obj, jlong faceHandle) {
Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
- return face->fStyle.weight;
+ return face->fStyle.weight();
}
static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray,
diff --git a/core/jni/android_app_ApplicationLoaders.cpp b/core/jni/android_app_ApplicationLoaders.cpp
deleted file mode 100644
index 8bbf24a..0000000
--- a/core/jni/android_app_ApplicationLoaders.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "ApplicationLoaders"
-
-#include <nativehelper/ScopedUtfChars.h>
-#include <nativeloader/native_loader.h>
-#include <vulkan/vulkan_loader_data.h>
-
-#include "core_jni_helpers.h"
-
-static void setupVulkanLayerPath_native(JNIEnv* env, jobject clazz,
- jobject classLoader, jstring librarySearchPath) {
- android_namespace_t* ns = android::FindNamespaceByClassLoader(env, classLoader);
- ScopedUtfChars layerPathChars(env, librarySearchPath);
-
- vulkan::LoaderData& loader_data = vulkan::LoaderData::GetInstance();
- if (loader_data.layer_path.empty()) {
- loader_data.layer_path = layerPathChars.c_str();
- loader_data.app_namespace = ns;
- } else {
- ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'",
- layerPathChars.c_str(), ns);
- }
-}
-
-static const JNINativeMethod g_methods[] = {
- { "setupVulkanLayerPath", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V",
- reinterpret_cast<void*>(setupVulkanLayerPath_native) },
-};
-
-static const char* const kApplicationLoadersName = "android/app/ApplicationLoaders";
-
-namespace android
-{
-
-int register_android_app_ApplicationLoaders(JNIEnv* env) {
- return RegisterMethodsOrDie(env, kApplicationLoadersName, g_methods, NELEM(g_methods));
-}
-
-} // namespace android
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 6163588..a9d75fd 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp
index df1aa20..75a25fe 100644
--- a/core/jni/android_opengl_EGLExt.cpp
+++ b/core/jni/android_opengl_EGLExt.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index 6d4f6ec..ee5b594 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index e630cfca..da7d0f0 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index ab9cbb1..391ae53 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 8f71a6d..09dce32 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index f83d204..99922cf 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES2/gl2.h>
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index b649daf..adc635e 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES3/gl3.h>
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index 07d920f..512f562 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <stdint.h>
diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp
index 723dd4c..5543fca 100644
--- a/core/jni/android_opengl_GLES31Ext.cpp
+++ b/core/jni/android_opengl_GLES31Ext.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES3/gl31.h>
diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp
index 62a6e6c..2f1e31e 100644
--- a/core/jni/android_opengl_GLES32.cpp
+++ b/core/jni/android_opengl_GLES32.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <stdint.h>
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index f749488..4ecfd4b 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -18,6 +18,7 @@
#include <graphicsenv/GraphicsEnv.h>
#include <nativehelper/ScopedUtfChars.h>
+#include <nativeloader/native_loader.h>
#include "core_jni_helpers.h"
namespace {
@@ -27,8 +28,23 @@
android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
}
+void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
+ android_namespace_t* appNamespace = android::FindNamespaceByClassLoader(env, classLoader);
+ ScopedUtfChars layerPathsChars(env, layerPaths);
+ android::GraphicsEnv::getInstance().setLayerPaths(appNamespace, layerPathsChars.c_str());
+}
+
+void setDebugLayers_native(JNIEnv* env, jobject clazz, jstring layers) {
+ if (layers != nullptr) {
+ ScopedUtfChars layersChars(env, layers);
+ android::GraphicsEnv::getInstance().setDebugLayers(layersChars.c_str());
+ }
+}
+
const JNINativeMethod g_methods[] = {
{ "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
+ { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
+ { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
};
const char* const kGraphicsEnvironmentName = "android/os/GraphicsEnvironment";
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 18afe6e..d7300c4 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -36,7 +36,7 @@
#include <hwui/MinikinUtils.h>
#include <hwui/Paint.h>
#include <minikin/FontCollection.h>
-#include <minikin/LineBreaker.h>
+#include <minikin/AndroidLineBreakerHelper.h>
#include <minikin/MinikinFont.h>
namespace android {
@@ -52,63 +52,6 @@
static jclass gLineBreaks_class;
static JLineBreaksID gLineBreaks_fieldID;
-class JNILineBreakerLineWidth : public minikin::LineBreaker::LineWidthDelegate {
- public:
- JNILineBreakerLineWidth(float firstWidth, int32_t firstLineCount, float restWidth,
- const std::vector<float>& indents, const std::vector<float>& leftPaddings,
- const std::vector<float>& rightPaddings, int32_t indentsAndPaddingsOffset)
- : mFirstWidth(firstWidth), mFirstLineCount(firstLineCount), mRestWidth(restWidth),
- mIndents(indents), mLeftPaddings(leftPaddings),
- mRightPaddings(rightPaddings), mOffset(indentsAndPaddingsOffset) {}
-
- float getLineWidth(size_t lineNo) override {
- const float width = ((ssize_t)lineNo < (ssize_t)mFirstLineCount)
- ? mFirstWidth : mRestWidth;
- return width - get(mIndents, lineNo);
- }
-
- float getMinLineWidth() override {
- // A simpler algorithm would have been simply looping until the larger of
- // mFirstLineCount and mIndents.size()-mOffset, but that does unnecessary calculations
- // when mFirstLineCount is large. Instead, we measure the first line, all the lines that
- // have an indent, and the first line after firstWidth ends and restWidth starts.
- float minWidth = std::min(getLineWidth(0), getLineWidth(mFirstLineCount));
- for (size_t lineNo = 1; lineNo + mOffset < mIndents.size(); lineNo++) {
- minWidth = std::min(minWidth, getLineWidth(lineNo));
- }
- return minWidth;
- }
-
- float getLeftPadding(size_t lineNo) override {
- return get(mLeftPaddings, lineNo);
- }
-
- float getRightPadding(size_t lineNo) override {
- return get(mRightPaddings, lineNo);
- }
-
- private:
- float get(const std::vector<float>& vec, size_t lineNo) {
- if (vec.empty()) {
- return 0;
- }
- const size_t index = lineNo + mOffset;
- if (index < vec.size()) {
- return vec[index];
- } else {
- return vec.back();
- }
- }
-
- const float mFirstWidth;
- const int32_t mFirstLineCount;
- const float mRestWidth;
- const std::vector<float>& mIndents;
- const std::vector<float>& mLeftPaddings;
- const std::vector<float>& mRightPaddings;
- const int32_t mOffset;
-};
-
static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
if (javaArray == nullptr) {
return std::vector<float>();
@@ -118,105 +61,8 @@
}
}
-class Run {
- public:
- Run(int32_t start, int32_t end) : mStart(start), mEnd(end) {}
- virtual ~Run() {}
-
- virtual void addTo(minikin::LineBreaker* lineBreaker) = 0;
-
- protected:
- const int32_t mStart;
- const int32_t mEnd;
-
- private:
- // Forbid copy and assign.
- Run(const Run&) = delete;
- void operator=(const Run&) = delete;
-};
-
-class StyleRun : public Run {
- public:
- StyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint, bool isRtl)
- : Run(start, end), mPaint(std::move(paint)), mIsRtl(isRtl) {}
-
- void addTo(minikin::LineBreaker* lineBreaker) override {
- lineBreaker->addStyleRun(&mPaint, mStart, mEnd, mIsRtl);
- }
-
- private:
- minikin::MinikinPaint mPaint;
- const bool mIsRtl;
-};
-
-class Replacement : public Run {
- public:
- Replacement(int32_t start, int32_t end, float width, uint32_t localeListId)
- : Run(start, end), mWidth(width), mLocaleListId(localeListId) {}
-
- void addTo(minikin::LineBreaker* lineBreaker) override {
- lineBreaker->addReplacement(mStart, mEnd, mWidth, mLocaleListId);
- }
-
- private:
- const float mWidth;
- const uint32_t mLocaleListId;
-};
-
-class StaticLayoutNative {
- public:
- StaticLayoutNative(
- minikin::BreakStrategy strategy, minikin::HyphenationFrequency frequency,
- bool isJustified, std::vector<float>&& indents, std::vector<float>&& leftPaddings,
- std::vector<float>&& rightPaddings)
- : mStrategy(strategy), mFrequency(frequency), mIsJustified(isJustified),
- mIndents(std::move(indents)), mLeftPaddings(std::move(leftPaddings)),
- mRightPaddings(std::move(rightPaddings)) {}
-
- void addStyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint, bool isRtl) {
- mRuns.emplace_back(std::make_unique<StyleRun>(start, end, std::move(paint), isRtl));
- }
-
- void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
- mRuns.emplace_back(std::make_unique<Replacement>(start, end, width, localeListId));
- }
-
- // Only valid while this instance is alive.
- inline std::unique_ptr<minikin::LineBreaker::LineWidthDelegate> buildLineWidthDelegate(
- float firstWidth, int32_t firstLineCount, float restWidth,
- int32_t indentsAndPaddingsOffset) {
- return std::make_unique<JNILineBreakerLineWidth>(
- firstWidth, firstLineCount, restWidth, mIndents, mLeftPaddings, mRightPaddings,
- indentsAndPaddingsOffset);
- }
-
- void addRuns(minikin::LineBreaker* lineBreaker) {
- for (const auto& run : mRuns) {
- run->addTo(lineBreaker);
- }
- }
-
- void clearRuns() {
- mRuns.clear();
- }
-
- inline minikin::BreakStrategy getStrategy() const { return mStrategy; }
- inline minikin::HyphenationFrequency getFrequency() const { return mFrequency; }
- inline bool isJustified() const { return mIsJustified; }
-
- private:
- const minikin::BreakStrategy mStrategy;
- const minikin::HyphenationFrequency mFrequency;
- const bool mIsJustified;
- const std::vector<float> mIndents;
- const std::vector<float> mLeftPaddings;
- const std::vector<float> mRightPaddings;
-
- std::vector<std::unique_ptr<Run>> mRuns;
-};
-
-static inline StaticLayoutNative* toNative(jlong ptr) {
- return reinterpret_cast<StaticLayoutNative*>(ptr);
+static inline minikin::android::StaticLayoutNative* toNative(jlong ptr) {
+ return reinterpret_cast<minikin::android::StaticLayoutNative*>(ptr);
}
// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
@@ -224,7 +70,7 @@
static jlong nInit(JNIEnv* env, jclass /* unused */,
jint breakStrategy, jint hyphenationFrequency, jboolean isJustified,
jintArray indents, jintArray leftPaddings, jintArray rightPaddings) {
- return reinterpret_cast<jlong>(new StaticLayoutNative(
+ return reinterpret_cast<jlong>(new minikin::android::StaticLayoutNative(
static_cast<minikin::BreakStrategy>(breakStrategy),
static_cast<minikin::HyphenationFrequency>(hyphenationFrequency),
isJustified,
@@ -268,7 +114,7 @@
static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
// Inputs
- jcharArray text,
+ jcharArray javaText,
jint length,
jfloat firstWidth,
jint firstWidthLineCount,
@@ -287,13 +133,12 @@
jintArray recycleFlags,
jfloatArray charWidths) {
- StaticLayoutNative* builder = toNative(nativePtr);
+ minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
+
+ ScopedCharArrayRO text(env, javaText);
// TODO: Reorganize minikin APIs.
- minikin::LineBreaker b;
- b.resize(length);
- env->GetCharArrayRegion(text, 0, length, b.buffer());
- b.setText();
+ minikin::LineBreaker b(minikin::U16StringPiece(text.get(), length));
if (variableTabStops == nullptr) {
b.setTabStops(nullptr, 0, defaultTabStop);
} else {
@@ -316,7 +161,6 @@
env->SetFloatArrayRegion(charWidths, 0, b.size(), b.charWidths());
- b.finish();
builder->clearRuns();
return static_cast<jint>(nBreaks);
@@ -325,17 +169,17 @@
// Basically similar to Paint.getTextRunAdvances but with C++ interface
// CriticalNative
static void nAddStyleRun(jlong nativePtr, jlong nativePaint, jint start, jint end, jboolean isRtl) {
- StaticLayoutNative* builder = toNative(nativePtr);
+ minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
Paint* paint = reinterpret_cast<Paint*>(nativePaint);
const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
- builder->addStyleRun(start, end, std::move(minikinPaint), isRtl);
+ builder->addStyleRun(start, end, std::move(minikinPaint), typeface->fFontCollection, isRtl);
}
// CriticalNative
static void nAddReplacementRun(jlong nativePtr, jlong nativePaint, jint start, jint end,
jfloat width) {
- StaticLayoutNative* builder = toNative(nativePtr);
+ minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
Paint* paint = reinterpret_cast<Paint*>(nativePaint);
builder->addReplacementRun(start, end, width, paint->getMinikinLocaleListId());
}
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 6e8c931..37ff8c8 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -174,6 +174,10 @@
return renderNode->stagingProperties().hasShadow();
}
+static jboolean android_view_RenderNode_setShadowColor(jlong renderNodePtr, jint shadowColor) {
+ return SET_AND_DIRTY(setShadowColor, static_cast<SkColor>(shadowColor), RenderNode::GENERIC);
+}
+
static jboolean android_view_RenderNode_setClipToOutline(jlong renderNodePtr,
jboolean clipToOutline) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
@@ -571,6 +575,7 @@
{ "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty },
{ "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone },
{ "nHasShadow", "(J)Z", (void*) android_view_RenderNode_hasShadow },
+ { "nSetShadowColor", "(JI)Z", (void*) android_view_RenderNode_setShadowColor },
{ "nSetClipToOutline", "(JZ)Z", (void*) android_view_RenderNode_setClipToOutline },
{ "nSetRevealClip", "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index cfeba83..bb1bfad 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -311,6 +311,14 @@
transaction->apply(sync);
}
+static void nativeMergeTransaction(JNIEnv* env, jclass clazz,
+ jlong transactionObj, jlong otherTransactionObj) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ auto otherTransaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(
+ otherTransactionObj);
+ transaction->merge(std::move(*otherTransaction));
+}
+
static void nativeSetAnimationTransaction(JNIEnv* env, jclass clazz, jlong transactionObj) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
transaction->setAnimationTransaction();
@@ -882,6 +890,8 @@
(void*)nativeApplyTransaction },
{"nativeGetNativeTransactionFinalizer", "()J",
(void*)nativeGetNativeTransactionFinalizer },
+ {"nativeMergeTransaction", "(JJ)V",
+ (void*)nativeMergeTransaction },
{"nativeSetAnimationTransaction", "(J)V",
(void*)nativeSetAnimationTransaction },
{"nativeSetLayer", "(JJI)V",
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index ac23eca..40ff7e4 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
diff --git a/core/proto/android/app/notification.proto b/core/proto/android/app/notification.proto
new file mode 100644
index 0000000..5376b0e
--- /dev/null
+++ b/core/proto/android/app/notification.proto
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option java_package = "android.app";
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.Notification object.
+ * Deprecated fields are not included in the proto.
+ */
+message NotificationProto {
+ optional string channel_id = 1;
+ optional bool has_ticker_text = 2;
+ optional int32 flags = 3;
+ optional int32 color = 4;
+ optional string category = 5;
+ optional string group_key = 6;
+ optional string sort_key = 7;
+ optional int32 action_length = 8;
+
+ // If this field is not set, then the value is unknown.
+ enum Visibility {
+ VISIBILITY_SECRET = -1;
+ VISIBILITY_PRIVATE = 0;
+ VISIBILITY_PUBLIC = 1;
+ }
+ optional Visibility visibility = 9;
+ optional NotificationProto public_version = 10;
+}
diff --git a/core/proto/android/os/cpufreq.proto b/core/proto/android/os/cpufreq.proto
new file mode 100644
index 0000000..a8da0bf
--- /dev/null
+++ b/core/proto/android/os/cpufreq.proto
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_outer_classname = "CpuFreqProto";
+
+package android.os;
+
+// cpu frequency time from /sys/devices/system/cpu/cpufreq/all_time_in_state
+message CpuFreq {
+
+ optional int32 jiffy_hz = 1; // obtain by system config.
+
+ repeated CpuFreqStats cpu_freqs = 2;
+}
+
+// frequency time pre cpu, unit in jiffy, TODO: obtain jiffies.
+message CpuFreqStats {
+
+ optional string cpu_name = 1;
+
+ message TimeInState {
+ optional int32 state_khz = 1; // cpu frequency
+ optional int64 time_jiffy = 2; // number of jiffies
+ }
+ repeated TimeInState times = 2;
+}
diff --git a/core/proto/android/os/cpuinfo.proto b/core/proto/android/os/cpuinfo.proto
new file mode 100644
index 0000000..a95fa57
--- /dev/null
+++ b/core/proto/android/os/cpuinfo.proto
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_outer_classname = "CpuInfoProto";
+
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
+package android.os;
+
+/**
+ * Data structure of the linux command
+ * 'top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name'
+ *
+ * Next Tag: 6
+ */
+message CpuInfo {
+
+ message TaskStats {
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+ optional int32 total = 1; // total number of cpu tasks
+ optional int32 running = 2; // number of running tasks
+ optional int32 sleeping = 3; // number of sleeping tasks
+ optional int32 stopped = 4; // number of stopped tasks
+ optional int32 zombie = 5; // number of zombie tasks
+ }
+ optional TaskStats task_stats = 1;
+
+ message MemStats { // unit in kB
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+ optional int32 total = 1;
+ optional int32 used = 2;
+ optional int32 free = 3;
+ optional int32 buffers = 4;
+ optional int32 cached = 5;
+ }
+ optional MemStats mem = 2;
+ optional MemStats swap = 3;
+
+ message CpuUsage { // unit is percentage %
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+ optional int32 cpu = 1; // 400% cpu indicates 4 cores
+ optional int32 user = 2;
+ optional int32 nice = 3;
+ optional int32 sys = 4;
+ optional int32 idle = 5;
+ optional int32 iow = 6;
+ optional int32 irq = 7;
+ optional int32 sirq = 8;
+ optional int32 host = 9;
+ }
+ optional CpuUsage cpu_usage = 4;
+
+ // Next Tag: 13
+ message Task {
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+ optional int32 pid = 1;
+ optional int32 tid = 2;
+ optional string user = 3;
+ optional string pr = 4; // priority of each task, using string type is because special value RT (real time)
+ optional sint32 ni = 5; // niceness value
+ optional float cpu = 6; // precentage of cpu usage of the task
+
+ enum Status {
+ option (stream_proto.stream_enum).enable_enums_mapping = true;
+
+ STATUS_UNKNOWN = 0;
+ STATUS_D = 1; // uninterruptible sleep
+ STATUS_R = 2; // running
+ STATUS_S = 3; // sleeping
+ STATUS_T = 4; // traced or stopped
+ STATUS_Z = 5; // zombie
+ }
+ optional Status s = 7; // process status
+ optional string virt = 8; // virtual memory size, i.e. 14.0G, 13.5M
+ optional string res = 9; // Resident size, i.e. 0, 3.1G
+
+ // How Android memory manager will treat the task
+ enum Policy {
+ option (stream_proto.stream_enum).enable_enums_mapping = true;
+
+ POLICY_UNKNOWN = 0;
+ POLICY_fg = 1; // foreground, the name is lower case for parsing the value
+ POLICY_bg = 2; // background, the name is lower case for parsing the value
+ POLICY_ta = 3; // TODO: figure out what is this value
+ }
+ optional Policy pcy = 10; // Policy of the task
+ optional string cmd = 11; // thread name
+ optional string name = 12; // program name
+ }
+ repeated Task tasks = 5;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index e998b09..ecdabcf 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,11 +20,14 @@
import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/libs/incident/proto/android/section.proto";
-import "frameworks/base/core/proto/android/providers/settings.proto";
+import "frameworks/base/core/proto/android/os/cpufreq.proto";
+import "frameworks/base/core/proto/android/os/cpuinfo.proto";
import "frameworks/base/core/proto/android/os/incidentheader.proto";
import "frameworks/base/core/proto/android/os/kernelwake.proto";
import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
import "frameworks/base/core/proto/android/os/procrank.proto";
+import "frameworks/base/core/proto/android/os/system_properties.proto";
+import "frameworks/base/core/proto/android/providers/settings.proto";
import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
@@ -50,7 +53,10 @@
repeated IncidentHeaderProto header = 1;
// Device information
- //SystemProperties system_properties = 1000;
+ optional SystemPropertiesProto system_properties = 1000 [
+ (section).type = SECTION_COMMAND,
+ (section).args = "/system/bin/getprop"
+ ];
// Linux services
optional Procrank procrank = 2000 [
@@ -68,6 +74,15 @@
(section).args = "/d/wakeup_sources"
];
+ optional CpuInfo cpu_info = 2003 [
+ (section).type = SECTION_COMMAND,
+ (section).args = "/system/bin/top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"
+ ];
+
+ optional CpuFreq cpu_freq = 2004 [
+ (section).type = SECTION_FILE,
+ (section).args = "/sys/devices/system/cpu/cpufreq/all_time_in_state"
+ ];
// System Services
optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
@@ -80,7 +95,11 @@
(section).args = "netstats --proto"
];
- optional android.providers.settings.SettingsServiceDumpProto settings = 3002;
+ optional android.providers.settings.SettingsServiceDumpProto settings = 3002 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "settings --proto"
+ ];
+
optional android.service.appwidget.AppWidgetServiceDumpProto appwidget = 3003;
optional android.service.notification.NotificationServiceDumpProto notification = 3004 [
(section).type = SECTION_DUMPSYS,
@@ -129,7 +148,11 @@
(section).args = "activity --proto broadcasts"
];
- optional com.android.server.am.proto.ServiceProto amservices = 3014;
+ optional com.android.server.am.proto.ActiveServicesProto amservices = 3014 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "activity --proto service"
+ ];
+
optional com.android.server.am.proto.ProcessProto amprocesses = 3015;
optional com.android.server.AlarmManagerServiceProto alarm = 3016 [
diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index d032a45..eaad37a 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -29,7 +29,7 @@
// Next Tag: 11
message WakeupSourceProto {
- option (stream_proto.stream).enable_fields_mapping = true;
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
// Name of the event which triggers application processor
optional string name = 1;
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index 22b3d73..b86ee01 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -63,7 +63,7 @@
// Next tag: 9
message BlockProto {
- option (stream_proto.stream).enable_fields_mapping = true;
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
optional int32 node = 1;
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index 4d62a60..9945f2e 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -33,7 +33,7 @@
// Next Tag: 11
message ProcessProto {
- option (stream_proto.stream).enable_fields_mapping = true;
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
// ID of the process
optional int32 pid = 1;
diff --git a/core/proto/android/os/system_properties.proto b/core/proto/android/os/system_properties.proto
new file mode 100644
index 0000000..921bb5a
--- /dev/null
+++ b/core/proto/android/os/system_properties.proto
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_outer_classname = "SystemPropertiesProtoMetadata";
+
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
+package android.os;
+
+// System Properties from getprop
+message SystemPropertiesProto {
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+ // Properties that are not specified below are appended here.
+ message Property {
+ optional string name = 1;
+ optional string value = 2;
+ }
+ repeated Property extra_properties = 1;
+
+ optional int32 aaudio_hw_burst_min_usec = 2;
+ optional int32 aaudio_mmap_exclusive_policy = 3;
+ optional int32 aaudio_mmap_policy = 4;
+
+ optional int32 af_fast_track_multiplier = 5;
+
+ optional int32 audio_adm_buffering_ms = 6;
+ optional int32 audio_hal_period_size = 7;
+
+ optional string dalvik_vm_appimageformat = 8;
+ optional string dalvik_vm_dex2oat_Xms = 9;
+ optional string dalvik_vm_dex2oat_Xmx = 10;
+ optional bool dalvik_vm_dexopt_secondary = 11;
+ optional string dalvik_vm_heapgrowthlimit = 12;
+ optional string dalvik_vm_heapmaxfree = 13;
+ optional string dalvik_vm_heapminfree = 14;
+ optional string dalvik_vm_heapsize = 15;
+ optional string dalvik_vm_heapstartsize = 16;
+ optional float dalvik_vm_heaptargetutilization = 17;
+ optional string dalvik_vm_image_dex2oat_Xms = 18;
+ optional string dalvik_vm_image_dex2oat_Xmx = 19;
+ optional string dalvik_vm_image_dex2oat_filter = 20;
+ optional string dalvik_vm_isa_arm_features = 21;
+ optional string dalvik_vm_isa_arm_variant = 22;
+ optional string dalvik_vm_isa_arm64_features = 23;
+ optional string dalvik_vm_isa_arm64_variant = 24;
+ optional int32 dalvik_vm_lockprof_threshold = 25;
+ optional string dalvik_vm_stack_trace_dir = 26;
+ optional bool dalvik_vm_usejit = 27;
+ optional bool dalvik_vm_usejitprofiles = 28;
+
+ optional int32 debug_atrace_tags_enableflags = 29;
+ optional int32 debug_force_rtl = 30;
+ optional string debug_htc_hrdump = 31;
+
+ optional int32 dev_bootcomplete = 32;
+
+ optional bool drm_service_enabled = 33;
+
+ optional int32 fmas_hdph_sgain = 34;
+
+ optional int32 gsm_current_phone_type = 35;
+ optional string gsm_network_type = 36;
+ optional string gsm_operator_alpha = 37;
+ optional string gsm_operator_iso_country = 38;
+ optional bool gsm_operator_isroaming = 39;
+ optional string gsm_operator_numeric = 40;
+ optional string gsm_sim_operator_alpha = 41;
+ optional string gsm_sim_operator_iso_country = 42;
+ optional int32 gsm_sim_operator_numeric = 43;
+ optional string gsm_sim_state = 44;
+ optional string gsm_version_baseband = 45;
+ optional string gsm_version_ril_impl = 46;
+
+ optional sint32 htc_camera_sensor_inf = 47;
+
+ optional bool hwservicemanager_ready = 48;
+
+ // Enum values for a lot of system properties
+ enum Status {
+ STATUS_UNKNOWN = 0;
+ STATUS_RUNNING = 1;
+ STATUS_STOPPED = 2;
+ }
+ optional Status init_svc_adbd = 49;
+ optional Status init_svc_audioserver = 50;
+ optional Status init_svc_bootanim = 51;
+ optional Status init_svc_bufferhubd = 52;
+ optional Status init_svc_cameraserver = 53;
+ optional Status init_svc_clear_bcb = 54;
+ optional Status init_svc_drm = 55;
+ optional Status init_svc_gatekeeperd = 56;
+ optional Status init_svc_healthd = 57;
+ optional Status init_svc_hidl_memory = 58;
+ optional Status init_svc_hostapd = 59;
+ optional Status init_svc_hwservicemanager = 60;
+ optional Status init_svc_installd = 61;
+ optional Status init_svc_keystore = 62;
+ optional Status init_svc_lmkd = 63;
+ optional Status init_svc_logd = 64;
+ optional Status init_svc_logd_reinit = 65;
+ optional Status init_svc_media = 66;
+ optional Status init_svc_mediadrm = 67;
+ optional Status init_svc_mediaextractor = 68;
+ optional Status init_svc_mediametrics = 69;
+ optional Status init_svc_netd = 70;
+ optional Status init_svc_performanced = 71;
+ optional Status init_svc_ril_daemon = 72;
+ optional Status init_svc_servicemanager = 73;
+ optional Status init_svc_storaged = 74;
+ optional Status init_svc_surfaceflinger = 75;
+ optional Status init_svc_thermalservice = 76;
+ optional Status init_svc_tombstoned = 77;
+ optional Status init_svc_ueventd = 78;
+ optional Status init_svc_update_engine = 79;
+ optional Status init_svc_update_verifier_nonencrypted = 80;
+ optional Status init_svc_vendor_adsprpcd = 81;
+ optional Status init_svc_vendor_atfwd = 82;
+ optional Status init_svc_vendor_audio_hal_2_0 = 83;
+ optional Status init_svc_vendor_bluetooth_1_0 = 84;
+ optional Status init_svc_vendor_boot_hal_1_0 = 85;
+ optional Status init_svc_vendor_camera_provider_2_4 = 86;
+ optional Status init_svc_vendor_cas_hal_1_0 = 87;
+ optional Status init_svc_vendor_cnd = 88;
+ optional Status init_svc_vendor_cnss_daemon = 89;
+ optional Status init_svc_vendor_cnss_diag = 90;
+ optional Status init_svc_vendor_configstore_hal = 91;
+ optional Status init_svc_vendor_contexthub_hal_1_0 = 92;
+ optional Status init_svc_vendor_devstart_sh = 93;
+ optional Status init_svc_vendor_drm_hal_1_0 = 94;
+ optional Status init_svc_vendor_drm_widevine_hal_1_0 = 95;
+ optional Status init_svc_vendor_dumpstate_1_0 = 96;
+ optional Status init_svc_vendor_flash_nanohub_fw = 97;
+ optional Status init_svc_vendor_foreground_sh = 98;
+ optional Status init_svc_vendor_fps_hal = 99;
+ optional Status init_svc_vendor_gatekeeper_1_0 = 100;
+ optional Status init_svc_vendor_gnss_service = 101;
+ optional Status init_svc_vendor_gralloc_2_0 = 102;
+ optional Status init_svc_vendor_hci_filter_root = 103;
+ optional Status init_svc_vendor_hwcomposer_2_1 = 104;
+ optional Status init_svc_vendor_imsdatadaemon = 105;
+ optional Status init_svc_vendor_imsqmidaemon = 106;
+ optional Status init_svc_vendor_init_radio_sh = 107;
+ optional Status init_svc_vendor_irsc_util = 108;
+ optional Status init_svc_vendor_keymaster_3_0 = 109;
+ optional Status init_svc_vendor_light_hal_2_0 = 110;
+ optional Status init_svc_vendor_loc_launcher = 111;
+ optional Status init_svc_vendor_media_omx = 112;
+ optional Status init_svc_vendor_memtrack_hal_1_0 = 113;
+ optional Status init_svc_vendor_mid_sh = 114;
+ optional Status init_svc_vendor_msm_irqbalance = 115;
+ optional Status init_svc_vendor_nanohub_slpi = 116;
+ optional Status init_svc_vendor_netmgrd = 117;
+ optional Status init_svc_vendor_nfc_hal_service = 118;
+ optional Status init_svc_vendor_per_mgr = 119;
+ optional Status init_svc_vendor_per_proxy = 120;
+ optional Status init_svc_vendor_perfd = 121;
+ optional Status init_svc_vendor_port_bridge = 122;
+ optional Status init_svc_vendor_power_hal_1_1 = 123;
+ optional Status init_svc_vendor_power_sh = 124;
+ optional Status init_svc_vendor_qseecomd = 125;
+ optional Status init_svc_vendor_ramdump_auto = 126;
+ optional Status init_svc_vendor_rmt_storage = 127;
+ optional Status init_svc_vendor_sensors_hal_1_0 = 128;
+ optional Status init_svc_vendor_ss_ramdump = 129;
+ optional Status init_svc_vendor_ssr_setup = 130;
+ optional Status init_svc_vendor_thermal_engine = 131;
+ optional Status init_svc_vendor_time_daemon = 132;
+ optional Status init_svc_vendor_usb_hal_1_1 = 133;
+ optional Status init_svc_vendor_vibrator_1_0 = 134;
+ optional Status init_svc_vendor_vr_1_0 = 135;
+ optional Status init_svc_vendor_wifi_hal_legacy = 136;
+ optional Status init_svc_virtual_touchpad = 137;
+ optional Status init_svc_vndservicemanager = 138;
+ optional Status init_svc_vold = 139;
+ optional Status init_svc_vr_hwc = 140;
+ optional Status init_svc_webview_zygote32 = 141;
+ optional Status init_svc_wificond = 142;
+ optional Status init_svc_wpa_supplicant = 143;
+ optional Status init_svc_zygote = 144;
+ optional Status init_svc_zygote_secondary = 145;
+
+ optional bool keyguard_no_require_sim = 146;
+
+ optional string log_tag_WifiHAL = 147;
+
+ optional bool logd_logpersistd_enable = 148;
+
+ optional bool media_mediadrmservice_enable = 149;
+ optional bool media_recorder_show_manufacturer_and_model = 150;
+
+ optional string net_bt_name = 151;
+ optional string net_dns1 = 152;
+ optional string net_dns2 = 153;
+ optional string net_dns3 = 154;
+ optional string net_dns4 = 155;
+ optional bool net_lte_ims_data_enabled = 156;
+ optional int32 net_qtaguid_enabled = 157;
+ optional int32 net_tcp_2g_init_rwnd = 158;
+ repeated int32 net_tcp_buffersize_default = 159;
+ repeated int32 net_tcp_buffersize_edge = 160;
+ repeated int32 net_tcp_buffersize_evdo = 161;
+ repeated int32 net_tcp_buffersize_gprs = 162;
+ repeated int32 net_tcp_buffersize_hsdpa = 163;
+ repeated int32 net_tcp_buffersize_hspa = 164;
+ repeated int32 net_tcp_buffersize_hspap = 165;
+ repeated int32 net_tcp_buffersize_hsupa = 166;
+ repeated int32 net_tcp_buffersize_lte = 167;
+ repeated int32 net_tcp_buffersize_umts = 168;
+ repeated int32 net_tcp_buffersize_wifi = 169;
+ optional int32 net_tcp_default_init_rwnd = 170;
+
+ optional bool nfc_initialized = 171;
+
+ optional bool persist_audio_fluence_speaker = 172;
+ optional bool persist_audio_fluence_voicecall = 173;
+ optional bool persist_audio_fluence_voicecomm = 174;
+ optional bool persist_audio_fluence_voicerec = 175;
+
+ optional int32 persist_camera_debug_logfile = 176;
+ optional int32 persist_camera_eis_enable = 177;
+ optional int32 persist_camera_gyro_android = 178;
+ optional int32 persist_camera_is_type = 179;
+ optional int32 persist_camera_tnr_preview = 180;
+ optional int32 persist_camera_tnr_video = 181;
+ optional int32 persist_camera_tof_direct = 182;
+
+ optional int32 persist_cne_feature = 183;
+
+ optional bool persist_data_iwlan_enable = 184;
+ optional string persist_data_mode = 185;
+
+ optional int32 persist_radio_RATE_ADAPT_ENABLE = 186;
+ optional int32 persist_radio_ROTATION_ENABLE = 187;
+ optional int32 persist_radio_VT_ENABLE = 188;
+ optional int32 persist_radio_VT_HYBRID_ENABLE = 189;
+ optional int32 persist_radio_adb_log_on = 190;
+ optional int32 persist_radio_airplane_mode_on = 191;
+ optional int32 persist_radio_apm_sim_not_pwdn = 192;
+ optional int32 persist_radio_custom_ecc = 193;
+ optional bool persist_radio_data_con_rprt = 194;
+ optional int32 persist_radio_data_ltd_sys_ind = 195;
+ optional string persist_radio_enable_tel_mon = 196;
+ optional bool persist_radio_eons_enabled = 197;
+ optional bool persist_radio_is_wps_enabled = 198;
+ optional int32 persist_radio_pwropt_modepref_0 = 199;
+ optional int32 persist_radio_ril_payload_on = 200;
+ optional int32 persist_radio_sglte_target = 201;
+ optional int32 persist_radio_sib16_support = 202;
+ optional int32 persist_radio_smlog_switch = 203;
+ optional int32 persist_radio_snapshot_enabled = 204;
+ optional int32 persist_radio_snapshot_timer = 205;
+ optional int32 persist_radio_sw_mbn_loaded = 206;
+ optional int32 persist_radio_sw_mbn_update = 207;
+ optional string persist_radio_ver_info = 208;
+ optional int32 persist_radio_videopause_mode = 209;
+
+ optional int32 persist_rcs_supported = 210;
+
+ optional string persist_sys_boot_reason = 211;
+ optional int32 persist_sys_cnd_iwlan = 212;
+ optional string persist_sys_dalvik_vm_lib_2 = 213;
+ optional string persist_sys_gps_lpp = 214;
+ optional string persist_sys_locale = 215;
+ optional int32 persist_sys_ssr_enable_ramdumps = 216;
+ optional string persist_sys_ssr_restart_level = 217;
+ optional string persist_sys_timezone = 218;
+ optional string persist_sys_usb_config = 219;
+ optional int32 persist_sys_webview_vmsize = 220;
+
+ optional string pm_dexopt_ab_ota = 221;
+ optional string pm_dexopt_bg_dexopt = 222;
+ optional string pm_dexopt_boot = 223;
+ optional string pm_dexopt_first_boot = 224;
+ optional string pm_dexopt_inactive = 225;
+ optional string pm_dexopt_install = 226;
+ optional string pm_dexopt_shared = 227;
+
+ optional string qcom_bluetooth_soc = 228;
+
+ optional int32 qdcm_diagonal_matrix_mode = 229;
+ optional int32 qdcm_only_pcc_for_trans = 230;
+
+ repeated string ril_ecclist = 231;
+ optional int32 ril_power_backoff_suppressed = 232;
+ optional int32 ril_qcril_pre_init_lock_held = 233;
+ optional string ril_voice_network_type = 234;
+ optional string rild_libpath = 235;
+
+ optional int32 ro_adb_secure = 236;
+ optional int32 ro_allow_mock_location = 237;
+ repeated string ro_atrace_core_services = 238;
+ optional string ro_baseband = 239;
+ optional int32 ro_bionic_ld_warning = 240;
+ optional bool ro_bluetooth_dun = 241;
+ optional string ro_bluetooth_hfp_ver = 242;
+ optional bool ro_bluetooth_sap = 243;
+ optional string ro_board_platform = 244;
+
+ optional string ro_boot_baseband = 245;
+ optional string ro_boot_bootdevice = 246;
+ optional string ro_boot_bootloader = 247;
+ optional string ro_boot_bootreason = 248;
+ repeated string ro_boot_boottime = 249;
+ optional int32 ro_boot_cid = 250;
+ optional string ro_boot_console = 251;
+ optional string ro_boot_ddrinfo = 252;
+ optional string ro_boot_ddrsize = 253;
+ optional int32 ro_boot_flash_locked = 254;
+ optional int32 ro_boot_fp_src = 255;
+ optional string ro_boot_hardware = 256;
+ optional string ro_boot_hardware_color = 257;
+ optional string ro_boot_hardware_ddr = 258;
+ optional string ro_boot_hardware_revision = 259;
+ optional string ro_boot_hardware_sku = 260;
+ optional string ro_boot_hardware_ufs = 261;
+ optional string ro_boot_htc_hrdump = 262;
+ optional int32 ro_boot_keymaster = 263;
+ optional string ro_boot_mid = 264;
+ optional int32 ro_boot_msm_hw_ver_id = 265;
+ optional int32 ro_boot_oem_unlock_support = 266;
+ optional int32 ro_boot_qf_st = 267;
+ optional int32 ro_boot_ramdump_enable = 268;
+ optional string ro_boot_serialno = 269;
+ optional string ro_boot_slot_suffix = 270;
+ optional int32 ro_boot_temp_protect_ignore = 271;
+ optional string ro_boot_vendor_overlay_theme = 272;
+ optional string ro_boot_verifiedbootstate = 273;
+ optional string ro_boot_veritymode = 274;
+ optional string ro_boot_wificountrycode = 275;
+
+ optional string ro_bootimage_build_date = 276;
+ optional int64 ro_bootimage_build_date_utc = 277;
+ optional string ro_bootimage_build_fingerprint = 278;
+
+ optional string ro_bootloader = 279;
+ optional string ro_bootmode = 280;
+
+ optional int64 ro_boottime_adbd = 281;
+ optional int64 ro_boottime_audioserver = 282;
+ optional int64 ro_boottime_bootanim = 283;
+ optional int64 ro_boottime_bufferhubd = 284;
+ optional int64 ro_boottime_cameraserver = 285;
+ optional int64 ro_boottime_clear_bcb = 286;
+ optional int64 ro_boottime_drm = 287;
+ optional int64 ro_boottime_gatekeeperd = 288;
+ optional int64 ro_boottime_healthd = 289;
+ optional int64 ro_boottime_hidl_memory = 290;
+ optional int64 ro_boottime_hwservicemanager = 291;
+ optional int64 ro_boottime_init = 292;
+ optional int64 ro_boottime_init_cold_boot_wait = 293;
+ optional int32 ro_boottime_init_mount_all_early = 294;
+ optional int32 ro_boottime_init_mount_all_late = 295;
+ optional int32 ro_boottime_init_selinux = 296;
+ optional int64 ro_boottime_installd = 297;
+ optional int64 ro_boottime_keystore = 298;
+ optional int64 ro_boottime_lmkd = 299;
+ optional int64 ro_boottime_logd = 300;
+ optional int64 ro_boottime_logd_reinit = 301;
+ optional int64 ro_boottime_media = 302;
+ optional int64 ro_boottime_mediadrm = 303;
+ optional int64 ro_boottime_mediaextractor = 304;
+ optional int64 ro_boottime_mediametrics = 305;
+ optional int64 ro_boottime_netd = 306;
+ optional int64 ro_boottime_performanced = 307;
+ optional int64 ro_boottime_ril_daemon = 308;
+ optional int64 ro_boottime_servicemanager = 309;
+ optional int64 ro_boottime_storaged = 310;
+ optional int64 ro_boottime_surfaceflinger = 311;
+ optional int64 ro_boottime_thermalservice = 312;
+ optional int64 ro_boottime_tombstoned = 313;
+ optional int64 ro_boottime_ueventd = 314;
+ optional int64 ro_boottime_update_engine = 315;
+ optional int64 ro_boottime_update_verifier_nonencrypted = 316;
+ optional int64 ro_boottime_vendor_adsprpcd = 317;
+ optional int64 ro_boottime_vendor_atfwd = 318;
+ optional int64 ro_boottime_vendor_audio_hal_2_0 = 319;
+ optional int64 ro_boottime_vendor_bluetooth_1_0 = 320;
+ optional int64 ro_boottime_vendor_boot_hal_1_0 = 321;
+ optional int64 ro_boottime_vendor_camera_provider_2_4 = 322;
+ optional int64 ro_boottime_vendor_cas_hal_1_0 = 323;
+ optional int64 ro_boottime_vendor_cnd = 324;
+ optional int64 ro_boottime_vendor_cnss_daemon = 325;
+ optional int64 ro_boottime_vendor_cnss_diag = 326;
+ optional int64 ro_boottime_vendor_configstore_hal = 327;
+ optional int64 ro_boottime_vendor_contexthub_hal_1_0 = 328;
+ optional int64 ro_boottime_vendor_devstart_sh = 329;
+ optional int64 ro_boottime_vendor_drm_hal_1_0 = 330;
+ optional int64 ro_boottime_vendor_drm_widevine_hal_1_0 = 331;
+ optional int64 ro_boottime_vendor_dumpstate_1_0 = 332;
+ optional int64 ro_boottime_vendor_flash_nanohub_fw = 333;
+ optional int64 ro_boottime_vendor_foreground_sh = 334;
+ optional int64 ro_boottime_vendor_fps_hal = 335;
+ optional int64 ro_boottime_vendor_gatekeeper_1_0 = 336;
+ optional int64 ro_boottime_vendor_gnss_service = 337;
+ optional int64 ro_boottime_vendor_gralloc_2_0 = 338;
+ optional int64 ro_boottime_vendor_hci_filter_root = 339;
+ optional int64 ro_boottime_vendor_hwcomposer_2_1 = 340;
+ optional int64 ro_boottime_vendor_imsdatadaemon = 341;
+ optional int64 ro_boottime_vendor_imsqmidaemon = 342;
+ optional int64 ro_boottime_vendor_init_radio_sh = 343;
+ optional int64 ro_boottime_vendor_irsc_util = 344;
+ optional int64 ro_boottime_vendor_keymaster_3_0 = 345;
+ optional int64 ro_boottime_vendor_light_hal_2_0 = 346;
+ optional int64 ro_boottime_vendor_loc_launcher = 347;
+ optional int64 ro_boottime_vendor_media_omx = 348;
+ optional int64 ro_boottime_vendor_memtrack_hal_1_0 = 349;
+ optional int64 ro_boottime_vendor_mid_sh = 350;
+ optional int64 ro_boottime_vendor_msm_irqbalance = 351;
+ optional int64 ro_boottime_vendor_nanohub_slpi = 352;
+ optional int64 ro_boottime_vendor_netmgrd = 353;
+ optional int64 ro_boottime_vendor_nfc_hal_service = 354;
+ optional int64 ro_boottime_vendor_per_mgr = 355;
+ optional int64 ro_boottime_vendor_per_proxy = 356;
+ optional int64 ro_boottime_vendor_perfd = 357;
+ optional int64 ro_boottime_vendor_port_bridge = 358;
+ optional int64 ro_boottime_vendor_power_hal_1_1 = 359;
+ optional int64 ro_boottime_vendor_power_sh = 360;
+ optional int64 ro_boottime_vendor_qseecomd = 361;
+ optional int64 ro_boottime_vendor_ramdump_auto = 362;
+ optional int64 ro_boottime_vendor_rmt_storage = 363;
+ optional int64 ro_boottime_vendor_sensors_hal_1_0 = 364;
+ optional int64 ro_boottime_vendor_ss_ramdump = 365;
+ optional int64 ro_boottime_vendor_ssr_setup = 366;
+ optional int64 ro_boottime_vendor_thermal_engine = 367;
+ optional int64 ro_boottime_vendor_time_daemon = 368;
+ optional int64 ro_boottime_vendor_usb_hal_1_1 = 369;
+ optional int64 ro_boottime_vendor_vibrator_1_0 = 370;
+ optional int64 ro_boottime_vendor_vr_1_0 = 371;
+ optional int64 ro_boottime_vendor_wifi_hal_legacy = 372;
+ optional int64 ro_boottime_virtual_touchpad = 373;
+ optional int64 ro_boottime_vndservicemanager = 374;
+ optional int64 ro_boottime_vold = 375;
+ optional int64 ro_boottime_vr_hwc = 376;
+ optional int64 ro_boottime_webview_zygote32 = 377;
+ optional int64 ro_boottime_wificond = 378;
+ optional int64 ro_boottime_wpa_supplicant = 379;
+ optional int64 ro_boottime_zygote = 380;
+ optional int64 ro_boottime_zygote_secondary = 381;
+
+ optional string ro_bt_bdaddr_path = 382;
+
+ optional bool ro_build_ab_update = 383;
+ optional string ro_build_characteristics = 384;
+ optional string ro_build_date = 385;
+ optional int64 ro_build_date_utc = 386;
+ optional string ro_build_description = 387;
+ optional string ro_build_display_id = 388;
+ optional string ro_build_expect_baseband = 389;
+ optional string ro_build_expect_bootloader = 390;
+ optional string ro_build_fingerprint = 391;
+ optional string ro_build_flavor = 392;
+ optional string ro_build_host = 393;
+ optional string ro_build_id = 394;
+ optional string ro_build_product = 395;
+ optional bool ro_build_system_root_image = 396;
+ optional string ro_build_tags = 397;
+ optional string ro_build_type = 398;
+ optional string ro_build_user = 399;
+ optional string ro_build_version_all_codenames = 400;
+ optional string ro_build_version_base_os = 401;
+ optional string ro_build_version_codename = 402;
+ optional string ro_build_version_incremental = 403;
+ optional int32 ro_build_version_preview_sdk = 404;
+ optional string ro_build_version_release = 405;
+ optional int32 ro_build_version_sdk = 406;
+ optional string ro_build_version_security_patch = 407;
+
+ optional int32 ro_camera_notify_nfc = 408;
+ optional string ro_carrier = 409;
+ optional bool ro_com_android_dataroaming = 410;
+ optional bool ro_com_android_prov_mobiledata = 411;
+ optional string ro_com_google_clientidbase = 412;
+ optional int32 ro_com_google_ime_theme_id = 413;
+
+ optional string ro_config_alarm_alert = 414;
+ optional string ro_config_notification_sound = 415;
+ optional string ro_config_ringtone = 416;
+ optional int32 ro_config_vc_call_vol_steps = 417;
+ optional string ro_control_privapp_permissions = 418;
+ optional int32 ro_cp_system_other_odex = 419;
+
+ optional string ro_crypto_scrypt_params = 420;
+ optional string ro_crypto_state = 421;
+ optional string ro_crypto_type = 422;
+ optional string ro_crypto_volume_filenames_mode = 423;
+ optional int32 ro_dalvik_vm_native_bridge = 424;
+ optional int32 ro_debuggable = 425;
+ optional bool ro_device_owner = 426;
+ optional string ro_error_receiver_system_apps = 427;
+
+ optional int32 ro_facelock_black_timeout = 428;
+ optional int32 ro_facelock_det_timeout = 429;
+ optional int32 ro_facelock_est_max_time = 430;
+ optional int32 ro_facelock_rec_timeout = 431;
+
+ optional string ro_frp_pst = 432;
+
+ optional string ro_hardware = 433;
+ optional string ro_hardware_power = 434;
+
+ optional int32 ro_hwui_drop_shadow_cache_size = 435;
+ optional int32 ro_hwui_gradient_cache_size = 436;
+ optional int32 ro_hwui_layer_cache_size = 437;
+ optional int32 ro_hwui_path_cache_size = 438;
+ optional int32 ro_hwui_r_buffer_cache_size = 439;
+ optional int32 ro_hwui_text_large_cache_height = 440;
+ optional int32 ro_hwui_text_large_cache_width = 441;
+ optional int32 ro_hwui_text_small_cache_height = 442;
+ optional int32 ro_hwui_text_small_cache_width = 443;
+ optional float ro_hwui_texture_cache_flushrate = 444;
+ optional int32 ro_hwui_texture_cache_size = 445;
+
+ optional bool ro_init_subcontexts_enabled = 446;
+ optional int32 ro_kernel_android_checkjni = 447;
+ optional string ro_logd_size = 448;
+ optional int32 ro_min_freq_0 = 449;
+ optional int32 ro_oem_unlock_supported = 450;
+ optional bool ro_opa_eligible_device = 451;
+ optional int32 ro_opengles_version = 452;
+ optional bool ro_persistent_properties_ready = 453;
+
+ optional string ro_product_board = 454;
+ optional string ro_product_brand = 455;
+ optional string ro_product_cpu_abi = 456;
+ repeated string ro_product_cpu_abilist = 457;
+ repeated string ro_product_cpu_abilist32 = 458;
+ optional string ro_product_cpu_abilist64 = 459;
+ optional string ro_product_device = 460;
+ optional int32 ro_product_first_api_level = 461;
+ optional string ro_product_locale = 462;
+ optional string ro_product_manufacturer = 463;
+ optional string ro_product_model = 464;
+ optional string ro_product_name = 465;
+ optional string ro_product_vendor_brand = 466;
+ optional string ro_product_vendor_device = 467;
+ optional string ro_product_vendor_manufacturer = 468;
+ optional string ro_product_vendor_model = 469;
+ optional string ro_product_vendor_name = 470;
+
+ optional int32 ro_property_service_version = 471;
+ optional string ro_qc_sdk_audio_fluencetype = 472;
+ optional int32 ro_qcom_adreno_qgl_ShaderStorageImageExtendedFormats = 473;
+ optional bool ro_qualcomm_bluetooth_ftp = 474;
+ optional bool ro_qualcomm_bluetooth_hfp = 475;
+ optional bool ro_qualcomm_bluetooth_hsp = 476;
+ optional bool ro_qualcomm_bluetooth_map = 477;
+ optional bool ro_qualcomm_bluetooth_nap = 478;
+ optional bool ro_qualcomm_bluetooth_opp = 479;
+ optional bool ro_qualcomm_bluetooth_pbap = 480;
+
+ optional string ro_radio_log_loc = 481;
+ optional string ro_radio_log_prefix = 482;
+ optional int32 ro_revision = 483;
+ optional bool ro_ril_svdo = 484;
+ optional bool ro_ril_svlte1x = 485;
+ optional int64 ro_runtime_firstboot = 486;
+ optional int32 ro_secure = 487;
+ optional string ro_serialno = 488;
+ optional int32 ro_setupwizard_enterprise_mode = 489;
+ optional bool ro_setupwizard_rotation_locked = 490;
+ optional int32 ro_sf_lcd_density = 491;
+ optional bool ro_storage_manager_enabled = 492;
+ optional bool ro_telephony_call_ring_multiple = 493;
+ optional int32 ro_telephony_default_cdma_sub = 494;
+ optional int32 ro_telephony_default_network = 495;
+ optional bool ro_treble_enabled = 496;
+ optional string ro_url_legal = 497;
+ optional string ro_url_legal_android_privacy = 498;
+ optional string ro_vendor_build_date = 499;
+ optional int64 ro_vendor_build_date_utc = 500;
+ optional string ro_vendor_build_fingerprint = 501;
+ optional string ro_vendor_extension_library = 502;
+ optional bool ro_wallpapers_loc_request_suw = 503;
+ optional string ro_wifi_channels = 504;
+ optional string ro_zygote = 505;
+
+ optional int32 sdm_debug_disable_rotator_split = 506;
+
+ optional string selinux_restorecon_recursive = 507;
+
+ optional string sendbug_preferred_domain = 508;
+
+ optional string sensors_contexthub_lid_state = 509;
+
+ optional int32 service_bootanim_exit = 510;
+ optional int32 service_sf_present_timestamp = 511;
+
+ optional string setupwizard_theme = 512;
+
+ optional string sys_boot_reason = 513;
+ optional int32 sys_boot_completed = 514;
+ optional int32 sys_ims_QMI_DAEMON_STATUS = 515;
+ optional bool sys_keymaster_loaded = 516;
+ optional bool sys_listeners_registered = 517;
+ optional int32 sys_logbootcomplete = 518;
+ optional int32 sys_oem_unlock_allowed = 519;
+ optional int32 sys_post_boot_parsed = 520;
+ optional int32 sys_qcom_devup = 521;
+ optional int32 sys_retaildemo_enabled = 522;
+ optional string sys_slpi_firmware_version = 523;
+ optional int32 sys_sysctl_extra_free_kbytes = 524;
+ optional int32 sys_sysctl_tcp_def_init_rwnd = 525;
+ optional bool sys_time_set = 526;
+ optional string sys_usb_config = 527;
+ optional int32 sys_usb_configfs = 528;
+ optional string sys_usb_controller = 529;
+ optional int32 sys_usb_ffs_max_read = 530;
+ optional int32 sys_usb_ffs_max_write = 531;
+ optional int32 sys_usb_ffs_mtp_ready = 532;
+ optional int32 sys_usb_ffs_ready = 533;
+ optional int32 sys_usb_mtp_device_type = 534;
+ optional int32 sys_usb_rps_mask = 535;
+ optional string sys_usb_state = 536;
+ optional bool sys_user_0_ce_available = 537;
+ optional int32 sys_wifitracing_started = 538;
+
+ optional int32 telephony_lteOnCdmaDevice = 539;
+
+ optional int32 tombstoned_max_tombstone_count = 540;
+
+ optional int32 vidc_debug_perf_mode = 541;
+
+ optional int32 vold_has_adoptable = 542;
+ optional int32 vold_has_quota = 543;
+ optional int32 vold_post_fs_data_done = 544;
+
+ optional int32 wc_transport_clean_up = 545;
+ optional int32 wc_transport_hci_filter_status = 546;
+ optional string wc_transport_patch_dnld_inprog = 547;
+ optional int32 wc_transport_ref_count = 548;
+ optional int32 wc_transport_soc_initialized = 549;
+ optional bool wc_transport_start_root = 550;
+ optional int32 wc_transport_vnd_power = 551;
+
+ optional string wifi_interface = 552;
+ optional string wlan_driver_status = 553;
+
+ // Next Tag: 554
+}
+
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 3411c6a..5d5aea2 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -39,9 +39,10 @@
optional SystemSettingsProto system_settings = 3;
}
+// Note: it's a conscious decision to add each setting as a separate field. This
+// allows annotating each setting with its own privacy tag.
message GlobalSettingsProto {
- // Historical operations
- repeated SettingsOperationProto historical_op = 1;
+ repeated SettingsOperationProto historical_operations = 1;
optional SettingProto add_users_when_locked = 2;
optional SettingProto enable_accessibility_global_gesture_enabled = 3;
@@ -54,6 +55,7 @@
optional SettingProto radio_nfc = 10;
optional SettingProto airplane_mode_radios = 11;
optional SettingProto airplane_mode_toggleable_radios = 12;
+ optional SettingProto bluetooth_class_of_device = 293;
optional SettingProto bluetooth_disabled_profiles = 13;
optional SettingProto bluetooth_interoperability_list = 14;
optional SettingProto wifi_sleep_policy = 15;
@@ -86,6 +88,7 @@
optional SettingProto data_roaming = 42;
optional SettingProto mdc_initial_max_retry = 43;
optional SettingProto force_allow_on_external = 44;
+ optional SettingProto euicc_provisioned = 294;
optional SettingProto development_force_resizable_activities = 45;
optional SettingProto development_enable_freeform_windows_support = 46;
optional SettingProto development_settings_enabled = 47;
@@ -99,6 +102,11 @@
optional SettingProto hdmi_system_audio_control_enabled = 55;
optional SettingProto hdmi_control_auto_wakeup_enabled = 56;
optional SettingProto hdmi_control_auto_device_off_enabled = 57;
+ optional SettingProto location_background_throttle_interval_ms = 295;
+ optional SettingProto location_background_throttle_proximity_alert_interval_ms = 296;
+ optional SettingProto location_background_throttle_package_whitelist = 297;
+ optional SettingProto wifi_scan_background_throttle_interval_ms = 298;
+ optional SettingProto wifi_scan_background_throttle_package_whitelist = 299;
optional SettingProto mhl_input_switching_enabled = 58;
optional SettingProto mhl_power_charge_enabled = 59;
optional SettingProto mobile_data = 60;
@@ -109,6 +117,7 @@
optional SettingProto netstats_time_cache_max_age = 65;
optional SettingProto netstats_global_alert_bytes = 66;
optional SettingProto netstats_sample_enabled = 67;
+ optional SettingProto netstats_augment_enabled = 300;
optional SettingProto netstats_dev_bucket_duration = 68;
optional SettingProto netstats_dev_persist_bytes = 69;
optional SettingProto netstats_dev_rotate_age = 70;
@@ -156,6 +165,7 @@
optional SettingProto tether_supported = 113;
optional SettingProto tether_dun_required = 114;
optional SettingProto tether_dun_apn = 115;
+ optional SettingProto tether_offload_disabled = 301;
optional SettingProto carrier_app_whitelist = 116;
optional SettingProto usb_mass_storage_enabled = 117;
optional SettingProto use_google_mail = 118;
@@ -166,6 +176,9 @@
optional SettingProto network_switch_notification_daily_limit = 123;
optional SettingProto network_switch_notification_rate_limit_millis = 124;
optional SettingProto network_avoid_bad_wifi = 125;
+ optional SettingProto network_metered_multipath_preference = 302;
+ optional SettingProto network_watchlist_last_report_time = 303;
+ optional SettingProto wifi_badging_thresholds = 304;
optional SettingProto wifi_display_on = 126;
optional SettingProto wifi_display_certification_on = 127;
optional SettingProto wifi_display_wps_config = 128;
@@ -179,7 +192,14 @@
optional SettingProto wifi_on = 136;
optional SettingProto wifi_scan_always_available = 137;
optional SettingProto wifi_wakeup_enabled = 138;
+ optional SettingProto wifi_wakeup_available = 305;
+ optional SettingProto network_scoring_ui_enabled = 306;
+ optional SettingProto speed_label_cache_eviction_age_millis = 307;
+ optional SettingProto recommended_network_evaluator_cache_expiry_ms = 308;
optional SettingProto network_recommendations_enabled = 139;
+ optional SettingProto network_recommendations_package = 286;
+ optional SettingProto use_open_wifi_package = 309;
+ optional SettingProto network_recommendation_request_timeout_ms = 310;
optional SettingProto ble_scan_always_available = 140;
optional SettingProto wifi_saved_state = 141;
optional SettingProto wifi_supplicant_scan_interval_ms = 142;
@@ -219,15 +239,19 @@
optional SettingProto sys_storage_threshold_percentage = 176;
optional SettingProto sys_storage_threshold_max_bytes = 177;
optional SettingProto sys_storage_full_threshold_bytes = 178;
+ optional SettingProto sys_storage_cache_percentage = 311;
+ optional SettingProto sys_storage_cache_max_bytes = 312;
optional SettingProto sync_max_retry_delay_in_seconds = 179;
optional SettingProto connectivity_change_delay = 180;
optional SettingProto connectivity_sampling_interval_in_seconds = 181;
optional SettingProto pac_change_delay = 182;
optional SettingProto captive_portal_mode = 183;
+ optional SettingProto captive_portal_detection_enabled = 313;
optional SettingProto captive_portal_server = 184;
optional SettingProto captive_portal_https_url = 185;
optional SettingProto captive_portal_http_url = 186;
optional SettingProto captive_portal_fallback_url = 187;
+ optional SettingProto captive_portal_other_fallback_urls = 314;
optional SettingProto captive_portal_use_https = 188;
optional SettingProto captive_portal_user_agent = 189;
optional SettingProto nsd_on = 190;
@@ -243,21 +267,33 @@
optional SettingProto global_http_proxy_pac = 200;
optional SettingProto set_global_http_proxy = 201;
optional SettingProto default_dns_server = 202;
+ // The requested Private DNS mode and an accompanying specifier.
+ optional SettingProto private_dns_mode = 315;
+ optional SettingProto private_dns_specifier = 316;
optional SettingProto bluetooth_headset_priority_prefix = 203;
optional SettingProto bluetooth_a2dp_sink_priority_prefix = 204;
optional SettingProto bluetooth_a2dp_src_priority_prefix = 205;
+ optional SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287;
+ optional SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288;
optional SettingProto bluetooth_input_device_priority_prefix = 206;
optional SettingProto bluetooth_map_priority_prefix = 207;
optional SettingProto bluetooth_map_client_priority_prefix = 208;
optional SettingProto bluetooth_pbap_client_priority_prefix = 209;
optional SettingProto bluetooth_sap_priority_prefix = 210;
optional SettingProto bluetooth_pan_priority_prefix = 211;
+ optional SettingProto activity_manager_constants = 317;
optional SettingProto device_idle_constants = 212;
optional SettingProto device_idle_constants_watch = 213;
+ optional SettingProto battery_saver_constants = 318;
+ optional SettingProto anomaly_detection_constants = 319;
+ optional SettingProto always_on_display_constants = 320;
optional SettingProto app_idle_constants = 214;
+ optional SettingProto power_manager_constants = 321;
optional SettingProto alarm_manager_constants = 215;
optional SettingProto job_scheduler_constants = 216;
optional SettingProto shortcut_manager_constants = 217;
+ optional SettingProto device_policy_constants = 322;
+ optional SettingProto text_classifier_constants = 323;
optional SettingProto window_animation_scale = 218;
optional SettingProto transition_animation_scale = 219;
optional SettingProto animator_duration_scale = 220;
@@ -269,6 +305,9 @@
optional SettingProto preferred_network_mode = 226;
optional SettingProto debug_app = 227;
optional SettingProto wait_for_debugger = 228;
+ optional SettingProto enable_gpu_debug_layers = 342;
+ optional SettingProto gpu_debug_app = 343;
+ optional SettingProto gpu_debug_layers = 344;
optional SettingProto low_power_mode = 229;
optional SettingProto low_power_mode_trigger_level = 230;
optional SettingProto always_finish_activities = 231;
@@ -287,6 +326,10 @@
optional SettingProto cert_pin_update_metadata_url = 244;
optional SettingProto intent_firewall_update_content_url = 245;
optional SettingProto intent_firewall_update_metadata_url = 246;
+ optional SettingProto lang_id_update_content_url = 324;
+ optional SettingProto lang_id_update_metadata_url = 325;
+ optional SettingProto smart_selection_update_content_url = 326;
+ optional SettingProto smart_selection_update_metadata_url = 327;
optional SettingProto selinux_status = 247;
optional SettingProto development_force_rtl = 248;
optional SettingProto low_battery_sound_timeout = 249;
@@ -308,13 +351,24 @@
optional SettingProto lte_service_forced = 265;
optional SettingProto ephemeral_cookie_max_size_bytes = 266;
optional SettingProto enable_ephemeral_feature = 267;
+ optional SettingProto instant_app_dexopt_enabled = 328;
optional SettingProto installed_instant_app_min_cache_period = 268;
+ optional SettingProto installed_instant_app_max_cache_period = 289;
+ optional SettingProto uninstalled_instant_app_min_cache_period = 290;
+ optional SettingProto uninstalled_instant_app_max_cache_period = 291;
+ optional SettingProto unused_static_shared_lib_min_cache_period = 292;
optional SettingProto allow_user_switching_when_system_user_locked = 269;
optional SettingProto boot_count = 270;
optional SettingProto safe_boot_disallowed = 271;
optional SettingProto device_demo_mode = 272;
+ optional SettingProto network_access_timeout_ms = 329;
optional SettingProto database_downgrade_reason = 274;
+ optional SettingProto database_creation_buildid = 330;
optional SettingProto contacts_database_wal_enabled = 275;
+ optional SettingProto location_settings_link_to_permissions_enabled = 331;
+ optional SettingProto backup_refactored_service_disabled = 332;
+ optional SettingProto euicc_factory_reset_timeout_millis = 333;
+ optional SettingProto storage_settings_clobber_threshold = 334;
optional SettingProto multi_sim_voice_call_subscription = 276;
optional SettingProto multi_sim_voice_prompt = 277;
optional SettingProto multi_sim_data_call_subscription = 278;
@@ -324,19 +378,20 @@
optional SettingProto contact_metadata_sync_enabled = 282;
optional SettingProto enable_cellular_on_boot = 283;
optional SettingProto max_notification_enqueue_rate = 284;
+ optional SettingProto show_notification_channel_warnings = 335;
optional SettingProto cell_on = 285;
- optional SettingProto network_recommendations_package = 286;
- optional SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287;
- optional SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288;
- optional SettingProto installed_instant_app_max_cache_period = 289;
- optional SettingProto uninstalled_instant_app_min_cache_period = 290;
- optional SettingProto uninstalled_instant_app_max_cache_period = 291;
- optional SettingProto unused_static_shared_lib_min_cache_period = 292;
+ optional SettingProto show_temperature_warning = 336;
+ optional SettingProto warning_temperature = 337;
+ optional SettingProto enable_diskstats_logging = 338;
+ optional SettingProto enable_cache_quota_calculation = 339;
+ optional SettingProto enable_deletion_helper_no_threshold_toggle = 340;
+ optional SettingProto notification_snooze_options = 341;
+
+ // Next tag = 345;
}
message SecureSettingsProto {
- // Historical operations
- repeated SettingsOperationProto historical_op = 1;
+ repeated SettingsOperationProto historical_operations = 1;
optional SettingProto android_id = 2;
optional SettingProto default_input_method = 3;
@@ -347,6 +402,10 @@
optional SettingProto autofill_service = 8;
optional SettingProto bluetooth_hci_log = 9;
optional SettingProto user_setup_complete = 10;
+ // Whether the current user has been set up via setup wizard (0 = false,
+ // 1 = true). This value differs from USER_SETUP_COMPLETE in that it can be
+ // reset back to 0 in case SetupWizard has been re-enabled on TV devices.
+ optional SettingProto tv_user_setup_complete = 170;
optional SettingProto completed_category_prefix = 11;
optional SettingProto enabled_input_methods = 12;
optional SettingProto disabled_system_input_methods = 13;
@@ -354,10 +413,12 @@
optional SettingProto always_on_vpn_app = 15;
optional SettingProto always_on_vpn_lockdown = 16;
optional SettingProto install_non_market_apps = 17;
+ optional SettingProto unknown_sources_default_reversed = 171;
optional SettingProto location_mode = 18;
optional SettingProto location_previous_mode = 19;
optional SettingProto lock_to_app_exit_locked = 20;
optional SettingProto lock_screen_lock_after_timeout = 21;
+ optional SettingProto lock_screen_allow_private_notifications = 172;
optional SettingProto lock_screen_allow_remote_input = 22;
optional SettingProto show_note_about_notification_hiding = 23;
optional SettingProto trust_agents_initialized = 24;
@@ -366,6 +427,11 @@
optional SettingProto parental_control_redirect_url = 27;
optional SettingProto settings_classname = 28;
optional SettingProto accessibility_enabled = 29;
+ optional SettingProto accessibility_shortcut_enabled = 173;
+ optional SettingProto accessibility_shortcut_on_lock_screen = 174;
+ optional SettingProto accessibility_shortcut_dialog_shown = 175;
+ optional SettingProto accessibility_shortcut_target_service = 176;
+ optional SettingProto accessibility_button_target_component = 177;
optional SettingProto touch_exploration_enabled = 30;
optional SettingProto enabled_accessibility_services = 31;
optional SettingProto touch_exploration_granted_accessibility_services = 32;
@@ -375,7 +441,9 @@
optional SettingProto accessibility_screen_reader_url = 36;
optional SettingProto accessibility_web_content_key_bindings = 37;
optional SettingProto accessibility_display_magnification_enabled = 38;
+ optional SettingProto accessibility_display_magnification_navbar_enabled = 178;
optional SettingProto accessibility_display_magnification_scale = 39;
+ optional SettingProto accessibility_display_magnification_auto_update = 179;
optional SettingProto accessibility_soft_keyboard_mode = 40;
optional SettingProto accessibility_captioning_enabled = 41;
optional SettingProto accessibility_captioning_locale = 42;
@@ -448,6 +516,7 @@
optional SettingProto doze_enabled = 109;
optional SettingProto doze_always_on = 110;
optional SettingProto doze_pulse_on_pick_up = 111;
+ optional SettingProto doze_pulse_on_long_press = 180;
optional SettingProto doze_pulse_on_double_tap = 112;
optional SettingProto ui_night_mode = 113;
optional SettingProto screensaver_enabled = 114;
@@ -470,6 +539,7 @@
optional SettingProto immersive_mode_confirmations = 131;
optional SettingProto print_service_search_uri = 132;
optional SettingProto payment_service_search_uri = 133;
+ optional SettingProto autofill_service_search_uri = 181;
optional SettingProto skip_first_use_hints = 134;
optional SettingProto unsafe_volume_music_active_ms = 135;
optional SettingProto lock_screen_show_notifications = 136;
@@ -482,10 +552,18 @@
optional SettingProto camera_gesture_disabled = 143;
optional SettingProto camera_double_tap_power_gesture_disabled = 144;
optional SettingProto camera_double_twist_to_flip_enabled = 145;
+ optional SettingProto camera_lift_trigger_enabled = 182;
+ optional SettingProto assist_gesture_enabled = 183;
+ optional SettingProto assist_gesture_sensitivity = 184;
+ optional SettingProto assist_gesture_silence_alerts_enabled = 185;
+ optional SettingProto assist_gesture_wake_enabled = 186;
+ optional SettingProto assist_gesture_setup_complete = 187;
optional SettingProto night_display_activated = 146;
optional SettingProto night_display_auto_mode = 147;
+ optional SettingProto night_display_color_temperature = 188;
optional SettingProto night_display_custom_start_time = 148;
optional SettingProto night_display_custom_end_time = 149;
+ optional SettingProto night_display_last_activated_time = 189;
optional SettingProto brightness_use_twilight = 150;
optional SettingProto enabled_vr_listeners = 151;
optional SettingProto vr_display_mode = 152;
@@ -495,6 +573,7 @@
optional SettingProto automatic_storage_manager_days_to_retain = 156;
optional SettingProto automatic_storage_manager_bytes_cleared = 157;
optional SettingProto automatic_storage_manager_last_run = 158;
+ optional SettingProto automatic_storage_manager_turned_off_by_policy = 190;
optional SettingProto system_navigation_keys_enabled = 159;
optional SettingProto downloads_backup_enabled = 160;
optional SettingProto downloads_backup_allow_metered = 161;
@@ -504,13 +583,18 @@
optional SettingProto demo_user_setup_complete = 165;
optional SettingProto instant_apps_enabled = 166;
optional SettingProto device_paired = 167;
+ optional SettingProto package_verifier_state = 191;
+ optional SettingProto cmas_additional_broadcast_pkg = 192;
optional SettingProto notification_badging = 168;
+ optional SettingProto qs_auto_added_tiles = 193;
+ optional SettingProto lockdown_in_power_menu = 194;
optional SettingProto backup_manager_constants = 169;
+
+ // Next tag = 195
}
message SystemSettingsProto {
- // Historical operations
- repeated SettingsOperationProto historical_op = 1;
+ repeated SettingsOperationProto historical_operations = 1;
optional SettingProto end_button_behavior = 2;
optional SettingProto advanced_settings = 3;
@@ -518,6 +602,7 @@
optional SettingProto bluetooth_discoverability_timeout = 5;
optional SettingProto font_scale = 6;
optional SettingProto system_locales = 7;
+ optional SettingProto display_color_mode = 67;
optional SettingProto screen_off_timeout = 8;
optional SettingProto screen_brightness = 9;
optional SettingProto screen_brightness_for_vr = 10;
@@ -534,8 +619,17 @@
optional SettingProto volume_alarm = 21;
optional SettingProto volume_notification = 22;
optional SettingProto volume_bluetooth_sco = 23;
+ optional SettingProto volume_accessibility = 68;
optional SettingProto volume_master = 24;
optional SettingProto master_mono = 25;
+ // Whether silent mode should allow vibration feedback. This is used
+ // internally in AudioService and the Sound settings activity to coordinate
+ // decoupling of vibrate and silent modes. This setting will likely be
+ // removed in a future release with support for audio/vibe feedback
+ // profiles.
+ // Not used anymore. On devices with vibrator, the user explicitly selects
+ // silent or vibrate mode. Kept for use by legacy database upgrade code in
+ // DatabaseHelper.
optional SettingProto vibrate_in_silent = 26;
optional SettingProto append_for_last_audible = 27;
optional SettingProto ringtone = 28;
@@ -566,6 +660,10 @@
optional SettingProto notification_light_pulse = 53;
optional SettingProto pointer_location = 54;
optional SettingProto show_touches = 55;
+ // Log raw orientation data from {@link
+ // com.android.server.policy.WindowOrientationListener} for use with the
+ // orientationplot.py tool.
+ // 0 = no, 1 = yes
optional SettingProto window_orientation_listener_log = 56;
optional SettingProto lockscreen_sounds_enabled = 57;
optional SettingProto lockscreen_disabled = 58;
@@ -576,7 +674,10 @@
optional SettingProto pointer_speed = 63;
optional SettingProto lock_to_app_enabled = 64;
optional SettingProto egg_mode = 65;
+ optional SettingProto show_battery_percent = 69;
optional SettingProto when_to_make_wifi_calls = 66;
+
+ // Next tag = 70;
}
message SettingProto {
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index c57cb72..889842c 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -15,11 +15,14 @@
*/
syntax = "proto2";
+
+import "frameworks/base/core/proto/android/app/notification.proto";
import "frameworks/base/core/proto/android/content/intent.proto";
import "frameworks/base/core/proto/android/server/intentresolver.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
import "frameworks/base/core/proto/android/graphics/rect.proto";
import "frameworks/base/core/proto/android/os/looper.proto";
+import "frameworks/base/core/proto/android/util/common.proto";
package com.android.server.am.proto;
@@ -30,11 +33,12 @@
optional BroadcastProto broadcasts = 2;
- optional ServiceProto services = 3;
+ optional ActiveServicesProto services = 3;
optional ProcessProto processes = 4;
}
+// "dumpsys activity --proto activities"
message ActivityStackSupervisorProto {
optional .com.android.server.wm.proto.ConfigurationContainerProto configuration_container = 1;
repeated ActivityDisplayProto displays = 2;
@@ -90,6 +94,7 @@
optional bool keyguard_occluded = 2;
}
+// "dumpsys activity --proto broadcasts"
message BroadcastProto {
repeated ReceiverListProto receiver_list = 1;
@@ -164,10 +169,158 @@
repeated StickyAction actions = 2;
}
-message ServiceProto {
- // TODO: "dumpsys activity --proto services"
+// "dumpsys activity --proto service"
+message ActiveServicesProto {
+
+ message ServicesByUser {
+ optional int32 user_id = 1;
+ repeated ServiceRecordProto service_records = 2;
+ }
+ repeated ServicesByUser services_by_users = 1;
}
+// corresponds to ActivityManagerService.GrantUri Java class
+message GrantUriProto {
+ optional int32 source_user_id = 1;
+ optional string uri = 2;
+}
+
+message NeededUriGrantsProto {
+ optional string target_package = 1;
+ optional int32 target_uid = 2;
+ optional int32 flags = 3;
+
+ repeated GrantUriProto grants = 4;
+}
+
+message UriPermissionOwnerProto {
+ optional string owner = 1;
+ repeated GrantUriProto read_perms = 2;
+ repeated GrantUriProto write_perms = 3;
+}
+
+message ServiceRecordProto {
+ optional string short_name = 1;
+ optional string hex_hash = 2;
+ optional bool is_running = 3; // false if the application service is null
+ optional int32 pid = 4;
+ optional .android.content.IntentProto intent = 5;
+ optional string package_name = 6;
+ optional string process_name = 7;
+ optional string permission = 8;
+
+ message AppInfo {
+ optional string base_dir = 1;
+ optional string res_dir = 2;
+ optional string data_dir = 3;
+ }
+ optional AppInfo appinfo = 9;
+ optional ProcessRecordProto app = 10;
+ optional ProcessRecordProto isolated_proc = 11;
+ optional bool whitelist_manager = 12;
+ optional bool delayed = 13;
+
+ message Foreground {
+ optional int32 id = 1;
+ optional .android.app.NotificationProto notification = 2;
+ }
+ optional Foreground foreground = 14;
+
+ optional .android.util.Duration create_real_time = 15;
+ optional .android.util.Duration starting_bg_timeout = 16;
+ optional .android.util.Duration last_activity_time = 17;
+ optional .android.util.Duration restart_time = 18;
+ optional bool created_from_fg = 19;
+
+ // variables used to track states related to service start
+ message Start {
+ optional bool start_requested = 1;
+ optional bool delayed_stop = 2;
+ optional bool stop_if_killed = 3;
+ optional bool call_start = 4;
+ optional int32 last_start_id = 5;
+ }
+ optional Start start = 20;
+
+ message ExecuteNesting {
+ optional int32 execute_nesting = 1;
+ optional bool execute_fg = 2;
+ optional .android.util.Duration executing_start = 3;
+ }
+ optional ExecuteNesting execute = 21;
+
+ optional .android.util.Duration destory_time = 22;
+
+ message Crash {
+ optional int32 restart_count = 1;
+ optional .android.util.Duration restart_delay = 2;
+ optional .android.util.Duration next_restart_time = 3;
+ optional int32 crash_count = 4;
+ }
+ optional Crash crash = 23;
+
+ message StartItemProto {
+ optional int32 id = 1;
+ optional .android.util.Duration duration = 2;
+ optional int32 delivery_count = 3;
+ optional int32 done_executing_count = 4;
+ optional .android.content.IntentProto intent = 5;
+ optional NeededUriGrantsProto needed_grants = 6;
+ optional UriPermissionOwnerProto uri_permissions = 7;
+ }
+ repeated StartItemProto delivered_starts = 24;
+ repeated StartItemProto pending_starts = 25;
+
+ repeated IntentBindRecordProto bindings = 26;
+ repeated ConnectionRecordProto connections = 27;
+}
+
+message ConnectionRecordProto {
+ optional string hex_hash = 1;
+ optional int32 user_id = 2;
+
+ enum Flag {
+ AUTO_CREATE = 0;
+ DEBUG_UNBIND = 1;
+ NOT_FG = 2;
+ IMPORTANT_BG = 3;
+ ABOVE_CLIENT = 4;
+ ALLOW_OOM_MANAGEMENT = 5;
+ WAIVE_PRIORITY = 6;
+ IMPORTANT = 7;
+ ADJUST_WITH_ACTIVITY = 8;
+ FG_SERVICE_WHILE_WAKE = 9;
+ FG_SERVICE = 10;
+ TREAT_LIKE_ACTIVITY = 11;
+ VISIBLE = 12;
+ SHOWING_UI = 13;
+ NOT_VISIBLE = 14;
+ DEAD = 15;
+ }
+ repeated Flag flags = 3;
+ optional string service_name = 4;
+ optional string conn_hex_hash = 5;
+}
+
+message AppBindRecordProto {
+ optional string hex_hash = 1;
+ optional ProcessRecordProto client = 2;
+ repeated ConnectionRecordProto connections = 3;
+}
+
+message IntentBindRecordProto {
+ optional string hex_hash = 1;
+ optional bool is_create = 2;
+ optional .android.content.IntentProto intent = 3;
+ optional string binder = 4;
+ optional bool requested = 5;
+ optional bool received = 6;
+ optional bool has_bound = 7;
+ optional bool do_rebind = 8;
+
+ repeated AppBindRecordProto apps = 9;
+}
+
+// TODO: "dumpsys activity --proto processes"
message ProcessProto {
- // TODO: "dumpsys activity --proto processes"
}
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index d442acf..d3b2cde 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -145,7 +145,7 @@
optional bool is_holding_display_suspend_blocker = 39;
// Settings and configuration
optional PowerServiceSettingsAndConfigurationDumpProto settings_and_configuration = 40;
- // Sleep timeout in ms
+ // Sleep timeout in ms. This can be -1.
optional sint32 sleep_timeout_ms = 41;
// Screen off timeout in ms
optional int32 screen_off_timeout_ms = 42;
@@ -263,7 +263,7 @@
optional float maximum_screen_dim_ratio_config = 24;
// The screen off timeout setting value in milliseconds.
optional int32 screen_off_timeout_setting_ms = 25;
- // The sleep timeout setting value in milliseconds.
+ // The sleep timeout setting value in milliseconds. Default value is -1.
optional sint32 sleep_timeout_setting_ms = 26;
// The maximum allowable screen off timeout according to the device administration policy.
optional int32 maximum_screen_off_timeout_from_device_admin_ms = 27;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 4d48a42..0228edb 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -48,7 +48,6 @@
/* represents PhoneWindowManager */
message WindowManagerPolicyProto {
- optional .android.graphics.RectProto stable_bounds = 1;
}
/* represents AppTransition */
@@ -101,8 +100,13 @@
optional .android.view.DisplayInfoProto display_info = 10;
optional int32 rotation = 11;
optional ScreenRotationAnimationProto screen_rotation_animation = 12;
+ optional DisplayFramesProto display_frames = 13;
}
+/* represents DisplayFrames */
+message DisplayFramesProto {
+ optional .android.graphics.RectProto stable_bounds = 1;
+}
/* represents DockedStackDividerController */
message DockedStackDividerControllerProto {
diff --git a/core/proto/android/util/common.proto b/core/proto/android/util/common.proto
index 429c3cad..308ef70 100644
--- a/core/proto/android/util/common.proto
+++ b/core/proto/android/util/common.proto
@@ -30,3 +30,13 @@
optional int64 max = 3;
}
+
+/**
+ * Very basic data structure to represent Duration.
+ */
+message Duration {
+
+ optional int64 start_ms = 1;
+
+ optional int64 end_ms = 2;
+}
\ No newline at end of file
diff --git a/core/res/Android.mk b/core/res/Android.mk
index b066929..370a01f 100644
--- a/core/res/Android.mk
+++ b/core/res/Android.mk
@@ -29,6 +29,9 @@
# Framework doesn't need versioning since it IS the platform.
LOCAL_AAPT_FLAGS += --no-auto-version
+# Allow overlay to add resource
+LOCAL_AAPT_FLAGS += --auto-add-overlay
+
# Install this alongside the libraries.
LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7af1b46..86103e4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -307,6 +307,8 @@
<protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
<protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
+ <protected-broadcast android:name="com.android.server.stats.action.TRIGGER_COLLECTION" />
+
<protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
<protected-broadcast android:name="com.android.server.WifiManager.action.START_PNO" />
<protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
@@ -2776,8 +2778,9 @@
android:protectionLevel="signature|appop" />
<!-- Allows an application to request deleting packages. Apps
- targeting APIs greater than 25 must hold this permission in
- order to use {@link android.content.Intent#ACTION_UNINSTALL_PACKAGE}.
+ targeting APIs {@link android.os.Build.VERSION_CODES#P} or greater must hold this
+ permission in order to use {@link android.content.Intent#ACTION_UNINSTALL_PACKAGE} or
+ {@link android.content.pm.PackageInstaller#uninstall}.
<p>Protection level: normal
-->
<permission android:name="android.permission.REQUEST_DELETE_PACKAGES"
@@ -2907,6 +2910,13 @@
<permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
android:protectionLevel="signature" />
+ <!-- Allows an application to collect usage infomation about brightness slider changes.
+ <p>Not for use by third-party applications.</p>
+ TODO: make a System API
+ @hide -->
+ <permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
diff --git a/core/res/res/anim/slide_out_micro.xml b/core/res/res/anim/slide_out_micro.xml
index 01df0da..c647093 100644
--- a/core/res/res/anim/slide_out_micro.xml
+++ b/core/res/res/anim/slide_out_micro.xml
@@ -19,7 +19,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:zAdjustment="normal">
+ android:zAdjustment="top">
<translate android:fromYDelta="0" android:toYDelta="5%p"
android:duration="250"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
diff --git a/core/res/res/drawable-hdpi/ic_print.png b/core/res/res/drawable-hdpi/ic_print.png
deleted file mode 100644
index aaff3dd..0000000
--- a/core/res/res/drawable-hdpi/ic_print.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_print_error.png b/core/res/res/drawable-hdpi/ic_print_error.png
deleted file mode 100644
index 7846a78..0000000
--- a/core/res/res/drawable-hdpi/ic_print_error.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_print.png b/core/res/res/drawable-mdpi/ic_print.png
deleted file mode 100644
index a3954b5..0000000
--- a/core/res/res/drawable-mdpi/ic_print.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_print_error.png b/core/res/res/drawable-mdpi/ic_print_error.png
deleted file mode 100644
index 44109eb..0000000
--- a/core/res/res/drawable-mdpi/ic_print_error.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_print.png b/core/res/res/drawable-xhdpi/ic_print.png
deleted file mode 100644
index 6b55a14..0000000
--- a/core/res/res/drawable-xhdpi/ic_print.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_print_error.png b/core/res/res/drawable-xhdpi/ic_print_error.png
deleted file mode 100644
index c3faa42..0000000
--- a/core/res/res/drawable-xhdpi/ic_print_error.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/ic_print.xml b/core/res/res/drawable/ic_print.xml
new file mode 100644
index 0000000..7aa2513
--- /dev/null
+++ b/core/res/res/drawable/ic_print.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19,8L5,8c-1.66,0 -3,1.34 -3,3v6h4v4h12v-4h4v-6c0,-1.66 -1.34,-3 -3,-3zM16,19L8,19v-5h8v5zM19,12c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM18,3L6,3v4h12L18,3z"
+ android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_print_error.xml b/core/res/res/drawable/ic_print_error.xml
new file mode 100644
index 0000000..37e5152
--- /dev/null
+++ b/core/res/res/drawable/ic_print_error.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M15.73,3L8.27,3L3,8.27v7.46L8.27,21h7.46L21,15.73L21,8.27L15.73,3zM12,17.3c-0.72,0 -1.3,-0.58 -1.3,-1.3 0,-0.72 0.58,-1.3 1.3,-1.3 0.72,0 1.3,0.58 1.3,1.3 0,0.72 -0.58,1.3 -1.3,1.3zM13,13h-2L11,7h2v6z"
+ android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index dddd52b..cea9cd7 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -28,7 +28,7 @@
<string name="unknownName" msgid="6867811765370350269">"غير معروف"</string>
<string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"البريد الصوتي"</string>
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
- <string name="mmiError" msgid="5154499457739052907">"حدثت مشكلة في الاتصال أو أن كود MMI غير صحيح."</string>
+ <string name="mmiError" msgid="5154499457739052907">"حدثت مشكلة في الاتصال أو أن رمز MMI غير صحيح."</string>
<string name="mmiFdnError" msgid="5224398216385316471">"تم تقييد التشغيل لأرقام الاتصال الثابت فقط."</string>
<string name="mmiErrorWhileRoaming" msgid="762488890299284230">"يتعذر تغيير إعدادات إعادة توجيه المكالمات من هاتفك أثناء التجوال."</string>
<string name="serviceEnabled" msgid="8147278346414714315">"تم تمكين الخدمة."</string>
@@ -43,7 +43,7 @@
<string name="mismatchPin" msgid="609379054496863419">"أرقام التعريف الشخصية التي كتبتها غير مطابقة."</string>
<string name="invalidPin" msgid="3850018445187475377">"ادخل رقم تعريف شخصي مكون من ٤ إلى ٨ أرقام."</string>
<string name="invalidPuk" msgid="8761456210898036513">"اكتب رمز PUK مكونًا من ٨ أرقام أو أكثر."</string>
- <string name="needPuk" msgid="919668385956251611">"شريحة SIM مؤمّنة بكود PUK. اكتب كود PUK لإلغاء تأمينها."</string>
+ <string name="needPuk" msgid="919668385956251611">"شريحة SIM مؤمّنة برمز PUK. اكتب رمز PUK لإلغاء تأمينها."</string>
<string name="needPuk2" msgid="4526033371987193070">"اكتب PUK2 لإلغاء تأمين شريحة SIM."</string>
<string name="enablePin" msgid="209412020907207950">"محاولة غير ناجحة، مكّن قفل SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
@@ -139,8 +139,8 @@
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانية"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string>
- <string name="fcComplete" msgid="3118848230966886575">"اكتمل كود الميزة."</string>
- <string name="fcError" msgid="3327560126588500777">"حدثت مشكلة بالاتصال أو أن كود الميزة غير صحيح."</string>
+ <string name="fcComplete" msgid="3118848230966886575">"اكتمل رمز الميزة."</string>
+ <string name="fcError" msgid="3327560126588500777">"حدثت مشكلة بالاتصال أو أن رمز الميزة غير صحيح."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"حسنًا"</string>
<string name="httpError" msgid="7956392511146698522">"حدث خطأ في الشبكة."</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"تعذر العثور على عنوان URL."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 039b6eb..10ead45 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1676,7 +1676,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Другая праца <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Трэцяя праца <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"Каб адмацаваць гэты экран, краніце і ўтрымлівайце кнопкі \"Назад\" і \"Агляд\""</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Гэту праграму нельга адмацаваць"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Экран замацаваны"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Экран адмацаваны"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Запытваць PIN-код перад адмацаваннем"</string>
@@ -1852,14 +1851,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Праверка экстранных паведамленняў"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Адказаць"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"Дзеянні з голасам для гэтай SIM-карты не дапускаюцца"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"Няма SIM-карты для дзеянняў з голасам"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"Дзеянні з голасам для гэтай SIM-карты не дапускаюцца"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Дзеянні з голасам для гэтага тэлефона не дапускаюцца"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Выплыўное акно"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Для гэтага ярлыка патрабуецца найноўшая версія праграмы"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 8e1b5b4..4ad3afb 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1745,7 +1745,7 @@
<string name="app_category_image" msgid="4867854544519846048">"ফটো ও ছবিগুলি"</string>
<string name="app_category_social" msgid="5842783057834965912">"সামাজিক ও যোগাযোগ"</string>
<string name="app_category_news" msgid="7496506240743986873">"খবর ও পত্রিকাগুলি"</string>
- <string name="app_category_maps" msgid="5878491404538024367">"মানচিত্র ও নেভিগেশান"</string>
+ <string name="app_category_maps" msgid="5878491404538024367">"ম্যাপ ও নেভিগেশান"</string>
<string name="app_category_productivity" msgid="3742083261781538852">"উৎপাদনশীলতা"</string>
<string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"ডিভাইসের স্টোরেজ"</string>
<string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"USB ডিবাগিং"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index bb793a5..4e9ab0a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -838,8 +838,8 @@
<string name="permdesc_setAlarm" msgid="316392039157473848">"Permet que l\'aplicació defineixi una alarma en una aplicació de despertador instal·lada. És possible que algunes aplicacions de despertador no incorporin aquesta funció."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"afegeix bústia de veu"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permet que l\'aplicació afegeixi missatges a la safata d\'entrada de la bústia de veu."</string>
- <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modifica els permisos d\'ubicació geogràfica del navegador"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet que l\'aplicació modifiqui els permisos d\'ubicació geogràfica del navegador. Les aplicacions malicioses poden utilitzar-ho per enviar la informació d\'ubicació a llocs web arbitraris."</string>
+ <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modifica els permisos d\'ubicació del navegador"</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet que l\'aplicació modifiqui els permisos d\'ubicació del navegador. Les aplicacions malicioses poden utilitzar-ho per enviar la informació d\'ubicació a llocs web arbitraris."</string>
<string name="save_password_message" msgid="767344687139195790">"Voleu que el navegador recordi aquesta contrasenya?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Ara no"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Recorda-ho"</string>
@@ -1203,7 +1203,7 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTEIX"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REBUTJA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Canvia el teclat"</string>
- <string name="show_ime" msgid="2506087537466597099">"Mantén-lo a la pantalla mentre el teclat físic està actiu"</string>
+ <string name="show_ime" msgid="2506087537466597099">"Mantén-lo en pantalla mentre el teclat físic està actiu"</string>
<string name="hardware" msgid="194658061510127999">"Mostra el teclat virtual"</string>
<string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configura el teclat físic"</string>
<string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Toca per seleccionar l\'idioma i el disseny"</string>
@@ -1289,7 +1289,7 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fons de pantalla"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Canvia el fons de pantalla"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Oient de notificacions"</string>
- <string name="vr_listener_binding_label" msgid="4316591939343607306">"Processador d\'RV"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Processador de RV"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Proveïdor de condicions"</string>
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Servei de classificació de notificacions"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 07450b2..e185683 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -979,16 +979,11 @@
<string name="inputMethod" msgid="1653630062304567879">"Input method"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Text actions"</string>
<string name="email" msgid="4560673117055050403">"Email"</string>
- <!-- no translation found for dial (1253998302767701559) -->
- <skip />
- <!-- no translation found for map (6521159124535543457) -->
- <skip />
- <!-- no translation found for browse (1245903488306147205) -->
- <skip />
- <!-- no translation found for sms (4560537514610063430) -->
- <skip />
- <!-- no translation found for add_contact (7867066569670597203) -->
- <skip />
+ <string name="dial" msgid="1253998302767701559">"Call"</string>
+ <string name="map" msgid="6521159124535543457">"Locate"</string>
+ <string name="browse" msgid="1245903488306147205">"Open"</string>
+ <string name="sms" msgid="4560537514610063430">"Message"</string>
+ <string name="add_contact" msgid="7867066569670597203">"Add"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -998,7 +993,6 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancel"</string>
- <string name="close" msgid="2318214661230355730">"CLOSE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Loading…"</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1055,8 +1049,6 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scale"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Always show"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Re-enable this in System settings > Apps > Downloaded."</string>
- <string name="top_app_killed_title" msgid="6814231368167994497">"App isn\'t responding"</string>
- <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> may be using too much memory."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Always show"</string>
<string name="smv_application" msgid="3307209192155442829">"The app <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has violated its self-enforced Strict Mode policy."</string>
@@ -1634,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2nd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3rd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"To unpin this screen, touch & hold Back and Overview buttons"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"This app can\'t be unpinned"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Screen pinned"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Screen unpinned"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Ask for PIN before unpinning"</string>
@@ -1790,18 +1781,14 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Emergency messages test"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Reply"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <string name="mmcc_authentication_reject" msgid="7729819349669603406">"SIM not allowed"</string>
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"SIM not provisioned"</string>
- <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
- <string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM not allowed for voice"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM not provisioned for voice"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM not allowed for voice"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Phone not allowed for voice"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <!-- no translation found for shortcut_restored_on_lower_version (5270675146351613828) -->
- <skip />
- <!-- no translation found for shortcut_restore_not_supported (5028808567940014190) -->
- <skip />
- <!-- no translation found for shortcut_restore_signature_mismatch (2406209324521327518) -->
- <skip />
- <!-- no translation found for shortcut_restore_unknown_issue (8703738064603262597) -->
- <skip />
+ <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"This shortcut requires latest app"</string>
+ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
+ <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
</resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 07450b2..e185683 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -979,16 +979,11 @@
<string name="inputMethod" msgid="1653630062304567879">"Input method"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Text actions"</string>
<string name="email" msgid="4560673117055050403">"Email"</string>
- <!-- no translation found for dial (1253998302767701559) -->
- <skip />
- <!-- no translation found for map (6521159124535543457) -->
- <skip />
- <!-- no translation found for browse (1245903488306147205) -->
- <skip />
- <!-- no translation found for sms (4560537514610063430) -->
- <skip />
- <!-- no translation found for add_contact (7867066569670597203) -->
- <skip />
+ <string name="dial" msgid="1253998302767701559">"Call"</string>
+ <string name="map" msgid="6521159124535543457">"Locate"</string>
+ <string name="browse" msgid="1245903488306147205">"Open"</string>
+ <string name="sms" msgid="4560537514610063430">"Message"</string>
+ <string name="add_contact" msgid="7867066569670597203">"Add"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -998,7 +993,6 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancel"</string>
- <string name="close" msgid="2318214661230355730">"CLOSE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Loading…"</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1055,8 +1049,6 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scale"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Always show"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Re-enable this in System settings > Apps > Downloaded."</string>
- <string name="top_app_killed_title" msgid="6814231368167994497">"App isn\'t responding"</string>
- <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> may be using too much memory."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Always show"</string>
<string name="smv_application" msgid="3307209192155442829">"The app <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has violated its self-enforced Strict Mode policy."</string>
@@ -1634,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2nd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3rd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"To unpin this screen, touch & hold Back and Overview buttons"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"This app can\'t be unpinned"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Screen pinned"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Screen unpinned"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Ask for PIN before unpinning"</string>
@@ -1790,18 +1781,14 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Emergency messages test"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Reply"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <string name="mmcc_authentication_reject" msgid="7729819349669603406">"SIM not allowed"</string>
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"SIM not provisioned"</string>
- <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
- <string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM not allowed for voice"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM not provisioned for voice"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM not allowed for voice"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Phone not allowed for voice"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <!-- no translation found for shortcut_restored_on_lower_version (5270675146351613828) -->
- <skip />
- <!-- no translation found for shortcut_restore_not_supported (5028808567940014190) -->
- <skip />
- <!-- no translation found for shortcut_restore_signature_mismatch (2406209324521327518) -->
- <skip />
- <!-- no translation found for shortcut_restore_unknown_issue (8703738064603262597) -->
- <skip />
+ <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"This shortcut requires latest app"</string>
+ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
+ <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 07450b2..e185683 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -979,16 +979,11 @@
<string name="inputMethod" msgid="1653630062304567879">"Input method"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Text actions"</string>
<string name="email" msgid="4560673117055050403">"Email"</string>
- <!-- no translation found for dial (1253998302767701559) -->
- <skip />
- <!-- no translation found for map (6521159124535543457) -->
- <skip />
- <!-- no translation found for browse (1245903488306147205) -->
- <skip />
- <!-- no translation found for sms (4560537514610063430) -->
- <skip />
- <!-- no translation found for add_contact (7867066569670597203) -->
- <skip />
+ <string name="dial" msgid="1253998302767701559">"Call"</string>
+ <string name="map" msgid="6521159124535543457">"Locate"</string>
+ <string name="browse" msgid="1245903488306147205">"Open"</string>
+ <string name="sms" msgid="4560537514610063430">"Message"</string>
+ <string name="add_contact" msgid="7867066569670597203">"Add"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -998,7 +993,6 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancel"</string>
- <string name="close" msgid="2318214661230355730">"CLOSE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Loading…"</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1055,8 +1049,6 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scale"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Always show"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Re-enable this in System settings > Apps > Downloaded."</string>
- <string name="top_app_killed_title" msgid="6814231368167994497">"App isn\'t responding"</string>
- <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> may be using too much memory."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Always show"</string>
<string name="smv_application" msgid="3307209192155442829">"The app <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has violated its self-enforced Strict Mode policy."</string>
@@ -1634,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2nd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3rd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"To unpin this screen, touch & hold Back and Overview buttons"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"This app can\'t be unpinned"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Screen pinned"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Screen unpinned"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Ask for PIN before unpinning"</string>
@@ -1790,18 +1781,14 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Emergency messages test"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Reply"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <string name="mmcc_authentication_reject" msgid="7729819349669603406">"SIM not allowed"</string>
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"SIM not provisioned"</string>
- <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
- <string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM not allowed for voice"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM not provisioned for voice"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM not allowed for voice"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Phone not allowed for voice"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <!-- no translation found for shortcut_restored_on_lower_version (5270675146351613828) -->
- <skip />
- <!-- no translation found for shortcut_restore_not_supported (5028808567940014190) -->
- <skip />
- <!-- no translation found for shortcut_restore_signature_mismatch (2406209324521327518) -->
- <skip />
- <!-- no translation found for shortcut_restore_unknown_issue (8703738064603262597) -->
- <skip />
+ <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"This shortcut requires latest app"</string>
+ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
+ <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 07450b2..e185683 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -979,16 +979,11 @@
<string name="inputMethod" msgid="1653630062304567879">"Input method"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Text actions"</string>
<string name="email" msgid="4560673117055050403">"Email"</string>
- <!-- no translation found for dial (1253998302767701559) -->
- <skip />
- <!-- no translation found for map (6521159124535543457) -->
- <skip />
- <!-- no translation found for browse (1245903488306147205) -->
- <skip />
- <!-- no translation found for sms (4560537514610063430) -->
- <skip />
- <!-- no translation found for add_contact (7867066569670597203) -->
- <skip />
+ <string name="dial" msgid="1253998302767701559">"Call"</string>
+ <string name="map" msgid="6521159124535543457">"Locate"</string>
+ <string name="browse" msgid="1245903488306147205">"Open"</string>
+ <string name="sms" msgid="4560537514610063430">"Message"</string>
+ <string name="add_contact" msgid="7867066569670597203">"Add"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -998,7 +993,6 @@
<string name="cancel" msgid="6442560571259935130">"Cancel"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Cancel"</string>
- <string name="close" msgid="2318214661230355730">"CLOSE"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
<string name="loading" msgid="7933681260296021180">"Loading…"</string>
<string name="capital_on" msgid="1544682755514494298">"ON"</string>
@@ -1055,8 +1049,6 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Scale"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Always show"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Re-enable this in System settings > Apps > Downloaded."</string>
- <string name="top_app_killed_title" msgid="6814231368167994497">"App isn\'t responding"</string>
- <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> may be using too much memory."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> does not support the current Display size setting and may behave unexpectedly."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Always show"</string>
<string name="smv_application" msgid="3307209192155442829">"The app <xliff:g id="APPLICATION">%1$s</xliff:g> (process <xliff:g id="PROCESS">%2$s</xliff:g>) has violated its self-enforced Strict Mode policy."</string>
@@ -1634,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2nd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3rd Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"To unpin this screen, touch & hold Back and Overview buttons"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"This app can\'t be unpinned"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Screen pinned"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Screen unpinned"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Ask for PIN before unpinning"</string>
@@ -1790,18 +1781,14 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Emergency messages test"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Reply"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <string name="mmcc_authentication_reject" msgid="7729819349669603406">"SIM not allowed"</string>
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"SIM not provisioned"</string>
- <string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
- <string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM not allowed for voice"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM not provisioned for voice"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM not allowed for voice"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Phone not allowed for voice"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
- <!-- no translation found for shortcut_restored_on_lower_version (5270675146351613828) -->
- <skip />
- <!-- no translation found for shortcut_restore_not_supported (5028808567940014190) -->
- <skip />
- <!-- no translation found for shortcut_restore_signature_mismatch (2406209324521327518) -->
- <skip />
- <!-- no translation found for shortcut_restore_unknown_issue (8703738064603262597) -->
- <skip />
+ <string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"This shortcut requires latest app"</string>
+ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string>
+ <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 9ec963b..878f9eb 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -273,7 +273,7 @@
<string name="permgrouprequest_sms" msgid="605618939583628306">"Anna sovellukselle <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> lupa lähettää ja katsella tekstiviestejä."</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Tallennustila"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"käyttää laitteellesi tallennettuja valokuvia, mediatiedostoja ja muita tiedostoja"</string>
- <string name="permgrouprequest_storage" msgid="7429669910547860218">"Anna <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> lupa käyttää laitteellasi olevia kuvia, mediaa ja tiedostoja."</string>
+ <string name="permgrouprequest_storage" msgid="7429669910547860218">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tarvitsee sinulta luvan käyttää laitteellasi olevia kuvia, mediaa ja tiedostoja"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofoni"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"tallentaa ääntä"</string>
<string name="permgrouprequest_microphone" msgid="8065941268709600606">"Anna sovellukselle <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> lupa nauhoittaa ääntä."</string>
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Toinen <xliff:g id="LABEL">%1$s</xliff:g>, työ"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Kolmas <xliff:g id="LABEL">%1$s</xliff:g>, työ"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"Irrota näyttö koskettamalla pitkään Takaisin- ja Viimeisimmät-painikkeita"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Tätä sovellusta ei voi irrottaa"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Näyttö kiinnitetty"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Näyttö irrotettu"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Pyydä PIN ennen irrotusta"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Hätäilmoitustesti"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Vastaa"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"Äänipalvelujen käyttö ei sallittua SIM-kortilla"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM-kortti ei käyttäjien hallinnassa"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"Äänipalvelujen käyttö ei sallittua SIM-kortilla"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Äänipalvelujen käyttö ei sallittua puhelimella"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Ponnahdusikkuna"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Tämä pikakuvake edellyttää uusinta sovellusta."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 16283e7..658e951 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -217,7 +217,7 @@
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Rapport interactif"</string>
<string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Utilisez cette option dans la plupart des circonstances. Elle vous permet de suivre la progression du rapport, d\'entrer plus d\'information sur le problème et d\'effectuer des saisies d\'écran. Certaines sections moins utilisées et dont le remplissage demande beaucoup de temps peuvent être omises."</string>
<string name="bugreport_option_full_title" msgid="6354382025840076439">"Rapport complet"</string>
- <string name="bugreport_option_full_summary" msgid="7210859858969115745">"Utilisez cette option pour qu\'il y ait le moins d\'interférences système possible lorsque votre appareil ne répond pas ou qu\'il est trop lent, ou lorsque vous avez besoin de toutes les sections du rapport de bogue. Aucune saisie d\'écran supplémentaire ne peut être capturée, et vous ne pouvez entrer aucune autre information."</string>
+ <string name="bugreport_option_full_summary" msgid="7210859858969115745">"Utilisez cette option pour qu\'il y ait le moins d\'interférences système possible lorsque votre appareil ne répond pas ou qu\'il est trop lent, ou lorsque vous avez besoin de toutes les sections du rapport de bogue. Aucune capture d\'écran supplémentaire ne peut être capturée, et vous ne pouvez entrer aucune autre information."</string>
<plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368">
<item quantity="one">Saisie d\'écran pour le rapport de bogue dans <xliff:g id="NUMBER_1">%d</xliff:g> seconde.</item>
<item quantity="other">Saisie d\'écran pour le rapport de bogue dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes.</item>
@@ -1169,7 +1169,7 @@
<string name="sim_done_button" msgid="827949989369963775">"Terminé"</string>
<string name="sim_added_title" msgid="3719670512889674693">"Carte SIM ajoutée."</string>
<string name="sim_added_message" msgid="6599945301141050216">"Redémarrez votre appareil pour accéder au réseau mobile."</string>
- <string name="sim_restart_button" msgid="4722407842815232347">"Recommencer"</string>
+ <string name="sim_restart_button" msgid="4722407842815232347">"Redémarrer"</string>
<string name="carrier_app_dialog_message" msgid="7066156088266319533">"Pour que la nouvelle carte SIM fonctionne correctement, vous devez installer et ouvrir une application fournie par votre fournisseur de services."</string>
<string name="carrier_app_dialog_button" msgid="7900235513678617329">"TÉLÉCHARGER L\'APPLICATION"</string>
<string name="carrier_app_dialog_not_now" msgid="6361378684292268027">"PAS MAINTENANT"</string>
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2e <xliff:g id="LABEL">%1$s</xliff:g> professionnel(le)"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3e <xliff:g id="LABEL">%1$s</xliff:g> professionnel(le)"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"Pour annuler l\'épinglage de cet écran, maintenez enfoncés les boutons Retour et Aperçu."</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Impossible d\'annuler l\'épinglage de cette application"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Écran épinglé"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Épinglage d\'écran annulé"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Demander le NIP avant d\'annuler l\'épinglage"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Test de messages d\'urgence"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Répondre"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"Cette carte SIM n\'est pas autorisée pour la voix"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"Cette carte SIM n\'est pas autorisée pour la voix"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"Cette carte SIM n\'est pas autorisée pour la voix"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Ce téléphone n\'est pas autorisé pour la voix"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Fenêtre contextuelle"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Ce raccourci nécessite la dernière version de l\'application"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index e3b93cb..d0f100a 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2.º <xliff:g id="LABEL">%1$s</xliff:g> do traballo"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3.º <xliff:g id="LABEL">%1$s</xliff:g> do traballo"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"Para soltar a pantalla, mantén premidos os botóns Volver e Visión xeral."</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Esta aplicación non se pode soltar"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Pantalla fixada"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Pantalla desactivada"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Solicitar un PIN antes de soltar a pantalla"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Proba de mensaxes de urxencia"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Responder"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"Non se permite a tarxeta SIM para as accións de voz"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"Non se introduciu ningunha tarxeta SIM para as accións de voz"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"Non se permite a tarxeta SIM para as accións de voz"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Non se permite o teléfono para as accións de voz"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Ventá emerxente"</string>
<string name="slice_more_content" msgid="8504342889413274608">"<xliff:g id="NUMBER">%1$d</xliff:g> máis"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Para utilizar este atallo, necesítase a versión máis recente da aplicación"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index d1c3700..1e9b38c 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2જું કાર્ય <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3જું કાર્ય <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"આ સ્ક્રીનને અનપિન કરવા માટે, પાછળ અને ઝલક બટનોને સ્પર્શ કરી રાખો"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"આ ઍપ્લિકેશનને અનપિન કરી શકાશે નહીં"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"સ્ક્રીન પિન કરી"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"સ્ક્રીન અનપિન કરી"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"અનપિન કરતા પહેલાં પિન માટે પૂછો"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"કટોકટી સંદેશાઓનું પરીક્ષણ"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"જવાબ આપો"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"વૉઇસ માટે આ સિમને મંજૂરી નથી"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"આ સિમમાં વૉઇસ માટે કોઈ જોગવાઈ નથી"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"વૉઇસ માટે આ સિમને મંજૂરી નથી"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"વૉઇસ માટે આ ફોનને મંજૂરી નથી"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"પૉપઅપ વિંડો"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"આ શૉર્ટકટ માટે નવી ઍપ જરૂરી છે"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 11a8820..f24ee79 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -525,7 +525,7 @@
<string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"नेटवर्क उपयोग हिसाब बदलें"</string>
<string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"ऐप्स को यह संशोधित करने देता है कि ऐप्स की तुलना में नेटवर्क उपयोग का मूल्यांकन कैसे किया जाता है. सामान्य ऐप्स द्वारा उपयोग करने के लिए नहीं."</string>
<string name="permlab_accessNotifications" msgid="7673416487873432268">"सूचना तक पहुंचें"</string>
- <string name="permdesc_accessNotifications" msgid="458457742683431387">"ऐप को सूचना पाने, जांच करने और साफ़ करने देता है, जिनमें अन्य ऐप के ज़रिए पोस्ट की गई सूचनाएं भी शामिल हैं."</string>
+ <string name="permdesc_accessNotifications" msgid="458457742683431387">"ऐप को सूचना पाने, जाँच करने और साफ़ करने देता है, जिनमें अन्य ऐप के ज़रिए पोस्ट की गई सूचनाएं भी शामिल हैं."</string>
<string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"सूचना श्रवणकर्ता सेवा से जुड़ें"</string>
<string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"उपयोगकर्ता को सूचना सुनने वाली सेवा के सबसे बेहतर इंटरफ़ेस से जुड़ने देती है. सामान्य ऐप के लिए कभी भी इसकी ज़रुरत नहीं होगी."</string>
<string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"किसी शर्तें लागू करने वाली (कंडीशन प्रोवाइडर) सेवा से जुड़ें"</string>
@@ -760,7 +760,7 @@
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"साइन इन करें"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"उपयोगकर्ता नाम या पासवर्ड गलत है."</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"अपना उपयोगकर्ता नाम या पासवर्ड भूल गए?\n "<b>"google.com/accounts/recovery"</b>" पर जाएं."</string>
- <string name="lockscreen_glogin_checking_password" msgid="7114627351286933867">"जांच रहा है…"</string>
+ <string name="lockscreen_glogin_checking_password" msgid="7114627351286933867">"जाँच रहा है…"</string>
<string name="lockscreen_unlock_label" msgid="737440483220667054">"अनलॉक करें"</string>
<string name="lockscreen_sound_on_label" msgid="9068877576513425970">"ध्वनि चालू करें"</string>
<string name="lockscreen_sound_off_label" msgid="996822825154319026">"ध्वनि बंद"</string>
@@ -1215,7 +1215,7 @@
<string name="alert_windows_notification_message" msgid="8917232109522912560">"अगर आप नहीं चाहते कि <xliff:g id="NAME">%s</xliff:g> इस सुविधा का उपयोग करे, तो सेटिंग खोलने और उसे बंद करने के लिए टैप करें."</string>
<string name="alert_windows_notification_turn_off_action" msgid="3367294525884949878">"बंद करें"</string>
<string name="ext_media_checking_notification_title" msgid="5734005953288045806">"<xliff:g id="NAME">%s</xliff:g> को तैयार किया जा रहा है"</string>
- <string name="ext_media_checking_notification_message" msgid="4747432538578886744">"त्रुटियों की जांच कर रहा है"</string>
+ <string name="ext_media_checking_notification_message" msgid="4747432538578886744">"त्रुटियों की जाँच कर रहा है"</string>
<string name="ext_media_new_notification_message" msgid="7589986898808506239">"नए <xliff:g id="NAME">%s</xliff:g> का पता लगा"</string>
<string name="ext_media_ready_notification_message" msgid="4083398150380114462">"फ़ोटो और मीडिया ट्रांसफर करने के लिए"</string>
<string name="ext_media_unmountable_notification_title" msgid="8295123366236989588">"दूषित <xliff:g id="NAME">%s</xliff:g>"</string>
@@ -1243,7 +1243,7 @@
<string name="ext_media_move_failure_message" msgid="1978096440816403360">"डेटा शुरुआती जगह पर छूट गया है"</string>
<string name="ext_media_status_removed" msgid="6576172423185918739">"निकाल दिया गया"</string>
<string name="ext_media_status_unmounted" msgid="2551560878416417752">"निकाला गया"</string>
- <string name="ext_media_status_checking" msgid="6193921557423194949">"जांच की जा रही है..."</string>
+ <string name="ext_media_status_checking" msgid="6193921557423194949">"जाँच की जा रही है..."</string>
<string name="ext_media_status_mounted" msgid="7253821726503179202">"तैयार"</string>
<string name="ext_media_status_mounted_ro" msgid="8020978752406021015">"केवल-पढ़ने के लिए"</string>
<string name="ext_media_status_bad_removal" msgid="8395398567890329422">"असुरक्षित रूप से निकाला गया"</string>
@@ -1475,7 +1475,7 @@
<string name="kg_login_submit_button" msgid="5355904582674054702">"साइन इन करें"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"उपयोगकर्ता नाम या पासवर्ड गलत है"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"अपना उपयोगकर्ता नाम या पासवर्ड भूल गए?\n "<b>"google.com/accounts/recovery"</b>" पर जाएं."</string>
- <string name="kg_login_checking_password" msgid="1052685197710252395">"खाते की जांच की जा रही है…"</string>
+ <string name="kg_login_checking_password" msgid="1052685197710252395">"खाते की जाँच की जा रही है…"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"आप अपना PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"आप अपना पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से आरेखित किया है. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string>
@@ -1789,6 +1789,6 @@
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"इस शॉर्टकट वाला ऐप चलाने के लिए इसका नया वर्शन डाउनलोड करें"</string>
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"शॉर्टकट बहाल नहीं किया जा सका क्योंकि इस ऐप में बैकअप लेने और उसे बहाल करने की सुविधा नहीं है"</string>
- <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ऐप का हस्ताक्षर अलग होने के कारण शॉर्टकट बहाल नहीं किया जा सका"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ऐप सिग्नेचर अलग होने के कारण शॉर्टकट बहाल नहीं किया जा सका"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"शॉर्टकट बहाल नहीं किया जा सका"</string>
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index f02a7ba..609bef0 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2 ನೇ ಕೆಲಸದ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3 ನೇ ಕೆಲಸದ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"ಈ ಪರದೆಯನ್ನು ಅನ್ಪಿನ್ ಮಾಡಲು, ಹಿಂದಕ್ಕೆ ಮತ್ತು ಸಮಗ್ರ ನೋಟ ಬಟನ್ಗಳನ್ನು ಸ್ಪರ್ಶಿಸಿ ಒತ್ತಿಹಿಡಿಯಿರಿ"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅನ್ಪಿನ್ ಮಾಡಲಾಗುವುದಿಲ್ಲ"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"ಸ್ಕ್ರೀನ್ ಪಿನ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"ಸ್ಕ್ರೀನ್ ಅನ್ಪಿನ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"ಅನ್ಪಿನ್ ಮಾಡಲು ಪಿನ್ ಕೇಳು"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"ತುರ್ತು ಸಂದೇಶಗಳ ಪರೀಕ್ಷೆ"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"ಪ್ರತ್ಯುತ್ತರ"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"ಧ್ವನಿಗಾಗಿ ಸಿಮ್ ಅನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"ಧ್ವನಿಗಾಗಿ ಸಿಮ್ ಸಿದ್ಧವಾಗಿಲ್ಲ"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"ಧ್ವನಿಗಾಗಿ ಸಿಮ್ ಅನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"ಧ್ವನಿಗಾಗಿ ಫೋನ್ ಅನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ಪಾಪ್ಅಪ್ ವಿಂಡೋ"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ಈ ಶಾರ್ಟ್ಕಟ್ಗೆ ಇತ್ತೀಚಿನ ಅಪ್ಲಿಕೇಶನ್ ಅಗತ್ಯವಿದೆ"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index ae897b3..8fb570b 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"രണ്ടാമത്തെ ഔദ്യോഗിക <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"മൂന്നാമത്തെ ഔദ്യോഗിക <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"ഈ സ്ക്രീൻ അൺപിൻ ചെയ്യാൻ, ബാക്ക്, ചുരുക്കവിവരണം എന്നീ ബട്ടണുകൾ സ്പർശിച്ച് പിടിക്കുക"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"ഈ ആപ്പ് അൺപിൻ ചെയ്യാൻ കഴിയില്ല"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"സ്ക്രീൻ പിൻ ചെയ്തു"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"സ്ക്രീൻ അൺപിൻ ചെയ്തു"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"ചെയ്യുംമുമ്പ് പിൻ ചോദിക്കൂ"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"അടിയന്തര സന്ദേശ ടെസ്റ്റ്"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"മറുപടി നൽകുക"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"സിം വോയ്സിന് അനുവദനീയമല്ല"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"സിം വോയ്സിന് പ്രൊവിഷൻ ചെയ്തിട്ടില്ല"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"സിം വോയ്സിന് അനുവദനീയമല്ല"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"ഫോൺ വോയ്സിന് അനുവദനീയമല്ല"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"പോപ്പ് അപ്പ് വിൻഡോ"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ഈ കുറുക്കുവഴിക്ക് ഏറ്റവും പുതിയ ആപ്പ് ആവശ്യമാണ്"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 7de97b3..c41547b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1199,8 +1199,8 @@
<string name="usb_notification_message" msgid="3370903770828407960">"थप विकल्पहरूका लागि ट्याप गर्नुहोस्।"</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"एनालग अडियोको सहायक उपकरण पत्ता लाग्यो"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"संलग्न गरिएको यन्त्र यो फोनसँग कम्प्याटिबल छैन। थप जान्न ट्याप गर्नुहोस्।"</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"USB डिबग गर्ने जडित छ"</string>
- <string name="adb_active_notification_message" msgid="4948470599328424059">"USB डिबगिङलाई असक्षम गर्न ट्याप गर्नुहोस्।"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB डिबग गर्ने सुविधा सुचारू छ"</string>
+ <string name="adb_active_notification_message" msgid="4948470599328424059">"USB डिबग गर्ने सुविधालाई असक्षम गर्न ट्याप गर्नुहोस्।"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB डिबगिङलाई असक्षम पार्न ट्याप गर्नुहोस्।"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"बग रिपोर्ट लिँदै..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"बग रिपोर्टलाई साझेदारी गर्ने हो?"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index d6233fb..d269fac 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"ਦੂਸਰੀ ਕਾਰਜ-ਸਥਾਨ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"ਤੀਸਰੀ ਕਾਰਜ-ਸਥਾਨ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"ਇਸ ਸਕ੍ਰੀਨ ਨੂੰ ਅਨਪਿੰਨ ਕਰਨ ਲਈ, \'ਪਿੱਛੇ\' ਅਤੇ \'ਰੂਪ-ਰੇਖਾ\' ਬਟਨਾਂ ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"ਇਸ ਐਪ ਨੂੰ ਅਨਪਿੰਨ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"ਸਕ੍ਰੀਨ ਪਿੰਨ ਕੀਤੀ"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"ਸਕ੍ਰੀਨ ਅਨਪਿਨ ਕੀਤੀ"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"ਅਨਪਿੰਨ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਪਿੰਨ ਮੰਗੋ"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"ਸੰਕਟਕਾਲੀਨ ਸੰਦੇਸ਼ ਟੈਸਟ"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"ਜਵਾਬ ਦਿਓ"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"ਸਿਮ ਰਾਹੀਂ ਅਵਾਜ਼ੀ ਕਾਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"ਸਿਮ ਰਾਹੀਂ ਅਵਾਜ਼ੀ ਕਾਲ ਕਰਨ ਦੀ ਵਿਵਸਥਾ ਨਹੀਂ ਹੈ"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"ਸਿਮ ਰਾਹੀਂ ਅਵਾਜ਼ੀ ਕਾਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"ਫ਼ੋਨ ਰਾਹੀਂ ਅਵਾਜ਼ੀ ਕਾਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ਪੌਪਅੱਪ ਵਿੰਡੋ"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ਇਸ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਨਵੀਨਤਮ ਐਪ ਦੀ ਲੋੜ ਹੈ"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d088cdd..03c4536a 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Segundo <xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Terceiro <xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"Para liberar esta tela, mantenha os botões Voltar e Visão geral pressionados"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Não é possível liberar este app"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Tela fixada"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Tela liberada"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Pedir PIN antes de liberar"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Teste de mensagens de emergência"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Responder"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM não autorizado para voz"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM não aprovisionado para voz"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM não autorizado para voz"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Smartphone não autorizado para voz"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
<string name="slice_more_content" msgid="8504342889413274608">"Mais <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Esse atalho requer o app mais recente"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d088cdd..03c4536a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"Segundo <xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"Terceiro <xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"Para liberar esta tela, mantenha os botões Voltar e Visão geral pressionados"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Não é possível liberar este app"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Tela fixada"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Tela liberada"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Pedir PIN antes de liberar"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Teste de mensagens de emergência"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Responder"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM não autorizado para voz"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM não aprovisionado para voz"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM não autorizado para voz"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Smartphone não autorizado para voz"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
<string name="slice_more_content" msgid="8504342889413274608">"Mais <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Esse atalho requer o app mais recente"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 0afa413..d65eaa7 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1651,7 +1651,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"<xliff:g id="LABEL">%1$s</xliff:g> pentru serviciu (2)"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"<xliff:g id="LABEL">%1$s</xliff:g> pentru serviciu (3)"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"Pentru a anula fixarea acestui ecran, atingeți lung butoanele Înapoi și Recente"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Nu se poate anula fixarea acestei aplicații"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Ecran fixat"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Fixarea ecranului anulată"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Solicită codul PIN înainte de a anula fixarea"</string>
@@ -1817,14 +1816,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Testarea mesajelor de urgență"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Răspundeți"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"Cardul SIM nu este permis pentru voce"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"Cardul SIM nu este activat pentru voce"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"Cardul SIM nu este permis pentru voce"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefonul nu este permis pentru voce"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Fereastră pop-up"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Această comandă rapidă necesită cea mai recentă aplicație"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 0eaaf91..c866c4c 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -172,7 +172,7 @@
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Wasifu wako wa kazini haupatikani tena kwenye kifaa hiki"</string>
<string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Umejaribu kuweka nenosiri mara nyingi mno"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Kifaa kinadhibitiwa"</string>
- <string name="network_logging_notification_text" msgid="7930089249949354026">"Shirika lako linadhibiti kifaa hiki na huenda likafuatilia shughuli kwenye mtandao. Gonga ili upate maelezo zaidi."</string>
+ <string name="network_logging_notification_text" msgid="7930089249949354026">"Shirika lako linadhibiti kifaa hiki na huenda likafuatilia shughuli kwenye mtandao. Gusa ili upate maelezo zaidi."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Data iliyomo kwenye kifaa chako itafutwa"</string>
<string name="factory_reset_message" msgid="7972496262232832457">"Huwezi kutumia programu ya msimamizi. Sasa data iliyo kwenye kifaa chako itafutwa.\n\nIkiwa una maswali yoyote, wasiliana na msimamizi wa shirika lako."</string>
<string name="me" msgid="6545696007631404292">"Mimi"</string>
@@ -251,7 +251,7 @@
<string name="notification_channel_foreground_service" msgid="3931987440602669158">"Programu zinazotumia betri"</string>
<string name="foreground_service_app_in_background" msgid="1060198778219731292">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumia betri"</string>
<string name="foreground_service_apps_in_background" msgid="7175032677643332242">"Programu <xliff:g id="NUMBER">%1$d</xliff:g> zinatumia betri"</string>
- <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Gonga ili upate maelezo kuhusu betri na matumizi ya data"</string>
+ <string name="foreground_service_tap_for_details" msgid="372046743534354644">"Gusa ili upate maelezo kuhusu betri na matumizi ya data"</string>
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"Mtindo salama"</string>
<string name="android_system_label" msgid="6577375335728551336">"Mfumo wa Android"</string>
@@ -807,7 +807,7 @@
<string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Bakia kwenye Ukurasa huu"</string>
<string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nJe, una uhakika unataka kutoka kwenye ukurasa huu?"</string>
<string name="save_password_label" msgid="6860261758665825069">"Thibitisha"</string>
- <string name="double_tap_toast" msgid="4595046515400268881">"Kidokezo: Gonga mara mbili ili kukuza ndani na nje."</string>
+ <string name="double_tap_toast" msgid="4595046515400268881">"Kidokezo: Gusa mara mbili ili kukuza ndani na nje."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Kujaza kiotomatiki"</string>
<string name="setup_autofill" msgid="7103495070180590814">"Weka uwezo wa kujaza kiotomatiki"</string>
<string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string>
@@ -971,7 +971,7 @@
<string name="undo" msgid="7905788502491742328">"Tendua"</string>
<string name="redo" msgid="7759464876566803888">"Rejesha"</string>
<string name="autofill" msgid="3035779615680565188">"Kujaza kiotomatiki"</string>
- <string name="textSelectionCABTitle" msgid="5236850394370820357">"Uchaguzi wa maandishi?"</string>
+ <string name="textSelectionCABTitle" msgid="5236850394370820357">"Maandishi yaliyoteuliwa"</string>
<string name="addToDictionary" msgid="4352161534510057874">"Ongeza kwenye kamusi"</string>
<string name="deleteText" msgid="6979668428458199034">"Futa"</string>
<string name="inputMethod" msgid="1653630062304567879">"Mbinu ya uingizaji"</string>
@@ -986,7 +986,7 @@
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
<string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumiwa"</string>
- <string name="app_running_notification_text" msgid="1197581823314971177">"Gonga ili upate maelezo zaidi au usitishe programu."</string>
+ <string name="app_running_notification_text" msgid="1197581823314971177">"Gusa ili upate maelezo zaidi au usitishe programu."</string>
<string name="ok" msgid="5970060430562524910">"Sawa"</string>
<string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
<string name="yes" msgid="5362982303337969312">"Sawa"</string>
@@ -1062,7 +1062,7 @@
<string name="android_upgrading_starting_apps" msgid="451464516346926713">"Programu zinaanza"</string>
<string name="android_upgrading_complete" msgid="1405954754112999229">"Inamaliza kuwasha."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> inaendelea"</string>
- <string name="heavy_weight_notification_detail" msgid="867643381388543170">"Gonga ili uende kwenye programu"</string>
+ <string name="heavy_weight_notification_detail" msgid="867643381388543170">"Gusa ili uende kwenye programu"</string>
<string name="heavy_weight_switcher_title" msgid="7153167085403298169">"Badilisha programu?"</string>
<string name="heavy_weight_switcher_text" msgid="7022631924534406403">"Programmu nyingine tayari inaendeshwa na lazima hiyo ikomeshwe kabla ya kuanza nyingine mpya."</string>
<string name="old_app_action" msgid="493129172238566282">"Rejea katika <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
@@ -1107,7 +1107,7 @@
<string name="wifi_available_title_connecting" msgid="1557292688310330032">"Inaunganisha kwenye mtandao wa Wi‑Fi unaotumiwa na mtu yeyote"</string>
<string name="wifi_available_title_connected" msgid="7542672851522241548">"Imeunganisha kwenye mtandao wa Wi-Fi"</string>
<string name="wifi_available_title_failed_to_connect" msgid="6861772233582618132">"Imeshindwa kuunganisha kwenye mtandao wa Wi‑Fi"</string>
- <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Gonga ili uone mitandao yote"</string>
+ <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"Gusa ili uone mitandao yote"</string>
<string name="wifi_available_action_connect" msgid="2635699628459488788">"Unganisha"</string>
<string name="wifi_available_action_all_networks" msgid="1100098935861622985">"Mitandao Yote"</string>
<string name="wifi_available_sign_in" msgid="9157196203958866662">"Ingia kwa mtandao wa Wi-Fi"</string>
@@ -1115,7 +1115,7 @@
<!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
<skip />
<string name="wifi_no_internet" msgid="8451173622563841546">"Wi-Fi haina muunganisho wa intaneti"</string>
- <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Gonga ili upate chaguo"</string>
+ <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Gusa ili upate chaguo"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="5325661434777870353">"Kifaa hutumia <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wakati <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> haina Intaneti. Huenda ukalipishwa."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Imebadilisha mtandao kutoka <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sasa inatumia <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1136,7 +1136,7 @@
<string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Anzisha Wi-Fi Moja kwa Moja. Hii itazima mteja/mtandao-hewa wa Wi-Fi."</string>
<string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Haikuweza kuanzisha Wi-Fi Moja kwa Moja."</string>
<string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi ya Moja kwa Moja imewashwa"</string>
- <string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"Gonga ili uweke mipangilio"</string>
+ <string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"Gusa ili uweke mipangilio"</string>
<string name="accept" msgid="1645267259272829559">"Kubali"</string>
<string name="decline" msgid="2112225451706137894">"Kataa"</string>
<string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Mwaliko umetumwa"</string>
@@ -1172,7 +1172,7 @@
<string name="carrier_app_dialog_button" msgid="7900235513678617329">"PATA PROGRAMU"</string>
<string name="carrier_app_dialog_not_now" msgid="6361378684292268027">"SIYO SASA"</string>
<string name="carrier_app_notification_title" msgid="8921767385872554621">"SIM mpya imewekwa"</string>
- <string name="carrier_app_notification_text" msgid="1132487343346050225">"Gonga ili uiweke"</string>
+ <string name="carrier_app_notification_text" msgid="1132487343346050225">"Gusa ili uiweke"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"Weka saa"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"Weka tarehe"</string>
<string name="date_time_set" msgid="5777075614321087758">"Weka"</string>
@@ -1188,11 +1188,11 @@
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB kwa ajili ya kuhamisha picha"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB kwa ajili ya MIDI"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Imeunganishwa kwa kifuasi cha USB"</string>
- <string name="usb_notification_message" msgid="3370903770828407960">"Gonga ili upate chaguo zaidi."</string>
+ <string name="usb_notification_message" msgid="3370903770828407960">"Gusa ili upate chaguo zaidi."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Imetambua kifaa cha sauti ya analogi"</string>
- <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Kifaa ulichoambatisha hakitumiki kwenye simu hii. Gonga ili upate maelezo zaidi."</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Kifaa ulichoambatisha hakitumiki kwenye simu hii. Gusa ili upate maelezo zaidi."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Utatuaji wa USB umeunganishwa"</string>
- <string name="adb_active_notification_message" msgid="4948470599328424059">"Gonga ili uzime utatuaji wa USB."</string>
+ <string name="adb_active_notification_message" msgid="4948470599328424059">"Gusa ili uzime utatuaji wa USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Chagua ili kulemaza utatuaji USB."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Inatayarisha ripoti ya hitilafu…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Ungependa kushiriki ripoti ya hitilafu?"</string>
@@ -1204,23 +1204,23 @@
<string name="show_ime" msgid="2506087537466597099">"Ionyeshe kwenye skrini wakati kibodi halisi inatumika"</string>
<string name="hardware" msgid="194658061510127999">"Onyesha kibodi pepe"</string>
<string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Sanidi kibodi halisi"</string>
- <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Gonga ili uchague lugha na muundo"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Gusa ili uchague lugha na muundo"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="alert_windows_notification_channel_group_name" msgid="1463953341148606396">"Onyesha juu ya programu zingine"</string>
<string name="alert_windows_notification_channel_name" msgid="3116610965549449803">"<xliff:g id="NAME">%s</xliff:g> inachomoza juu ya programu zingine"</string>
<string name="alert_windows_notification_title" msgid="3697657294867638947">"<xliff:g id="NAME">%s</xliff:g> inachomoza juu ya programu zingine."</string>
- <string name="alert_windows_notification_message" msgid="8917232109522912560">"Ikiwa hutaki <xliff:g id="NAME">%s</xliff:g> kutumia kipengele hiki, gonga ili ufungue mipangilio na ukizime."</string>
+ <string name="alert_windows_notification_message" msgid="8917232109522912560">"Ikiwa hutaki <xliff:g id="NAME">%s</xliff:g> kutumia kipengele hiki, gusa ili ufungue mipangilio na ukizime."</string>
<string name="alert_windows_notification_turn_off_action" msgid="3367294525884949878">"ZIMA"</string>
<string name="ext_media_checking_notification_title" msgid="5734005953288045806">"Inaandaa <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_checking_notification_message" msgid="4747432538578886744">"Inakagua hitilafu"</string>
<string name="ext_media_new_notification_message" msgid="7589986898808506239">"<xliff:g id="NAME">%s</xliff:g> mpya imegunduliwa"</string>
<string name="ext_media_ready_notification_message" msgid="4083398150380114462">"Kwa ajili ya kuhamisha picha na maudhui"</string>
<string name="ext_media_unmountable_notification_title" msgid="8295123366236989588">"<xliff:g id="NAME">%s</xliff:g> iliyoharibika"</string>
- <string name="ext_media_unmountable_notification_message" msgid="2343202057122495773">"<xliff:g id="NAME">%s</xliff:g> ina hitilafu. Gonga ili uirekebishe."</string>
+ <string name="ext_media_unmountable_notification_message" msgid="2343202057122495773">"<xliff:g id="NAME">%s</xliff:g> ina hitilafu. Gusa ili uirekebishe."</string>
<string name="ext_media_unmountable_notification_message" product="tv" msgid="3941179940297874950">"<xliff:g id="NAME">%s</xliff:g> imeharibika. Ichague ili uirekebishe."</string>
<string name="ext_media_unsupported_notification_title" msgid="3797642322958803257">"<xliff:g id="NAME">%s</xliff:g> isiyotumika"</string>
- <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"Kifaa hiki hakitumii <xliff:g id="NAME">%s</xliff:g>. Gonga ili uweke mipangilio ya muundo unaoweza kutumika."</string>
+ <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"Kifaa hiki hakitumii <xliff:g id="NAME">%s</xliff:g>. Gusa ili uweke mipangilio ya muundo unaoweza kutumika."</string>
<string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"Kifaa hiki hakitumii <xliff:g id="NAME">%s</xliff:g> hii. Ichague ili uweke muundo unaotumika."</string>
<string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"<xliff:g id="NAME">%s</xliff:g> imeondolewa bila kutarajiwa"</string>
<string name="ext_media_badremoval_notification_message" msgid="380176703346946313">"Ondoa <xliff:g id="NAME">%s</xliff:g> kabla ya kuchomoa ili kuepuka kupoteza data"</string>
@@ -1261,7 +1261,7 @@
<string name="permdesc_requestDeletePackages" msgid="3406172963097595270">"Huruhusu programu kuomba idhini ya kufuta vifurushi."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"omba kupuuza uimarishji wa betri"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="8359147856007447638">"Huruhusu programu kuomba ruhusa ya kupuuza uimarishaji wa betri katika programu yako."</string>
- <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Gonga mara mbili kwa udhibiti wa kuza"</string>
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Gusa mara mbili kwa udhibiti wa kuza"</string>
<string name="gadget_host_error_inflating" msgid="4882004314906466162">"Haikuweza kuongeza wijeti."</string>
<string name="ime_action_go" msgid="8320845651737369027">"Nenda"</string>
<string name="ime_action_search" msgid="658110271822807811">"Tafuta"</string>
@@ -1292,8 +1292,8 @@
<string name="notification_ranker_binding_label" msgid="774540592299064747">"Huduma ya kupanga arifa"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN imewezeshwa"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN imeamilishwa na <xliff:g id="APP">%s</xliff:g>"</string>
- <string name="vpn_text" msgid="1610714069627824309">"Gonga ili kudhibiti mtandao."</string>
- <string name="vpn_text_long" msgid="4907843483284977618">"Imeunganishwa kwa <xliff:g id="SESSION">%s</xliff:g>. Gonga ili kudhibiti mtandao"</string>
+ <string name="vpn_text" msgid="1610714069627824309">"Gusa ili kudhibiti mtandao."</string>
+ <string name="vpn_text_long" msgid="4907843483284977618">"Imeunganishwa kwa <xliff:g id="SESSION">%s</xliff:g>. Gusa ili kudhibiti mtandao"</string>
<string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Kila mara VPN iliyowashwa inaunganishwa…"</string>
<string name="vpn_lockdown_connected" msgid="8202679674819213931">"Kila mara VPN iliyowashwa imeunganishwa"</string>
<string name="vpn_lockdown_disconnected" msgid="735805531187559719">"Imeondolewa kwenye VPN iliyowashwa kila wakati"</string>
@@ -1304,9 +1304,9 @@
<string name="reset" msgid="2448168080964209908">"Weka upya"</string>
<string name="submit" msgid="1602335572089911941">"Wasilisha"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Mtindo wa gari umewezeshwa"</string>
- <string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Gonga ili ufunge hali ya garini."</string>
+ <string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Gusa ili ufunge hali ya garini."</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"Kushiriki au kusambaza intaneti kumewashwa"</string>
- <string name="tethered_notification_message" msgid="2113628520792055377">"Gonga ili uweke mipangilio."</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Gusa ili uweke mipangilio."</string>
<string name="disable_tether_notification_title" msgid="7526977944111313195">"Umezima kipengele cha kusambaza mtandao"</string>
<string name="disable_tether_notification_message" msgid="2913366428516852495">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string>
<string name="back_button_label" msgid="2300470004503343439">"Nyuma"</string>
@@ -1383,7 +1383,7 @@
<string name="storage_usb" msgid="3017954059538517278">"Hifadhi ya USB"</string>
<string name="extract_edit_menu_button" msgid="8940478730496610137">"Badilisha"</string>
<string name="data_usage_warning_title" msgid="3620440638180218181">"Tahadhari ya matumizi ya data"</string>
- <string name="data_usage_warning_body" msgid="6660692274311972007">"Gonga ili uangalie matumizi na mipangilio."</string>
+ <string name="data_usage_warning_body" msgid="6660692274311972007">"Gusa ili uangalie matumizi na mipangilio."</string>
<string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Kikomo data ya 2G-3G kimefikiwa"</string>
<string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Kikomo cha data ya 4G kimefikiwa"</string>
<string name="data_usage_mobile_limit_title" msgid="6561099244084267376">"Umefikisha kipimo cha juu cha data"</string>
@@ -1395,7 +1395,7 @@
<string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Taarifa za Wi-fi zimevuka kiwanga"</string>
<string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"<xliff:g id="SIZE">%s</xliff:g> juu ya kikomo kilichobainishwa."</string>
<string name="data_usage_restricted_title" msgid="5965157361036321914">"Data ya mandhari nyuma imezuiwa"</string>
- <string name="data_usage_restricted_body" msgid="469866376337242726">"Gonga ili uondoe kizuizi."</string>
+ <string name="data_usage_restricted_body" msgid="469866376337242726">"Gusa ili uondoe kizuizi."</string>
<string name="ssl_certificate" msgid="6510040486049237639">"Cheti cha usalama"</string>
<string name="ssl_certificate_is_valid" msgid="6825263250774569373">"Cheti hiki ni halali."</string>
<string name="issued_to" msgid="454239480274921032">"Kimetolewa kwa:"</string>
@@ -1594,7 +1594,7 @@
<string name="reason_unknown" msgid="6048913880184628119">"haijulikani"</string>
<string name="reason_service_unavailable" msgid="7824008732243903268">"Huduma ya uchapishaji haijawashwa"</string>
<string name="print_service_installed_title" msgid="2246317169444081628">"Huduma ya <xliff:g id="NAME">%s</xliff:g> imesakinisha"</string>
- <string name="print_service_installed_message" msgid="5897362931070459152">"Gonga ili uwashe"</string>
+ <string name="print_service_installed_message" msgid="5897362931070459152">"Gusa ili uwashe"</string>
<string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"Weka PIN ya msimamizi"</string>
<string name="restr_pin_enter_pin" msgid="3395953421368476103">"Ingiza PIN"</string>
<string name="restr_pin_incorrect" msgid="8571512003955077924">"Sio sahihi"</string>
@@ -1624,7 +1624,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"<xliff:g id="LABEL">%1$s</xliff:g> ya 2 ya Kazini"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"<xliff:g id="LABEL">%1$s</xliff:g> ya 3 ya Kazini"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"Ili kubandua skrini hii, gusa na ushikilie vitufe vya Nyuma na Muhtasari"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"Huwezi kubandua programu hii"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"Skrini imebandikwa"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"Skrini imebanduliwa"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"Itisha PIN kabla hujabandua"</string>
@@ -1723,12 +1722,12 @@
<string name="new_sms_notification_title" msgid="8442817549127555977">"Una ujumbe mpya"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Fungua programu ya SMS ili uweze kuangalia"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"Huenda baadhi ya utendaji ukawa vikwazo"</string>
- <string name="user_encrypted_message" msgid="4923292604515744267">"Gonga ili ufungue"</string>
+ <string name="user_encrypted_message" msgid="4923292604515744267">"Gusa ili ufungue"</string>
<string name="user_encrypted_detail" msgid="5708447464349420392">"Data ya mtumiaji imefungwa"</string>
<string name="profile_encrypted_detail" msgid="3700965619978314974">"Wasifu wa kazini umefungwa"</string>
- <string name="profile_encrypted_message" msgid="6964994232310195874">"Gonga ili ufungue wasifu wa kazini"</string>
+ <string name="profile_encrypted_message" msgid="6964994232310195874">"Gusa ili ufungue wasifu wa kazini"</string>
<string name="usb_mtp_launch_notification_title" msgid="8359219638312208932">"Imeunganishwa na <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
- <string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"Gonga ili uangalie faili"</string>
+ <string name="usb_mtp_launch_notification_description" msgid="8541876176425411358">"Gusa ili uangalie faili"</string>
<string name="pin_target" msgid="3052256031352291362">"Bandika"</string>
<string name="unpin_target" msgid="3556545602439143442">"Bandua"</string>
<string name="app_info" msgid="6856026610594615344">"Maelezo ya programu"</string>
@@ -1780,14 +1779,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Jaribio la ujumbe wa dharura"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Jibu"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM hairuhusiwi katika huduma ya sauti"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM haina mipangilio ya huduma ya sauti"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM hairuhusiwi katika huduma ya sauti"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"Simu hairuhusiwi katika huduma ya sauti"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Dirisha Ibukizi"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"Njia hii ya mkato inahitaji toleo jipya la programu"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index bf0f46a..e03824b 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1184,7 +1184,7 @@
<string name="no_permissions" msgid="7283357728219338112">"அனுமதிகள் தேவையில்லை"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"இதனால் நீங்கள் கட்டணம் செலுத்த வேண்டியிருக்கலாம்"</string>
<string name="dlg_ok" msgid="7376953167039865701">"சரி"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"இந்தச் சாதனத்தை USB சார்ஜ் செய்கிறது"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"சாதனம் USB சார்ஜிங் செய்யப்படுகிறது"</string>
<string name="usb_supplying_notification_title" msgid="5310642257296510271">"இணைத்துள்ள சாதனத்திற்கு USB சக்தி அளிக்கிறது"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB, கோப்புப் பரிமாற்றத்துக்கு மட்டும்"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB, படப் பரிமாற்றத்துக்கு மட்டும்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 9b3b89b..8ae8401f 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2వ కార్యాలయం <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3వ కార్యాలయం <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"ఈ స్క్రీన్ను అన్పిన్ చేయడానికి, వెనుకకు మరియు స్థూలదృష్టి బటన్లను నొక్కి & పట్టుకోండి"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"ఈ అనువర్తనాన్ని అన్పిన్ చేయడం సాధ్యపడదు"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"స్క్రీన్ పిన్ చేయబడింది"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"స్క్రీన్ అన్పిన్ చేయబడింది"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"అన్పిన్ చేయడానికి ముందు పిన్ కోసం అడుగు"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"అత్యవసర సందేశాల పరీక్ష"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"ప్రత్యుత్తరం పంపండి"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"వాయిస్ కోసం SIM అనుమతించబడదు"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"వాయిస్ కోసం SIM సదుపాయం లేదు"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"వాయిస్ కోసం SIM అనుమతించబడదు"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"వాయిస్ కోసం ఫోన్ అనుమతించబడదు"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"పాప్అప్ విండో"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"ఈ సత్వరమార్గానికి తాజా యాప్ అవసరం"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index b89af42..f009e08 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"دوسرا کام <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"تیسرا کام <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"اس اسکرین سے پن ہٹانے کیلئے، ٹچ کریں اور مجموعی جائزہ اور واپس جائیں بٹنز کو دبائے رکھیں"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"اس ایپ سے پن ہٹایا نہیں جا سکتا"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"اسکرین کو پن کر دیا گیا"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"اسکرین کا پن ہٹا دیا گیا"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"پن ہٹانے سے پہلے PIN طلب کریں"</string>
@@ -1782,18 +1781,14 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"ایمرجنسی پیغامات کی جانچ"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"جواب دیں"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"آواز کیلئے SIM کو اجازت نہیں ہے"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM میں آواز کیلئے سہولت نہیں ہے"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"آواز کیلئے SIM کو اجازت نہیں ہے"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"آواز کیلئے فون کو اجازت نہیں ہے"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"پاپ اپ ونڈو"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"اس شارٹ کٹ کیلئے جدید ترین ایپ درکار ہے"</string>
<string name="shortcut_restore_not_supported" msgid="5028808567940014190">"شارٹ کٹ کو بحال نہیں کیا جا سکا، کیونکہ ایپ بیک اپ اور بحالی کو سپورٹ نہیں کرتی ہے"</string>
- <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ایپ کی دستخط کے غیر مماثل ہونے کی وجہ سے شارٹ کٹ کو بحال نہیں کیا جا سکا"</string>
+ <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ایپ دستخط غیر مماثل ہونے کی وجہ سے شارٹ کٹ کو بحال نہیں کیا جا سکا"</string>
<string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"شارٹ کٹ کو بحال نہیں کیا جا سکا"</string>
</resources>
diff --git a/core/res/res/values-watch/styles_material.xml b/core/res/res/values-watch/styles_material.xml
index fd88102..6cef47d 100644
--- a/core/res/res/values-watch/styles_material.xml
+++ b/core/res/res/values-watch/styles_material.xml
@@ -39,8 +39,8 @@
<item name="taskOpenExitAnimation">@anim/slide_in_exit_micro</item>
<item name="taskCloseEnterAnimation">@null</item>
<item name="taskCloseExitAnimation">@anim/slide_out_micro</item>
- <item name="taskToFrontEnterAnimation">@anim/slide_in_enter_micro</item>
- <item name="taskToFrontExitAnimation">@anim/slide_in_exit_micro</item>
+ <item name="taskToFrontEnterAnimation">@null</item>
+ <item name="taskToFrontExitAnimation">@anim/slide_out_micro</item>
<item name="taskToBackEnterAnimation">@null</item>
<item name="taskToBackExitAnimation">@anim/slide_out_micro</item>
<item name="wallpaperOpenEnterAnimation">@null</item>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 5629df1..6b95eda 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -979,10 +979,10 @@
<string name="inputMethod" msgid="1653630062304567879">"输入法"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"文字操作"</string>
<string name="email" msgid="4560673117055050403">"电子邮件"</string>
- <string name="dial" msgid="1253998302767701559">"通话"</string>
+ <string name="dial" msgid="1253998302767701559">"拨打电话"</string>
<string name="map" msgid="6521159124535543457">"定位"</string>
<string name="browse" msgid="1245903488306147205">"打开"</string>
- <string name="sms" msgid="4560537514610063430">"短信"</string>
+ <string name="sms" msgid="4560537514610063430">"发短信"</string>
<string name="add_contact" msgid="7867066569670597203">"添加"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"存储空间不足"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"某些系统功能可能无法正常使用"</string>
@@ -1626,7 +1626,6 @@
<string name="managed_profile_label_badge_2" msgid="5048136430082124036">"第二个工作<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="2808305070321719040">"第三个工作<xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_toast" msgid="6820571533009838261">"要取消固定此屏幕,请触摸并按住“返回”和“概览”按钮"</string>
- <string name="lock_to_app_toast_locked" msgid="7849470948648628704">"无法取消固定此应用"</string>
<string name="lock_to_app_start" msgid="6643342070839862795">"已固定屏幕"</string>
<string name="lock_to_app_exit" msgid="8598219838213787430">"已取消固定屏幕"</string>
<string name="lock_to_app_unlock_pin" msgid="2552556656504331634">"取消时要求输入PIN码"</string>
@@ -1782,14 +1781,10 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"紧急消息测试"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"回复"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <!-- no translation found for mmcc_authentication_reject (5767701075994754356) -->
- <skip />
- <!-- no translation found for mmcc_imsi_unknown_in_hlr (5316658473301462825) -->
- <skip />
- <!-- no translation found for mmcc_illegal_ms (807334478177362062) -->
- <skip />
- <!-- no translation found for mmcc_illegal_me (1950705155760872972) -->
- <skip />
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM 卡不支持语音"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"未配置支持语音的 SIM 卡"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM 卡不支持语音"</string>
+ <string name="mmcc_illegal_me" msgid="1950705155760872972">"手机不支持语音"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"弹出式窗口"</string>
<string name="slice_more_content" msgid="8504342889413274608">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="shortcut_restored_on_lower_version" msgid="5270675146351613828">"您必须拥有最新版的应用才能使用此快捷方式"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1879ab7..edd793d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3164,4 +3164,6 @@
<!-- Corner radius of system dialogs -->
<dimen name="config_dialogCornerRadius">2dp</dimen>
+
+ <string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9e0722b..7a3fa1a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -152,7 +152,7 @@
<!-- Displayed to tell the user that they should switch their network preference. -->
<string name="NetworkPreferenceSwitchTitle">Can\u2019t reach network</string>
<!-- Displayed to tell the user that they should switch their network preference. -->
- <string name="NetworkPreferenceSwitchSummary">To improve reception, try changing the type selected at Settings > Network & Internet > Mobile networks > Preferred network type."</string>
+ <string name="NetworkPreferenceSwitchSummary">To improve reception, try changing the type selected at Settings > Network & internet > Mobile networks > Preferred network type."</string>
<!-- Displayed to tell the user that emergency calls might not be available. -->
<string name="EmergencyCallWarningTitle">Wi\u2011Fi calling is active</string>
<!-- Displayed to tell the user that emergency calls might not be available. -->
@@ -3014,7 +3014,7 @@
<string name="network_available_sign_in_detailed"><xliff:g id="network_ssid">%1$s</xliff:g></string>
<!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's title. -->
- <string name="wifi_no_internet">Wi-Fi has no Internet access</string>
+ <string name="wifi_no_internet">Wi-Fi has no internet access</string>
<!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
<string name="wifi_no_internet_detailed">Tap for options</string>
@@ -3023,7 +3023,7 @@
<string name="network_switch_metered">Switched to <xliff:g id="network_type">%1$s</xliff:g></string>
<!-- A notification might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's message. %1$s is the network that the device switched to, e.g., cellular data. %2$s is the network type the device switched from, e.g., Wi-Fi. Both are strings in the network_switch_type_name array. -->
- <string name="network_switch_metered_detail">Device uses <xliff:g id="new_network">%1$s</xliff:g> when <xliff:g id="previous_network">%2$s</xliff:g> has no Internet access. Charges may apply.</string>
+ <string name="network_switch_metered_detail">Device uses <xliff:g id="new_network">%1$s</xliff:g> when <xliff:g id="previous_network">%2$s</xliff:g> has no internet access. Charges may apply.</string>
<!-- A toast might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the text of the toast. %1$s is the network that the device switched from, e.g., Wi-Fi. %2$s is the network type the device switched from, e.g., cellular data. Both are strings in the network_switch_type_name array. -->
<string name="network_switch_metered_toast">Switched from <xliff:g id="previous_network">%1$s</xliff:g> to <xliff:g id="new_network">%2$s</xliff:g></string>
@@ -3043,7 +3043,7 @@
<!-- A notification is shown when a user's selected SSID is later disabled due to connectivity problems. This is the notification's title / ticker. -->
<string name="wifi_watchdog_network_disabled">Couldn\'t connect to Wi-Fi</string>
<!-- A notification is shown when a user's selected SSID is later disabled due to connectivity problems. The complete alert msg is: <hotspot name> + this string, i.e. "Linksys has a poor internet connection" -->
- <string name="wifi_watchdog_network_disabled_detailed">\u0020has a poor Internet connection.</string>
+ <string name="wifi_watchdog_network_disabled_detailed">\u0020has a poor internet connection.</string>
<!-- Do not translate. Default access point SSID used for tethering -->
<string name="wifi_tether_configure_ssid_default" translatable="false">AndroidAP</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e476d89..5497085 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3150,4 +3150,5 @@
<!-- From media projection -->
<java-symbol type="string" name="config_mediaProjectionPermissionDialogComponent" />
+ <java-symbol type="string" name="config_batterySaverDeviceSpecificConfig" />
</resources>
diff --git a/core/tests/coretests/res/layout/add_column_in_table.xml b/core/tests/coretests/res/layout/add_column_in_table.xml
index 05f55a8..d929b02 100644
--- a/core/tests/coretests/res/layout/add_column_in_table.xml
+++ b/core/tests/coretests/res/layout/add_column_in_table.xml
@@ -18,6 +18,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <requestFocus />
<TableLayout android:id="@+id/table"
android:layout_width="match_parent"
diff --git a/core/tests/coretests/res/layout/baseline_0width_and_weight.xml b/core/tests/coretests/res/layout/baseline_0width_and_weight.xml
index acbb10b..eac9b9d 100644
--- a/core/tests/coretests/res/layout/baseline_0width_and_weight.xml
+++ b/core/tests/coretests/res/layout/baseline_0width_and_weight.xml
@@ -21,6 +21,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <requestFocus />
<LinearLayout android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/tests/coretests/res/layout/focus_after_removal.xml b/core/tests/coretests/res/layout/focus_after_removal.xml
index f4e388d..84449d1 100644
--- a/core/tests/coretests/res/layout/focus_after_removal.xml
+++ b/core/tests/coretests/res/layout/focus_after_removal.xml
@@ -22,6 +22,7 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <requestFocus />
<LinearLayout android:id="@+id/leftLayout"
android:orientation="vertical"
diff --git a/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml b/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml
index ab76e29..6b3b5a7 100644
--- a/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml
+++ b/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml
@@ -22,6 +22,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <requestFocus />
<EditText
android:id="@+id/editText"
diff --git a/core/tests/coretests/res/layout/visibility_callback.xml b/core/tests/coretests/res/layout/visibility_callback.xml
index 9034b3f..ff918f5 100644
--- a/core/tests/coretests/res/layout/visibility_callback.xml
+++ b/core/tests/coretests/res/layout/visibility_callback.xml
@@ -24,6 +24,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <requestFocus />
<LinearLayout
android:orientation="vertical"
diff --git a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
index 75eeb93..9ed3f11b 100644
--- a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
+++ b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
@@ -16,6 +16,7 @@
package android.database;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -61,6 +62,10 @@
super(context, name, null, 1);
}
+ TestHelper(Context context, String name, int version, SQLiteDatabase.OpenParams params) {
+ super(context, name, version, params);
+ }
+
@Override
public void onCreate(SQLiteDatabase db) {
}
@@ -168,4 +173,25 @@
}
assertTrue("No dbstat found for " + dbName, dbStatFound);
}
+
+ @Test
+ public void testOpenParamsConstructor() {
+ SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
+ .setJournalMode("DELETE")
+ .setSynchronousMode("OFF")
+ .build();
+
+ TestHelper helper = new TestHelper(mContext, "openhelper_test_constructor", 1, params);
+ mHelpersToClose.add(helper);
+
+ String journalMode = DatabaseUtils
+ .stringForQuery(helper.getReadableDatabase(), "PRAGMA journal_mode", null);
+
+ assertEquals("DELETE", journalMode.toUpperCase());
+ String syncMode = DatabaseUtils
+ .stringForQuery(helper.getReadableDatabase(), "PRAGMA synchronous", null);
+
+ assertEquals("0", syncMode);
+ }
+
}
diff --git a/core/tests/coretests/src/android/os/UserHandleTest.java b/core/tests/coretests/src/android/os/UserHandleTest.java
new file mode 100644
index 0000000..af559fd
--- /dev/null
+++ b/core/tests/coretests/src/android/os/UserHandleTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static android.os.UserHandle.ERR_GID;
+import static android.os.UserHandle.getAppId;
+import static android.os.UserHandle.getCacheAppGid;
+import static android.os.UserHandle.getSharedAppGid;
+import static android.os.UserHandle.getUid;
+import static android.os.UserHandle.getUserId;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class UserHandleTest {
+ // NOTE: keep logic in sync with system/core/libcutils/tests/multiuser_test.cpp
+
+ @Test
+ public void testMerge() throws Exception {
+ EXPECT_EQ(0, multiuser_get_uid(0, 0));
+ EXPECT_EQ(1000, multiuser_get_uid(0, 1000));
+ EXPECT_EQ(10000, multiuser_get_uid(0, 10000));
+ EXPECT_EQ(50000, multiuser_get_uid(0, 50000));
+ EXPECT_EQ(1000000, multiuser_get_uid(10, 0));
+ EXPECT_EQ(1001000, multiuser_get_uid(10, 1000));
+ EXPECT_EQ(1010000, multiuser_get_uid(10, 10000));
+ EXPECT_EQ(1050000, multiuser_get_uid(10, 50000));
+ }
+
+ @Test
+ public void testSplitUser() throws Exception {
+ EXPECT_EQ(0, multiuser_get_user_id(0));
+ EXPECT_EQ(0, multiuser_get_user_id(1000));
+ EXPECT_EQ(0, multiuser_get_user_id(10000));
+ EXPECT_EQ(0, multiuser_get_user_id(50000));
+ EXPECT_EQ(10, multiuser_get_user_id(1000000));
+ EXPECT_EQ(10, multiuser_get_user_id(1001000));
+ EXPECT_EQ(10, multiuser_get_user_id(1010000));
+ EXPECT_EQ(10, multiuser_get_user_id(1050000));
+ }
+
+ @Test
+ public void testSplitApp() throws Exception {
+ EXPECT_EQ(0, multiuser_get_app_id(0));
+ EXPECT_EQ(1000, multiuser_get_app_id(1000));
+ EXPECT_EQ(10000, multiuser_get_app_id(10000));
+ EXPECT_EQ(50000, multiuser_get_app_id(50000));
+ EXPECT_EQ(0, multiuser_get_app_id(1000000));
+ EXPECT_EQ(1000, multiuser_get_app_id(1001000));
+ EXPECT_EQ(10000, multiuser_get_app_id(1010000));
+ EXPECT_EQ(50000, multiuser_get_app_id(1050000));
+ }
+
+ @Test
+ public void testCache() throws Exception {
+ EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 0));
+ EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 1000));
+ EXPECT_EQ(20000, multiuser_get_cache_gid(0, 10000));
+ EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 50000));
+ EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 0));
+ EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 1000));
+ EXPECT_EQ(1020000, multiuser_get_cache_gid(10, 10000));
+ EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 50000));
+ }
+
+ @Test
+ public void testShared() throws Exception {
+ EXPECT_EQ(0, multiuser_get_shared_gid(0, 0));
+ EXPECT_EQ(1000, multiuser_get_shared_gid(0, 1000));
+ EXPECT_EQ(50000, multiuser_get_shared_gid(0, 10000));
+ EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 50000));
+ EXPECT_EQ(0, multiuser_get_shared_gid(10, 0));
+ EXPECT_EQ(1000, multiuser_get_shared_gid(10, 1000));
+ EXPECT_EQ(50000, multiuser_get_shared_gid(10, 10000));
+ EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(10, 50000));
+ }
+
+ private static void EXPECT_EQ(int expected, int actual) {
+ assertEquals(expected, actual);
+ }
+
+ private static int multiuser_get_uid(int userId, int appId) {
+ return getUid(userId, appId);
+ }
+
+ private static int multiuser_get_cache_gid(int userId, int appId) {
+ return getCacheAppGid(userId, appId);
+ }
+
+ private static int multiuser_get_shared_gid(int userId, int appId) {
+ return getSharedAppGid(userId, appId);
+ }
+
+ private static int multiuser_get_user_id(int uid) {
+ return getUserId(uid);
+ }
+
+ private static int multiuser_get_app_id(int uid) {
+ return getAppId(uid);
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index d36ed63..0d35f4e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -356,6 +356,9 @@
Settings.Global.USE_GOOGLE_MAIL,
Settings.Global.VT_IMS_ENABLED,
Settings.Global.WAIT_FOR_DEBUGGER,
+ Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
+ Settings.Global.GPU_DEBUG_APP,
+ Settings.Global.GPU_DEBUG_LAYERS,
Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
Settings.Global.WARNING_TEMPERATURE,
Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index 24e3646..dee51dc 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -117,13 +117,13 @@
assertEquals("2 days", Formatter.formatShortElapsedTime(mContext, 2 * DAY));
assertEquals("1 day, 23 hr",
Formatter.formatShortElapsedTime(mContext, 1 * DAY + 23 * HOUR + 59 * MINUTE));
- assertEquals("1 day, 0 hr",
+ assertEquals("1 day",
Formatter.formatShortElapsedTime(mContext, 1 * DAY + 59 * MINUTE));
- assertEquals("1 day, 0 hr", Formatter.formatShortElapsedTime(mContext, 1 * DAY));
+ assertEquals("1 day", Formatter.formatShortElapsedTime(mContext, 1 * DAY));
assertEquals("24 hr", Formatter.formatShortElapsedTime(mContext, 23 * HOUR + 30 * MINUTE));
assertEquals("3 hr", Formatter.formatShortElapsedTime(mContext, 2 * HOUR + 30 * MINUTE));
assertEquals("2 hr", Formatter.formatShortElapsedTime(mContext, 2 * HOUR));
- assertEquals("1 hr, 0 min", Formatter.formatShortElapsedTime(mContext, 1 * HOUR));
+ assertEquals("1 hr", Formatter.formatShortElapsedTime(mContext, 1 * HOUR));
assertEquals("60 min",
Formatter.formatShortElapsedTime(mContext, 59 * MINUTE + 30 * SECOND));
assertEquals("59 min",
@@ -132,7 +132,7 @@
assertEquals("2 min", Formatter.formatShortElapsedTime(mContext, 2 * MINUTE));
assertEquals("1 min, 59 sec",
Formatter.formatShortElapsedTime(mContext, 1 * MINUTE + 59 * SECOND + 999));
- assertEquals("1 min, 0 sec", Formatter.formatShortElapsedTime(mContext, 1 * MINUTE));
+ assertEquals("1 min", Formatter.formatShortElapsedTime(mContext, 1 * MINUTE));
assertEquals("59 sec", Formatter.formatShortElapsedTime(mContext, 59 * SECOND + 999));
assertEquals("1 sec", Formatter.formatShortElapsedTime(mContext, 1 * SECOND));
assertEquals("0 sec", Formatter.formatShortElapsedTime(mContext, 1));
@@ -154,9 +154,9 @@
mContext, 2 * DAY));
assertEquals("1 day, 23 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 1 * DAY + 23 * HOUR + 59 * MINUTE));
- assertEquals("1 day, 0 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ assertEquals("1 day", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 1 * DAY + 59 * MINUTE));
- assertEquals("1 day, 0 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ assertEquals("1 day", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 1 * DAY));
assertEquals("24 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 23 * HOUR + 30 * MINUTE));
@@ -164,9 +164,9 @@
mContext, 2 * HOUR + 30 * MINUTE));
assertEquals("2 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 2 * HOUR));
- assertEquals("1 hr, 0 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ assertEquals("1 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 1 * HOUR));
- assertEquals("1 hr, 0 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ assertEquals("1 hr", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 59 * MINUTE + 30 * SECOND));
assertEquals("59 min", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 59 * MINUTE));
diff --git a/core/tests/coretests/src/android/util/ListScenario.java b/core/tests/coretests/src/android/util/ListScenario.java
index fa088a3..129484a 100644
--- a/core/tests/coretests/src/android/util/ListScenario.java
+++ b/core/tests/coretests/src/android/util/ListScenario.java
@@ -28,6 +28,7 @@
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
+
import com.google.android.collect.Maps;
import java.util.ArrayList;
@@ -60,18 +61,18 @@
// separators
private Set<Integer> mUnselectableItems = new HashSet<Integer>();
-
+
private boolean mStackFromBottom;
private int mClickedPosition = -1;
-
+
private int mLongClickedPosition = -1;
-
+
private int mConvertMisses = 0;
-
+
private int mHeaderViewCount;
private boolean mHeadersFocusable;
-
+
private int mFooterViewCount;
private LinearLayout mLinearLayout;
@@ -193,7 +194,7 @@
mIncludeHeader = includeHeader;
return this;
}
-
+
/**
* Sets the stacking direction
* @param stackFromBottom
@@ -203,7 +204,7 @@
mStackFromBottom = stackFromBottom;
return this;
}
-
+
/**
* Sets whether the sum of the height of the list items must be at least the
* height of the list view.
@@ -220,7 +221,7 @@
mFadingEdgeScreenSizeFactor = fadingEdgeScreenSizeFactor;
return this;
}
-
+
/**
* Set the number of header views to appear within the list
*/
@@ -246,7 +247,7 @@
mFooterViewCount = footerViewCount;
return this;
}
-
+
/**
* Sets whether the {@link ListScenario} will automatically set the
* adapter on the list view. If this is false, the client MUST set it
@@ -278,7 +279,7 @@
*/
protected void nothingSelected() {
}
-
+
/**
* Override this if you want to know when something has been clicked (perhaps
* more importantly, that {@link android.widget.AdapterView.OnItemClickListener} has
@@ -287,7 +288,7 @@
protected void positionClicked(int position) {
setClickedPosition(position);
}
-
+
/**
* Override this if you want to know when something has been long clicked (perhaps
* more importantly, that {@link android.widget.AdapterView.OnItemLongClickListener} has
@@ -303,7 +304,7 @@
// for test stability, turn off title bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
-
+
mScreenHeight = getWindowManager().getDefaultDisplay().getHeight();
@@ -326,7 +327,7 @@
header.setText("Header: " + i);
mListView.addHeaderView(header);
}
-
+
for (int i=0; i<mFooterViewCount; i++) {
TextView header = new TextView(this);
header.setText("Footer: " + i);
@@ -336,7 +337,7 @@
if (params.mConnectAdapter) {
setAdapter(mListView);
}
-
+
mListView.setItemsCanFocus(mItemsFocusable);
if (mStartingSelectionPosition >= 0) {
mListView.setSelection(mStartingSelectionPosition);
@@ -360,11 +361,12 @@
positionClicked(position);
}
});
-
+
// set the fading edge length porportionally to the screen
// height for test stability
if (params.mFadingEdgeScreenSizeFactor != null) {
- mListView.setFadingEdgeLength((int) (params.mFadingEdgeScreenSizeFactor * mScreenHeight));
+ mListView.setFadingEdgeLength(
+ (int) (params.mFadingEdgeScreenSizeFactor * mScreenHeight));
} else {
mListView.setFadingEdgeLength((int) ((64.0 / 480) * mScreenHeight));
}
@@ -403,6 +405,7 @@
mLinearLayout.addView(mListView);
setContentView(mLinearLayout);
}
+ mLinearLayout.restoreDefaultFocus();
}
/**
@@ -426,7 +429,7 @@
}
});
}
-
+
/**
* @return The newly created ListView widget.
*/
@@ -440,16 +443,16 @@
protected Params createParams() {
return new Params();
}
-
+
/**
* Sets an adapter on a ListView.
- *
+ *
* @param listView The ListView to set the adapter on.
*/
protected void setAdapter(ListView listView) {
listView.setAdapter(new MyAdapter());
}
-
+
/**
* Read in and validate all of the params passed in by the scenario.
* @param params
@@ -525,7 +528,7 @@
if (!mIncludeHeader) {
throw new IllegalArgumentException("no header above list");
}
- mHeaderTextView.setText(value);
+ mHeaderTextView.setText(value);
}
/**
@@ -543,12 +546,12 @@
}
/**
- * Convert a non-null view.
+ * Convert a non-null view.
*/
public View convertView(int position, View convertView, ViewGroup parent) {
return ListItemFactory.convertText(convertView, getValueAtPosition(position), position);
}
-
+
public void setClickedPosition(int clickedPosition) {
mClickedPosition = clickedPosition;
}
@@ -580,7 +583,7 @@
}
});
}
-
+
/**
* Return an item type for the specified position in the adapter. Override if your
* adapter creates more than one type.
@@ -596,7 +599,7 @@
public int getViewTypeCount() {
return 1;
}
-
+
/**
* @return The number of times convertView failed
*/
@@ -647,7 +650,7 @@
}
return result;
}
-
+
@Override
public int getItemViewType(int position) {
return ListScenario.this.getItemViewType(position);
diff --git a/core/tests/coretests/src/android/view/FocusFinderTest.java b/core/tests/coretests/src/android/view/FocusFinderTest.java
index 6f1dd7c..2732a04 100644
--- a/core/tests/coretests/src/android/view/FocusFinderTest.java
+++ b/core/tests/coretests/src/android/view/FocusFinderTest.java
@@ -117,8 +117,8 @@
// at edge
rect2.offset(0, 1);
- assertBeamsOverlap(View.FOCUS_LEFT, rect1, rect2);
- assertBeamsOverlap(View.FOCUS_RIGHT, rect1, rect2);
+ assertBeamsDontOverlap(View.FOCUS_LEFT, rect1, rect2);
+ assertBeamsDontOverlap(View.FOCUS_RIGHT, rect1, rect2);
// just beyond
rect2.offset(0, 1);
@@ -133,8 +133,8 @@
// at top edge
rect2.offset(0, -1);
- assertBeamsOverlap(View.FOCUS_LEFT, rect1, rect2);
- assertBeamsOverlap(View.FOCUS_RIGHT, rect1, rect2);
+ assertBeamsDontOverlap(View.FOCUS_LEFT, rect1, rect2);
+ assertBeamsDontOverlap(View.FOCUS_RIGHT, rect1, rect2);
// just beyond top edge
rect2.offset(0, -1);
@@ -154,8 +154,8 @@
// at edge
rect2.offset(1, 0);
- assertBeamsOverlap(View.FOCUS_UP, rect1, rect2);
- assertBeamsOverlap(View.FOCUS_DOWN, rect1, rect2);
+ assertBeamsDontOverlap(View.FOCUS_UP, rect1, rect2);
+ assertBeamsDontOverlap(View.FOCUS_DOWN, rect1, rect2);
// just beyond
rect2.offset(1, 0);
@@ -170,8 +170,8 @@
// at edge
rect2.offset(-1, 0);
- assertBeamsOverlap(View.FOCUS_UP, rect1, rect2);
- assertBeamsOverlap(View.FOCUS_DOWN, rect1, rect2);
+ assertBeamsDontOverlap(View.FOCUS_UP, rect1, rect2);
+ assertBeamsDontOverlap(View.FOCUS_DOWN, rect1, rect2);
// just beyond edge
rect2.offset(-1, 0);
@@ -445,7 +445,7 @@
assertBetterCandidate(View.FOCUS_LEFT,
// L T R B
new Rect(150, 0, 200, 50), // src
- new Rect(0, 50, 50, 50), // better, (way further, but in beam)
+ new Rect(0, 0, 50, 50), // better, (way further, but in beam)
new Rect(49, 99, 149, 101)); // worse, even though it is closer
}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 0245570..0e460b9 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -637,6 +637,41 @@
}
@Test
+ public void testSelectionHandles_visibleEvenWithEmptyMenu() {
+ ((TextView) mActivity.findViewById(R.id.textview)).setCustomSelectionActionModeCallback(
+ new ActionMode.Callback() {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ menu.clear();
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ menu.clear();
+ return true;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {}
+ });
+ final String text = "abcd efg hijk lmn";
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('f')));
+
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .check(matches(isDisplayed()));
+ onHandleView(com.android.internal.R.id.selection_end_handle)
+ .check(matches(isDisplayed()));
+ }
+
+ @Test
public void testSetSelectionAndActionMode() throws Throwable {
final TextView textView = mActivity.findViewById(R.id.textview);
final ActionMode.Callback amCallback = mock(ActionMode.Callback.class);
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java b/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java
index 1ba56ba..0d8d834 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java
@@ -16,12 +16,12 @@
package android.widget.layout.linear;
-import com.android.frameworks.coretests.R;
-
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
+import com.android.frameworks.coretests.R;
+
public class LLOfTwoFocusableInTouchMode extends Activity {
private View mButton1;
@@ -63,6 +63,7 @@
mB3Fired = true;
}
});
+ getWindow().getDecorView().restoreDefaultFocus();
}
public View getButton1() {
diff --git a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
index 98fbed3..9e49719 100644
--- a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
+++ b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
@@ -16,10 +16,9 @@
package android.widget.listview;
-import android.util.InternalSelectionView;
-
import android.app.Activity;
import android.os.Bundle;
+import android.util.InternalSelectionView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
@@ -80,6 +79,7 @@
setContentView(combineAdjacent(mLeftListView, mRightListView));
+ getWindow().getDecorView().restoreDefaultFocus();
}
private static View combineAdjacent(View... views) {
diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
index 8f971bb..edc60b5 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
@@ -16,15 +16,12 @@
package android.widget.listview.focus;
-import android.widget.listview.ListHorizontalFocusWithinItemWins;
-
import android.test.ActivityInstrumentationTestCase;
import android.test.suitebuilder.annotation.MediumTest;
-import android.view.FocusFinder;
import android.view.KeyEvent;
-import android.view.View;
import android.widget.Button;
import android.widget.ListView;
+import android.widget.listview.ListHorizontalFocusWithinItemWins;
public class ListHorizontalFocusWithinItemWinsTest extends ActivityInstrumentationTestCase<ListHorizontalFocusWithinItemWins> {
@@ -51,12 +48,6 @@
public void testPreconditions() {
assertEquals("list position", 0, mListView.getSelectedItemPosition());
assertTrue("mTopLeftButton.isFocused()", mTopLeftButton.isFocused());
- assertEquals("global focus search to right from top left is bottom middle",
- mBottomMiddleButton,
- FocusFinder.getInstance().findNextFocus(mListView, mTopLeftButton, View.FOCUS_RIGHT));
- assertEquals("global focus search to left from top right is bottom middle",
- mBottomMiddleButton,
- FocusFinder.getInstance().findNextFocus(mListView, mTopRightButton, View.FOCUS_LEFT));
}
@MediumTest
diff --git a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
index bd6977e..5a6110c 100644
--- a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
@@ -16,7 +16,6 @@
package android.widget.touchmode;
-import android.widget.layout.linear.LLOfButtons1;
import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterClick;
import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterTap;
import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
@@ -25,6 +24,8 @@
import android.test.suitebuilder.annotation.MediumTest;
import android.view.KeyEvent;
import android.widget.Button;
+import android.widget.layout.linear.LLOfButtons1;
+
/**
* Make sure focus isn't kept by buttons when entering touch mode.
@@ -52,7 +53,6 @@
@MediumTest
public void testPreconditions() {
assertFalse("we should not be in touch mode", mActivity.isInTouchMode());
- assertTrue("top button should have focus", mFirstButton.isFocused());
}
@MediumTest
diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
index ebfa45c..b8c4123 100644
--- a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
+++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
@@ -20,7 +20,10 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import android.content.Context;
import android.os.SystemProperties;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -35,8 +38,11 @@
private static final String TEST_FEATURE_NAME = "feature_foobar";
+ private Context mContext;
+
@Before
public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
cleanup();
}
@@ -46,6 +52,7 @@
}
private void cleanup() {
+ Settings.Global.putString(mContext.getContentResolver(), TEST_FEATURE_NAME, "");
SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "");
SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "");
}
@@ -54,22 +61,32 @@
public void testGetFlag_enabled_shouldReturnTrue() {
SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "true");
- assertTrue(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME));
+ assertTrue(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
}
@Test
- public void testGetFlag_override_shouldReturnTrue() {
+ public void testGetFlag_adb_override_shouldReturnTrue() {
SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "false");
SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "true");
- assertTrue(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME));
+ assertTrue(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
+ }
+
+ @Test
+ public void testGetFlag_settings_override_shouldReturnTrue() {
+ SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "false");
+ SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, "false");
+
+ Settings.Global.putString(mContext.getContentResolver(), TEST_FEATURE_NAME, "true");
+
+ assertTrue(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
}
@Test
public void testSetEnabled_shouldSetOverrideFlag() {
- assertFalse(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME));
+ assertFalse(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
- FeatureFlagUtils.setEnabled(TEST_FEATURE_NAME, true);
+ FeatureFlagUtils.setEnabled(null /* context */, TEST_FEATURE_NAME, true);
assertEquals(SystemProperties.get(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, null),
"");
@@ -79,7 +96,7 @@
@Test
public void testGetFlag_notSet_shouldReturnFalse() {
- assertFalse(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME));
+ assertFalse(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
}
}
diff --git a/core/tests/webkit/apk_with_native_libs/Android.mk b/core/tests/webkit/apk_with_native_libs/Android.mk
index 7c6c36e..c51de6a 100644
--- a/core/tests/webkit/apk_with_native_libs/Android.mk
+++ b/core/tests/webkit/apk_with_native_libs/Android.mk
@@ -20,6 +20,7 @@
MY_JNI_SHARED_LIBRARIES := libwebviewtest_jni
MY_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
MY_SRC_FILES := $(call all-java-files-under, src)
+MY_CFLAGS := -Wall -Werror
MY_SDK_VERSION := system_current
MY_PROGUARD_ENABLED := disabled
MY_MULTILIB := both
@@ -40,6 +41,7 @@
LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES)
LOCAL_MODULE_PATH := $(MY_MODULE_PATH)
LOCAL_SRC_FILES := $(MY_SRC_FILES)
+LOCAL_CFLAGS := $(MY_CFLAGS)
LOCAL_SDK_VERSION := $(MY_SDK_VERSION)
LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED)
LOCAL_MULTILIB := $(MY_MULTILIB)
@@ -59,6 +61,7 @@
LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES)
LOCAL_MODULE_PATH := $(MY_MODULE_PATH)
LOCAL_SRC_FILES := $(MY_SRC_FILES)
+LOCAL_CFLAGS := $(MY_CFLAGS)
LOCAL_SDK_VERSION := $(MY_SDK_VERSION)
LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED)
LOCAL_MULTILIB := $(MY_MULTILIB)
diff --git a/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp b/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
index 9b0502f..0ced4ee 100644
--- a/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
+++ b/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
@@ -16,6 +16,6 @@
#include <jni.h>
-jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+jint JNI_OnLoad(JavaVM * /*vm*/, void * /*reserved*/) {
return JNI_VERSION_1_4;
}
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 7b2fa76..76eb4e6 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -112,14 +112,29 @@
# Run sanity tests on fonts on checkbuild
checkbuild: fontchain_lint
-FONTCHAIN_LINTER := frameworks/base/tools/fonts/fontchain_lint.py
+FONTCHAIN_LINTER := $(HOST_OUT_EXECUTABLES)/fontchain_linter
ifeq ($(MINIMAL_FONT_FOOTPRINT),true)
CHECK_EMOJI := false
else
CHECK_EMOJI := true
endif
+fontchain_lint_timestamp := $(call intermediates-dir-for,PACKAGING,fontchain_lint)/stamp
+
.PHONY: fontchain_lint
-fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img
- PYTHONPATH=$$PYTHONPATH:external/fonttools/Lib \
- python $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
+fontchain_lint: $(fontchain_lint_timestamp)
+
+fontchain_lint_deps := \
+ external/unicode/DerivedAge.txt \
+ external/unicode/emoji-data.txt \
+ external/unicode/emoji-sequences.txt \
+ external/unicode/emoji-variation-sequences.txt \
+ external/unicode/emoji-zwj-sequences.txt \
+ external/unicode/additions/emoji-data.txt \
+ external/unicode/additions/emoji-sequences.txt \
+ external/unicode/additions/emoji-zwj-sequences.txt \
+
+$(fontchain_lint_timestamp): $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img $(fontchain_lint_deps)
+ @echo Running fontchain lint
+ $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
+ touch $@
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 209f364..dad24da 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -115,10 +115,14 @@
<family lang="und-Hebr">
<font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
<font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
</family>
<family lang="und-Thai" variant="elegant">
<font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
<font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
</family>
<family lang="und-Thai" variant="compact">
<font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
@@ -127,14 +131,20 @@
<family lang="und-Armn">
<font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
<font weight="700" style="normal">NotoSansArmenian-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-Bold.ttf</font>
</family>
<family lang="und-Geor und-Geok">
<font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGeorgian-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-Bold.ttf</font>
</family>
<family lang="und-Deva" variant="elegant">
<font weight="400" style="normal">NotoSansDevanagari-Regular.ttf</font>
<font weight="700" style="normal">NotoSansDevanagari-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-Bold.ttf</font>
</family>
<family lang="und-Deva" variant="compact">
<font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
@@ -147,6 +157,8 @@
<family lang="und-Gujr" variant="elegant">
<font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-Bold.ttf</font>
</family>
<family lang="und-Gujr" variant="compact">
<font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
@@ -163,6 +175,8 @@
<family lang="und-Taml" variant="elegant">
<font weight="400" style="normal">NotoSansTamil-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTamil-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-Bold.ttf</font>
</family>
<family lang="und-Taml" variant="compact">
<font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
@@ -171,6 +185,8 @@
<family lang="und-Mlym" variant="elegant">
<font weight="400" style="normal">NotoSansMalayalam-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMalayalam-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-Bold.ttf</font>
</family>
<family lang="und-Mlym" variant="compact">
<font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
@@ -179,6 +195,8 @@
<family lang="und-Beng" variant="elegant">
<font weight="400" style="normal">NotoSansBengali-Regular.ttf</font>
<font weight="700" style="normal">NotoSansBengali-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-Bold.ttf</font>
</family>
<family lang="und-Beng" variant="compact">
<font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
@@ -187,6 +205,8 @@
<family lang="und-Telu" variant="elegant">
<font weight="400" style="normal">NotoSansTelugu-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTelugu-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-Bold.ttf</font>
</family>
<family lang="und-Telu" variant="compact">
<font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
@@ -195,6 +215,8 @@
<family lang="und-Knda" variant="elegant">
<font weight="400" style="normal">NotoSansKannada-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKannada-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-Bold.ttf</font>
</family>
<family lang="und-Knda" variant="compact">
<font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
@@ -258,6 +280,8 @@
<family lang="und-Laoo" variant="elegant">
<font weight="400" style="normal">NotoSansLao-Regular.ttf</font>
<font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="compact">
<font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
@@ -472,15 +496,19 @@
</family>
<family lang="zh-Hans">
<font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
<family lang="zh-Hant zh-Bopo">
<font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
<family lang="ja">
<font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
<family lang="ko">
<font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
<family lang="und-Zsye">
<font weight="400" style="normal">NotoColorEmoji.ttf</font>
diff --git a/docs/html/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png b/docs/html/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png
new file mode 100644
index 0000000..7578b48
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png
new file mode 100644
index 0000000..7b10f6b
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png
new file mode 100644
index 0000000..41972cf
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png
new file mode 100644
index 0000000..d26600b
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png
new file mode 100644
index 0000000..1e7208e
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png
new file mode 100644
index 0000000..ecef3ae
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
new file mode 100644
index 0000000..a02fd89
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
new file mode 100644
index 0000000..c309ac5
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
new file mode 100644
index 0000000..414fad4
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
new file mode 100644
index 0000000..c147a87
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
new file mode 100644
index 0000000..4ce2125
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
Binary files differ
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 3dc928d..aff942d 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -475,6 +475,19 @@
}
/**
+ * If the specified rectangle intersects this rectangle, set this rectangle to that
+ * intersection, otherwise set this rectangle to the empty rectangle.
+ * @see #inset(int, int, int, int) but without checking if the rects overlap.
+ * @hide
+ */
+ public void intersectUnchecked(Rect other) {
+ left = Math.max(left, other.left);
+ top = Math.max(top, other.top);
+ right = Math.min(right, other.right);
+ bottom = Math.min(bottom, other.bottom);
+ }
+
+ /**
* If rectangles a and b intersect, return true and set this rectangle to
* that intersection, otherwise return false and do not change this
* rectangle. No check is performed to see if either rectangle is empty.
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 3bf4f90..dea194e 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -36,138 +36,69 @@
private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
- private static final int OPACITY_ENTER_DURATION = 600;
- private static final int OPACITY_ENTER_DURATION_FAST = 120;
- private static final int OPACITY_EXIT_DURATION = 480;
+ private static final int OPACITY_DURATION = 80;
- // Hardware rendering properties.
- private CanvasProperty<Paint> mPropPaint;
- private CanvasProperty<Float> mPropRadius;
- private CanvasProperty<Float> mPropX;
- private CanvasProperty<Float> mPropY;
+ private ObjectAnimator mAnimator;
- // Software rendering properties.
private float mOpacity = 0;
/** Whether this ripple is bounded. */
private boolean mIsBounded;
- public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded,
- boolean forceSoftware) {
- super(owner, bounds, forceSoftware);
+ private boolean mFocused = false;
+ private boolean mHovered = false;
+
+ public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded) {
+ super(owner, bounds);
mIsBounded = isBounded;
}
public boolean isVisible() {
- return mOpacity > 0 || isHardwareAnimating();
+ return mOpacity > 0;
}
- @Override
- protected boolean drawSoftware(Canvas c, Paint p) {
- boolean hasContent = false;
-
+ public void draw(Canvas c, Paint p) {
final int origAlpha = p.getAlpha();
- final int alpha = (int) (origAlpha * mOpacity + 0.5f);
+ final int alpha = Math.min((int) (origAlpha * mOpacity + 0.5f), 255);
if (alpha > 0) {
p.setAlpha(alpha);
c.drawCircle(0, 0, mTargetRadius, p);
p.setAlpha(origAlpha);
- hasContent = true;
}
-
- return hasContent;
}
- @Override
- protected boolean drawHardware(DisplayListCanvas c) {
- c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
- return true;
- }
-
- @Override
- protected Animator createSoftwareEnter(boolean fast) {
- // Linear enter based on current opacity.
- final int maxDuration = fast ? OPACITY_ENTER_DURATION_FAST : OPACITY_ENTER_DURATION;
- final int duration = (int) ((1 - mOpacity) * maxDuration);
-
- final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
- opacity.setAutoCancel(true);
- opacity.setDuration(duration);
- opacity.setInterpolator(LINEAR_INTERPOLATOR);
-
- return opacity;
- }
-
- @Override
- protected Animator createSoftwareExit() {
- final AnimatorSet set = new AnimatorSet();
-
- // Linear exit after enter is completed.
- final ObjectAnimator exit = ObjectAnimator.ofFloat(this, OPACITY, 0);
- exit.setInterpolator(LINEAR_INTERPOLATOR);
- exit.setDuration(OPACITY_EXIT_DURATION);
- exit.setAutoCancel(true);
-
- final AnimatorSet.Builder builder = set.play(exit);
-
- // Linear "fast" enter based on current opacity.
- final int fastEnterDuration = mIsBounded ?
- (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
- if (fastEnterDuration > 0) {
- final ObjectAnimator enter = ObjectAnimator.ofFloat(this, OPACITY, 1);
- enter.setInterpolator(LINEAR_INTERPOLATOR);
- enter.setDuration(fastEnterDuration);
- enter.setAutoCancel(true);
-
- builder.after(enter);
+ public void setState(boolean focused, boolean hovered, boolean animateChanged) {
+ if (mHovered != hovered || mFocused != focused) {
+ mHovered = hovered;
+ mFocused = focused;
+ onStateChanged(animateChanged);
}
-
- return set;
}
- @Override
- protected RenderNodeAnimatorSet createHardwareExit(Paint p) {
- final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet();
-
- final int targetAlpha = p.getAlpha();
- final int currentAlpha = (int) (mOpacity * targetAlpha + 0.5f);
- p.setAlpha(currentAlpha);
-
- mPropPaint = CanvasProperty.createPaint(p);
- mPropRadius = CanvasProperty.createFloat(mTargetRadius);
- mPropX = CanvasProperty.createFloat(0);
- mPropY = CanvasProperty.createFloat(0);
-
- final int fastEnterDuration = mIsBounded ?
- (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
-
- // Linear exit after enter is completed.
- final RenderNodeAnimator exit = new RenderNodeAnimator(
- mPropPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
- exit.setInterpolator(LINEAR_INTERPOLATOR);
- exit.setDuration(OPACITY_EXIT_DURATION);
- if (fastEnterDuration > 0) {
- exit.setStartDelay(fastEnterDuration);
- exit.setStartValue(targetAlpha);
+ private void onStateChanged(boolean animateChanged) {
+ float newOpacity = 0.0f;
+ if (mHovered) newOpacity += 1.0f;
+ if (mFocused) newOpacity += 1.0f;
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ mAnimator = null;
}
- set.add(exit);
-
- // Linear "fast" enter based on current opacity.
- if (fastEnterDuration > 0) {
- final RenderNodeAnimator enter = new RenderNodeAnimator(
- mPropPaint, RenderNodeAnimator.PAINT_ALPHA, targetAlpha);
- enter.setInterpolator(LINEAR_INTERPOLATOR);
- enter.setDuration(fastEnterDuration);
- set.add(enter);
+ if (animateChanged) {
+ mAnimator = ObjectAnimator.ofFloat(this, OPACITY, newOpacity);
+ mAnimator.setDuration(OPACITY_DURATION);
+ mAnimator.setInterpolator(LINEAR_INTERPOLATOR);
+ mAnimator.start();
+ } else {
+ mOpacity = newOpacity;
}
-
- return set;
}
- @Override
- protected void jumpValuesToExit() {
- mOpacity = 0;
+ public void jumpToFinal() {
+ if (mAnimator != null) {
+ mAnimator.end();
+ mAnimator = null;
+ }
}
private static abstract class BackgroundProperty extends FloatProperty<RippleBackground> {
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index e83513c..0e38826 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -27,23 +27,14 @@
import java.util.ArrayList;
/**
- * Abstract class that handles hardware/software hand-off and lifecycle for
- * animated ripple foreground and background components.
+ * Abstract class that handles size & positioning common to the ripple & focus states.
*/
abstract class RippleComponent {
- private final RippleDrawable mOwner;
+ protected final RippleDrawable mOwner;
/** Bounds used for computing max radius. May be modified by the owner. */
protected final Rect mBounds;
- /** Whether we can use hardware acceleration for the exit animation. */
- private boolean mHasDisplayListCanvas;
-
- private boolean mHasPendingHardwareAnimator;
- private RenderNodeAnimatorSet mHardwareAnimator;
-
- private Animator mSoftwareAnimator;
-
/** Whether we have an explicit maximum radius. */
private boolean mHasMaxRadius;
@@ -53,16 +44,9 @@
/** Screen density used to adjust pixel-based constants. */
protected float mDensityScale;
- /**
- * If set, force all ripple animations to not run on RenderThread, even if it would be
- * available.
- */
- private final boolean mForceSoftware;
-
- public RippleComponent(RippleDrawable owner, Rect bounds, boolean forceSoftware) {
+ public RippleComponent(RippleDrawable owner, Rect bounds) {
mOwner = owner;
mBounds = bounds;
- mForceSoftware = forceSoftware;
}
public void onBoundsChange() {
@@ -92,89 +76,6 @@
}
/**
- * Starts a ripple enter animation.
- *
- * @param fast whether the ripple should enter quickly
- */
- public final void enter(boolean fast) {
- cancel();
-
- mSoftwareAnimator = createSoftwareEnter(fast);
-
- if (mSoftwareAnimator != null) {
- mSoftwareAnimator.start();
- }
- }
-
- /**
- * Starts a ripple exit animation.
- */
- public final void exit() {
- cancel();
-
- if (mHasDisplayListCanvas) {
- // We don't have access to a canvas here, but we expect one on the
- // next frame. We'll start the render thread animation then.
- mHasPendingHardwareAnimator = true;
-
- // Request another frame.
- invalidateSelf();
- } else {
- mSoftwareAnimator = createSoftwareExit();
- mSoftwareAnimator.start();
- }
- }
-
- /**
- * Cancels all animations. Software animation values are left in the
- * current state, while hardware animation values jump to the end state.
- */
- public void cancel() {
- cancelSoftwareAnimations();
- endHardwareAnimations();
- }
-
- /**
- * Ends all animations, jumping values to the end state.
- */
- public void end() {
- endSoftwareAnimations();
- endHardwareAnimations();
- }
-
- /**
- * Draws the ripple to the canvas, inheriting the paint's color and alpha
- * properties.
- *
- * @param c the canvas to which the ripple should be drawn
- * @param p the paint used to draw the ripple
- * @return {@code true} if something was drawn, {@code false} otherwise
- */
- public boolean draw(Canvas c, Paint p) {
- final boolean hasDisplayListCanvas = !mForceSoftware && c.isHardwareAccelerated()
- && c instanceof DisplayListCanvas;
- if (mHasDisplayListCanvas != hasDisplayListCanvas) {
- mHasDisplayListCanvas = hasDisplayListCanvas;
-
- if (!hasDisplayListCanvas) {
- // We've switched from hardware to non-hardware mode. Panic.
- endHardwareAnimations();
- }
- }
-
- if (hasDisplayListCanvas) {
- final DisplayListCanvas hw = (DisplayListCanvas) c;
- startPendingAnimation(hw, p);
-
- if (mHardwareAnimator != null) {
- return drawHardware(hw);
- }
- }
-
- return drawSoftware(c, p);
- }
-
- /**
* Populates {@code bounds} with the maximum drawing bounds of the ripple
* relative to its center. The resulting bounds should be translated into
* parent drawable coordinates before use.
@@ -186,77 +87,10 @@
bounds.set(-r, -r, r, r);
}
- /**
- * Starts the pending hardware animation, if available.
- *
- * @param hw hardware canvas on which the animation should draw
- * @param p paint whose properties the hardware canvas should use
- */
- private void startPendingAnimation(DisplayListCanvas hw, Paint p) {
- if (mHasPendingHardwareAnimator) {
- mHasPendingHardwareAnimator = false;
-
- mHardwareAnimator = createHardwareExit(new Paint(p));
- mHardwareAnimator.start(hw);
-
- // Preemptively jump the software values to the end state now that
- // the hardware exit has read whatever values it needs.
- jumpValuesToExit();
- }
- }
-
- /**
- * Cancels any current software animations, leaving the values in their
- * current state.
- */
- private void cancelSoftwareAnimations() {
- if (mSoftwareAnimator != null) {
- mSoftwareAnimator.cancel();
- mSoftwareAnimator = null;
- }
- }
-
- /**
- * Ends any current software animations, jumping the values to their end
- * state.
- */
- private void endSoftwareAnimations() {
- if (mSoftwareAnimator != null) {
- mSoftwareAnimator.end();
- mSoftwareAnimator = null;
- }
- }
-
- /**
- * Ends any pending or current hardware animations.
- * <p>
- * Hardware animations can't synchronize values back to the software
- * thread, so there is no "cancel" equivalent.
- */
- private void endHardwareAnimations() {
- if (mHardwareAnimator != null) {
- mHardwareAnimator.end();
- mHardwareAnimator = null;
- }
-
- if (mHasPendingHardwareAnimator) {
- mHasPendingHardwareAnimator = false;
-
- // Manually jump values to their exited state. Normally we'd do that
- // later when starting the hardware exit, but we're aborting early.
- jumpValuesToExit();
- }
- }
-
protected final void invalidateSelf() {
mOwner.invalidateSelf(false);
}
- protected final boolean isHardwareAnimating() {
- return mHardwareAnimator != null && mHardwareAnimator.isRunning()
- || mHasPendingHardwareAnimator;
- }
-
protected final void onHotspotBoundsChanged() {
if (!mHasMaxRadius) {
final float halfWidth = mBounds.width() / 2.0f;
@@ -276,76 +110,4 @@
protected void onTargetRadiusChanged(float targetRadius) {
// Stub.
}
-
- protected abstract Animator createSoftwareEnter(boolean fast);
-
- protected abstract Animator createSoftwareExit();
-
- protected abstract RenderNodeAnimatorSet createHardwareExit(Paint p);
-
- protected abstract boolean drawHardware(DisplayListCanvas c);
-
- protected abstract boolean drawSoftware(Canvas c, Paint p);
-
- /**
- * Called when the hardware exit is cancelled. Jumps software values to end
- * state to ensure that software and hardware values are synchronized.
- */
- protected abstract void jumpValuesToExit();
-
- public static class RenderNodeAnimatorSet {
- private final ArrayList<RenderNodeAnimator> mAnimators = new ArrayList<>();
-
- public void add(RenderNodeAnimator anim) {
- mAnimators.add(anim);
- }
-
- public void clear() {
- mAnimators.clear();
- }
-
- public void start(DisplayListCanvas target) {
- if (target == null) {
- throw new IllegalArgumentException("Hardware canvas must be non-null");
- }
-
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- anim.setTarget(target);
- anim.start();
- }
- }
-
- public void cancel() {
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- anim.cancel();
- }
- }
-
- public void end() {
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- anim.end();
- }
- }
-
- public boolean isRunning() {
- final ArrayList<RenderNodeAnimator> animators = mAnimators;
- final int N = animators.size();
- for (int i = 0; i < N; i++) {
- final RenderNodeAnimator anim = animators.get(i);
- if (anim.isRunning()) {
- return true;
- }
- }
- return false;
- }
- }
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 1727eca..8b185f2 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -16,11 +16,6 @@
package android.graphics.drawable;
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo.Config;
@@ -42,6 +37,11 @@
import android.graphics.Shader;
import android.util.AttributeSet;
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.util.Arrays;
@@ -135,9 +135,6 @@
private PorterDuffColorFilter mMaskColorFilter;
private boolean mHasValidMask;
- /** Whether we expect to draw a background when visible. */
- private boolean mBackgroundActive;
-
/** The current ripple. May be actively animating or pending entry. */
private RippleForeground mRipple;
@@ -217,7 +214,7 @@
}
if (mBackground != null) {
- mBackground.end();
+ mBackground.jumpToFinal();
}
cancelExitingRipples();
@@ -266,9 +263,9 @@
}
}
- setRippleActive(focused || (enabled && pressed));
+ setRippleActive(enabled && pressed);
- setBackgroundActive(hovered, hovered);
+ setBackgroundActive(hovered, focused);
return changed;
}
@@ -283,14 +280,13 @@
}
}
- private void setBackgroundActive(boolean active, boolean focused) {
- if (mBackgroundActive != active) {
- mBackgroundActive = active;
- if (active) {
- tryBackgroundEnter(focused);
- } else {
- tryBackgroundExit();
- }
+ private void setBackgroundActive(boolean hovered, boolean focused) {
+ if (mBackground == null && (hovered || focused)) {
+ mBackground = new RippleBackground(this, mHotspotBounds, isBounded());
+ mBackground.setup(mState.mMaxRadius, mDensity);
+ }
+ if (mBackground != null) {
+ mBackground.setState(focused, hovered, true);
}
}
@@ -327,10 +323,6 @@
tryRippleEnter();
}
- if (mBackgroundActive) {
- tryBackgroundEnter(false);
- }
-
// Skip animations, just show the correct final states.
jumpToCurrentState();
}
@@ -546,26 +538,6 @@
}
/**
- * Creates an active hotspot at the specified location.
- */
- private void tryBackgroundEnter(boolean focused) {
- if (mBackground == null) {
- final boolean isBounded = isBounded();
- mBackground = new RippleBackground(this, mHotspotBounds, isBounded, mForceSoftware);
- }
-
- mBackground.setup(mState.mMaxRadius, mDensity);
- mBackground.enter(focused);
- }
-
- private void tryBackgroundExit() {
- if (mBackground != null) {
- // Don't null out the background, we need it to draw!
- mBackground.exit();
- }
- }
-
- /**
* Attempts to start an enter animation for the active hotspot. Fails if
* there are too many animating ripples.
*/
@@ -593,7 +565,7 @@
}
mRipple.setup(mState.mMaxRadius, mDensity);
- mRipple.enter(false);
+ mRipple.enter();
}
/**
@@ -623,9 +595,7 @@
}
if (mBackground != null) {
- mBackground.end();
- mBackground = null;
- mBackgroundActive = false;
+ mBackground.setState(false, false, false);
}
cancelExitingRipples();
@@ -858,38 +828,8 @@
final float y = mHotspotBounds.exactCenterY();
canvas.translate(x, y);
- updateMaskShaderIfNeeded();
-
- // Position the shader to account for canvas translation.
- if (mMaskShader != null) {
- final Rect bounds = getBounds();
- mMaskMatrix.setTranslate(bounds.left - x, bounds.top - y);
- mMaskShader.setLocalMatrix(mMaskMatrix);
- }
-
- // Grab the color for the current state and cut the alpha channel in
- // half so that the ripple and background together yield full alpha.
- final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
- final int halfAlpha = (Color.alpha(color) / 2) << 24;
final Paint p = getRipplePaint();
- if (mMaskColorFilter != null) {
- // The ripple timing depends on the paint's alpha value, so we need
- // to push just the alpha channel into the paint and let the filter
- // handle the full-alpha color.
- final int fullAlphaColor = color | (0xFF << 24);
- mMaskColorFilter.setColor(fullAlphaColor);
-
- p.setColor(halfAlpha);
- p.setColorFilter(mMaskColorFilter);
- p.setShader(mMaskShader);
- } else {
- final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha;
- p.setColor(halfAlphaColor);
- p.setColorFilter(null);
- p.setShader(null);
- }
-
if (background != null && background.isVisible()) {
background.draw(canvas, p);
}
@@ -912,13 +852,49 @@
mMask.draw(canvas);
}
- private Paint getRipplePaint() {
+ Paint getRipplePaint() {
if (mRipplePaint == null) {
mRipplePaint = new Paint();
mRipplePaint.setAntiAlias(true);
mRipplePaint.setStyle(Paint.Style.FILL);
}
- return mRipplePaint;
+
+ final float x = mHotspotBounds.exactCenterX();
+ final float y = mHotspotBounds.exactCenterY();
+
+ updateMaskShaderIfNeeded();
+
+ // Position the shader to account for canvas translation.
+ if (mMaskShader != null) {
+ final Rect bounds = getBounds();
+ mMaskMatrix.setTranslate(bounds.left - x, bounds.top - y);
+ mMaskShader.setLocalMatrix(mMaskMatrix);
+ }
+
+ // Grab the color for the current state and cut the alpha channel in
+ // half so that the ripple and background together yield full alpha.
+ final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
+ final int halfAlpha = (Color.alpha(color) / 2) << 24;
+ final Paint p = mRipplePaint;
+
+ if (mMaskColorFilter != null) {
+ // The ripple timing depends on the paint's alpha value, so we need
+ // to push just the alpha channel into the paint and let the filter
+ // handle the full-alpha color.
+ final int fullAlphaColor = color | (0xFF << 24);
+ mMaskColorFilter.setColor(fullAlphaColor);
+
+ p.setColor(halfAlpha);
+ p.setColorFilter(mMaskColorFilter);
+ p.setShader(mMaskShader);
+ } else {
+ final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha;
+ p.setColor(halfAlphaColor);
+ p.setColorFilter(null);
+ p.setShader(null);
+ }
+
+ return p;
}
@Override
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index a675eaf..0b5020c 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -18,7 +18,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Canvas;
@@ -29,8 +28,11 @@
import android.util.MathUtils;
import android.view.DisplayListCanvas;
import android.view.RenderNodeAnimator;
+import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
+import java.util.ArrayList;
+
/**
* Draws a ripple foreground.
*/
@@ -40,7 +42,7 @@
400f, 1.4f, 0);
// Pixel-based accelerations and velocities.
- private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024;
+ private static final float WAVE_TOUCH_DOWN_ACCELERATION = 2048;
private static final float WAVE_OPACITY_DECAY_VELOCITY = 3;
// Bounded ripple animation properties.
@@ -49,8 +51,9 @@
private static final int BOUNDED_OPACITY_EXIT_DURATION = 400;
private static final float MAX_BOUNDED_RADIUS = 350;
- private static final int RIPPLE_ENTER_DELAY = 80;
- private static final int OPACITY_ENTER_DURATION_FAST = 120;
+ private static final int OPACITY_ENTER_DURATION = 75;
+ private static final int OPACITY_EXIT_DURATION = 150;
+ private static final int OPACITY_HOLD_DURATION = OPACITY_ENTER_DURATION + 150;
// Parent-relative values for starting position.
private float mStartingX;
@@ -72,7 +75,7 @@
private float mBoundedRadius = 0;
// Software rendering properties.
- private float mOpacity = 1;
+ private float mOpacity = 0;
// Values used to tween between the start and end positions.
private float mTweenRadius = 0;
@@ -82,6 +85,22 @@
/** Whether this ripple has finished its exit animation. */
private boolean mHasFinishedExit;
+ /** Whether we can use hardware acceleration for the exit animation. */
+ private boolean mUsingProperties;
+
+ private long mEnterStartedAtMillis;
+
+ private ArrayList<RenderNodeAnimator> mPendingHwAnimators = new ArrayList<>();
+ private ArrayList<RenderNodeAnimator> mRunningHwAnimators = new ArrayList<>();
+
+ private ArrayList<Animator> mRunningSwAnimators = new ArrayList<>();
+
+ /**
+ * If set, force all ripple animations to not run on RenderThread, even if it would be
+ * available.
+ */
+ private final boolean mForceSoftware;
+
/**
* If we have a bound, don't start from 0. Start from 60% of the max out of width and height.
*/
@@ -89,8 +108,9 @@
public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
boolean isBounded, boolean forceSoftware) {
- super(owner, bounds, forceSoftware);
+ super(owner, bounds);
+ mForceSoftware = forceSoftware;
mStartingX = startingX;
mStartingY = startingY;
@@ -109,10 +129,7 @@
clampStartingPosition();
}
- @Override
- protected boolean drawSoftware(Canvas c, Paint p) {
- boolean hasContent = false;
-
+ private void drawSoftware(Canvas c, Paint p) {
final int origAlpha = p.getAlpha();
final int alpha = (int) (origAlpha * mOpacity + 0.5f);
final float radius = getCurrentRadius();
@@ -122,16 +139,51 @@
p.setAlpha(alpha);
c.drawCircle(x, y, radius, p);
p.setAlpha(origAlpha);
- hasContent = true;
}
-
- return hasContent;
}
- @Override
- protected boolean drawHardware(DisplayListCanvas c) {
- c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
- return true;
+ private void startPending(DisplayListCanvas c) {
+ if (!mPendingHwAnimators.isEmpty()) {
+ for (int i = 0; i < mPendingHwAnimators.size(); i++) {
+ RenderNodeAnimator animator = mPendingHwAnimators.get(i);
+ animator.setTarget(c);
+ animator.start();
+ mRunningHwAnimators.add(animator);
+ }
+ mPendingHwAnimators.clear();
+ }
+ }
+
+ private void pruneHwFinished() {
+ if (!mRunningHwAnimators.isEmpty()) {
+ for (int i = mRunningHwAnimators.size() - 1; i >= 0; i--) {
+ if (!mRunningHwAnimators.get(i).isRunning()) {
+ mRunningHwAnimators.remove(i);
+ }
+ }
+ }
+ }
+
+ private void pruneSwFinished() {
+ if (!mRunningSwAnimators.isEmpty()) {
+ for (int i = mRunningSwAnimators.size() - 1; i >= 0; i--) {
+ if (!mRunningSwAnimators.get(i).isRunning()) {
+ mRunningSwAnimators.remove(i);
+ }
+ }
+ }
+ }
+
+ private void drawHardware(DisplayListCanvas c, Paint p) {
+ startPending(c);
+ pruneHwFinished();
+ if (mPropPaint != null) {
+ mUsingProperties = true;
+ c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint);
+ } else {
+ mUsingProperties = false;
+ drawSoftware(c, p);
+ }
}
/**
@@ -162,31 +214,115 @@
return mHasFinishedExit;
}
- @Override
- protected Animator createSoftwareEnter(boolean fast) {
+ private long computeFadeOutDelay() {
+ long timeSinceEnter = AnimationUtils.currentAnimationTimeMillis() - mEnterStartedAtMillis;
+ if (timeSinceEnter > 0 && timeSinceEnter < OPACITY_HOLD_DURATION) {
+ return OPACITY_HOLD_DURATION - timeSinceEnter;
+ }
+ return 0;
+ }
+
+ private void startSoftwareEnter() {
+ for (int i = 0; i < mRunningSwAnimators.size(); i++) {
+ mRunningSwAnimators.get(i).cancel();
+ }
+ mRunningSwAnimators.clear();
+
final int duration = getRadiusDuration();
final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
- tweenRadius.setAutoCancel(true);
tweenRadius.setDuration(duration);
tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
- tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY);
+ tweenRadius.start();
+ mRunningSwAnimators.add(tweenRadius);
final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
- tweenOrigin.setAutoCancel(true);
tweenOrigin.setDuration(duration);
tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
- tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY);
+ tweenOrigin.start();
+ mRunningSwAnimators.add(tweenOrigin);
final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
- opacity.setAutoCancel(true);
- opacity.setDuration(OPACITY_ENTER_DURATION_FAST);
+ opacity.setDuration(OPACITY_ENTER_DURATION);
opacity.setInterpolator(LINEAR_INTERPOLATOR);
+ opacity.start();
+ mRunningSwAnimators.add(opacity);
+ }
- final AnimatorSet set = new AnimatorSet();
- set.play(tweenOrigin).with(tweenRadius).with(opacity);
+ private void startSoftwareExit() {
+ final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 0);
+ opacity.setDuration(OPACITY_EXIT_DURATION);
+ opacity.setInterpolator(LINEAR_INTERPOLATOR);
+ opacity.addListener(mAnimationListener);
+ opacity.setStartDelay(computeFadeOutDelay());
+ opacity.start();
+ mRunningSwAnimators.add(opacity);
+ }
- return set;
+ private void startHardwareEnter() {
+ if (mForceSoftware) { return; }
+ mPropX = CanvasProperty.createFloat(getCurrentX());
+ mPropY = CanvasProperty.createFloat(getCurrentY());
+ mPropRadius = CanvasProperty.createFloat(getCurrentRadius());
+ final Paint paint = mOwner.getRipplePaint();
+ mPropPaint = CanvasProperty.createPaint(paint);
+
+ final int radiusDuration = getRadiusDuration();
+
+ final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mTargetRadius);
+ radius.setDuration(radiusDuration);
+ radius.setInterpolator(DECELERATE_INTERPOLATOR);
+ mPendingHwAnimators.add(radius);
+
+ final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mTargetX);
+ x.setDuration(radiusDuration);
+ x.setInterpolator(DECELERATE_INTERPOLATOR);
+ mPendingHwAnimators.add(x);
+
+ final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mTargetY);
+ y.setDuration(radiusDuration);
+ y.setInterpolator(DECELERATE_INTERPOLATOR);
+ mPendingHwAnimators.add(y);
+
+ final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
+ RenderNodeAnimator.PAINT_ALPHA, paint.getAlpha());
+ opacity.setDuration(OPACITY_ENTER_DURATION);
+ opacity.setInterpolator(LINEAR_INTERPOLATOR);
+ opacity.setStartValue(0);
+ mPendingHwAnimators.add(opacity);
+
+ invalidateSelf();
+ }
+
+ private void startHardwareExit() {
+ // Only run a hardware exit if we had a hardware enter to continue from
+ if (mForceSoftware || mPropPaint == null) return;
+
+ final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
+ RenderNodeAnimator.PAINT_ALPHA, 0);
+ opacity.setDuration(OPACITY_EXIT_DURATION);
+ opacity.setInterpolator(LINEAR_INTERPOLATOR);
+ opacity.addListener(mAnimationListener);
+ opacity.setStartDelay(computeFadeOutDelay());
+ mPendingHwAnimators.add(opacity);
+ invalidateSelf();
+ }
+
+ /**
+ * Starts a ripple enter animation.
+ */
+ public final void enter() {
+ mEnterStartedAtMillis = AnimationUtils.currentAnimationTimeMillis();
+ startSoftwareEnter();
+ startHardwareEnter();
+ }
+
+ /**
+ * Starts a ripple exit animation.
+ */
+ public final void exit() {
+ startSoftwareExit();
+ startHardwareExit();
}
private float getCurrentX() {
@@ -207,96 +343,23 @@
return MathUtils.lerp(mStartRadius, mTargetRadius, mTweenRadius);
}
- private int getOpacityExitDuration() {
- return (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
- }
+ /**
+ * Draws the ripple to the canvas, inheriting the paint's color and alpha
+ * properties.
+ *
+ * @param c the canvas to which the ripple should be drawn
+ * @param p the paint used to draw the ripple
+ */
+ public void draw(Canvas c, Paint p) {
+ final boolean hasDisplayListCanvas = !mForceSoftware && c instanceof DisplayListCanvas;
- @Override
- protected Animator createSoftwareExit() {
- final int radiusDuration;
- final int originDuration;
- final int opacityDuration;
-
- radiusDuration = getRadiusDuration();
- originDuration = radiusDuration;
- opacityDuration = getOpacityExitDuration();
-
- final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
- tweenRadius.setAutoCancel(true);
- tweenRadius.setDuration(radiusDuration);
- tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
-
- final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
- tweenOrigin.setAutoCancel(true);
- tweenOrigin.setDuration(originDuration);
- tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
-
- final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 0);
- opacity.setAutoCancel(true);
- opacity.setDuration(opacityDuration);
- opacity.setInterpolator(LINEAR_INTERPOLATOR);
-
- final AnimatorSet set = new AnimatorSet();
- set.play(tweenOrigin).with(tweenRadius).with(opacity);
- set.addListener(mAnimationListener);
-
- return set;
- }
-
- @Override
- protected RenderNodeAnimatorSet createHardwareExit(Paint p) {
- final int radiusDuration;
- final int originDuration;
- final int opacityDuration;
-
- radiusDuration = getRadiusDuration();
- originDuration = radiusDuration;
- opacityDuration = getOpacityExitDuration();
-
- final float startX = getCurrentX();
- final float startY = getCurrentY();
- final float startRadius = getCurrentRadius();
-
- p.setAlpha((int) (p.getAlpha() * mOpacity + 0.5f));
-
- mPropPaint = CanvasProperty.createPaint(p);
- mPropRadius = CanvasProperty.createFloat(startRadius);
- mPropX = CanvasProperty.createFloat(startX);
- mPropY = CanvasProperty.createFloat(startY);
-
- final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mTargetRadius);
- radius.setDuration(radiusDuration);
- radius.setInterpolator(DECELERATE_INTERPOLATOR);
-
- final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mTargetX);
- x.setDuration(originDuration);
- x.setInterpolator(DECELERATE_INTERPOLATOR);
-
- final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mTargetY);
- y.setDuration(originDuration);
- y.setInterpolator(DECELERATE_INTERPOLATOR);
-
- final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
- RenderNodeAnimator.PAINT_ALPHA, 0);
- opacity.setDuration(opacityDuration);
- opacity.setInterpolator(LINEAR_INTERPOLATOR);
- opacity.addListener(mAnimationListener);
-
- final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet();
- set.add(radius);
- set.add(opacity);
- set.add(x);
- set.add(y);
-
- return set;
- }
-
- @Override
- protected void jumpValuesToExit() {
- mOpacity = 0;
- mTweenX = 1;
- mTweenY = 1;
- mTweenRadius = 1;
+ pruneSwFinished();
+ if (hasDisplayListCanvas) {
+ final DisplayListCanvas hw = (DisplayListCanvas) c;
+ drawHardware(hw, p);
+ } else {
+ drawSoftware(c, p);
+ }
}
/**
@@ -319,10 +382,39 @@
}
}
+ /**
+ * Ends all animations, jumping values to the end state.
+ */
+ public void end() {
+ for (int i = 0; i < mRunningSwAnimators.size(); i++) {
+ mRunningSwAnimators.get(i).end();
+ }
+ mRunningSwAnimators.clear();
+ for (int i = 0; i < mRunningHwAnimators.size(); i++) {
+ mRunningHwAnimators.get(i).end();
+ }
+ mRunningHwAnimators.clear();
+ }
+
+ private void onAnimationPropertyChanged() {
+ if (!mUsingProperties) {
+ invalidateSelf();
+ }
+ }
+
private final AnimatorListenerAdapter mAnimationListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
mHasFinishedExit = true;
+ pruneHwFinished();
+ pruneSwFinished();
+
+ if (mRunningHwAnimators.isEmpty()) {
+ mPropPaint = null;
+ mPropRadius = null;
+ mPropX = null;
+ mPropY = null;
+ }
}
};
@@ -361,7 +453,7 @@
@Override
public void setValue(RippleForeground object, float value) {
object.mTweenRadius = value;
- object.invalidateSelf();
+ object.onAnimationPropertyChanged();
}
@Override
@@ -375,18 +467,18 @@
*/
private static final FloatProperty<RippleForeground> TWEEN_ORIGIN =
new FloatProperty<RippleForeground>("tweenOrigin") {
- @Override
- public void setValue(RippleForeground object, float value) {
- object.mTweenX = value;
- object.mTweenY = value;
- object.invalidateSelf();
- }
+ @Override
+ public void setValue(RippleForeground object, float value) {
+ object.mTweenX = value;
+ object.mTweenY = value;
+ object.onAnimationPropertyChanged();
+ }
- @Override
- public Float get(RippleForeground object) {
- return object.mTweenX;
- }
- };
+ @Override
+ public Float get(RippleForeground object) {
+ return object.mTweenX;
+ }
+ };
/**
* Property for animating opacity between 0 and its target value.
@@ -396,7 +488,7 @@
@Override
public void setValue(RippleForeground object, float value) {
object.mOpacity = value;
- object.invalidateSelf();
+ object.onAnimationPropertyChanged();
}
@Override
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index c4bb72c..635432d 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -28,6 +28,8 @@
String requestPrivateKey(String alias);
byte[] getCertificate(String alias);
byte[] getCaCertificates(String alias);
+ boolean isUserSelectable(String alias);
+ void setUserSelectable(String alias, boolean isUserSelectable);
// APIs used by CertInstaller and DevicePolicyManager
String installCaCertificate(in byte[] caCertificate);
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index a7469cd..399dddd 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -54,7 +54,7 @@
public class KeyStore {
private static final String TAG = "KeyStore";
- // ResponseCodes
+ // ResponseCodes - see system/security/keystore/include/keystore/keystore.h
public static final int NO_ERROR = 1;
public static final int LOCKED = 2;
public static final int UNINITIALIZED = 3;
@@ -168,10 +168,14 @@
public byte[] get(String key, int uid) {
try {
+ key = key != null ? key : "";
return mBinder.get(key, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
+ } catch (android.os.ServiceSpecificException e) {
+ Log.w(TAG, "KeyStore exception", e);
+ return null;
}
}
@@ -185,6 +189,9 @@
public int insert(String key, byte[] value, int uid, int flags) {
try {
+ if (value == null) {
+ value = new byte[0];
+ }
return mBinder.insert(key, value, uid, flags);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -228,6 +235,9 @@
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
+ } catch (android.os.ServiceSpecificException e) {
+ Log.w(TAG, "KeyStore exception", e);
+ return null;
}
}
@@ -276,6 +286,7 @@
*/
public boolean unlock(int userId, String password) {
try {
+ password = password != null ? password : "";
mError = mBinder.unlock(userId, password);
return mError == NO_ERROR;
} catch (RemoteException e) {
@@ -330,16 +341,25 @@
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
+ } catch (android.os.ServiceSpecificException e) {
+ Log.w(TAG, "KeyStore exception", e);
+ return null;
}
+
}
public boolean verify(String key, byte[] data, byte[] signature) {
try {
+ signature = signature != null ? signature : new byte[0];
return mBinder.verify(key, data, signature) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
+ } catch (android.os.ServiceSpecificException e) {
+ Log.w(TAG, "KeyStore exception", e);
+ return false;
}
+
}
public String grant(String key, int uid) {
@@ -432,6 +452,8 @@
public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid,
int flags, KeyCharacteristics outCharacteristics) {
try {
+ entropy = entropy != null ? entropy : new byte[0];
+ args = args != null ? args : new KeymasterArguments();
return mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -447,6 +469,8 @@
public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
int uid, KeyCharacteristics outCharacteristics) {
try {
+ clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
+ appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
return mBinder.getKeyCharacteristics(alias, clientId, appId, uid, outCharacteristics);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -478,6 +502,8 @@
public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
KeymasterBlob appId, int uid) {
try {
+ clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
+ appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
return mBinder.exportKey(alias, format, clientId, appId, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -492,6 +518,8 @@
public OperationResult begin(String alias, int purpose, boolean pruneable,
KeymasterArguments args, byte[] entropy, int uid) {
try {
+ args = args != null ? args : new KeymasterArguments();
+ entropy = entropy != null ? entropy : new byte[0];
return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -501,11 +529,15 @@
public OperationResult begin(String alias, int purpose, boolean pruneable,
KeymasterArguments args, byte[] entropy) {
+ entropy = entropy != null ? entropy : new byte[0];
+ args = args != null ? args : new KeymasterArguments();
return begin(alias, purpose, pruneable, args, entropy, UID_SELF);
}
public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) {
try {
+ arguments = arguments != null ? arguments : new KeymasterArguments();
+ input = input != null ? input : new byte[0];
return mBinder.update(token, arguments, input);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -516,6 +548,9 @@
public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature,
byte[] entropy) {
try {
+ arguments = arguments != null ? arguments : new KeymasterArguments();
+ entropy = entropy != null ? entropy : new byte[0];
+ signature = signature != null ? signature : new byte[0];
return mBinder.finish(token, arguments, signature, entropy);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -632,6 +667,12 @@
public int attestKey(
String alias, KeymasterArguments params, KeymasterCertificateChain outChain) {
try {
+ if (params == null) {
+ params = new KeymasterArguments();
+ }
+ if (outChain == null) {
+ outChain = new KeymasterCertificateChain();
+ }
return mBinder.attestKey(alias, params, outChain);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -641,6 +682,12 @@
public int attestDeviceIds(KeymasterArguments params, KeymasterCertificateChain outChain) {
try {
+ if (params == null) {
+ params = new KeymasterArguments();
+ }
+ if (outChain == null) {
+ outChain = new KeymasterCertificateChain();
+ }
return mBinder.attestDeviceIds(params, outChain);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
index bec22c9..4c150c8 100644
--- a/legacy-test/Android.mk
+++ b/legacy-test/Android.mk
@@ -146,6 +146,7 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src/android) \
$(call all-java-files-under, ../test-runner/src/android) \
+ $(call all-java-files-under, ../test-mock/src/android) \
$(call all-java-files-under, src/com)
LOCAL_MODULE := legacy-android-test
LOCAL_NO_STANDARD_LIBRARIES := true
diff --git a/legacy-test/src/com/android/internal/util/Predicate.java b/legacy-test/src/com/android/internal/util/Predicate.java
index 1b5eaff..e87f489 100644
--- a/legacy-test/src/com/android/internal/util/Predicate.java
+++ b/legacy-test/src/com/android/internal/util/Predicate.java
@@ -27,6 +27,7 @@
* strongly encouraged to state this fact clearly in their API documentation.
*
* @deprecated Use {@code java.util.function.Predicate} instead.
+ * This must not be used outside frameworks/base/test-runner.
*/
@Deprecated
public interface Predicate<T> {
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 5d248c2..3fe75cf 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -812,7 +812,13 @@
*outLen = encLen;
if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
- return (const char*)str;
+ // Reject malformed (non null-terminated) strings
+ if (str[encLen] != 0x00) {
+ ALOGW("Bad string block: string #%d is not null-terminated",
+ (int)idx);
+ return NULL;
+ }
+ return (const char*)str;
} else {
ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
(int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 24ce1e4..5c577ae 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -2,9 +2,10 @@
name: "hwui_defaults",
defaults: [
"hwui_static_deps",
- "skia_deps"
+ "skia_deps",
//"hwui_bugreport_font_cache_usage",
//"hwui_compile_for_perf",
+ "hwui_pgo",
],
cpp_std: "c++17",
@@ -109,6 +110,22 @@
include_dirs: ["frameworks/native/opengl/libs/GLES2"],
}
+// Build libhwui with PGO by default.
+// Location of PGO profile data is defined in build/soong/cc/pgo.go
+// and is separate from hwui.
+// To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable
+// or set enable_profile_use property to false.
+cc_defaults {
+ name: "hwui_pgo",
+
+ pgo: {
+ instrumentation: true,
+ profile_file: "hwui/hwui.profdata",
+ benchmarks: ["hwui"],
+ enable_profile_use: false,
+ },
+}
+
// ------------------------
// library
// ------------------------
@@ -255,18 +272,6 @@
// Has moderate overhead
"hwui_enable_opengl_validation",
],
-
- // Build libhwui with PGO by default.
- // Location of PGO profile data is defined in build/soong/cc/pgo.go
- // and is separate from hwui.
- // To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable
- // or set enable_profile_use property to false.
- pgo: {
- instrumentation: true,
- profile_file: "hwui/hwui.profdata",
- benchmarks: ["hwui"],
- enable_profile_use: false,
- },
}
// ------------------------
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 91756e7..5aea04d 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -245,22 +245,19 @@
// last call to dumpData(). In other words if there's a dumpData(), draw frame,
// dumpData(), the last dumpData() should only log 1 frame.
- FILE* file = fdopen(fd, "a");
- fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
+ dprintf(fd, "\n\tDraw\tPrepare\tProcess\tExecute\n");
for (size_t i = 0; i < mFrameSource.size(); i++) {
if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
continue;
}
mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
- fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
+ dprintf(fd, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
durationMS(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
durationMS(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
}
-
- fflush(file);
}
} /* namespace uirenderer */
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 5b67693..afdd339 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -174,24 +174,22 @@
}
void JankTracker::dumpFrames(int fd) {
- FILE* file = fdopen(fd, "a");
- fprintf(file, "\n\n---PROFILEDATA---\n");
+ dprintf(fd, "\n\n---PROFILEDATA---\n");
for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
- fprintf(file, "%s", FrameInfoNames[i].c_str());
- fprintf(file, ",");
+ dprintf(fd, "%s", FrameInfoNames[i].c_str());
+ dprintf(fd, ",");
}
for (size_t i = 0; i < mFrames.size(); i++) {
FrameInfo& frame = mFrames[i];
if (frame[FrameInfoIndex::SyncStart] == 0) {
continue;
}
- fprintf(file, "\n");
+ dprintf(fd, "\n");
for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
- fprintf(file, "%" PRId64 ",", frame[i]);
+ dprintf(fd, "%" PRId64 ",", frame[i]);
}
}
- fprintf(file, "\n---PROFILEDATA---\n\n");
- fflush(file);
+ dprintf(fd, "\n---PROFILEDATA---\n\n");
}
void JankTracker::reset() {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index d90caf9..d41db63 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -178,7 +178,7 @@
}
RenderPipelineType Properties::getRenderPipelineType() {
- if (RenderPipelineType::NotInitialized != sRenderPipelineType) {
+ if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
return sRenderPipelineType;
}
char prop[PROPERTY_VALUE_MAX];
@@ -196,11 +196,17 @@
return sRenderPipelineType;
}
-#ifdef HWUI_GLES_WRAP_ENABLED
void Properties::overrideRenderPipelineType(RenderPipelineType type) {
+#if !defined(HWUI_GLES_WRAP_ENABLED)
+ // If we're doing actual rendering then we can't change the renderer after it's been set.
+ // Unit tests can freely change this as often as it wants, though, as there's no actual
+ // GL rendering happening
+ if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
+ return;
+ }
+#endif
sRenderPipelineType = type;
}
-#endif
bool Properties::isSkiaEnabled() {
auto renderType = getRenderPipelineType();
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index fbd9b7a..9c30e4a 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -255,10 +255,8 @@
static bool skpCaptureEnabled;
-// Used for testing only to change the render pipeline.
-#ifdef HWUI_GLES_WRAP_ENABLED
+ // Used for testing only to change the render pipeline.
static void overrideRenderPipelineType(RenderPipelineType);
-#endif
private:
static ProfileType sProfileType;
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 837c4ef..3d2c252 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -26,6 +26,7 @@
#include <SkBlendMode.h>
#include <SkCamera.h>
+#include <SkColor.h>
#include <SkMatrix.h>
#include <SkRegion.h>
@@ -506,6 +507,14 @@
getOutline().getAlpha() != 0.0f;
}
+ SkColor getShadowColor() const {
+ return mPrimitiveFields.mShadowColor;
+ }
+
+ bool setShadowColor(SkColor shadowColor) {
+ return RP_SET(mPrimitiveFields.mShadowColor, shadowColor);
+ }
+
bool fitsOnLayer() const {
const DeviceInfo* deviceInfo = DeviceInfo::get();
return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() &&
@@ -529,6 +538,7 @@
int mLeft = 0, mTop = 0, mRight = 0, mBottom = 0;
int mWidth = 0, mHeight = 0;
int mClippingFlags = CLIP_TO_BOUNDS;
+ SkColor mShadowColor = SK_ColorBLACK;
float mAlpha = 1;
float mTranslationX = 0, mTranslationY = 0, mTranslationZ = 0;
float mElevation = 0;
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 2a1a1f3..bad766c 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -29,44 +29,42 @@
minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint,
const Typeface* typeface) {
const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
- minikin::FontStyle resolved = resolvedFace->fStyle;
- const minikin::FontVariant minikinVariant =
- (paint->getFontVariant() == minikin::FontVariant::ELEGANT)
- ? minikin::FontVariant::ELEGANT
- : minikin::FontVariant::COMPACT;
- float textSize = paint->getTextSize();
- if (!paint->isLinearText()) {
- // If linear text is not specified, truncate the value.
- textSize = trunc(textSize);
- }
- return minikin::MinikinPaint(
- textSize,
- paint->getTextScaleX(),
- paint->getTextSkewX(),
- paint->getLetterSpacing(),
- paint->getWordSpacing(),
- MinikinFontSkia::packPaintFlags(paint),
- paint->getMinikinLocaleListId(),
- minikin::FontStyle(minikinVariant, resolved.weight, resolved.slant),
- minikin::HyphenEdit(paint->getHyphenEdit()),
- paint->getFontFeatureSettings(),
- resolvedFace->fFontCollection);
+
+ minikin::MinikinPaint minikinPaint;
+ /* Prepare minikin Paint */
+ minikinPaint.size =
+ paint->isLinearText() ? paint->getTextSize() : static_cast<int>(paint->getTextSize());
+ minikinPaint.scaleX = paint->getTextScaleX();
+ minikinPaint.skewX = paint->getTextSkewX();
+ minikinPaint.letterSpacing = paint->getLetterSpacing();
+ minikinPaint.wordSpacing = paint->getWordSpacing();
+ minikinPaint.paintFlags = MinikinFontSkia::packPaintFlags(paint);
+ minikinPaint.localeListId = paint->getMinikinLocaleListId();
+ minikinPaint.familyVariant = paint->getFamilyVariant();
+ minikinPaint.fontStyle = resolvedFace->fStyle;
+ minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
+ minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
+ return minikinPaint;
}
minikin::Layout MinikinUtils::doLayout(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf, size_t start,
size_t count, size_t bufSize) {
+ minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
minikin::Layout layout;
- layout.doLayout(buf, start, count, bufSize, bidiFlags, prepareMinikinPaint(paint, typeface));
+ layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint,
+ Typeface::resolveDefault(typeface)->fFontCollection);
return layout;
}
float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf, size_t start,
size_t count, size_t bufSize, float* advances) {
- return minikin::Layout::measureText(
- buf, start, count, bufSize, bidiFlags, prepareMinikinPaint(paint, typeface), advances,
- nullptr /* extent */, nullptr /* overhangs */);
+ minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
+ const Typeface* resolvedTypeface = Typeface::resolveDefault(typeface);
+ return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinPaint,
+ resolvedTypeface->fFontCollection, advances,
+ nullptr /* extent */, nullptr /* overhangs */);
}
bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 76beb11..002f759 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -73,9 +73,9 @@
uint32_t getMinikinLocaleListId() const { return mMinikinLocaleListId; }
- void setFontVariant(minikin::FontVariant variant) { mFontVariant = variant; }
+ void setFamilyVariant(minikin::FontFamily::Variant variant) { mFamilyVariant = variant; }
- minikin::FontVariant getFontVariant() const { return mFontVariant; }
+ minikin::FontFamily::Variant getFamilyVariant() const { return mFamilyVariant; }
void setHyphenEdit(uint32_t hyphen) { mHyphenEdit = hyphen; }
@@ -90,7 +90,7 @@
float mWordSpacing = 0;
std::string mFontFeatureSettings;
uint32_t mMinikinLocaleListId;
- minikin::FontVariant mFontVariant;
+ minikin::FontFamily::Variant mFamilyVariant;
uint32_t mHyphenEdit = 0;
// The native Typeface object has the same lifetime of the Java Typeface
// object. The Java Paint object holds a strong reference to the Java Typeface
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 94492c5..ae9c475 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -24,7 +24,7 @@
, mWordSpacing(0)
, mFontFeatureSettings()
, mMinikinLocaleListId(0)
- , mFontVariant(minikin::FontVariant::DEFAULT) {}
+ , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {}
Paint::Paint(const Paint& paint)
: SkPaint(paint)
@@ -32,7 +32,7 @@
, mWordSpacing(paint.mWordSpacing)
, mFontFeatureSettings(paint.mFontFeatureSettings)
, mMinikinLocaleListId(paint.mMinikinLocaleListId)
- , mFontVariant(paint.mFontVariant)
+ , mFamilyVariant(paint.mFamilyVariant)
, mHyphenEdit(paint.mHyphenEdit)
, mTypeface(paint.mTypeface) {}
@@ -42,7 +42,7 @@
, mWordSpacing(0)
, mFontFeatureSettings()
, mMinikinLocaleListId(0)
- , mFontVariant(minikin::FontVariant::DEFAULT) {}
+ , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {}
Paint::~Paint() {}
@@ -52,7 +52,7 @@
mWordSpacing = other.mWordSpacing;
mFontFeatureSettings = other.mFontFeatureSettings;
mMinikinLocaleListId = other.mMinikinLocaleListId;
- mFontVariant = other.mFontVariant;
+ mFamilyVariant = other.mFamilyVariant;
mHyphenEdit = other.mHyphenEdit;
mTypeface = other.mTypeface;
return *this;
@@ -62,7 +62,8 @@
return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) &&
a.mLetterSpacing == b.mLetterSpacing && a.mWordSpacing == b.mWordSpacing &&
a.mFontFeatureSettings == b.mFontFeatureSettings &&
- a.mMinikinLocaleListId == b.mMinikinLocaleListId && a.mFontVariant == b.mFontVariant &&
- a.mHyphenEdit == b.mHyphenEdit && a.mTypeface == b.mTypeface;
+ a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
+ a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
+ a.mTypeface == b.mTypeface;
}
} // namespace android
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index e527adc..ebc14c8 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -45,7 +45,7 @@
static minikin::FontStyle computeMinikinStyle(int weight, bool italic) {
return minikin::FontStyle(uirenderer::MathUtils::clamp(weight, 1, 1000),
- static_cast<minikin::FontSlant>(italic));
+ static_cast<minikin::FontStyle::Slant>(italic));
}
// Resolve the relative weight from the baseWeight and target style.
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 67e0602..7b59ccf 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -189,7 +189,7 @@
}
SkShadowUtils::DrawShadow(
canvas, *casterPath, zParams, skiaLightPos, SkiaPipeline::getLightRadius(),
- ambientAlpha, spotAlpha, SK_ColorBLACK,
+ ambientAlpha, spotAlpha, casterProperties.getShadowColor(),
casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 6657fe8..91b35c2 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -144,22 +144,25 @@
// ----------------------------------------------------------------------------
inline static const SkPaint* bitmapPaint(const SkPaint* origPaint, SkPaint* tmpPaint,
- sk_sp<SkColorFilter> colorFilter) {
- if ((origPaint && origPaint->isAntiAlias()) || colorFilter) {
+ sk_sp<SkColorFilter> colorSpaceFilter) {
+ if ((origPaint && origPaint->isAntiAlias()) || colorSpaceFilter) {
if (origPaint) {
*tmpPaint = *origPaint;
}
- sk_sp<SkColorFilter> filter;
- if (colorFilter && tmpPaint->getColorFilter()) {
- filter = SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorFilter);
- LOG_ALWAYS_FATAL_IF(!filter);
- } else {
- filter = colorFilter;
+ if (colorSpaceFilter) {
+ if (tmpPaint->getColorFilter()) {
+ tmpPaint->setColorFilter(
+ SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorSpaceFilter));
+ } else {
+ tmpPaint->setColorFilter(colorSpaceFilter);
+ }
+ LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
}
+
+ // disabling AA on bitmap draws matches legacy HWUI behavior
tmpPaint->setAntiAlias(false);
- tmpPaint->setColorFilter(filter);
return tmpPaint;
} else {
return origPaint;
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index c22364b..a33b287 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -18,7 +18,6 @@
#include "Layer.h"
#include "RenderThread.h"
-#include "pipeline/skia/ShaderCache.h"
#include "renderstate/RenderState.h"
#include <GrContextOptions.h>
@@ -128,8 +127,6 @@
}
contextOptions->fExecutor = mTaskProcessor.get();
}
-
- contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get();
}
void CacheManager::trimMemory(TrimMemoryMode mode) {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 574bb02..05a9b75 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,7 +16,6 @@
#include "RenderThread.h"
-#include "pipeline/skia/ShaderCache.h"
#include "CanvasContext.h"
#include "EglManager.h"
#include "OpenGLReadback.h"
@@ -106,7 +105,6 @@
mRenderState = new RenderState(*this);
mVkManager = new VulkanManager(*this);
mCacheManager = new CacheManager(mDisplayInfo);
- uirenderer::skiapipeline::ShaderCache::get().initShaderDiskCache();
}
void RenderThread::dumpGraphicsMemory(int fd) {
@@ -141,10 +139,8 @@
break;
}
- FILE* file = fdopen(fd, "a");
- fprintf(file, "\n%s\n", cachesOutput.string());
- fprintf(file, "\nPipeline=%s\n", pipeline.string());
- fflush(file);
+ dprintf(fd, "\n%s\n", cachesOutput.string());
+ dprintf(fd, "\nPipeline=%s\n", pipeline.string());
}
Readback& RenderThread::readback() {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 1d8cdd6..a693e68 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -602,6 +602,7 @@
mDeviceWaitIdle(mBackendContext->fDevice);
}
+ SkASSERT(surface->mBackbuffers);
VulkanSurface::BackbufferInfo* backbuffer =
surface->mBackbuffers + surface->mCurrentBackbufferIndex;
GrVkImageInfo* imageInfo;
@@ -683,6 +684,7 @@
}
int VulkanManager::getAge(VulkanSurface* surface) {
+ SkASSERT(surface->mBackbuffers);
VulkanSurface::BackbufferInfo* backbuffer =
surface->mBackbuffers + surface->mCurrentBackbufferIndex;
if (mSwapBehavior == SwapBehavior::Discard ||
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index b9898a7..c319c9e 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -59,11 +59,11 @@
VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
- BackbufferInfo* mBackbuffers;
+ BackbufferInfo* mBackbuffers = nullptr;
uint32_t mCurrentBackbufferIndex;
uint32_t mImageCount;
- VkImage* mImages;
+ VkImage* mImages = nullptr;
ImageInfo* mImageInfos;
uint16_t mCurrentTime = 0;
};
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 5cd90c5..91022cf 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -16,6 +16,9 @@
#pragma once
+#include <gui/Surface.h>
+#include <utils/StrongPointer.h>
+
#include <string>
#include <unordered_map>
@@ -66,6 +69,8 @@
static std::unordered_map<std::string, Info>& testMap();
static void registerScene(const Info& info);
+
+ sp<Surface> renderTarget;
};
} // namespace test
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
new file mode 100644
index 0000000..9eddc20
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+#include "renderthread/RenderProxy.h"
+#include "utils/Color.h"
+
+class MagnifierAnimation;
+
+static TestScene::Registrar _Magnifier(TestScene::Info{
+ "magnifier", "A sample magnifier using Readback",
+ TestScene::simpleCreateScene<MagnifierAnimation>});
+
+class MagnifierAnimation : public TestScene {
+public:
+ sp<RenderNode> card;
+ sp<RenderNode> zoomImageView;
+
+ void createContent(int width, int height, Canvas& canvas) override {
+ magnifier = TestUtils::createBitmap(200, 100);
+ SkBitmap temp;
+ magnifier->getSkBitmap(&temp);
+ temp.eraseColor(Color::White);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
+ card = TestUtils::createNode(
+ 0, 0, width, height, [&](RenderProperties& props, Canvas& canvas) {
+ SkPaint paint;
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setAntiAlias(true);
+ paint.setTextSize(50);
+
+ paint.setColor(Color::Black);
+ TestUtils::drawUtf8ToCanvas(&canvas, "Test string", paint, 10, 400);
+ });
+ canvas.drawRenderNode(card.get());
+ zoomImageView = TestUtils::createNode(
+ 100, 100, 500, 300, [&](RenderProperties& props, Canvas& canvas) {
+ props.setElevation(dp(16));
+ props.mutableOutline().setRoundRect(0, 0, props.getWidth(), props.getHeight(),
+ dp(6), 1);
+ props.mutableOutline().setShouldClip(true);
+ canvas.drawBitmap(*magnifier, 0.0f, 0.0f, (float)magnifier->width(),
+ (float)magnifier->height(), 0, 0, (float)props.getWidth(),
+ (float)props.getHeight(), nullptr);
+ });
+ canvas.insertReorderBarrier(true);
+ canvas.drawRenderNode(zoomImageView.get());
+ canvas.insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ if (renderTarget) {
+ SkBitmap temp;
+ magnifier->getSkBitmap(&temp);
+ constexpr int x = 90;
+ constexpr int y = 325;
+ RenderProxy::copySurfaceInto(renderTarget, x, y, x + magnifier->width(),
+ y + magnifier->height(), &temp);
+ }
+ }
+
+ sk_sp<Bitmap> magnifier;
+};
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index b9ce24b..a502116 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -15,7 +15,6 @@
*/
#include "TestSceneBase.h"
-#include "utils/Color.h"
class TextAnimation;
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 3eb58a9..9428f53 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -111,8 +111,6 @@
// Switch to the real display
gDisplay = getBuiltInDisplay();
- std::unique_ptr<TestScene> scene(info.createScene(opts));
-
Properties::forceDrawFrame = true;
TestContext testContext;
testContext.setRenderOffscreen(opts.renderOffscreen);
@@ -122,6 +120,9 @@
const int height = gDisplay.h;
sp<Surface> surface = testContext.surface();
+ std::unique_ptr<TestScene> scene(info.createScene(opts));
+ scene->renderTarget = surface;
+
sp<RenderNode> rootNode = TestUtils::createNode(
0, 0, width, height, [&scene, width, height](RenderProperties& props, Canvas& canvas) {
props.setClipToBounds(false);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index f1ff939..8c0ca5c 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -68,6 +68,7 @@
--onscreen Render tests on device screen. By default tests
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
+ --renderer=TYPE Sets the render pipeline to use. May be opengl, skiagl, or skiavk
)");
}
@@ -146,6 +147,20 @@
return true;
}
+static bool setRenderer(const char* renderer) {
+ if (!strcmp(renderer, "opengl")) {
+ Properties::overrideRenderPipelineType(RenderPipelineType::OpenGL);
+ } else if (!strcmp(renderer, "skiagl")) {
+ Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
+ } else if (!strcmp(renderer, "skiavk")) {
+ Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan);
+ } else {
+ fprintf(stderr, "Unknown format '%s'", renderer);
+ return false;
+ }
+ return true;
+}
+
// For options that only exist in long-form. Anything in the
// 0-255 range is reserved for short options (which just use their ASCII value)
namespace LongOpts {
@@ -158,6 +173,7 @@
BenchmarkFormat,
Onscreen,
Offscreen,
+ Renderer,
};
}
@@ -172,6 +188,7 @@
{"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat},
{"onscreen", no_argument, nullptr, LongOpts::Onscreen},
{"offscreen", no_argument, nullptr, LongOpts::Offscreen},
+ {"renderer", required_argument, nullptr, LongOpts::Renderer},
{0, 0, 0, 0}};
static const char* SHORT_OPTIONS = "c:r:h";
@@ -252,6 +269,16 @@
}
break;
+ case LongOpts::Renderer:
+ if (!optarg) {
+ error = true;
+ break;
+ }
+ if (!setRenderer(optarg)) {
+ error = true;
+ }
+ break;
+
case LongOpts::Onscreen:
gOpts.renderOffscreen = false;
break;
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 1fcc028..66d6f52 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -81,39 +81,39 @@
TEST(TypefaceTest, createWithDifferentBaseWeight) {
std::unique_ptr<Typeface> bold(Typeface::createWithDifferentBaseWeight(nullptr, 700));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, bold->fAPIStyle);
std::unique_ptr<Typeface> light(Typeface::createWithDifferentBaseWeight(nullptr, 300));
- EXPECT_EQ(300, light->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, light->fStyle.slant);
+ EXPECT_EQ(300, light->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, light->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, light->fAPIStyle);
}
TEST(TypefaceTest, createRelativeTest_fromRegular) {
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, Typeface::kNormal));
- EXPECT_EQ(400, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(400, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, Typeface::kBold));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, Typeface::kItalic));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(Typeface::createRelative(nullptr, Typeface::kBoldItalic));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -123,30 +123,30 @@
// In Java, Typeface.create(Typeface.create("sans-serif-bold"),
// Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
- EXPECT_EQ(700, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(700, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-bold"),
// Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
- EXPECT_EQ(1000, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(1000, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-bold"),
// Typeface.ITALIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
- EXPECT_EQ(700, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(700, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-bold"),
// Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
Typeface::createRelative(base.get(), Typeface::kBoldItalic));
- EXPECT_EQ(1000, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(1000, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -156,30 +156,30 @@
// In Java, Typeface.create(Typeface.create("sans-serif-light"),
// Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
- EXPECT_EQ(300, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(300, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-light"),
// Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
- EXPECT_EQ(600, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(600, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-light"),
// Typeface.ITLIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
- EXPECT_EQ(300, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(300, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-light"),
// Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
Typeface::createRelative(base.get(), Typeface::kBoldItalic));
- EXPECT_EQ(600, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(600, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -189,22 +189,22 @@
// In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
// Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
- EXPECT_EQ(400, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(400, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
// Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD),
// Typeface.ITALIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
- EXPECT_EQ(400, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -212,8 +212,8 @@
// Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
Typeface::createRelative(base.get(), Typeface::kBoldItalic));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -224,23 +224,23 @@
// Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC),
// Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
- EXPECT_EQ(400, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(400, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.create(Typeface.DEFAULT,
// Typeface.ITALIC), Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java,
// Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC),
// Typeface.ITALIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -248,8 +248,8 @@
// Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
Typeface::createRelative(base.get(), Typeface::kBoldItalic));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -261,8 +261,8 @@
// .setWeight(700).setItalic(false).build();
// Typeface.create(typeface, Typeface.NORMAL);
std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
- EXPECT_EQ(400, normal->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, normal->fStyle.slant);
+ EXPECT_EQ(400, normal->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, normal->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java,
@@ -270,8 +270,8 @@
// .setWeight(700).setItalic(false).build();
// Typeface.create(typeface, Typeface.BOLD);
std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java,
@@ -279,8 +279,8 @@
// .setWeight(700).setItalic(false).build();
// Typeface.create(typeface, Typeface.ITALIC);
std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -289,8 +289,8 @@
// Typeface.create(typeface, Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
Typeface::createRelative(base.get(), Typeface::kBoldItalic));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
@@ -300,8 +300,8 @@
// Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(false)
// .build();
std::unique_ptr<Typeface> regular(Typeface::createAbsolute(nullptr, 400, false));
- EXPECT_EQ(400, regular->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+ EXPECT_EQ(400, regular->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java,
@@ -309,8 +309,8 @@
// Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(false)
// .build();
std::unique_ptr<Typeface> bold(Typeface::createAbsolute(nullptr, 700, false));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java,
@@ -318,8 +318,8 @@
// Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(true)
// .build();
std::unique_ptr<Typeface> italic(Typeface::createAbsolute(nullptr, 400, true));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -327,8 +327,8 @@
// Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(true)
// .build();
std::unique_ptr<Typeface> boldItalic(Typeface::createAbsolute(nullptr, 700, true));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
// In Java,
@@ -336,8 +336,8 @@
// Typeface.Builder(invalid).setFallback("sans-serif").setWeight(1100).setItalic(true)
// .build();
std::unique_ptr<Typeface> over1000(Typeface::createAbsolute(nullptr, 1100, false));
- EXPECT_EQ(1000, over1000->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, over1000->fStyle.slant);
+ EXPECT_EQ(1000, over1000->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
}
@@ -346,24 +346,24 @@
// Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build();
std::unique_ptr<Typeface> regular(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), 400, false));
- EXPECT_EQ(400, regular->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+ EXPECT_EQ(400, regular->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new
// Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build();
std::unique_ptr<Typeface> bold(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 700, false));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new
// Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build();
std::unique_ptr<Typeface> italic(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), 400, true));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -371,8 +371,8 @@
// Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build();
std::unique_ptr<Typeface> boldItalic(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), 700, true));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
@@ -380,8 +380,8 @@
// Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build();
std::unique_ptr<Typeface> over1000(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 1100, false));
- EXPECT_EQ(1000, over1000->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, over1000->fStyle.slant);
+ EXPECT_EQ(1000, over1000->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
}
@@ -389,30 +389,30 @@
// In Java, new Typeface.Builder("Roboto-Regular.ttf").build();
std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
makeSingleFamlyVector(kRobotoRegular), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(400, regular->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, regular->fStyle.slant);
+ EXPECT_EQ(400, regular->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-Bold.ttf").build();
std::unique_ptr<Typeface> bold(Typeface::createFromFamilies(
makeSingleFamlyVector(kRobotoBold), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(700, bold->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, bold->fStyle.slant);
+ EXPECT_EQ(700, bold->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-Italic.ttf").build();
std::unique_ptr<Typeface> italic(Typeface::createFromFamilies(
makeSingleFamlyVector(kRobotoItalic), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(400, italic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, italic->fStyle.slant);
+ EXPECT_EQ(400, italic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-BoldItalic.ttf").build();
std::unique_ptr<Typeface> boldItalic(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic),
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(700, boldItalic->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::ITALIC, boldItalic->fStyle.slant);
+ EXPECT_EQ(700, boldItalic->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
}
@@ -422,8 +422,8 @@
buildFamily(kRobotoBoldItalic)};
std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(400, typeface->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, typeface->fStyle.slant);
+ EXPECT_EQ(400, typeface->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
}
TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) {
@@ -431,8 +431,8 @@
buildFamily(kRobotoBold), buildFamily(kRobotoItalic), buildFamily(kRobotoBoldItalic)};
std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
- EXPECT_EQ(700, typeface->fStyle.weight);
- EXPECT_EQ(minikin::FontSlant::UPRIGHT, typeface->fStyle.slant);
+ EXPECT_EQ(700, typeface->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
}
} // namespace
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index ce41849..faea9b2 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -110,6 +110,12 @@
void end(long long token);
/**
+ * Returns how many bytes are buffered in ProtoOutputStream.
+ * Notice, this is not the actual(compact) size of the output data.
+ */
+ size_t bytesWritten();
+
+ /**
* Flushes the protobuf data out to given fd. When the following functions are called,
* it is not able to write to ProtoOutputStream any more since the data is compact.
*/
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 9d8ee72..1904d40 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -295,6 +295,12 @@
}
}
+size_t
+ProtoOutputStream::bytesWritten()
+{
+ return mBuffer.size();
+}
+
bool
ProtoOutputStream::compact() {
if (mCompact) return true;
diff --git a/location/Android.mk b/location/Android.mk
index feeb8ce..50509c6 100644
--- a/location/Android.mk
+++ b/location/Android.mk
@@ -14,4 +14,4 @@
LOCAL_PATH := $(call my-dir)
-include $(call all-makefiles-under, $(LOCAL_PATH))
+include $(call all-subdir-makefiles, $(LOCAL_PATH))
\ No newline at end of file
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index 902cd96..73b2bb5 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -10,5 +10,13 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksLocationTests
-include $(BUILD_PACKAGE)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ core-test-rules \
+ guava \
+ mockito-target-minus-junit4 \
+ frameworks-base-testutils \
+ truth-prebuilt \
+LOCAL_COMPATIBILITY_SUITE := device-tests
+include $(BUILD_PACKAGE)
diff --git a/location/tests/locationtests/AndroidManifest.xml b/location/tests/locationtests/AndroidManifest.xml
index 1d9df0f..ddb8ea6 100644
--- a/location/tests/locationtests/AndroidManifest.xml
+++ b/location/tests/locationtests/AndroidManifest.xml
@@ -4,9 +4,9 @@
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.
@@ -23,13 +23,13 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
+
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
- android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.frameworks.locationtests"
- android:label="Frameworks Location Tests" />
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.locationtests"
+ android:label="Frameworks Location Tests" />
</manifest>
diff --git a/location/tests/locationtests/AndroidTest.xml b/location/tests/locationtests/AndroidTest.xml
new file mode 100644
index 0000000..0c5b7cc
--- /dev/null
+++ b/location/tests/locationtests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks Location API Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FrameworksLocationTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="FrameworksLocationTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.locationtests" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/location/tests/locationtests/src/android/location/GnssStatusTest.java b/location/tests/locationtests/src/android/location/GnssStatusTest.java
new file mode 100644
index 0000000..79ea0d6
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/GnssStatusTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.List;
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link GnssStatus}.
+ */
+@SmallTest
+public class GnssStatusTest extends TestCase {
+
+ private static final String TAG = GnssStatusTest.class.getSimpleName();
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /*
+ * Create {@link GnssStatus} with default value, verify whether its fields are set correctly.
+ *
+ */
+ public void testEmptyGnssStatus() throws Exception {
+ Log.i(TAG, "testEmptyGnssStatus");
+ List<SatelliteInfo> svInfos = new ArrayList<>();
+ GnssStatus gnssStatus = createGnssStatus(svInfos);
+ verifyGnssStatus(svInfos, gnssStatus);
+ }
+
+ /*
+ * Create {@link GnssStatus} with only one satellite info, verify whether its fields are set
+ * correctly.
+ */
+ public void testOneSatelliteGnssStatus() throws Exception {
+ Log.i(TAG, "testOneSatelliteGnssStatus");
+ List<SatelliteInfo> svInfos = new ArrayList<>();
+ SatelliteInfo svInfo =
+ new SatelliteInfo(100,1, true, true, true, true, 100f, 20.3f, 45.5f, 100.23f);
+ svInfos.add(svInfo);
+ GnssStatus gnssStatus = createGnssStatus(svInfos);
+ verifyGnssStatus(svInfos, gnssStatus);
+ }
+
+ /*
+ * Create {@link GnssStatus} with multiple satellite info, verify whether its fields are set
+ * correctly.
+ */
+ public void testMultipleSatellitesGnssStatus() throws Exception {
+ Log.i(TAG, "testMultipleSatellitesGnssStatus");
+ List<SatelliteInfo> svInfos = new ArrayList<>();
+ SatelliteInfo svInfo1 =
+ new SatelliteInfo(20, 1,true, true, true, true, 10.1f, 20.3f, 45.5f, 111.23f);
+ SatelliteInfo svInfo2 =
+ new SatelliteInfo(50, 2, true, false, true, false, 20.2f, 21.3f, 46.5f, 222.23f);
+ SatelliteInfo svInfo3 =
+ new SatelliteInfo(192, 3, false, true, false, true, 30.3f, 22.3f, 47.5f, 333.23f);
+ SatelliteInfo svInfo4 =
+ new SatelliteInfo(250, 4, false, false, false, false, 40.4f, 23.3f, 48.5f, 444.23f);
+ svInfos.add(svInfo1);
+ svInfos.add(svInfo2);
+ svInfos.add(svInfo3);
+ svInfos.add(svInfo4);
+ GnssStatus gnssStatus = createGnssStatus(svInfos);
+ verifyGnssStatus(svInfos, gnssStatus);
+ }
+
+ private void verifyGnssStatus(List<SatelliteInfo> svInfos, GnssStatus gnssStatus) {
+ Log.i(TAG, String.format("Verifing {0} satellites info.",svInfos.size()));
+ assertEquals(TAG + "::SatelliteCount", svInfos.size(),
+ gnssStatus.getSatelliteCount());
+ for (int i = 0; i< svInfos.size(); i++) {
+ SatelliteInfo svInfo = svInfos.get(i);
+ assertEquals(TAG + "::Svid", svInfo.mSvid, gnssStatus.getSvid(i));
+ assertEquals(TAG + "::ConstellationType", svInfo.mConstellationType,
+ gnssStatus.getConstellationType(i));
+ assertEquals(TAG + "::Cn0DbHz", svInfo.mCn0DbHz, gnssStatus.getCn0DbHz(i));
+ assertEquals(TAG + "::Elevation", svInfo.mElevation,
+ gnssStatus.getElevationDegrees(i));
+ assertEquals(TAG + "::Azimuth", svInfo.mAzimuth, gnssStatus.getAzimuthDegrees(i));
+ assertEquals(TAG + "::CarrierFrequencyHz", svInfo.mCarrierFrequency,
+ gnssStatus.getCarrierFrequencyHz(i));
+ assertEquals(TAG + "::hasEphemerisData", svInfo.mHasEphemris,
+ gnssStatus.hasEphemerisData(i));
+ assertEquals(TAG + "::HasAlmanacData", svInfo.mHasAlmanac,
+ gnssStatus.hasAlmanacData(i));
+ assertEquals(TAG + "::UsedInFix", svInfo.mUsedInFix, gnssStatus.usedInFix(i));
+ assertEquals(TAG + "::HasCarrierFrequencyHz", svInfo.mHasCarriesFrequency,
+ gnssStatus.hasCarrierFrequencyHz(i));
+ }
+ }
+
+ private static GnssStatus createGnssStatus(List<SatelliteInfo> svInfos) throws Exception {
+ Class<?> intClass = Integer.TYPE;
+ Class<?> floatArrayClass = Class.forName("[F");
+ Class<?> intArrayClass = Class.forName("[I");
+ Class[] cArg = new Class[6];
+ cArg[0] = intClass;
+ cArg[1] = intArrayClass;
+ cArg[2] = floatArrayClass;
+ cArg[3] = floatArrayClass;
+ cArg[4] = floatArrayClass;
+ cArg[5] = floatArrayClass;
+ Constructor<GnssStatus> ctor = GnssStatus.class.getDeclaredConstructor(cArg);
+ ctor.setAccessible(true);
+ return ctor.newInstance(svInfos.size(),
+ SatelliteInfo.getSvidWithFlagsArray(svInfos),
+ SatelliteInfo.getCn0sArray(svInfos),
+ SatelliteInfo.getElevationsArray(svInfos),
+ SatelliteInfo.getAzimuthsArray(svInfos),
+ SatelliteInfo.getCarrierFrequencyArray(svInfos));
+ }
+}
diff --git a/location/tests/locationtests/src/android/location/GpsStatusTest.java b/location/tests/locationtests/src/android/location/GpsStatusTest.java
deleted file mode 100644
index 316e88d..0000000
--- a/location/tests/locationtests/src/android/location/GpsStatusTest.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.location;
-
-import junit.framework.TestCase;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Random;
-import java.util.Set;
-
-/**
- * Unit tests for {@link GpsStatus}.
- */
-@SmallTest
-public class GpsStatusTest extends TestCase {
-
- private static final int MAX_VALUE = 250;
-
- private final Random mRandom = new Random();
-
- private GpsStatus mStatus;
- private int mCount;
- private int[] mPrns;
- private float[] mCn0s;
- private float[] mElevations;
- private float[] mAzimuth;
- private int mEphemerisMask;
- private int mAlmanacMask;
- private int mUsedInFixMask;
-
- public void setUp() throws Exception {
- super.setUp();
- mStatus = createGpsStatus();
- generateSatellitesData(generateInt());
- }
-
- public void testEmptyGpsStatus() throws Exception {
- verifyIsEmpty(mStatus);
- }
-
- public void testGpsStatusIterator() throws Exception {
- generateSatellitesData(2);
- setSatellites(mStatus);
- Iterator<GpsSatellite> iterator = mStatus.getSatellites().iterator();
- assertTrue("hasNext(1)", iterator.hasNext());
- assertTrue("hasNext(1) does not overflow", iterator.hasNext());
- GpsSatellite satellite1 = iterator.next();
- assertNotNull("satellite", satellite1);
- assertTrue("hasNext(2)", iterator.hasNext());
- assertTrue("hasNext(2) does not overflow", iterator.hasNext());
- GpsSatellite satellite2 = iterator.next();
- assertNotNull("satellite", satellite2);
- assertFalse("hasNext() no elements", iterator.hasNext());
- }
-
- public void testTtff() throws Exception {
- int testTtff = generateInt();
- set(mStatus, testTtff);
- verifyTtff(mStatus, testTtff);
- }
-
- public void testCopyTtff() throws Exception {
- int testTtff = generateInt();
- verifyTtff(mStatus, 0);
-
- GpsStatus otherStatus = createGpsStatus();
- set(otherStatus, testTtff);
- verifyTtff(otherStatus, testTtff);
-
- set(mStatus, otherStatus);
- verifyTtff(mStatus, testTtff);
- }
-
- public void testSetSatellites() throws Exception {
- setSatellites(mStatus);
- verifySatellites(mStatus);
- }
-
- public void testCopySatellites() throws Exception {
- verifyIsEmpty(mStatus);
-
- GpsStatus otherStatus = createGpsStatus();
- setSatellites(otherStatus);
- verifySatellites(otherStatus);
-
- set(mStatus, otherStatus);
- verifySatellites(mStatus);
- }
-
- public void testOverrideSatellites() throws Exception {
- setSatellites(mStatus);
- verifySatellites(mStatus);
-
- GpsStatus otherStatus = createGpsStatus();
- generateSatellitesData(mCount, true /* reusePrns */);
- setSatellites(otherStatus);
- verifySatellites(otherStatus);
-
- set(mStatus, otherStatus);
- verifySatellites(mStatus);
- }
-
- public void testAddSatellites() throws Exception {
- int count = 10;
- generateSatellitesData(count);
- setSatellites(mStatus);
- verifySatellites(mStatus);
-
- GpsStatus otherStatus = createGpsStatus();
- generateSatellitesData(count);
- setSatellites(otherStatus);
- verifySatellites(otherStatus);
-
- set(mStatus, otherStatus);
- verifySatellites(mStatus);
- }
-
- public void testAddMoreSatellites() throws Exception {
- int count = 25;
- generateSatellitesData(count);
- setSatellites(mStatus);
- verifySatellites(mStatus);
-
- GpsStatus otherStatus = createGpsStatus();
- generateSatellitesData(count * 2);
- setSatellites(otherStatus);
- verifySatellites(otherStatus);
-
- set(mStatus, otherStatus);
- verifySatellites(mStatus);
- }
-
- public void testAddLessSatellites() throws Exception {
- int count = 25;
- generateSatellitesData(count * 2);
- setSatellites(mStatus);
- verifySatellites(mStatus);
-
- GpsStatus otherStatus = createGpsStatus();
- generateSatellitesData(count);
- setSatellites(otherStatus);
- verifySatellites(otherStatus);
-
- set(mStatus, otherStatus);
- verifySatellites(mStatus);
- }
-
- private static void verifyIsEmpty(GpsStatus status) {
- verifySatelliteCount(status, 0);
- verifyTtff(status, 0);
- }
-
- private static void verifySatelliteCount(GpsStatus status, int expectedCount) {
- int satellites = 0;
- for (GpsSatellite s : status.getSatellites()) {
- ++satellites;
- }
- assertEquals("GpsStatus::SatelliteCount", expectedCount, satellites);
- }
-
- private void verifySatellites(GpsStatus status) {
- verifySatelliteCount(status, mCount);
- verifySatellites(status, mCount, mPrns, mCn0s, mElevations, mAzimuth, mEphemerisMask,
- mAlmanacMask, mUsedInFixMask);
- }
-
- private static void verifySatellites(
- GpsStatus status,
- int count,
- int[] prns,
- float[] cn0s,
- float[] elevations,
- float[] azimuth,
- int ephemerisMask,
- int almanacMask,
- int usedInFixMask) {
- for (int i = 0; i < count; ++i) {
- int prn = prns[i];
- GpsSatellite satellite = getSatellite(status, prn);
- assertNotNull(getSatelliteAssertInfo(i, prn, "non-null"), satellite);
- assertEquals(getSatelliteAssertInfo(i, prn, "Snr"), cn0s[i], satellite.getSnr());
- assertEquals(
- getSatelliteAssertInfo(i, prn, "Elevation"),
- elevations[i],
- satellite.getElevation());
- assertEquals(
- getSatelliteAssertInfo(i, prn, "Azimuth"),
- azimuth[i],
- satellite.getAzimuth());
- int prnShift = 1 << (prn - 1);
- assertEquals(
- getSatelliteAssertInfo(i, prn, "ephemeris"),
- (ephemerisMask & prnShift) != 0,
- satellite.hasEphemeris());
- assertEquals(
- getSatelliteAssertInfo(i, prn, "almanac"),
- (almanacMask & prnShift) != 0,
- satellite.hasAlmanac());
- assertEquals(
- getSatelliteAssertInfo(i, prn, "usedInFix"),
- (usedInFixMask & prnShift) != 0,
- satellite.usedInFix());
- }
- }
-
- private static void verifyTtff(GpsStatus status, int expectedTtff) {
- assertEquals("GpsStatus::TTFF", expectedTtff, status.getTimeToFirstFix());
- }
-
- private static GpsStatus createGpsStatus() throws Exception {
- Constructor<GpsStatus> ctor = GpsStatus.class.getDeclaredConstructor();
- ctor.setAccessible(true);
- return ctor.newInstance();
- }
-
- private static void set(GpsStatus status, int ttff) throws Exception {
- Class<?> statusClass = status.getClass();
- Method setTtff = statusClass.getDeclaredMethod("setTimeToFirstFix", Integer.TYPE);
- setTtff.setAccessible(true);
- setTtff.invoke(status, ttff);
- }
-
- private static void set(GpsStatus status, GpsStatus statusToSet) throws Exception {
- Class<?> statusClass = status.getClass();
- Method setStatus = statusClass.getDeclaredMethod("setStatus", statusClass);
- setStatus.setAccessible(true);
- setStatus.invoke(status, statusToSet);
- }
-
- private void setSatellites(GpsStatus status) throws Exception {
- set(status, mCount, mPrns, mCn0s, mElevations, mAzimuth, mEphemerisMask, mAlmanacMask,
- mUsedInFixMask);
- }
-
- private static void set(
- GpsStatus status,
- int count,
- int[] prns,
- float[] cn0s,
- float[] elevations,
- float[] azimuth,
- int ephemerisMask,
- int almanacMask,
- int usedInFixMask) throws Exception {
- Class<?> statusClass = status.getClass();
- Class<?> intClass = Integer.TYPE;
- Class<?> floatArrayClass = Class.forName("[F");
- Method setStatus = statusClass.getDeclaredMethod(
- "setStatus",
- intClass,
- Class.forName("[I"),
- floatArrayClass,
- floatArrayClass,
- floatArrayClass,
- intClass,
- intClass,
- intClass);
- setStatus.setAccessible(true);
- setStatus.invoke(
- status,
- count,
- prns,
- cn0s,
- elevations,
- azimuth,
- ephemerisMask,
- almanacMask,
- usedInFixMask);
- }
-
- private int generateInt() {
- return mRandom.nextInt(MAX_VALUE) + 1;
- }
-
- private int[] generateIntArray(int count) {
- Set<Integer> generatedPrns = new HashSet<>();
- int[] array = new int[count];
- for(int i = 0; i < count; ++i) {
- int generated;
- do {
- generated = generateInt();
- } while (generatedPrns.contains(generated));
- array[i] = generated;
- generatedPrns.add(generated);
- }
- return array;
- }
-
- private float[] generateFloatArray(int count) {
- float[] array = new float[count];
- for(int i = 0; i < count; ++i) {
- array[i] = generateInt();
- }
- return array;
- }
-
- private int generateMask(int[] prns) {
- int mask = 0;
- int prnsLength = prns.length;
- for (int i = 0; i < prnsLength; ++i) {
- if (mRandom.nextBoolean()) {
- mask |= 1 << (prns[i] - 1);
- }
- }
- return mask;
- }
-
- private void generateSatellitesData(int count) {
- generateSatellitesData(count, false /* reusePrns */);
- }
-
- private void generateSatellitesData(int count, boolean reusePrns) {
- mCount = count;
- if (!reusePrns) {
- mPrns = generateIntArray(count);
- }
- mCn0s = generateFloatArray(count);
- mElevations = generateFloatArray(count);
- mAzimuth = generateFloatArray(count);
- mEphemerisMask = generateMask(mPrns);
- mAlmanacMask = generateMask(mPrns);
- mUsedInFixMask = generateMask(mPrns);
- }
-
- private static GpsSatellite getSatellite(GpsStatus status, int prn) {
- for (GpsSatellite satellite : status.getSatellites()) {
- if (satellite.getPrn() == prn) {
- return satellite;
- }
- }
- return null;
- }
-
- private static String getSatelliteAssertInfo(int index, int prn, String param) {
- return String.format("Satellite::%s [i=%d, prn=%d]", param, index, prn);
- }
-}
diff --git a/location/tests/locationtests/src/android/location/SatelliteInfo.java b/location/tests/locationtests/src/android/location/SatelliteInfo.java
new file mode 100644
index 0000000..b6453ef
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/SatelliteInfo.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import java.util.List;
+
+/*
+ * Helper class to store single Satellite info, only used it in the unit test.
+ */
+public class SatelliteInfo {
+ private static final int SVID_MAX_BIT_INDEX = 32;
+ private static final int SVID_SHIFT_WIDTH = 8;
+ private static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 4;
+
+ // Index for the bits in mSvidWithFlag
+ private static final int GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA_BIT_INDEX = 0;
+ private static final int GNSS_SV_FLAGS_HAS_ALMANAC_DATA_BIT_INDEX = 1;
+ private static final int GNSS_SV_FLAGS_USED_IN_FIX_BIT_INDEX = 2;
+ private static final int GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY_BIT_INDEX = 3;
+ public int mSvid;
+ public int mSvidWithFlag;
+ public float mCn0DbHz;
+ public float mElevation;
+ public float mAzimuth;
+ public float mCarrierFrequency;
+
+ /*
+ * Flag fields, it stores the same information as svidWithFlag, but in different format, easy for
+ * the unit test.
+ */
+ public int mConstellationType;
+ public boolean mHasEphemris;
+ public boolean mHasAlmanac;
+ public boolean mUsedInFix;
+ public boolean mHasCarriesFrequency;
+
+ public SatelliteInfo(int svid, int constellationType, boolean hasEphemris, boolean hasAlmanac,
+ boolean usedInFix, boolean hasCarriesFrequency, float cn0, float elevation, float azimuth,
+ float carrierFrequency) {
+ mSvidWithFlag =
+ setRange(mSvidWithFlag, constellationType, CONSTELLATION_TYPE_SHIFT_WIDTH, SVID_SHIFT_WIDTH);
+ mSvidWithFlag = setRange(mSvidWithFlag, svid, SVID_SHIFT_WIDTH, SVID_MAX_BIT_INDEX);
+ mSvidWithFlag = setBit(mSvidWithFlag, hasEphemris, GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA_BIT_INDEX);
+ mSvidWithFlag = setBit(mSvidWithFlag, hasAlmanac, GNSS_SV_FLAGS_HAS_ALMANAC_DATA_BIT_INDEX);
+ mSvidWithFlag = setBit(mSvidWithFlag, usedInFix, GNSS_SV_FLAGS_USED_IN_FIX_BIT_INDEX);
+ mSvidWithFlag =
+ setBit(mSvidWithFlag, hasCarriesFrequency, GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY_BIT_INDEX);
+ this.mSvid = svid;
+ this.mConstellationType = constellationType;
+ this.mCn0DbHz = cn0;
+ this.mElevation = elevation;
+ this.mAzimuth = azimuth;
+ this.mCarrierFrequency = carrierFrequency;
+ this.mHasEphemris = hasEphemris;
+ this.mHasAlmanac = hasAlmanac;
+ this.mUsedInFix = usedInFix;
+ this.mHasCarriesFrequency = hasCarriesFrequency;
+ }
+
+ /*
+ * Gernerate svidWithFlags array from svInfos
+ */
+ public static int[] getSvidWithFlagsArray(List<SatelliteInfo> svInfos) {
+ int[] svidWithFlags = new int[svInfos.size()];
+ for (int i = 0; i< svInfos.size(); i++) {
+ svidWithFlags[i] = svInfos.get(i).mSvidWithFlag;
+ }
+ return svidWithFlags;
+ }
+
+ /*
+ * Gernerate cn0s array from svInfos
+ */
+ public static float[] getCn0sArray(List<SatelliteInfo> svInfos) {
+ float[] cn0s = new float[svInfos.size()];
+ for (int i = 0; i< svInfos.size(); i++) {
+ cn0s[i] = svInfos.get(i).mCn0DbHz;
+ }
+ return cn0s;
+ }
+
+ /*
+ * Gernerate elevations array from svInfos
+ */
+ public static float[] getElevationsArray(List<SatelliteInfo> svInfos) {
+ float[] elevations = new float[svInfos.size()];
+ for (int i = 0; i< svInfos.size(); i++) {
+ elevations[i] = svInfos.get(i).mElevation;
+ }
+ return elevations;
+ }
+
+ /*
+ * Gernerate azimuths array from svInfos
+ */
+ public static float[] getAzimuthsArray(List<SatelliteInfo> svInfos) {
+ float[] azimuths = new float[svInfos.size()];
+ for (int i = 0; i< svInfos.size(); i++) {
+ azimuths[i] = svInfos.get(i).mAzimuth;
+ }
+ return azimuths;
+ }
+
+ /*
+ * Gernerate carrierFrequency array from svInfos
+ */
+ public static float[] getCarrierFrequencyArray(List<SatelliteInfo> svInfos) {
+ float[] carrierFrequencies = new float[svInfos.size()];
+ for (int i = 0; i< svInfos.size(); i++) {
+ carrierFrequencies[i] = svInfos.get(i).mCarrierFrequency;
+ }
+ return carrierFrequencies;
+ }
+
+ private int setBit(int targetValue, boolean value, int index) {
+ if (value) {
+ targetValue = targetValue | (1 << index);
+ } else {
+ targetValue = targetValue & ~(1 << index);
+ }
+ return targetValue;
+ }
+
+ /*
+ * Set the bit in the range [fromIndex, toIndex), index start from the lowest bit.
+ * value -> 1 1 0 1 1 0 1 0
+ * index -> 7 6 5 4 3 2 1 0
+ * This function will set the bit in the range to the lowest X bits of the value.
+ */
+ private int setRange(int targetValue, int value, int fromIndex, int toIndex) {
+ int rangeLen = toIndex - fromIndex;
+ int valueMask = (1 << rangeLen) -1;
+ value &= valueMask;
+ value = value << fromIndex;
+ valueMask = valueMask << fromIndex;
+ targetValue &= (~valueMask);
+ targetValue |= value;
+ return targetValue;
+ }
+
+}
\ No newline at end of file
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 20405d3..7afe267 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -244,6 +244,7 @@
SUPPRESSIBLE_USAGES.put(USAGE_GAME, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
+ SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
}
/**
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index dab7632a..58976ca 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -43,17 +43,16 @@
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.Pair;
+import android.util.Slog;
import android.view.KeyEvent;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -1966,9 +1965,28 @@
*/
private boolean querySoundEffectsEnabled(int user) {
return Settings.System.getIntForUser(getContext().getContentResolver(),
- Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
+ Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0
+ && !areSystemSoundsZenModeBlocked(getContext());
}
+ private boolean areSystemSoundsZenModeBlocked(Context context) {
+ int zenMode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ZEN_MODE, 0);
+
+ switch (zenMode) {
+ case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+ case Settings.Global.ZEN_MODE_ALARMS:
+ return true;
+ case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+ final NotificationManager noMan = (NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ return (noMan.getNotificationPolicy().priorityCategories
+ & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) == 0;
+ case Settings.Global.ZEN_MODE_OFF:
+ default:
+ return false;
+ }
+ }
/**
* Load Sound effects.
diff --git a/media/java/android/media/BufferingParams.java b/media/java/android/media/BufferingParams.java
index 681271b..521e897 100644
--- a/media/java/android/media/BufferingParams.java
+++ b/media/java/android/media/BufferingParams.java
@@ -26,170 +26,68 @@
/**
* Structure for source buffering management params.
*
- * Used by {@link MediaPlayer#getDefaultBufferingParams()},
- * {@link MediaPlayer#getBufferingParams()} and
+ * Used by {@link MediaPlayer#getBufferingParams()} and
* {@link MediaPlayer#setBufferingParams(BufferingParams)}
* to control source buffering behavior.
*
* <p>There are two stages of source buffering in {@link MediaPlayer}: initial buffering
* (when {@link MediaPlayer} is being prepared) and rebuffering (when {@link MediaPlayer}
- * is playing back source). {@link BufferingParams} includes mode and corresponding
- * watermarks for each stage of source buffering. The watermarks could be either size
- * based (in milliseconds), or time based (in kilobytes) or both, depending on the mode.
+ * is playing back source). {@link BufferingParams} includes corresponding marks for each
+ * stage of source buffering. The marks are time based (in milliseconds).
*
- * <p>There are 4 buffering modes: {@link #BUFFERING_MODE_NONE},
- * {@link #BUFFERING_MODE_TIME_ONLY}, {@link #BUFFERING_MODE_SIZE_ONLY} and
- * {@link #BUFFERING_MODE_TIME_THEN_SIZE}.
- * {@link MediaPlayer} source component has default buffering modes which can be queried
- * by calling {@link MediaPlayer#getDefaultBufferingParams()}.
- * Users should always use those default modes or their downsized version when trying to
- * change buffering params. For example, {@link #BUFFERING_MODE_TIME_THEN_SIZE} can be
- * downsized to {@link #BUFFERING_MODE_NONE}, {@link #BUFFERING_MODE_TIME_ONLY} or
- * {@link #BUFFERING_MODE_SIZE_ONLY}. But {@link #BUFFERING_MODE_TIME_ONLY} can not be
- * downsized to {@link #BUFFERING_MODE_SIZE_ONLY}.
+ * <p>{@link MediaPlayer} source component has default marks which can be queried by
+ * calling {@link MediaPlayer#getBufferingParams()} before any change is made by
+ * {@link MediaPlayer#setBufferingParams()}.
* <ul>
- * <li><strong>initial buffering stage:</strong> has one watermark which is used when
- * {@link MediaPlayer} is being prepared. When cached data amount exceeds this watermark,
- * {@link MediaPlayer} is prepared.</li>
- * <li><strong>rebuffering stage:</strong> has two watermarks, low and high, which are
- * used when {@link MediaPlayer} is playing back content.
+ * <li><strong>initial buffering:</strong> initialMarkMs is used when
+ * {@link MediaPlayer} is being prepared. When cached data amount exceeds this mark
+ * {@link MediaPlayer} is prepared. </li>
+ * <li><strong>rebuffering during playback:</strong> resumePlaybackMarkMs is used when
+ * {@link MediaPlayer} is playing back content.
* <ul>
- * <li> When cached data amount exceeds high watermark, {@link MediaPlayer} will pause
- * buffering. Buffering will resume when cache runs below some limit which could be low
- * watermark or some intermediate value decided by the source component.</li>
- * <li> When cached data amount runs below low watermark, {@link MediaPlayer} will paused
- * playback. Playback will resume when cached data amount exceeds high watermark
- * or reaches end of stream.</li>
- * </ul>
+ * <li> {@link MediaPlayer} has internal mark, namely pausePlaybackMarkMs, to decide when
+ * to pause playback if cached data amount runs low. This internal mark varies based on
+ * type of data source. </li>
+ * <li> When cached data amount exceeds resumePlaybackMarkMs, {@link MediaPlayer} will
+ * resume playback if it has been paused due to low cached data amount. The internal mark
+ * pausePlaybackMarkMs shall be less than resumePlaybackMarkMs. </li>
+ * <li> {@link MediaPlayer} has internal mark, namely pauseRebufferingMarkMs, to decide
+ * when to pause rebuffering. Apparently, this internal mark shall be no less than
+ * resumePlaybackMarkMs. </li>
+ * <li> {@link MediaPlayer} has internal mark, namely resumeRebufferingMarkMs, to decide
+ * when to resume buffering. This internal mark varies based on type of data source. This
+ * mark shall be larger than pausePlaybackMarkMs, and less than pauseRebufferingMarkMs.
+ * </li>
+ * </ul> </li>
* </ul>
* <p>Users should use {@link Builder} to change {@link BufferingParams}.
* @hide
*/
public final class BufferingParams implements Parcelable {
- /**
- * This mode indicates that source buffering is not supported.
- */
- public static final int BUFFERING_MODE_NONE = 0;
- /**
- * This mode indicates that only time based source buffering is supported. This means
- * the watermark(s) are time based.
- */
- public static final int BUFFERING_MODE_TIME_ONLY = 1;
- /**
- * This mode indicates that only size based source buffering is supported. This means
- * the watermark(s) are size based.
- */
- public static final int BUFFERING_MODE_SIZE_ONLY = 2;
- /**
- * This mode indicates that both time and size based source buffering are supported,
- * and time based calculation precedes size based. Size based calculation will be used
- * only when time information is not available from the source.
- */
- public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3;
-
- /** @hide */
- @IntDef(
- value = {
- BUFFERING_MODE_NONE,
- BUFFERING_MODE_TIME_ONLY,
- BUFFERING_MODE_SIZE_ONLY,
- BUFFERING_MODE_TIME_THEN_SIZE,
- }
- )
- @Retention(RetentionPolicy.SOURCE)
- public @interface BufferingMode {}
-
- private static final int BUFFERING_NO_WATERMARK = -1;
+ private static final int BUFFERING_NO_MARK = -1;
// params
- private int mInitialBufferingMode = BUFFERING_MODE_NONE;
- private int mRebufferingMode = BUFFERING_MODE_NONE;
+ private int mInitialMarkMs = BUFFERING_NO_MARK;
- private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
- private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
-
- private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
+ private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;
private BufferingParams() {
}
/**
- * Return the initial buffering mode used when {@link MediaPlayer} is being prepared.
- * @return one of the values that can be set in {@link Builder#setInitialBufferingMode(int)}
+ * Return initial buffering mark in milliseconds.
+ * @return initial buffering mark in milliseconds
*/
- public int getInitialBufferingMode() {
- return mInitialBufferingMode;
+ public int getInitialMarkMs() {
+ return mInitialMarkMs;
}
/**
- * Return the rebuffering mode used when {@link MediaPlayer} is playing back source.
- * @return one of the values that can be set in {@link Builder#setRebufferingMode(int)}
+ * Return the mark in milliseconds for resuming playback.
+ * @return the mark for resuming playback in milliseconds
*/
- public int getRebufferingMode() {
- return mRebufferingMode;
- }
-
- /**
- * Return the time based initial buffering watermark in milliseconds.
- * It is meaningful only when initial buffering mode obatined from
- * {@link #getInitialBufferingMode()} is time based.
- * @return time based initial buffering watermark in milliseconds
- */
- public int getInitialBufferingWatermarkMs() {
- return mInitialWatermarkMs;
- }
-
- /**
- * Return the size based initial buffering watermark in kilobytes.
- * It is meaningful only when initial buffering mode obatined from
- * {@link #getInitialBufferingMode()} is size based.
- * @return size based initial buffering watermark in kilobytes
- */
- public int getInitialBufferingWatermarkKB() {
- return mInitialWatermarkKB;
- }
-
- /**
- * Return the time based low watermark in milliseconds for rebuffering.
- * It is meaningful only when rebuffering mode obatined from
- * {@link #getRebufferingMode()} is time based.
- * @return time based low watermark for rebuffering in milliseconds
- */
- public int getRebufferingWatermarkLowMs() {
- return mRebufferingWatermarkLowMs;
- }
-
- /**
- * Return the time based high watermark in milliseconds for rebuffering.
- * It is meaningful only when rebuffering mode obatined from
- * {@link #getRebufferingMode()} is time based.
- * @return time based high watermark for rebuffering in milliseconds
- */
- public int getRebufferingWatermarkHighMs() {
- return mRebufferingWatermarkHighMs;
- }
-
- /**
- * Return the size based low watermark in kilobytes for rebuffering.
- * It is meaningful only when rebuffering mode obatined from
- * {@link #getRebufferingMode()} is size based.
- * @return size based low watermark for rebuffering in kilobytes
- */
- public int getRebufferingWatermarkLowKB() {
- return mRebufferingWatermarkLowKB;
- }
-
- /**
- * Return the size based high watermark in kilobytes for rebuffering.
- * It is meaningful only when rebuffering mode obatined from
- * {@link #getRebufferingMode()} is size based.
- * @return size based high watermark for rebuffering in kilobytes
- */
- public int getRebufferingWatermarkHighKB() {
- return mRebufferingWatermarkHighKB;
+ public int getResumePlaybackMarkMs() {
+ return mResumePlaybackMarkMs;
}
/**
@@ -200,27 +98,19 @@
* <pre class="prettyprint">
* BufferingParams myParams = mediaplayer.getDefaultBufferingParams();
* myParams = new BufferingParams.Builder(myParams)
- * .setInitialBufferingWatermarkMs(10000)
- * .build();
+ * .setInitialMarkMs(10000)
+ * .setResumePlaybackMarkMs(15000)
+ * .build();
* mediaplayer.setBufferingParams(myParams);
* </pre>
*/
public static class Builder {
- private int mInitialBufferingMode = BUFFERING_MODE_NONE;
- private int mRebufferingMode = BUFFERING_MODE_NONE;
-
- private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
- private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
-
- private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
+ private int mInitialMarkMs = BUFFERING_NO_MARK;
+ private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;
/**
* Constructs a new Builder with the defaults.
- * By default, both initial buffering mode and rebuffering mode are
- * {@link BufferingParams#BUFFERING_MODE_NONE}, and all watermarks are -1.
+ * By default, all marks are -1.
*/
public Builder() {
}
@@ -231,16 +121,8 @@
* in the new Builder.
*/
public Builder(BufferingParams bp) {
- mInitialBufferingMode = bp.mInitialBufferingMode;
- mRebufferingMode = bp.mRebufferingMode;
-
- mInitialWatermarkMs = bp.mInitialWatermarkMs;
- mInitialWatermarkKB = bp.mInitialWatermarkKB;
-
- mRebufferingWatermarkLowMs = bp.mRebufferingWatermarkLowMs;
- mRebufferingWatermarkHighMs = bp.mRebufferingWatermarkHighMs;
- mRebufferingWatermarkLowKB = bp.mRebufferingWatermarkLowKB;
- mRebufferingWatermarkHighKB = bp.mRebufferingWatermarkHighKB;
+ mInitialMarkMs = bp.mInitialMarkMs;
+ mResumePlaybackMarkMs = bp.mResumePlaybackMarkMs;
}
/**
@@ -250,179 +132,37 @@
* @return a new {@link BufferingParams} object
*/
public BufferingParams build() {
- if (isTimeBasedMode(mRebufferingMode)
- && mRebufferingWatermarkLowMs > mRebufferingWatermarkHighMs) {
- throw new IllegalStateException("Illegal watermark:"
- + mRebufferingWatermarkLowMs + " : " + mRebufferingWatermarkHighMs);
- }
- if (isSizeBasedMode(mRebufferingMode)
- && mRebufferingWatermarkLowKB > mRebufferingWatermarkHighKB) {
- throw new IllegalStateException("Illegal watermark:"
- + mRebufferingWatermarkLowKB + " : " + mRebufferingWatermarkHighKB);
- }
-
BufferingParams bp = new BufferingParams();
- bp.mInitialBufferingMode = mInitialBufferingMode;
- bp.mRebufferingMode = mRebufferingMode;
+ bp.mInitialMarkMs = mInitialMarkMs;
+ bp.mResumePlaybackMarkMs = mResumePlaybackMarkMs;
- bp.mInitialWatermarkMs = mInitialWatermarkMs;
- bp.mInitialWatermarkKB = mInitialWatermarkKB;
-
- bp.mRebufferingWatermarkLowMs = mRebufferingWatermarkLowMs;
- bp.mRebufferingWatermarkHighMs = mRebufferingWatermarkHighMs;
- bp.mRebufferingWatermarkLowKB = mRebufferingWatermarkLowKB;
- bp.mRebufferingWatermarkHighKB = mRebufferingWatermarkHighKB;
return bp;
}
- private boolean isTimeBasedMode(int mode) {
- return (mode == BUFFERING_MODE_TIME_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
- }
-
- private boolean isSizeBasedMode(int mode) {
- return (mode == BUFFERING_MODE_SIZE_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
- }
-
/**
- * Sets the initial buffering mode.
- * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
- * {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
- * {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
- * {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
+ * Sets the time based mark in milliseconds for initial buffering.
+ * @param markMs time based mark in milliseconds
* @return the same Builder instance.
*/
- public Builder setInitialBufferingMode(@BufferingMode int mode) {
- switch (mode) {
- case BUFFERING_MODE_NONE:
- case BUFFERING_MODE_TIME_ONLY:
- case BUFFERING_MODE_SIZE_ONLY:
- case BUFFERING_MODE_TIME_THEN_SIZE:
- mInitialBufferingMode = mode;
- break;
- default:
- throw new IllegalArgumentException("Illegal buffering mode " + mode);
- }
+ public Builder setInitialMarkMs(int markMs) {
+ mInitialMarkMs = markMs;
return this;
}
/**
- * Sets the rebuffering mode.
- * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
- * {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
- * {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
- * {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
+ * Sets the time based mark in milliseconds for resuming playback.
+ * @param markMs time based mark in milliseconds for resuming playback
* @return the same Builder instance.
*/
- public Builder setRebufferingMode(@BufferingMode int mode) {
- switch (mode) {
- case BUFFERING_MODE_NONE:
- case BUFFERING_MODE_TIME_ONLY:
- case BUFFERING_MODE_SIZE_ONLY:
- case BUFFERING_MODE_TIME_THEN_SIZE:
- mRebufferingMode = mode;
- break;
- default:
- throw new IllegalArgumentException("Illegal buffering mode " + mode);
- }
- return this;
- }
-
- /**
- * Sets the time based watermark in milliseconds for initial buffering.
- * @param watermarkMs time based watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setInitialBufferingWatermarkMs(int watermarkMs) {
- mInitialWatermarkMs = watermarkMs;
- return this;
- }
-
- /**
- * Sets the size based watermark in kilobytes for initial buffering.
- * @param watermarkKB size based watermark in kilobytes
- * @return the same Builder instance.
- */
- public Builder setInitialBufferingWatermarkKB(int watermarkKB) {
- mInitialWatermarkKB = watermarkKB;
- return this;
- }
-
- /**
- * Sets the time based low watermark in milliseconds for rebuffering.
- * @param watermarkMs time based low watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarkLowMs(int watermarkMs) {
- mRebufferingWatermarkLowMs = watermarkMs;
- return this;
- }
-
- /**
- * Sets the time based high watermark in milliseconds for rebuffering.
- * @param watermarkMs time based high watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarkHighMs(int watermarkMs) {
- mRebufferingWatermarkHighMs = watermarkMs;
- return this;
- }
-
- /**
- * Sets the size based low watermark in milliseconds for rebuffering.
- * @param watermarkKB size based low watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarkLowKB(int watermarkKB) {
- mRebufferingWatermarkLowKB = watermarkKB;
- return this;
- }
-
- /**
- * Sets the size based high watermark in milliseconds for rebuffering.
- * @param watermarkKB size based high watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarkHighKB(int watermarkKB) {
- mRebufferingWatermarkHighKB = watermarkKB;
- return this;
- }
-
- /**
- * Sets the time based low and high watermarks in milliseconds for rebuffering.
- * @param lowWatermarkMs time based low watermark in milliseconds
- * @param highWatermarkMs time based high watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarksMs(int lowWatermarkMs, int highWatermarkMs) {
- mRebufferingWatermarkLowMs = lowWatermarkMs;
- mRebufferingWatermarkHighMs = highWatermarkMs;
- return this;
- }
-
- /**
- * Sets the size based low and high watermarks in kilobytes for rebuffering.
- * @param lowWatermarkKB size based low watermark in kilobytes
- * @param highWatermarkKB size based high watermark in kilobytes
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarksKB(int lowWatermarkKB, int highWatermarkKB) {
- mRebufferingWatermarkLowKB = lowWatermarkKB;
- mRebufferingWatermarkHighKB = highWatermarkKB;
+ public Builder setResumePlaybackMarkMs(int markMs) {
+ mResumePlaybackMarkMs = markMs;
return this;
}
}
private BufferingParams(Parcel in) {
- mInitialBufferingMode = in.readInt();
- mRebufferingMode = in.readInt();
-
- mInitialWatermarkMs = in.readInt();
- mInitialWatermarkKB = in.readInt();
-
- mRebufferingWatermarkLowMs = in.readInt();
- mRebufferingWatermarkHighMs = in.readInt();
- mRebufferingWatermarkLowKB = in.readInt();
- mRebufferingWatermarkHighKB = in.readInt();
+ mInitialMarkMs = in.readInt();
+ mResumePlaybackMarkMs = in.readInt();
}
public static final Parcelable.Creator<BufferingParams> CREATOR =
@@ -446,15 +186,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mInitialBufferingMode);
- dest.writeInt(mRebufferingMode);
-
- dest.writeInt(mInitialWatermarkMs);
- dest.writeInt(mInitialWatermarkKB);
-
- dest.writeInt(mRebufferingWatermarkLowMs);
- dest.writeInt(mRebufferingWatermarkHighMs);
- dest.writeInt(mRebufferingWatermarkLowKB);
- dest.writeInt(mRebufferingWatermarkHighKB);
+ dest.writeInt(mInitialMarkMs);
+ dest.writeInt(mResumePlaybackMarkMs);
}
}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 1feea89..12e5744 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -91,10 +91,10 @@
* are only decrypted when the samples are delivered to the decoder.
* <p>
* MediaDrm methods throw {@link android.media.MediaDrm.MediaDrmStateException}
- * when a method is called on a MediaDrm object that has had an unrecoverable failure
- * in the DRM plugin or security hardware.
- * {@link android.media.MediaDrm.MediaDrmStateException} extends
- * {@link java.lang.IllegalStateException} with the addition of a developer-readable
+ * when a method is called on a MediaDrm object that has had an unrecoverable failure
+ * in the DRM plugin or security hardware.
+ * {@link android.media.MediaDrm.MediaDrmStateException} extends
+ * {@link java.lang.IllegalStateException} with the addition of a developer-readable
* diagnostic information string associated with the exception.
* <p>
* In the event of a mediaserver process crash or restart while a MediaDrm object
@@ -102,9 +102,9 @@
* To recover, the app must release the MediaDrm object, then create and initialize
* a new one.
* <p>
- * As {@link android.media.MediaDrmResetException} and
- * {@link android.media.MediaDrm.MediaDrmStateException} both extend
- * {@link java.lang.IllegalStateException}, they should be in an earlier catch()
+ * As {@link android.media.MediaDrmResetException} and
+ * {@link android.media.MediaDrm.MediaDrmStateException} both extend
+ * {@link java.lang.IllegalStateException}, they should be in an earlier catch()
* block than {@link java.lang.IllegalStateException} if handled separately.
* <p>
* <a name="Callbacks"></a>
@@ -165,7 +165,7 @@
/**
* Query if the given scheme identified by its UUID is supported on
- * this device, and whether the drm plugin is able to handle the
+ * this device, and whether the DRM plugin is able to handle the
* media container format specified by mimeType.
* @param uuid The UUID of the crypto scheme.
* @param mimeType The MIME type of the media container, e.g. "video/mp4"
@@ -745,7 +745,7 @@
* returned in KeyRequest.defaultUrl.
* <p>
* After the app has received the key request response from the server,
- * it should deliver to the response to the DRM engine plugin using the method
+ * it should deliver to the response to the MediaDrm instance using the method
* {@link #provideKeyResponse}.
*
* @param scope may be a sessionId or a keySetId, depending on the specified keyType.
@@ -781,7 +781,7 @@
/**
* A key response is received from the license server by the app, then it is
- * provided to the DRM engine plugin using provideKeyResponse. When the
+ * provided to the MediaDrm instance using provideKeyResponse. When the
* response is for an offline key request, a keySetId is returned that can be
* used to later restore the keys to a new session with the method
* {@link #restoreKeys}.
@@ -829,7 +829,7 @@
* in the form of {name, value} pairs. Since DRM license policies vary by vendor,
* the specific status field names are determined by each DRM vendor. Refer to your
* DRM provider documentation for definitions of the field names for a particular
- * DRM engine plugin.
+ * DRM plugin.
*
* @param sessionId the session ID for the DRM session
*/
@@ -897,11 +897,11 @@
@NonNull String certAuthority);
/**
- * After a provision response is received by the app, it is provided to the DRM
- * engine plugin using this method.
+ * After a provision response is received by the app, it is provided to the
+ * MediaDrm instance using this method.
*
* @param response the opaque provisioning response byte array to provide to the
- * DRM engine plugin.
+ * MediaDrm instance.
*
* @throws DeniedByServerException if the response indicates that the
* server rejected the request
@@ -912,7 +912,6 @@
}
@NonNull
- /* could there be a valid response with 0-sized certificate or key? */
private native Certificate provideProvisionResponseNative(@NonNull byte[] response)
throws DeniedByServerException;
@@ -953,26 +952,26 @@
/**
* Remove all secure stops without requiring interaction with the server.
*/
- public native void releaseAllSecureStops();
+ public native void releaseAllSecureStops();
/**
- * String property name: identifies the maker of the DRM engine plugin
+ * String property name: identifies the maker of the DRM plugin
*/
public static final String PROPERTY_VENDOR = "vendor";
/**
- * String property name: identifies the version of the DRM engine plugin
+ * String property name: identifies the version of the DRM plugin
*/
public static final String PROPERTY_VERSION = "version";
/**
- * String property name: describes the DRM engine plugin
+ * String property name: describes the DRM plugin
*/
public static final String PROPERTY_DESCRIPTION = "description";
/**
* String property name: a comma-separated list of cipher and mac algorithms
- * supported by CryptoSession. The list may be empty if the DRM engine
+ * supported by CryptoSession. The list may be empty if the DRM
* plugin does not support CryptoSession operations.
*/
public static final String PROPERTY_ALGORITHMS = "algorithms";
@@ -988,7 +987,7 @@
public @interface StringProperty {}
/**
- * Read a DRM engine plugin String property value, given the property name string.
+ * Read a MediaDrm String property value, given the property name string.
* <p>
* Standard fields names are:
* {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION},
@@ -998,6 +997,13 @@
public native String getPropertyString(@NonNull @StringProperty String propertyName);
/**
+ * Set a MediaDrm String property value, given the property name string
+ * and new value for the property.
+ */
+ public native void setPropertyString(@NonNull @StringProperty String propertyName,
+ @NonNull String value);
+
+ /**
* Byte array property name: the device unique identifier is established during
* device provisioning and provides a means of uniquely identifying each device.
*/
@@ -1011,7 +1017,7 @@
public @interface ArrayProperty {}
/**
- * Read a DRM engine plugin byte array property value, given the property name string.
+ * Read a MediaDrm byte array property value, given the property name string.
* <p>
* Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID}
*/
@@ -1019,17 +1025,13 @@
public native byte[] getPropertyByteArray(@ArrayProperty String propertyName);
/**
- * Set a DRM engine plugin String property value.
- */
- public native void setPropertyString(
- String propertyName, @NonNull String value);
-
- /**
- * Set a DRM engine plugin byte array property value.
- */
- public native void setPropertyByteArray(
+ * Set a MediaDrm byte array property value, given the property name string
+ * and new value for the property.
+ */
+ public native void setPropertyByteArray(@NonNull @ArrayProperty
String propertyName, @NonNull byte[] value);
+
private static final native void setCipherAlgorithmNative(
@NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
@@ -1158,7 +1160,7 @@
* The algorithm string conforms to JCA Standard Names for Mac
* Algorithms and is case insensitive. For example "HmacSHA256".
* <p>
- * The list of supported algorithms for a DRM engine plugin can be obtained
+ * The list of supported algorithms for a DRM plugin can be obtained
* using the method {@link #getPropertyString} with the property name
* "algorithms".
*/
@@ -1272,7 +1274,7 @@
* storage, and used when invoking the signRSA method.
*
* @param response the opaque certificate response byte array to provide to the
- * DRM engine plugin.
+ * MediaDrm instance.
*
* @throws DeniedByServerException if the response indicates that the
* server rejected the request
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index bdc0fda..31eb948 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -34,6 +34,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Set;
+import java.util.Objects;
/**
* Contains metadata about an item, such as the title, artist, etc.
@@ -616,6 +617,71 @@
};
/**
+ * Compares the contents of this object to another MediaMetadata object. It
+ * does not compare Bitmaps and Ratings as the media player can choose to
+ * forgo these fields depending on how you retrieve the MediaMetadata.
+ *
+ * @param o The Metadata object to compare this object against
+ * @return Whether or not the two objects have matching fields (excluding
+ * Bitmaps and Ratings)
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof MediaMetadata)) {
+ return false;
+ }
+
+ final MediaMetadata m = (MediaMetadata) o;
+
+ for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) {
+ String key = METADATA_KEYS_TYPE.keyAt(i);
+ switch (METADATA_KEYS_TYPE.valueAt(i)) {
+ case METADATA_TYPE_TEXT:
+ if (!Objects.equals(getString(key), m.getString(key))) {
+ return false;
+ }
+ break;
+ case METADATA_TYPE_LONG:
+ if (getLong(key) != m.getLong(key)) {
+ return false;
+ }
+ break;
+ default:
+ // Ignore ratings and bitmaps when comparing
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = 17;
+
+ for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) {
+ String key = METADATA_KEYS_TYPE.keyAt(i);
+ switch (METADATA_KEYS_TYPE.valueAt(i)) {
+ case METADATA_TYPE_TEXT:
+ hashCode = 31 * hashCode + Objects.hash(getString(key));
+ break;
+ case METADATA_TYPE_LONG:
+ hashCode = 31 * hashCode + Long.hashCode(getLong(key));
+ break;
+ default:
+ // Ignore ratings and bitmaps when comparing
+ break;
+ }
+ }
+
+ return hashCode;
+ }
+
+ /**
* Use to build MediaMetadata objects. The system defined metadata keys must
* use the appropriate data type.
*/
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 17af44b..649c091 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1698,21 +1698,9 @@
public native boolean isPlaying();
/**
- * Gets the default buffering management params.
- * Calling it only after {@code setDataSource} has been called.
- * Each type of data source might have different set of default params.
- *
- * @return the default buffering management params supported by the source component.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized, or {@code setDataSource} has not been called.
- * @hide
- */
- @NonNull
- public native BufferingParams getDefaultBufferingParams();
-
- /**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
+ * Each type of data source might have different set of default params.
*
* @return the current buffering management params used by the source component.
* @throws IllegalStateException if the internal player engine has not been
@@ -1727,8 +1715,7 @@
* The object sets its internal BufferingParams to the input, except that the input is
* invalid or not supported.
* Call it only after {@code setDataSource} has been called.
- * Users should only use supported mode returned by {@link #getDefaultBufferingParams()}
- * or its downsized version as described in {@link BufferingParams}.
+ * The input is a hint to MediaPlayer.
*
* @param params the buffering management params.
*
diff --git a/media/jni/android_media_BufferingParams.h b/media/jni/android_media_BufferingParams.h
index 24c51f5..b004672 100644
--- a/media/jni/android_media_BufferingParams.h
+++ b/media/jni/android_media_BufferingParams.h
@@ -29,14 +29,8 @@
jclass clazz;
jmethodID constructID;
- jfieldID initial_buffering_mode;
- jfieldID rebuffering_mode;
- jfieldID initial_watermark_ms;
- jfieldID initial_watermark_kb;
- jfieldID rebuffering_watermark_low_ms;
- jfieldID rebuffering_watermark_high_ms;
- jfieldID rebuffering_watermark_low_kb;
- jfieldID rebuffering_watermark_high_kb;
+ jfieldID initial_mark_ms;
+ jfieldID resume_playback_mark_ms;
void init(JNIEnv *env) {
jclass lclazz = env->FindClass("android/media/BufferingParams");
@@ -51,14 +45,8 @@
constructID = env->GetMethodID(clazz, "<init>", "()V");
- initial_buffering_mode = env->GetFieldID(clazz, "mInitialBufferingMode", "I");
- rebuffering_mode = env->GetFieldID(clazz, "mRebufferingMode", "I");
- initial_watermark_ms = env->GetFieldID(clazz, "mInitialWatermarkMs", "I");
- initial_watermark_kb = env->GetFieldID(clazz, "mInitialWatermarkKB", "I");
- rebuffering_watermark_low_ms = env->GetFieldID(clazz, "mRebufferingWatermarkLowMs", "I");
- rebuffering_watermark_high_ms = env->GetFieldID(clazz, "mRebufferingWatermarkHighMs", "I");
- rebuffering_watermark_low_kb = env->GetFieldID(clazz, "mRebufferingWatermarkLowKB", "I");
- rebuffering_watermark_high_kb = env->GetFieldID(clazz, "mRebufferingWatermarkHighKB", "I");
+ initial_mark_ms = env->GetFieldID(clazz, "mInitialMarkMs", "I");
+ resume_playback_mark_ms = env->GetFieldID(clazz, "mResumePlaybackMarkMs", "I");
env->DeleteLocalRef(lclazz);
}
@@ -70,22 +58,10 @@
};
void fillFromJobject(JNIEnv *env, const fields_t& fields, jobject params) {
- settings.mInitialBufferingMode =
- (BufferingMode)env->GetIntField(params, fields.initial_buffering_mode);
- settings.mRebufferingMode =
- (BufferingMode)env->GetIntField(params, fields.rebuffering_mode);
- settings.mInitialWatermarkMs =
- env->GetIntField(params, fields.initial_watermark_ms);
- settings.mInitialWatermarkKB =
- env->GetIntField(params, fields.initial_watermark_kb);
- settings.mRebufferingWatermarkLowMs =
- env->GetIntField(params, fields.rebuffering_watermark_low_ms);
- settings.mRebufferingWatermarkHighMs =
- env->GetIntField(params, fields.rebuffering_watermark_high_ms);
- settings.mRebufferingWatermarkLowKB =
- env->GetIntField(params, fields.rebuffering_watermark_low_kb);
- settings.mRebufferingWatermarkHighKB =
- env->GetIntField(params, fields.rebuffering_watermark_high_kb);
+ settings.mInitialMarkMs =
+ env->GetIntField(params, fields.initial_mark_ms);
+ settings.mResumePlaybackMarkMs =
+ env->GetIntField(params, fields.resume_playback_mark_ms);
}
jobject asJobject(JNIEnv *env, const fields_t& fields) {
@@ -93,14 +69,8 @@
if (params == NULL) {
return NULL;
}
- env->SetIntField(params, fields.initial_buffering_mode, (jint)settings.mInitialBufferingMode);
- env->SetIntField(params, fields.rebuffering_mode, (jint)settings.mRebufferingMode);
- env->SetIntField(params, fields.initial_watermark_ms, (jint)settings.mInitialWatermarkMs);
- env->SetIntField(params, fields.initial_watermark_kb, (jint)settings.mInitialWatermarkKB);
- env->SetIntField(params, fields.rebuffering_watermark_low_ms, (jint)settings.mRebufferingWatermarkLowMs);
- env->SetIntField(params, fields.rebuffering_watermark_high_ms, (jint)settings.mRebufferingWatermarkHighMs);
- env->SetIntField(params, fields.rebuffering_watermark_low_kb, (jint)settings.mRebufferingWatermarkLowKB);
- env->SetIntField(params, fields.rebuffering_watermark_high_kb, (jint)settings.mRebufferingWatermarkHighKB);
+ env->SetIntField(params, fields.initial_mark_ms, (jint)settings.mInitialMarkMs);
+ env->SetIntField(params, fields.resume_playback_mark_ms, (jint)settings.mResumePlaybackMarkMs);
return params;
}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index cfa3cc36..eda22d5 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -371,25 +371,6 @@
}
static jobject
-android_media_MediaPlayer_getDefaultBufferingParams(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
-
- BufferingParams bp;
- BufferingSettings &settings = bp.settings;
- process_media_player_call(
- env, thiz, mp->getDefaultBufferingSettings(&settings),
- "java/lang/IllegalStateException", "unexpected error");
- ALOGV("getDefaultBufferingSettings:{%s}", settings.toString().string());
-
- return bp.asJobject(env, gBufferingParamsFields);
-}
-
-static jobject
android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
@@ -1436,7 +1417,6 @@
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setDataSource", "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
- {"getDefaultBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getDefaultBufferingParams},
{"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
{"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
{"_prepare", "()V", (void *)android_media_MediaPlayer_prepare},
diff --git a/packages/CarrierDefaultApp/res/values-sw/strings.xml b/packages/CarrierDefaultApp/res/values-sw/strings.xml
index c546fcee..a52a733 100644
--- a/packages/CarrierDefaultApp/res/values-sw/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-sw/strings.xml
@@ -5,7 +5,7 @@
<string name="android_system_label" msgid="2797790869522345065">"Mtoa Huduma za Simu"</string>
<string name="portal_notification_id" msgid="5155057562457079297">"Data ya mtandao wa simu imekwisha"</string>
<string name="no_data_notification_id" msgid="668400731803969521">"Data yako ya mtandao wa simu imezimwa"</string>
- <string name="portal_notification_detail" msgid="2295729385924660881">"Gonga ili utembelee tovuti ya %s"</string>
+ <string name="portal_notification_detail" msgid="2295729385924660881">"Gusa ili utembelee tovuti ya %s"</string>
<string name="no_data_notification_detail" msgid="3112125343857014825">"Tafadhali wasiliana na mtoa huduma wako %s"</string>
<string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Hakuna muunganisho wa data kwa simu za mkononi"</string>
<string name="no_mobile_data_connection" msgid="544980465184147010">"Ongeza mpango wa mitandao mingine au data kupitia %s"</string>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index f535368..6fe8975 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -23,16 +23,37 @@
import android.app.INotificationManager;
import android.content.Context;
import android.ext.services.R;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Environment;
+import android.os.storage.StorageManager;
import android.service.notification.Adjustment;
import android.service.notification.NotificationAssistantService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Map;
/**
* Notification assistant that provides guidance on notification channel blocking
@@ -41,19 +62,112 @@
private static final String TAG = "ExtAssistant";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final ArrayList<Integer> DISMISS_WITH_PREJUDICE = new ArrayList<>();
+ private static final String TAG_ASSISTANT = "assistant";
+ private static final String TAG_IMPRESSION = "impression-set";
+ private static final String ATT_KEY = "key";
+ private static final int DB_VERSION = 1;
+ private static final String ATTR_VERSION = "version";
+
+ private static final ArrayList<Integer> PREJUDICAL_DISMISSALS = new ArrayList<>();
static {
- DISMISS_WITH_PREJUDICE.add(REASON_CANCEL);
- DISMISS_WITH_PREJUDICE.add(REASON_LISTENER_CANCEL);
+ PREJUDICAL_DISMISSALS.add(REASON_CANCEL);
+ PREJUDICAL_DISMISSALS.add(REASON_LISTENER_CANCEL);
}
// key : impressions tracker
- // TODO: persist across reboots
+ // TODO: prune deleted channels and apps
ArrayMap<String, ChannelImpressions> mkeyToImpressions = new ArrayMap<>();
// SBN key : channel id
ArrayMap<String, String> mLiveNotifications = new ArrayMap<>();
private Ranking mFakeRanking = null;
+ private AtomicFile mFile = null;
+
+ public Assistant() {
+ }
+
+ private void loadFile() {
+ if (DEBUG) Slog.d(TAG, "loadFile");
+ AsyncTask.execute(() -> {
+ InputStream infile = null;
+ try {
+ infile = mFile.openRead();
+ readXml(infile);
+ } catch (FileNotFoundException e) {
+ // No data yet
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to read channel impressions", e);
+ } catch (NumberFormatException | XmlPullParserException e) {
+ Log.e(TAG, "Unable to parse channel impressions", e);
+ } finally {
+ IoUtils.closeQuietly(infile);
+ }
+ });
+ }
+
+ protected void readXml(InputStream stream)
+ throws XmlPullParserException, NumberFormatException, IOException {
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+ final int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (!TAG_ASSISTANT.equals(parser.getName())) {
+ continue;
+ }
+ final int impressionOuterDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, impressionOuterDepth)) {
+ if (!TAG_IMPRESSION.equals(parser.getName())) {
+ continue;
+ }
+ String key = parser.getAttributeValue(null, ATT_KEY);
+ ChannelImpressions ci = new ChannelImpressions();
+ ci.populateFromXml(parser);
+ synchronized (mkeyToImpressions) {
+ ci.append(mkeyToImpressions.get(key));
+ mkeyToImpressions.put(key, ci);
+ }
+ }
+ }
+ }
+
+ private void saveFile() throws IOException {
+ AsyncTask.execute(() -> {
+ final FileOutputStream stream;
+ try {
+ stream = mFile.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file", e);
+ return;
+ }
+ try {
+ final XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ writeXml(out);
+ mFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save impressions file, restoring backup", e);
+ mFile.failWrite(stream);
+ }
+ });
+ }
+
+ protected void writeXml(XmlSerializer out) throws IOException {
+ out.startDocument(null, true);
+ out.startTag(null, TAG_ASSISTANT);
+ out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
+ synchronized (mkeyToImpressions) {
+ for (Map.Entry<String, ChannelImpressions> entry
+ : mkeyToImpressions.entrySet()) {
+ // TODO: ensure channel still exists
+ out.startTag(null, TAG_IMPRESSION);
+ out.attribute(null, ATT_KEY, entry.getKey());
+ entry.getValue().writeXml(out);
+ out.endTag(null, TAG_IMPRESSION);
+ }
+ }
+ out.endTag(null, TAG_ASSISTANT);
+ out.endDocument();
+ }
@Override
public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
@@ -87,26 +201,38 @@
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
NotificationStats stats, int reason) {
try {
+ boolean updatedImpressions = false;
String channelId = mLiveNotifications.remove(sbn.getKey());
String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId);
- ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, new ChannelImpressions());
- if (stats.hasSeen()) {
- ci.incrementViews();
+ synchronized (mkeyToImpressions) {
+ ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
+ new ChannelImpressions());
+ if (stats.hasSeen()) {
+ ci.incrementViews();
+ updatedImpressions = true;
+ }
+ if (PREJUDICAL_DISMISSALS.contains(reason)) {
+ if ((!sbn.isAppGroup() || sbn.getNotification().isGroupChild())
+ && !stats.hasInteracted()
+ && stats.getDismissalSurface() != NotificationStats.DISMISSAL_AOD
+ && stats.getDismissalSurface() != NotificationStats.DISMISSAL_PEEK
+ && stats.getDismissalSurface() != NotificationStats.DISMISSAL_OTHER) {
+ if (DEBUG) Log.i(TAG, "increment dismissals " + key);
+ ci.incrementDismissals();
+ updatedImpressions = true;
+ } else {
+ if (DEBUG) Slog.i(TAG, "reset streak " + key);
+ if (ci.getStreak() > 0) {
+ updatedImpressions = true;
+ }
+ ci.resetStreak();
+ }
+ }
+ mkeyToImpressions.put(key, ci);
}
- if (DISMISS_WITH_PREJUDICE.contains(reason)
- && !sbn.isAppGroup()
- && !sbn.getNotification().isGroupChild()
- && !stats.hasInteracted()
- && stats.getDismissalSurface() != NotificationStats.DISMISSAL_AOD
- && stats.getDismissalSurface() != NotificationStats.DISMISSAL_PEEK
- && stats.getDismissalSurface() != NotificationStats.DISMISSAL_OTHER) {
- if (DEBUG) Log.i(TAG, "increment dismissals");
- ci.incrementDismissals();
- } else {
- if (DEBUG) Slog.i(TAG, "reset streak");
- ci.resetStreak();
+ if (updatedImpressions) {
+ saveFile();
}
- mkeyToImpressions.put(key, ci);
} catch (Throwable e) {
Slog.e(TAG, "Error occurred processing removal", e);
}
@@ -121,6 +247,11 @@
public void onListenerConnected() {
if (DEBUG) Log.i(TAG, "CONNECTED");
try {
+ mFile = new AtomicFile(new File(new File(
+ Environment.getDataUserCePackageDirectory(
+ StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()),
+ "assistant"), "block_stats.xml"));
+ loadFile();
for (StatusBarNotification sbn : getActiveNotifications()) {
onNotificationPosted(sbn);
}
@@ -129,7 +260,7 @@
}
}
- private String getKey(String pkg, int userId, String channelId) {
+ protected String getKey(String pkg, int userId, String channelId) {
return pkg + "|" + userId + "|" + channelId;
}
@@ -151,6 +282,11 @@
}
// for testing
+
+ protected void setFile(AtomicFile file) {
+ mFile = file;
+ }
+
protected void setFakeRanking(Ranking ranking) {
mFakeRanking = ranking;
}
@@ -162,4 +298,16 @@
protected void setContext(Context context) {
mSystemContext = context;
}
+
+ protected ChannelImpressions getImpressions(String key) {
+ synchronized (mkeyToImpressions) {
+ return mkeyToImpressions.get(key);
+ }
+ }
+
+ protected void insertImpressions(String key, ChannelImpressions ci) {
+ synchronized (mkeyToImpressions) {
+ mkeyToImpressions.put(key, ci);
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
index 30567cc..4ad4b24 100644
--- a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
+++ b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
@@ -18,14 +18,23 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.Log;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
public final class ChannelImpressions implements Parcelable {
private static final String TAG = "ExtAssistant.CI";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
static final double DISMISS_TO_VIEW_RATIO_LIMIT = .8;
static final int STREAK_LIMIT = 2;
+ static final String ATT_DISMISSALS = "dismisses";
+ static final String ATT_VIEWS = "views";
+ static final String ATT_STREAK = "streak";
private int mDismissals = 0;
private int mViews = 0;
@@ -62,6 +71,14 @@
mStreak++;
}
+ public void append(ChannelImpressions additionalImpressions) {
+ if (additionalImpressions != null) {
+ mViews += additionalImpressions.getViews();
+ mStreak += additionalImpressions.getStreak();
+ mDismissals += additionalImpressions.getDismissals();
+ }
+ }
+
public void incrementViews() {
mViews++;
}
@@ -134,4 +151,36 @@
sb.append('}');
return sb.toString();
}
+
+ protected void populateFromXml(XmlPullParser parser) {
+ mDismissals = safeInt(parser, ATT_DISMISSALS, 0);
+ mStreak = safeInt(parser, ATT_STREAK, 0);
+ mViews = safeInt(parser, ATT_VIEWS, 0);
+ }
+
+ protected void writeXml(XmlSerializer out) throws IOException {
+ if (mDismissals != 0) {
+ out.attribute(null, ATT_DISMISSALS, String.valueOf(mDismissals));
+ }
+ if (mStreak != 0) {
+ out.attribute(null, ATT_STREAK, String.valueOf(mStreak));
+ }
+ if (mViews != 0) {
+ out.attribute(null, ATT_VIEWS, String.valueOf(mViews));
+ }
+ }
+
+ private static int safeInt(XmlPullParser parser, String att, int defValue) {
+ final String val = parser.getAttributeValue(null, att);
+ return tryParseInt(val, defValue);
+ }
+
+ private static int tryParseInt(String value, int defValue) {
+ if (TextUtils.isEmpty(value)) return defValue;
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
+ }
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 4e5e9f9..7c35b48 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -31,7 +31,6 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Intent;
-import android.ext.services.R;
import android.os.UserHandle;
import android.service.notification.Adjustment;
import android.service.notification.NotificationListenerService;
@@ -42,6 +41,10 @@
import android.support.test.InstrumentationRegistry;
import android.test.ServiceTestCase;
import android.testing.TestableContext;
+import android.util.AtomicFile;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
import org.junit.Before;
import org.junit.Rule;
@@ -49,6 +52,14 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
public class AssistantTest extends ServiceTestCase<Assistant> {
@@ -67,6 +78,8 @@
new NotificationChannel("one", "", IMPORTANCE_LOW);
@Mock INotificationManager mNoMan;
+ @Mock
+ AtomicFile mFile;
Assistant mAssistant;
@@ -88,6 +101,8 @@
bindService(startIntent);
mAssistant = getService();
mAssistant.setNoMan(mNoMan);
+ mAssistant.setFile(mFile);
+ when(mFile.startWrite()).thenReturn(mock(FileOutputStream.class));
}
private StatusBarNotification generateSbn(String pkg, int uid, NotificationChannel channel,
@@ -170,18 +185,43 @@
}
@Test
- public void testGroupCannotTriggerAdjustment() throws Exception {
+ public void testGroupChildCanTriggerAdjustment() throws Exception {
almostBlockChannel(PKG1, UID1, P1C1);
StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP");
- mAssistant.setFakeRanking(mock(Ranking.class));
+ mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
NotificationStats stats = new NotificationStats();
stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
stats.setSeen();
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
mAssistant.onNotificationRemoved(
sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+ sbn = generateSbn(PKG1, UID1, P1C1, "new one!", "group");
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+ ArgumentCaptor<Adjustment> captor = ArgumentCaptor.forClass(Adjustment.class);
+ verify(mNoMan, times(1)).applyAdjustmentFromAssistant(any(), captor.capture());
+ assertEquals(sbn.getKey(), captor.getValue().getKey());
+ assertEquals(Ranking.USER_SENTIMENT_NEGATIVE,
+ captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT));
+ }
+
+ @Test
+ public void testGroupSummaryCannotTriggerAdjustment() throws Exception {
+ almostBlockChannel(PKG1, UID1, P1C1);
+
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP");
+ sbn.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+ mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
+ NotificationStats stats = new NotificationStats();
+ stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
+ stats.setSeen();
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+ mAssistant.onNotificationRemoved(
+ sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
+
+ sbn = generateSbn(PKG1, UID1, P1C1, "new one!", "group");
mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
@@ -192,10 +232,11 @@
almostBlockChannel(PKG1, UID1, P1C1);
StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(mock(Ranking.class));
+ mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
NotificationStats stats = new NotificationStats();
stats.setDismissalSurface(NotificationStats.DISMISSAL_AOD);
stats.setSeen();
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
mAssistant.onNotificationRemoved(
sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
@@ -208,13 +249,13 @@
@Test
public void testInteractedCannotTriggerAdjustment() throws Exception {
almostBlockChannel(PKG1, UID1, P1C1);
-
StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(mock(Ranking.class));
+ mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
NotificationStats stats = new NotificationStats();
stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
stats.setSeen();
stats.setExpanded();
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
mAssistant.onNotificationRemoved(
sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
@@ -229,10 +270,11 @@
almostBlockChannel(PKG1, UID1, P1C1);
StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(mock(Ranking.class));
+ mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
NotificationStats stats = new NotificationStats();
stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
stats.setSeen();
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
mAssistant.onNotificationRemoved(
sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_APP_CANCEL);
@@ -265,4 +307,88 @@
verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
}
+
+ @Test
+ public void testReadXml() throws Exception {
+ String key1 = mAssistant.getKey("pkg1", 1, "channel1");
+ int streak1 = 2;
+ int views1 = 5;
+ int dismiss1 = 9;
+
+ int streak1a = 3;
+ int views1a = 10;
+ int dismiss1a = 99;
+ String key1a = mAssistant.getKey("pkg1", 1, "channel1a");
+
+ int streak2 = 7;
+ int views2 = 77;
+ int dismiss2 = 777;
+ String key2 = mAssistant.getKey("pkg2", 2, "channel2");
+
+ String xml = "<assistant version=\"1\">\n"
+ + "<impression-set key=\"" + key1 + "\" "
+ + "dismisses=\"" + dismiss1 + "\" views=\"" + views1
+ + "\" streak=\"" + streak1 + "\"/>\n"
+ + "<impression-set key=\"" + key1a + "\" "
+ + "dismisses=\"" + dismiss1a + "\" views=\"" + views1a
+ + "\" streak=\"" + streak1a + "\"/>\n"
+ + "<impression-set key=\"" + key2 + "\" "
+ + "dismisses=\"" + dismiss2 + "\" views=\"" + views2
+ + "\" streak=\"" + streak2 + "\"/>\n"
+ + "</assistant>\n";
+ mAssistant.readXml(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())));
+
+ ChannelImpressions c1 = mAssistant.getImpressions(key1);
+ assertEquals(2, c1.getStreak());
+ assertEquals(5, c1.getViews());
+ assertEquals(9, c1.getDismissals());
+
+ ChannelImpressions c1a = mAssistant.getImpressions(key1a);
+ assertEquals(3, c1a.getStreak());
+ assertEquals(10, c1a.getViews());
+ assertEquals(99, c1a.getDismissals());
+
+ ChannelImpressions c2 = mAssistant.getImpressions(key2);
+ assertEquals(7, c2.getStreak());
+ assertEquals(77, c2.getViews());
+ assertEquals(777, c2.getDismissals());
+ }
+
+ @Test
+ public void testRoundTripXml() throws Exception {
+ String key1 = mAssistant.getKey("pkg1", 1, "channel1");
+ ChannelImpressions ci1 = new ChannelImpressions(9, 10);
+ String key2 = mAssistant.getKey("pkg1", 1, "channel2");
+ ChannelImpressions ci2 = new ChannelImpressions();
+ for (int i = 0; i < 3; i++) {
+ ci2.incrementViews();
+ ci2.incrementDismissals();
+ }
+ ChannelImpressions ci3 = new ChannelImpressions();
+ String key3 = mAssistant.getKey("pkg3", 3, "channel2");
+ for (int i = 0; i < 9; i++) {
+ ci3.incrementViews();
+ if (i % 3 == 0) {
+ ci3.incrementDismissals();
+ }
+ }
+
+ mAssistant.insertImpressions(key1, ci1);
+ mAssistant.insertImpressions(key2, ci2);
+ mAssistant.insertImpressions(key3, ci3);
+
+
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ mAssistant.writeXml(serializer);
+
+ Assistant assistant = new Assistant();
+ assistant.readXml(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())));
+
+ assertEquals(ci1, assistant.getImpressions(key1));
+ assertEquals(ci2, assistant.getImpressions(key2));
+ assertEquals(ci3, assistant.getImpressions(key3));
+ }
+
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
index a8c9fa3..d28e2ac 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
@@ -21,6 +21,8 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
import org.junit.Test;
public class ChannelImpressionsTest {
@@ -82,4 +84,28 @@
assertFalse(ci.shouldTriggerBlock());
}
+
+ @Test
+ public void testAppend() {
+ ChannelImpressions ci = new ChannelImpressions();
+ ci.incrementViews();
+ ci.incrementDismissals();
+
+ ChannelImpressions ci2 = new ChannelImpressions();
+ ci2.incrementViews();
+ ci2.incrementDismissals();
+ ci2.incrementViews();
+
+ ci.append(ci2);
+ assertEquals(3, ci.getViews());
+ assertEquals(2, ci.getDismissals());
+ assertEquals(2, ci.getStreak());
+
+ assertEquals(2, ci2.getViews());
+ assertEquals(1, ci2.getDismissals());
+ assertEquals(1, ci2.getStreak());
+
+ // no crash
+ ci.append(null);
+ }
}
diff --git a/packages/PrintSpooler/res/drawable/ic_print.xml b/packages/PrintSpooler/res/drawable/ic_print.xml
deleted file mode 100644
index e5e4d075..0000000
--- a/packages/PrintSpooler/res/drawable/ic_print.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@*android:drawable/ic_print"
- android:tint="?android:attr/colorAccent" />
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index a9a6cbd..7c2e55f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -122,7 +122,7 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- getActionBar().setIcon(R.drawable.ic_print);
+ getActionBar().setIcon(com.android.internal.R.drawable.ic_print);
setContentView(R.layout.select_printer_activity);
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index 1ad4fea..99f7f71 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -5,14 +5,20 @@
LOCAL_MODULE := SettingsLib
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
+
LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations \
android-support-v4 \
android-support-v7-recyclerview \
android-support-v7-preference \
android-support-v7-appcompat \
android-support-v14-preference
+LOCAL_SHARED_JAVA_LIBRARIES := \
+ apptoolkit-lifecycle-common \
+ apptoolkit-lifecycle-runtime
+
LOCAL_STATIC_JAVA_LIBRARY := legacy-android-test
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index 46b66c4..b9abde2 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -14,8 +14,10 @@
#
ifeq ($(LOCAL_USE_AAPT2),true)
+LOCAL_STATIC_JAVA_LIBRARIES += \
+ android-support-annotations
+
LOCAL_STATIC_ANDROID_LIBRARIES += \
- android-support-annotations \
android-support-v4 \
SettingsLib
else
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 2dc8dc9..e0a979d 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -24,7 +24,7 @@
<string name="wifi_security_none" msgid="7985461072596594400">"لا شيء"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"تم الحفظ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"معطلة"</string>
- <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"أخفقت تهيئة عنوان IP"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"تعذّرت تهيئة عنوان IP"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"الجهاز غير متصل بسبب انخفاض جودة الشبكة"</string>
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"تعذّر اتصال WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"حدثت مشكلة في المصادقة"</string>
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"تعذر الإقران مع <xliff:g id="DEVICE_NAME">%1$s</xliff:g> نظرًا لوجود رقم تعريف شخصي أو مفتاح مرور غير صحيح."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"لا يمكن الاتصال بـ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"تم رفض الاقتران بواسطة <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"كمبيوتر"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"سماعة رأس"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"هاتف"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"تصوير"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"سماعة أذن"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"جهاز إدخال طرفي"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"بلوتوث"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"تم إيقاف Wi-Fi."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"تم قطع اتصال Wi-Fi."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"إشارة Wi-Fi تتكون من شريط واحد."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"برنامج ترميز LDAC لصوت البلوتوث: جودة التشغيل"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"اختيار برنامج ترميز LDAC لصوت البلوتوث:\nجودة التشغيل"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"البث: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"نظام أسماء النطاقات عبر طبقة النقل الآمنة"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"عند تمكينه، يمكن استخدام نظام أسماء النطاقات عبر طبقة النقل الآمنة على المنفذ 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"نظام أسماء النطاقات الخاص"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"اختر وضع نظام أسماء النطاقات الخاص"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"غير مفعّل"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"انتهازي"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"اسم مضيف مزوّد نظام أسماء النطاقات الخاص"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"أدخل اسم مضيف مزوّد نظام أسماء النطاقات"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"عرض خيارات شهادة عرض شاشة لاسلكي"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"زيادة مستوى تسجيل Wi-Fi، وعرض لكل SSID RSSI في منتقي Wi-Fi"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"عند تمكينه، سيكون Wi-Fi أكثر حدة في تسليم اتصال البيانات إلى الجوّال، وذلك عندما تكون إشارة WiFi منخفضة"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index e855f9b..3efe3bf 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Не атрымалася спалучыцца з прыладай <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, таму што PIN-код або пароль няправiльныя."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Не магу размаўляць з прыладай <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Злучэнне адхілена прыладай <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Камп\'ютар"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Гарнітура"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Тэлефон"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Прылада апрацоўкі відарысаў"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Навушнікі"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Перыферыйная прылада ўводу"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi выключаны."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi адлучаны."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Адзiн слупок Wi-Fi."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Аўдыякодэк Bluetooth LDAC: якасць прайгравання"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Выбраць аўдыякодэк Bluetooth LDAC:\nякасць прайгравання"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Перадача плынню: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS праз TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Калі магчыма, паспрабаваць DNS праз TLS на порце 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Прыватная DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Выберыце рэжым прыватнай DNS"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Выключана"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Гібкі"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Імя вузла аператара прыватнай DNS"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Увядзіце імя вузла аператара DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Паказаць опцыі сертыфікацыі бесправаднога дысплея"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Падвыс. узровень дэтал-цыі журнала Wi‑Fi у залежн. ад SSID RSSI у Wi‑Fi Picker"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Калі гэта функцыя ўключана, Wi-Fi будзе больш інтэнсіўна імкнуцца перайсці на падключ. маб. перад. даных пры слабым сігнале Wi‑Fi"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 24a9084..b2afcb5 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -78,7 +78,7 @@
<string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"মিডিয়া অডিওতে সংযুক্ত রয়েছে"</string>
<string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"ফোন অডিওতে সংযুক্ত"</string>
<string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"ফাইল স্থানান্তর সার্ভারের সঙ্গে সংযুক্ত"</string>
- <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"মানচিত্রে সংযুক্ত"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"ম্যাপে সংযুক্ত"</string>
<string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"SAP -তে সংযুক্ত হয়েছে"</string>
<string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"ফাইল স্থানান্তর সার্ভারের সঙ্গে সংযুক্ত নয়"</string>
<string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ইনপুট ডিভাইসে সংযুক্ত"</string>
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ভুল পিন বা কোড দেওয়ার কারণে <xliff:g id="DEVICE_NAME">%1$s</xliff:g> এর সঙ্গে চেনানো যায়নি।"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> এর সঙ্গে যোগাযোগ করতে পারবেন না।"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"চেনানো <xliff:g id="DEVICE_NAME">%1$s</xliff:g> প্রত্যাখ্যান করেছে।"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"কম্পিউটার"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"হেডসেট"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"ফোন"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"ইমেজিং"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"হেডফোন"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"পেরিফেরাল ইনপুট"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"ব্লুটুথ"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"ওয়াই ফাই বন্ধ৷"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"ওয়াই ফাই এর সংযোগ বিচ্ছিন্ন হয়েছে৷"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"ওয়াই ফাই এ একটি দণ্ড৷"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ব্লুটুথ অডিও LDAC কোডেক: প্লেব্যাক গুণমান"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ব্লুটুথ অডিও LDAC কোডেক বেছে নিন:\nপ্লেব্যাক গুণমান"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"স্ট্রিমিং: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"TLS এ ডিএনএস"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"যদি সক্ষম করা থাকে তাহলে পোর্ট ৮৫৩ তে TLS এ ডিএনএস এর চেষ্টা করুন।"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"ব্যক্তিগত ডিএনএস"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"ব্যক্তিগত ডিএনএস মোড বেছে নিন"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"বন্ধ আছে"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"সুবিধাবাদী"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"ব্যক্তিগত ডিএনএস প্রদানকারীর হোস্টনেম"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ডিএনএস প্রদানকারীর হোস্টনেম লিখুন"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ওয়্যারলেস প্রদর্শন সার্টিফিকেশন জন্য বিকল্পগুলি দেখান"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ওয়াই-ফাই লগিং স্তর বাড়ান, ওয়াই-ফাই চয়নকারীতে SSID RSSI অনুযায়ী দেখান"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"সক্ষম করা থাকলে, ওয়াই ফাই সিগন্যালের মান খারাপ হলে ডেটা সংযোগ মোবাইলের কাছে হস্তান্তর করার জন্য ওয়াই ফাই আরো বেশি তৎপর হবে।"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 0ee111a..fc81c76 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"No s\'ha pogut vincular amb <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, perquè el PIN o la contrasenya són incorrectes."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"No es pot comunicar amb <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Vinculació rebutjada per <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Ordinador"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Auriculars"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Telèfon"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imatges"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Auricular"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Perifèric d\'entrada"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi desactivada."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi desconnectada."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Senyal Wi-Fi: una barra."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Còdec LDAC d\'àudio per Bluetooth: qualitat de reproducció"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Selecciona el còdec LDAC d\'àudio per Bluetooth:\nQualitat de reproducció"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"S\'està reproduint en temps real: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS per TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Si s\'ha activat, prova d\'utilitzar DNS per TLS al port 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privat"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecciona el mode de DNS privat"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Desactivat"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nom d\'amfitrió del proveïdor de DNS privat"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introdueix el nom d\'amfitrió del proveïdor de DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra les opcions de certificació de pantalla sense fil"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Augmenta nivell de registre Wi‑Fi i mostra\'l per SSID RSSI al Selector de Wi‑Fi"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Quan s\'activa, la Wi-Fi és més agressiva en transferir la connexió de dades al mòbil quan el senyal de la Wi-Fi sigui dèbil"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index e85242a..e9fa086 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Couldn\'t pair with <xliff:g id="DEVICE_NAME">%1$s</xliff:g> because of an incorrect PIN or passkey."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Can\'t communicate with <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Pairing rejected by <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Computer"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Headset"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Telephone"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imaging"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Headphone"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Input Peripheral"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi off."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi disconnected."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi one bar."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth Audio LDAC Codec: Playback Quality"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Select Bluetooth Audio LDAC Codec:\nPlayback Quality"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS over TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"If enabled, attempt DNS over TLS on port 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Select private DNS mode"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Private DNS provider hostname"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to mobile, when Wi‑Fi signal is low"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index e85242a..e9fa086 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Couldn\'t pair with <xliff:g id="DEVICE_NAME">%1$s</xliff:g> because of an incorrect PIN or passkey."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Can\'t communicate with <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Pairing rejected by <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Computer"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Headset"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Telephone"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imaging"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Headphone"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Input Peripheral"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi off."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi disconnected."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi one bar."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth Audio LDAC Codec: Playback Quality"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Select Bluetooth Audio LDAC Codec:\nPlayback Quality"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS over TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"If enabled, attempt DNS over TLS on port 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Select private DNS mode"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Private DNS provider hostname"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to mobile, when Wi‑Fi signal is low"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index e85242a..e9fa086 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Couldn\'t pair with <xliff:g id="DEVICE_NAME">%1$s</xliff:g> because of an incorrect PIN or passkey."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Can\'t communicate with <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Pairing rejected by <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Computer"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Headset"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Telephone"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imaging"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Headphone"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Input Peripheral"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi off."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi disconnected."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi one bar."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth Audio LDAC Codec: Playback Quality"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Select Bluetooth Audio LDAC Codec:\nPlayback Quality"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS over TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"If enabled, attempt DNS over TLS on port 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Select private DNS mode"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Private DNS provider hostname"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to mobile, when Wi‑Fi signal is low"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index e85242a..e9fa086 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Couldn\'t pair with <xliff:g id="DEVICE_NAME">%1$s</xliff:g> because of an incorrect PIN or passkey."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Can\'t communicate with <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Pairing rejected by <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Computer"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Headset"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Telephone"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imaging"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Headphone"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Input Peripheral"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi off."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi disconnected."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi one bar."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth Audio LDAC Codec: Playback Quality"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Select Bluetooth Audio LDAC Codec:\nPlayback Quality"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS over TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"If enabled, attempt DNS over TLS on port 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Select private DNS mode"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Private DNS provider hostname"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to mobile, when Wi‑Fi signal is low"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 2a4983a..88fba49 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -216,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth Audio LDAC Codec: Playback Quality"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Select Bluetooth Audio LDAC Codec:\nPlayback Quality"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS over TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"If enabled, attempt DNS over TLS on port 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Select Private DNS Mode"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Private DNS provider hostname"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to mobile, when Wi‑Fi signal is low"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 8474c49..a5929e2 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"No se ha podido vincular con <xliff:g id="DEVICE_NAME">%1$s</xliff:g> porque la clave de acceso o el PIN son incorrectos."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"No se puede establecer comunicación con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Vinculación rechazada por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Ordenador"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Cascos"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Teléfono"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Escáner"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Auriculares"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Periférico de entrada"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi desactivado."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi desconectado."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Una barra de Wi-Fi."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Selecciona el códec LDAC por Bluetooth: calidad de reproducción"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Selecciona el códec LDAC de audio por Bluetooth:\nCalidad de reproducción"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS a través de TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Si esta opción está inhabilitada, prueba DNS a través de TLS en el puerto 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privado"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecciona el modo de DNS privado"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"No"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nombre de host de proveedor de DNS privado"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduce el nombre de host del proveedor de DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opciones para la certificación de la pantalla inalámbrica"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar el nivel de registro de Wi-Fi, mostrar por SSID RSSI en el selector Wi-Fi"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Si se activa esta opción, la conexión Wi-Fi será más agresiva al pasar la conexión a datos móviles (si la señal Wi-Fi es débil)"</string>
@@ -382,10 +393,10 @@
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="help_feedback_label" msgid="6815040660801785649">"Ayuda y sugerencias"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string>
- <string name="retail_demo_reset_message" msgid="118771671364131297">"Escribe una contraseña para restablecer datos de fábrica en modo demostración"</string>
+ <string name="retail_demo_reset_message" msgid="118771671364131297">"Escribe una contraseña para restablecer estado de fábrica en modo demostración"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Siguiente"</string>
<string name="retail_demo_reset_title" msgid="696589204029930100">"Contraseña obligatoria"</string>
- <string name="active_input_method_subtypes" msgid="3596398805424733238">"Métodos de introducción activos"</string>
+ <string name="active_input_method_subtypes" msgid="3596398805424733238">"Métodos de introducción de texto activos"</string>
<string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"Usar idiomas del sistema"</string>
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Error al abrir los ajustes de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Este método de entrada puede registrar todo lo que escribas, incluidos datos personales, como las contraseñas y los números de las tarjetas de crédito. Procede de la aplicación <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. ¿Quieres usar este método de entrada?"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index bb5d2d8..1b78d30 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Impossible d\'établir l\'association avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g> en raison d\'un NIP ou d\'une clé d\'accès incorrects."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Impossible d\'établir la communication avec <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Association refusée par <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Ordinateur"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Écouteurs"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Téléphone"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imagerie"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Écouteurs"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Périphérique d\'entrée"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi désactivé."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi déconnecté."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi : une barre."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec audio Bluetooth LDAC : qualité de lecture"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Sélectionner le codec audio Bluetooth LDAC :\nQualité de lecture"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Diffusion : <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS par TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Si cette option est activée, essayez le DNS par TLS sur le port 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privé"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Sélectionnez le mode DNS privé"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Désactivé"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportuniste"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nom d\'hôte du fournisseur DNS privé"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Entrez le nom d\'hôte du fournisseur DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options pour la certification d\'affichage sans fil"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Détailler davantage les données Wi-Fi, afficher par SSID RSSI dans sélect. Wi-Fi"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Si cette option est activée, le passage du Wi-Fi aux données cellulaires est forcé lorsque le signal Wi-Fi est faible"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 29b1e2b..a8d3cca 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Non se puido sincronizar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g> debido a que se introduciu un contrasinal ou PIN incorrecto."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Non se pode comunicar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Sincronización rexeitada por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Ordenador"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Auriculares con micrófono"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Teléfono"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imaxes"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Auriculares"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Periférico de entrada"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi desactivada."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi desconectada."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Unha barra de wifi."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Códec LDAC de audio por Bluetooth: calidade de reprodución"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Seleccionar códec LDAC de audio por Bluetooth:\ncalidade de reprodución"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Reprodución en tempo real: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS a través de TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Se se activa esta opción, téntase utilizar o método DNS a través de TLS no porto 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privado"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecciona o modo de DNS privado"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Desactivar"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nome de host de provedor de DNS privado"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduce o nome de host de provedor de DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra opcións para o certificado de visualización sen fíos"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nivel de rexistro da wifi, mostrar por SSID RSSI no selector de wifi"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Cando estea activada esta función, a wifi será máis agresiva ao transferir a conexión de datos ao móbil cando o sinal wifi sexa feble"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index cbaa414..e3f9746 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"એક ખોટા પિન અથવા પાસકીને કારણે <xliff:g id="DEVICE_NAME">%1$s</xliff:g> સાથે જોડી બનાવી શકાઈ નથી."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> સાથે સંચાર કરી શકાતો નથી."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> દ્વારા જોડી કરવાનું નકાર્યું."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"કમ્પ્યુટર"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"હૅડસેટ"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"ફોન"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"ઇમેજિંગ"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"હેડફોન"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"ઇનપુટ પેરિફેરલ"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"બ્લૂટૂથ"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi બંધ."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi ડિસ્કનેક્ટ થયું."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi એક બાર."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"બ્લૂટૂથ ઑડિઓ LDAC કોડેક: પ્લેબૅક ગુણવત્તા"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"બ્લૂટૂથ ઑડિઓ LDAC કોડેક પસંદ કરો:\nપ્લેબૅક ગુણવત્તા"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"સ્ટ્રીમિંગ: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS ઓવર TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"જો ચાલુ કરવામાં આવે, તો પોર્ટ 853 પરથી DNS ઓવર TLSનો પ્રયાસ કરો."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"ખાનગી DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"ખાનગી DNS મોડને પસંદ કરો"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"બંધ"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"તકવાદી"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"ખાનગી DNS પ્રદાતા હોસ્ટનું નામ"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS પ્રદાતાના હોસ્ટનું નામ દાખલ કરો"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"વાયરલેસ ડિસ્પ્લે પ્રમાણપત્ર માટેના વિકલ્પો બતાવો"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"વાઇ-ફાઇ લોગિંગ સ્તર વધારો, વાઇ-ફાઇ પીકરમાં SSID RSSI દીઠ બતાવો"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"જ્યારે સક્ષમ કરેલ હોય, ત્યારે વાઇ-ફાઇ સિગ્નલ નબળું હોવા પર, વાઇ-ફાઇ વધુ ઝડપથી ડેટા કનેક્શનને મોબાઇલ પર મોકલશે"</string>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index a41c4f2..1a765b3 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -49,14 +49,14 @@
<item msgid="1805837518286731242">"खराब कनेक्शन को अस्थायी रूप से अनदेखा कर रहा है"</item>
</string-array>
<string-array name="hdcp_checking_titles">
- <item msgid="441827799230089869">"कभी नही जांचें"</item>
- <item msgid="6042769699089883931">"केवल DRM सामग्री जांचें"</item>
- <item msgid="9174900380056846820">"हमेशा जांचें"</item>
+ <item msgid="441827799230089869">"कभी नही जाँचें"</item>
+ <item msgid="6042769699089883931">"केवल DRM सामग्री जाँचें"</item>
+ <item msgid="9174900380056846820">"हमेशा जाँचें"</item>
</string-array>
<string-array name="hdcp_checking_summaries">
- <item msgid="505558545611516707">"कभी भी HDCP जांच का उपयोग न करें"</item>
- <item msgid="3878793616631049349">"HDCP जांच का उपयोग केवल DRM सामग्री के लिए करें"</item>
- <item msgid="45075631231212732">"हमेशा HDCP जांच का उपयोग करें"</item>
+ <item msgid="505558545611516707">"कभी भी HDCP जाँच का उपयोग न करें"</item>
+ <item msgid="3878793616631049349">"HDCP जाँच का उपयोग केवल DRM सामग्री के लिए करें"</item>
+ <item msgid="45075631231212732">"हमेशा HDCP जाँच का उपयोग करें"</item>
</string-array>
<string-array name="bluetooth_avrcp_versions">
<item msgid="5347678900838034763">"AVRCP 1.4 (डिफ़ॉल्ट)"</item>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 5d25d20..3b5ea2d 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -30,7 +30,7 @@
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"प्रमाणीकरण समस्या"</string>
<string name="wifi_cant_connect" msgid="5410016875644565884">"कनेक्ट नहीं हो पा रहा है"</string>
<string name="wifi_cant_connect_to_ap" msgid="1222553274052685331">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' से कनेक्ट नहीं हो पा रहा है"</string>
- <string name="wifi_check_password_try_again" msgid="516958988102584767">"पासवर्ड जांचें और दोबारा कोशिश करें"</string>
+ <string name="wifi_check_password_try_again" msgid="516958988102584767">"पासवर्ड जाँचें और दोबारा कोशिश करें"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"रेंज में नहीं"</string>
<string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"अपने आप कनेक्ट नहीं होगा"</string>
<string name="wifi_no_internet" msgid="3880396223819116454">"इंटरनेट नहीं है"</string>
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"गलत पिन या पासकी के कारण <xliff:g id="DEVICE_NAME">%1$s</xliff:g> के साथ युग्मित नहीं हो सका."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> से संचार नहीं कर सकता."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ने जोड़ने का अनुरोध नहीं माना."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"कंप्यूटर"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"हेडसेट"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"फ़ोन"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"इमेजिंग"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"हेडफ़ोन"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"इनपुट पेरिफ़ेरल"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"ब्लूटूथ"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"वाई-फ़ाई बंद है."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"वाई-फ़ाई डिसकनेक्ट है."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"वाई-फ़ाई का एक बार है."</string>
@@ -142,7 +149,7 @@
<string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> पूरी तरह से समर्थित है"</string>
<string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> के लिए नेटवर्क कनेक्शन की आवश्यकता है"</string>
<string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> समर्थित नहीं है"</string>
- <string name="tts_status_checking" msgid="5339150797940483592">"जांच की जा रही है..."</string>
+ <string name="tts_status_checking" msgid="5339150797940483592">"जाँच की जा रही है..."</string>
<string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> के लिए सेटिंग"</string>
<string name="tts_engine_settings_button" msgid="1030512042040722285">"इंजन सेटिंग लॉन्च करें"</string>
<string name="tts_engine_preference_section_title" msgid="448294500990971413">"पसंदीदा इंजन"</string>
@@ -209,10 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ब्लूटूथ ऑडियो LDAC कोडेक: प्लेबैक क्वालिटी"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ब्लूटूथ ऑडियो LDAC कोडेक चुनें:\nप्लेबैक क्वालिटी"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"चलाया जा रहा है: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <!-- no translation found for dns_tls (6773814174391131955) -->
- <skip />
- <!-- no translation found for dns_tls_summary (3692494150251071380) -->
- <skip />
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"निजी DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"निजी DNS मोड चुनें"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"बंद"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"मौका मिलते ही काम करने वाला"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"निजी DNS सेवा देने वाले का होस्टनाम"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS सेवा देने वाले का होस्टनाम डालें"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"वायरलेस दिखाई देने के लिए प्रमाणन विकल्प दिखाएं"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"वाई-फ़ाई प्रवेश स्तर बढ़ाएं, वाई-फ़ाई पिकर में प्रति SSID RSSI दिखाएं"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"इसके सक्षम होने पर, जब वाई-फ़ाई संकेत कमज़ोर हों तो वाई-फ़ाई, डेटा कनेक्शन को मोबाइल पर ज़्यादा तेज़ी से भेजेगा"</string>
@@ -236,14 +245,14 @@
<string name="dev_settings_warning_title" msgid="7244607768088540165">"विकास सेटिंग की अनुमति दें?"</string>
<string name="dev_settings_warning_message" msgid="2298337781139097964">"ये सेटिंग केवल विकास संबंधी उपयोग के प्रयोजन से हैं. वे आपके डिवाइस और उस पर स्थित ऐप्स को खराब कर सकती हैं या उनके दुर्व्यवहार का कारण हो सकती हैं."</string>
<string name="verify_apps_over_usb_title" msgid="4177086489869041953">"USB पर ऐप की पुष्टि करें"</string>
- <string name="verify_apps_over_usb_summary" msgid="9164096969924529200">"नुकसानदेह व्यवहार के लिए ADB/ADT के द्वारा इंस्टॉल किए गए ऐप्स जांचें."</string>
+ <string name="verify_apps_over_usb_summary" msgid="9164096969924529200">"नुकसानदेह व्यवहार के लिए ADB/ADT के द्वारा इंस्टॉल किए गए ऐप्स जाँचें."</string>
<string name="bluetooth_show_devices_without_names_summary" msgid="2351196058115755520">"बिना नाम वाले ब्लूटूथ डिवाइस (केवल MAC पते वाले) दिखाए जाएंगे"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="6031284410786545957">"दूरस्थ डिवाइस के साथ वॉल्यूम की समस्याओं जैसे अस्वीकार्य तेज़ वॉल्यूम या नियंत्रण की कमी की स्थिति में ब्लूटूथ पूर्ण वॉल्यूम सुविधा को अक्षम करता है."</string>
<string name="bluetooth_enable_inband_ringing_summary" msgid="2787866074741784975">"फ़ोन की रिंगटोन को ब्लूटूथ हेडसेट पर बजने दें"</string>
<string name="enable_terminal_title" msgid="95572094356054120">"स्थानीय टर्मिनल"</string>
<string name="enable_terminal_summary" msgid="67667852659359206">"लोकल शेल तक पहुंचने की सुविधा देने वाले टर्मिनल ऐप को चालू करें"</string>
- <string name="hdcp_checking_title" msgid="8605478913544273282">"HDCP जांच"</string>
- <string name="hdcp_checking_dialog_title" msgid="5141305530923283">"HDCP जांच व्यवहार सेट करें"</string>
+ <string name="hdcp_checking_title" msgid="8605478913544273282">"HDCP जाँच"</string>
+ <string name="hdcp_checking_dialog_title" msgid="5141305530923283">"HDCP जाँच व्यवहार सेट करें"</string>
<string name="debug_debugging_category" msgid="6781250159513471316">"डीबग करना"</string>
<string name="debug_app" msgid="8349591734751384446">"डीबग ऐप्स को चुनें"</string>
<string name="debug_app_not_set" msgid="718752499586403499">"कोई डीबग ऐप्स सेट नहीं है"</string>
@@ -304,7 +313,7 @@
<string name="force_resizable_activities" msgid="8615764378147824985">"आकार बदले जाने के लिए गतिविधियों को बाध्य करें"</string>
<string name="force_resizable_activities_summary" msgid="6667493494706124459">"सभी गतिविधियों को मल्टी-विंडो (एक से ज़्यादा ऐप, एक साथ) के लिए आकार बदलने लायक बनाएं, चाहे उनकी मेनिफ़ेस्ट वैल्यू कुछ भी हो."</string>
<string name="enable_freeform_support" msgid="1461893351278940416">"फ़्रीफ़ॉर्म विंडो (एक साथ कई विंडो दिखाना) चालू करें"</string>
- <string name="enable_freeform_support_summary" msgid="8247310463288834487">"जांच के लिए बनी फ़्रीफ़ॉर्म विंडो के लिए सहायता चालू करें."</string>
+ <string name="enable_freeform_support_summary" msgid="8247310463288834487">"जाँच के लिए बनी फ़्रीफ़ॉर्म विंडो के लिए सहायता चालू करें."</string>
<string name="local_backup_password_title" msgid="3860471654439418822">"डेस्कटॉप बैकअप पासवर्ड"</string>
<string name="local_backup_password_summary_none" msgid="6951095485537767956">"डेस्कटॉप पूर्ण बैकअप वर्तमान में सुरक्षित नहीं हैं"</string>
<string name="local_backup_password_summary_change" msgid="5376206246809190364">"डेस्कटॉप के पूर्ण बैकअप का पासवर्ड बदलने या निकालने के लिए टैप करें"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 2365058..34f471b 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Հնարավոր չեղավ զուգավորվել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ սխալ PIN-ի կամ անցաբառի պատճառով:."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Հնարավոր չէ կապ հաստատել <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի հետ:"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Զուգավորումը մերժվեց <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ի կողմից:"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Համակարգիչ"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Ականջակալ"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Հեռախոս"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Պատկերներ"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Ականջակալ"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Մուտքի արտաքին սարքեր"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi-ն անջատված է:"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi-ը կապակցված չէ:"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi-ի ուժգնությունը՝ մեկ գիծ:"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth աուդիո LDAC կոդեկ՝ նվագարկման որակ"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Ընտրեք Bluetooth աուդիո LDAC կոդեկը՝\nնվագարկման որակ"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Հեռարձակում՝ <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS TLS-ի միջոցով"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Միացնելու դեպքում փորձել DNS-ը TLS-ի միջոցով միացք 853-ում:"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Անհատական DNS սերվեր"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Ընտրեք անհատական DNS սերվերի ռեժիմը"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Անջատված է"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Ճկուն"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Անհատական DNS ծառայության մատակարարի խնամորդի անունը"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Մուտքագրեք DNS ծառայության մատակարարի խնամորդի անունը"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ցույց տալ անլար էկրանի հավաստագրման ընտրանքները"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Բարձրացնել մակարդակը, Wi‑Fi ընտրիչում ամեն մի SSID-ի համար ցույց տալ RSSI"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Եթե այս գործառույթը միացված է, Wi-Fi-ի թույլ ազդանշանի դեպքում Wi‑Fi ինտերնետից բջջային ինտերնետի անցումը ավելի կտրուկ կկատարվի"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 435abbb..c5f4f85 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"לא ניתן להתאים את <xliff:g id="DEVICE_NAME">%1$s</xliff:g> בשל קוד גישה או סיסמה שגויים."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"לא ניתן לתקשר עם <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ההתאמה נדחתה על ידי <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"מחשב"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"אוזניות"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"טלפון"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"הדמיה"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"אוזנייה"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"ציוד קלט היקפי"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi כבוי."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi מנותק."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"פס אחד של Wi-Fi."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec אודיו LDAC ל-Bluetooth: איכות נגינה"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"בחירת Codec אודיו LDAC ל-Bluetooth:\nאיכות נגינה"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"סטרימינג: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS באמצעות TLS (אבטחת שכבת התעבורה)"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"אם האפשרות מופעלת, יש לנסות DNS באמצעות TLS (אבטחת שכבת התעבורה) ביציאה 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS פרטי"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"צריך לבחור במצב DNS פרטי"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"מושבת"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"אופרטוניסטי"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"שם מארח של ספק DNS פרטי"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"צריך להזין את שם המארח של ספק DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"הצג אפשרויות עבור אישור של תצוגת WiFi"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"העלה את רמת הרישום של Wi‑Fi ביומן, הצג לכל SSID RSSI ב-Wi‑Fi Picker"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"כשאפשרות זו מופעלת, Wi-Fi יתנהג בצורה אגרסיבית יותר בעת העברת חיבור הנתונים לרשת הסלולרית כשאות ה-Wi-Fi חלש."</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 308e97d8..b66f95f 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -48,7 +48,7 @@
<string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
<string name="speed_label_medium" msgid="3175763313268941953">"普通"</string>
<string name="speed_label_fast" msgid="7715732164050975057">"速い"</string>
- <string name="speed_label_very_fast" msgid="2265363430784523409">"とても速い"</string>
+ <string name="speed_label_very_fast" msgid="2265363430784523409">"非常に速い"</string>
<string name="preference_summary_default_combination" msgid="8532964268242666060">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="6557104142667339895">"切断"</string>
<string name="bluetooth_disconnecting" msgid="8913264760027764974">"切断中..."</string>
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PINまたはパスキーが正しくないため、<xliff:g id="DEVICE_NAME">%1$s</xliff:g>をペアに設定できませんでした。"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>と通信できません。"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ペア設定が<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に拒否されました。"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"コンピュータ"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ヘッドセット"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"スマートフォン"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"画像"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"ヘッドフォン"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"入力用周辺機器"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-FiはOFFです。"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fiが切断されました。"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fiはレベル1です。"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth オーディオ LDAC コーデック: 再生音質"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth オーディオ LDAC コーデックを選択:\n再生音質"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ストリーミング: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS over TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"この設定を有効にした場合、ポート 853 で DNS over TLS を試行します。"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"プライベート DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"プライベート DNS モードを選択"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"OFF"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"日和見"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"プライベート DNS プロバイダのホスト名"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS プロバイダのホスト名を入力"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ワイヤレスディスプレイ認証のオプションを表示"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fiログレベルを上げて、Wi-Fi選択ツールでSSID RSSIごとに表示します"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ON にすると、Wi-Fi の電波強度が弱い場合は強制的にモバイルデータ接続に切り替わるようになります"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 8d9a7e1..d7ab9fe 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ತಪ್ಪಾಗಿರುವ ಪಿನ್ ಅಥವಾ ಪಾಸ್ಕೀ ಕಾರಣದಿಂದಾಗಿ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಜೊತೆಗೆ ಜೋಡಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಜೊತೆಗೆ ಸಂವಹನ ನಡೆಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ಜೋಡಿಸುವಿಕೆಯನ್ನು <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ತಿರಸ್ಕರಿಸಿದೆ"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"ಕಂಪ್ಯೂಟರ್"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ಹೆಡ್ಸೆಟ್"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"ಫೋನ್"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"ಇಮೇಜಿಂಗ್"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"ಹೆಡ್ಫೋನ್"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"ಪೆರಿಪೆರಲ್ ಇನ್ಪುಟ್"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"ಬ್ಲೂಟೂತ್"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"ವೈಫೈ ಆಫ್."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"ವೈಫೈ ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"ವೈಫೈ ಒಂದು ಪಟ್ಟಿ."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೊ LDAC ಕೋಡೆಕ್: ಪ್ಲೇಬ್ಯಾಕ್ ಗುಣಮಟ್ಟ"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೊ LDAC ಕೋಡೆಕ್:\nಪ್ಲೇಬ್ಯಾಕ್ ಗುಣಮಟ್ಟ ಆಯ್ಕೆ ಮಾಡಿ"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ಸ್ಟ್ರೀಮಿಂಗ್: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"TLS ಮೇಲೆ DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"ಸಕ್ರಿಯಗೊಂಡರೆ, ಪೋರ್ಟ್ 853 ನಲ್ಲಿ TLS ಮೇಲೆ DNS ಅನ್ನು ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"ಖಾಸಗಿ DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"ಖಾಸಗಿ DNS ಮೋಡ್ ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"ಆಫ್"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ಅವಕಾಶವಾದಿ"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"ಖಾಸಗಿ DNS ಪೂರೈಕೆದಾರರ ಹೋಸ್ಟ್ಹೆಸರು"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ಪೂರೈಕೆದಾರರ ಹೋಸ್ಟ್ಹೆಸರನ್ನು ನಮೂದಿಸಿ"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ವೈರ್ಲೆಸ್ ಪ್ರದರ್ಶನ ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಆಯ್ಕೆಗಳನ್ನು ತೋರಿಸು"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ಲಾಗಿಂಗ್ ಮಟ್ಟನ್ನು ಹೆಚ್ಚಿಸಿ, Wi‑Fi ಆಯ್ಕೆಯಲ್ಲಿ ಪ್ರತಿಯೊಂದು SSID RSSI ತೋರಿಸಿ"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ಇದು ಸಕ್ರಿಯಗೊಂಡರೆ, ವೈ-ಫೈ ಸಿಗ್ನಲ್ ದುರ್ಬಲವಾಗಿದ್ದಾಗ, ಮೊಬೈಲ್ಗೆ ಡೇಟಾ ಸಂಪರ್ಕವನ್ನು ಹಸ್ತಾಂತರಿಸುವಲ್ಲಿ ವೈ-ಫೈ ಹೆಚ್ಚು ಆಕ್ರಮಣಕಾರಿಯಾಗಿರುತ್ತದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 4e3bc33..5a9bbc5 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PIN 또는 패스키가 잘못되어 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 페어링하지 못했습니다."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>와(과) 통신할 수 없습니다."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 페어링을 거부했습니다."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"컴퓨터"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"헤드셋"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"전화"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"이미징"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"헤드폰"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"입력 주변기기"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"블루투스"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi가 꺼져 있습니다."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 연결이 끊어졌습니다."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 신호 막대가 한 개입니다."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"블루투스 오디오 LDAC 코덱: 재생 품질"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"블루투스 오디오 LDAC 코덱 선택:\n재생 품질"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"스트리밍: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"TLS를 통한 DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"사용 설정한 경우, 포트 853에서 TLS를 통해 DNS를 시도합니다."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"비공개 DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"비공개 DNS 모드 선택"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"사용 안함"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"기회주의적"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"비공개 DNS 제공업체 호스트 이름"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS 제공업체의 호스트 이름 입력"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"무선 디스플레이 인증서 옵션 표시"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi 로깅 수준을 높이고, Wi‑Fi 선택도구에서 SSID RSSI당 값을 표시합니다."</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"사용 설정하면 Wi-Fi 신호가 약할 때 데이터 연결을 Wi-Fi에서 모바일 네트워크로 더욱 적극적으로 핸드오버합니다."</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index a2a10f2..a96fbcf 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ഒരു തെറ്റായ പിൻ അല്ലെങ്കിൽ പാസ്കീ കാരണം <xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതുമായി ജോടിയാക്കാനായില്ല."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതുമായി ആശയവിനിമയം നടത്താനായില്ല."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>, ജോടിയാക്കൽ നിരസിച്ചു."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"കമ്പ്യൂട്ടർ"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ഹെഡ്സെറ്റ്"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"ഫോൺ"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"ഇമേജിംഗ്"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"ഹെഡ്ഫോൺ"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"ഇൻപുട്ട് പെരിഫറൽ"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"വൈഫൈ ഓഫാണ്."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"വൈഫൈ വിച്ഛേദിച്ചു."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"വൈഫൈ സിഗ്നൽ ഒരു ബാർ."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth ഓഡിയോ LDAC കോഡെക്: പ്ലേബാക്ക് നിലവാരം"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth ഓഡിയോ LDAC കോഡെക് തിരഞ്ഞെടുക്കുക:\nപ്ലേബാക്ക് നിലവാരം"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"സ്ട്രീമിംഗ്: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"TLS വഴിയുള്ള DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"പ്രവർത്തനക്ഷമമാക്കിയിട്ടുണ്ടെങ്കിൽ, പോർട്ട് 853-ലെ TLS വഴി DNS-ന് ശ്രമിക്കുക."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"സ്വകാര്യ DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"സ്വകാര്യ DNS മോഡ് തിരഞ്ഞെടുക്കുക"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"ഓഫ്"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"അവസരവാദപരം"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"സ്വകാര്യ DNS ദാതാവിന്റെ ഹോസ്റ്റുനാമം"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ദാതാവിന്റെ ഹോസ്റ്റുനാമം നൽകുക"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"വയർലെസ് ഡിസ്പ്ലേ സർട്ടിഫിക്കേഷനായി ഓപ്ഷനുകൾ ദൃശ്യമാക്കുക"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"വൈഫൈ പിക്കറിൽ ഓരോ SSID RSSI പ്രകാരം കാണിക്കാൻ വൈഫൈ ലോഗിംഗ് നില വർദ്ധിപ്പിക്കുക"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"പ്രവർത്തനക്ഷമമായിരിക്കുമ്പോൾ, വൈഫൈ സിഗ്നൽ കുറവായിരിക്കുന്ന സമയത്ത് മൊബൈലിലേക്ക് ഡാറ്റ കണക്ഷൻ വഴി കൈമാറുന്നതിൽ വൈഫൈ കൂടുതൽ സക്രിയമായിരിക്കും"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 17d7191..3ac6923 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -216,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ब्लूटूथ ऑडिओ LDAC कोडेक: प्लेबॅक गुणवत्ता"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ब्लूटूथ ऑडिओ LDAC कोडेक निवडा:\nप्लेबॅक गुणवत्ता"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"स्ट्रीमिंग: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS ओव्हर TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"सुरू केल्यास, पोर्ट 853 वर DNS ओव्हर TLS करण्याचा प्रयत्न करा."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"खाजगी DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"खाजगी DNS मोड निवडा"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"बंद"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"संधिसाधू"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"खाजगी DNS पुरवठादार होस्ट नाव"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS पुरवठादाराचे होस्टनाव टाका"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"वायरलेस डिस्प्ले प्रमाणिकरणाचे पर्याय दाखवा"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"वाय-फाय लॉगिंग स्तर वाढवा, वाय-फाय सिलेक्टरमध्ये प्रति SSID RSSI दर्शवा"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"सक्षम केले असताना, वाय-फाय सिग्नल कमी असताना, मोबाइलकडे डेटा कनेक्शन सोपवण्यासाठी वाय-फाय अधिक अॅग्रेसिव्ह असेल."</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 7d166b4..c1892c3 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ပင်နံပါတ် သို့မဟုတ် ဖြတ်သန်းခွင့်ကီးမမှန်ကန်သောကြောင့်<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့် တွဲချိတ်မရပါ။"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့်ဆက်သွယ်မရပါ"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့်တွဲချိတ်ရန် ပယ်ချခံရသည်"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"ကွန်ပျူတာ"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"မိုက်ခွက်ပါနားကြပ်"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"ဖုန်း"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"ဓာတ်ပုံဆိုင်ရာ"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"နားကြပ်"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"ချိတ်ဆက်အသုံးပြုရသည့် စက်ပစ္စည်းများ"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"ဘလူးတုသ်"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi ပိတ်ထားသည်"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi ချိတ်ဆက်ထားမှု မရှိပါ"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi ၁ ဘားရှိ"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ဘလူးတုသ်အသံ LDAC ကိုးဒက်ခ်− နားထောင်ရန် အရည်အသွေး"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ဘလူးတုသ်အသံ LDAC ကိုးဒက်ခ်ကို ရွေးပါ−\nနားထောင်ရန် အရည်အသွေး"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"တိုက်ရိုက်လွှင့်နေသည်− <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS over TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"ဖွင့်ထားပါက DNS over TLS ကို ပို့တ် ၈၅၃ တွင် စမ်းကြည့်ပါ။"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"သီးသန့် DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"သီးသန့် DNS မုဒ်ကို ရွေးပါ"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"ပိတ်ရန်"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"အခွင့်အရေးသမား"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"သီးသန့် DNS ပံ့ပိုးသူ၏ အင်တာနက်လက်ခံဝန်ဆောင်ပေးသူအမည်"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ဝန်ဆောင်မှုပေးသူ၏ အင်တာနက်လက်ခံဝန်ဆောင်ပေးသူအမည်ကို ထည့်ပါ"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ကြိုးမဲ့ အခင်းအကျင်း အသိအမှတ်ပြုလက်မှတ်အတွက် ရွေးချယ်စရာများပြရန်"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi မှတ်တမ်းတင်ခြင်း နှုန်းအားမြင့်ကာ၊ Wi‑Fi ရွေးရာတွင် SSID RSSI ဖြင့်ပြပါ"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ဖွင့်ထားပါက Wi‑Fi လွှင့်အား နည်းချိန်တွင် Wi‑Fi မှ မိုဘိုင်းသို့ ဒေတာချိတ်ဆက်မှုကို လွှဲပြောင်းရာ၌ ပိုမိုထိရောက်ပါသည်"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index a3875a6..172fed1 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>गलत PIN वा पासकिका कारण सँग जोडा बाँध्न सक्दैन।"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> सँग कुराकानी हुन सक्दैन।"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> द्वारा जोडा बाँध्ने कार्य अस्वीकृत"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"कम्प्युटर"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"हेडसेट"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"फोन"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"छवि सम्बन्धी"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"हेडफोन"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"इनपुट सम्बन्धी बाह्य यन्त्र"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"ब्लुटुथ"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi बन्द।"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi जडान विच्छेद भयो।"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi एक पट्टि।"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ब्लुटुथ अडियो LDAC कोडेक: प्लेब्याक गुणस्तर"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ब्लुटुथ अडियो LDAC कोडेक चयन गर्नुहोस्:\nप्लेब्याक गुणस्तर"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"स्ट्रिमिङ: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"TLS मा DNS को प्रयोग"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"सक्षम पारिएको खण्डमा पोर्ट ८५३ मा TLS मा DNS प्रयोग गर्ने अनुरोध गर्नुहोस्।"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"निजी DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"निजी DNS मोड चयन गर्नुहोस्"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"निष्क्रिय छ"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"अवसरवादी"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"निजी DNS प्रदायकको होस्टनाम"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS प्रदायकको होस्टनाम प्रविष्ट गर्नुहोस्"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ताररहित प्रदर्शन प्रमाणीकरणका लागि विकल्पहरू देखाउनुहोस्"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi लग स्तर बढाउनुहोस्, Wi-Fi चयनकर्तामा प्रति SSID RSSI देखाइन्छ"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"सक्षम गरिएको अवस्थामा, Wi-Fi सिग्नल न्यून हुँदा, Wi-Fi ले बढी आक्रामक ढंगले मोबाइलमा डेटा जडान हस्तान्तरण गर्नेछ"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 4d0d7fe..38853f0 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Kan niet koppelen aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> vanwege een onjuiste pincode of toegangscode."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Kan niet communiceren met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Koppeling geweigerd door <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Computer"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Headset"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Telefoon"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Replicatieapparaat"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Hoofdtelefoon"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Randapparaat voor invoer"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi: uitgeschakeld."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi-verbinding verbroken."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi: één streepje."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"LDAC-codec voor Bluetooth-audio: afspeelkwaliteit"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"LDAC-codec voor Bluetooth-audio selecteren:\nafspeelkwaliteit"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS via TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Als deze optie is ingeschakeld, wordt geprobeerd om DNS via TLS uit te voeren via poort 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privé-DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecteer de modus Privé-DNS"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Uit"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistisch"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Hostnaam van privé-DNS-provider"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Geef hostnaam van DNS-provider op"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Opties weergeven voor certificering van draadloze weergave"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Logniveau voor wifi verhogen, weergeven per SSID RSSI in wifi-kiezer"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Indien ingeschakeld, is wifi agressiever bij het overgeven van de gegevensverbinding aan mobiel wanneer het wifi-signaal zwak is"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 56e361f..be4fa8d 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ਇੱਕ ਗਲਤ ਪਿੰਨ ਜਾਂ ਪਾਸਕੁੰਜੀ ਦੇ ਕਾਰਨ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਨਾਲ ਜੋੜਾਬੱਧ ਨਹੀਂ ਹੋ ਸਕਿਆ।"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਨਾਲ ਸੰਚਾਰ ਨਹੀਂ ਕਰ ਸਕਦਾ।"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"ਪੇਅਰਿੰਗ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਵੱਲੋਂ ਰੱਦ ਕੀਤੀ ਗਈ।"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"ਕੰਪਿਊਟਰ"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ਹੈੱਡਸੈੱਟ"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"ਫ਼ੋਨ ਕਰੋ"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"ਇਮੇਜਿੰਗ"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"ਹੈੱਡਫੋਨ"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"ਇਨਪੁੱਟ ਪੈਰਿਫੈਰਲ"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"ਬਲੂਟੁੱਥ"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi ਬੰਦ।"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi ਡਿਸਕਨੈਕਟ ਕੀਤਾ।"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi ਇੱਕ ਬਾਰ।"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ਬਲੂਟੁੱਥ ਔਡੀਓ LDAC ਕੋਡੇਕ: ਪਲੇਬੈਕ ਗੁਣਵੱਤਾ"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ਬਲੂਟੁੱਥ ਔਡੀਓ LDAC ਕੋਡੇਕ ਚੁਣੋ:\nਪਲੇਬੈਕ ਗੁਣਵੱਤਾ"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ਸਟ੍ਰੀਮਿੰਗ: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"TLS \'ਤੇ DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"ਚਾਲੂ ਕੀਤੇ ਜਾਣ \'ਤੇ, ਪੋਰਟ 853 \'ਤੇ TLS \'ਤੇ DNS ਵਰਤਣ ਦੀ ਬੇਨਤੀ ਕਰੋ।"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"ਨਿੱਜੀ DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"ਨਿੱਜੀ DNS ਮੋਡ ਚੁਣੋ"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"ਬੰਦ"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ਮੌਕਾਪ੍ਰਸਤ"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"ਨਿੱਜੀ DNS ਪ੍ਰਦਾਨਕ ਹੋਸਟਨਾਮ"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ਪ੍ਰਦਾਨਕ ਦਾ ਹੋਸਟਨਾਮ ਦਾਖਲ ਕਰੋ"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ਵਾਇਰਲੈੱਸ ਡਿਸਪਲੇ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਚੋਣਾਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ਵਾਈ‑ਫਾਈ ਲੌਗਿੰਗ ਪੱਧਰ ਵਧਾਓ, ਵਾਈ‑ਫਾਈ Picker ਵਿੱਚ ਪ੍ਰਤੀ SSID RSSI ਦਿਖਾਓ"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ਜਦੋਂ ਯੋਗ ਬਣਾਇਆ ਹੋਵੇ, ਤਾਂ ਵਾਈ‑ਫਾਈ ਸਿਗਨਲ ਘੱਟ ਹੋਣ \'ਤੇ ਵਾਈ‑ਫਾਈ ਡਾਟਾ ਕਨੈਕਸ਼ਨ ਮੋਬਾਈਲ ਨੂੰ ਹੈਂਡ ਓਵਰ ਕਰਨ ਵਿੱਚ ਵੱਧ ਆਕਰਮਣਸ਼ੀਲ ਹੋਵੇਗਾ।"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 7be64e4..89a18ef 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nu s-a putut împerechea cu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> din cauza unui cod PIN sau al unei chei de acces incorecte."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Nu se poate comunica cu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Împerechere respinsă de <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Computer"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Set căști-microfon"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Telefon"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Dispozitiv pentru imagini"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Căști"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Dispozitiv periferic de intrare"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi dezactivat."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi deconectat."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Semnal Wi-Fi: o bară."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codecul LDAC audio pentru Bluetooth: calitatea redării"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Selectați codecul LDAC audio pentru Bluetooth:\ncalitatea redării"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Transmitere în flux: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS prin TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Dacă opțiunea este activată, încercați DNS prin TLS pe portul 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privat"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selectați modul DNS privat"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Dezactivat"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunist"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nume de gazdă al furnizorului de DNS privat"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduceți numele de gazdă al furnizorului de DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afișați opțiunile pentru certificarea Ecran wireless"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Măriți niv. de înr. prin Wi‑Fi, afișați în fcț. de SSID RSSI în Selectorul Wi‑Fi"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Când este activată, Wi-Fi va fi mai agresivă la predarea conexiunii de date către rețeaua mobilă când semnalul Wi-Fi este slab"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 756254b..2b31bf1 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Haikuweza kulingana na <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kwa sababu ya PIN isiyo sahihi au msimbo ya kuingia."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Haiwezi kuanzisha mawasiliano na <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Ulinganishaji umekataliwa na <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Kompyuta"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Vifaa vya sauti"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Simu"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Kupiga picha"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Kifaa cha sauti"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Vifaa vya Ziada vya Kuingiza Data"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi imezimwa."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi imeondolewa."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Kipima mtandao kimoja cha Wifi."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Kodeki ya LDAC ya Sauti ya Bluetooth: Ubora wa Kucheza"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Chagua Kodeki ya LDAC ya Sauti ya Bluetooth:\nUbora wa Kucheza"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Kutiririsha: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS kupitia TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Ukiwasha mipangilio hii, jaribu kutumia DNS kupitia TLS kwenye mlango wa 853."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS ya Faragha"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Chagua Hali ya DNS ya Faragha"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"Imezimwa"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Inayojitokeza"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Jina la mpangishi wa huduma za DNS ya faragha"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Weka jina la mpangishi wa huduma za DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Onyesha chaguo za cheti cha kuonyesha pasiwaya"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Ongeza hatua ya uwekaji kumbukumbu ya Wi-Fi, onyesha kwa kila SSID RSSI kwenye Kichukuzi cha Wi-Fi"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Ikiwashwa, Wi-Fi itakabidhi kwa hima muunganisho wa data kwa mtandao wa simu, wakati mtandao wa Wi-Fi si thabiti"</string>
@@ -305,7 +316,7 @@
<string name="enable_freeform_support_summary" msgid="8247310463288834487">"Ruhusu uwezo wa kutumia madirisha ya majaribio yenye muundo huru."</string>
<string name="local_backup_password_title" msgid="3860471654439418822">"Nenosiri la hifadhi rudufu ya eneo kazi"</string>
<string name="local_backup_password_summary_none" msgid="6951095485537767956">"Hifadhi rudufu kamili za eneo kazi hazijalindwa kwa sasa"</string>
- <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Gonga ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
+ <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Gusa ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
<string name="local_backup_password_toast_success" msgid="582016086228434290">"Nenosiri jipya la hifadhi rudufu limewekwa"</string>
<string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nenosiri jipya na uthibitisho havioani"</string>
<string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Imeshindwa kuweka nenosiri la hifadhi rudufu"</string>
@@ -320,8 +331,8 @@
<item msgid="5363960654009010371">"Rangi zilizoboreshwa kwa ajili ya maudhui dijitali"</item>
</string-array>
<string name="inactive_apps_title" msgid="1317817863508274533">"Programu zilizozimwa"</string>
- <string name="inactive_app_inactive_summary" msgid="5091363706699855725">"Haitumika. Gonga ili ugeuze."</string>
- <string name="inactive_app_active_summary" msgid="4174921824958516106">"Inatumika. Gonga ili ugeuze."</string>
+ <string name="inactive_app_inactive_summary" msgid="5091363706699855725">"Haitumika. Gusa ili ugeuze."</string>
+ <string name="inactive_app_active_summary" msgid="4174921824958516106">"Inatumika. Gusa ili ugeuze."</string>
<string name="runningservices_settings_title" msgid="8097287939865165213">"Huduma zinazoendeshwa"</string>
<string name="runningservices_settings_summary" msgid="854608995821032748">"Onyesha na dhibiti huduma zinazoendeshwa kwa sasa"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Utekelezaji wa WebView"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index e31e3e0..3a8b175 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"தவறான பின் அல்லது கடவுச்சொல் காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> இணைப்பதை நிராகரித்தது."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"கணினி"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"தலையணி"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"ஃபோன்"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"இமேஜிங்"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"ஹெட்ஃபோன்"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"இன்புட் பெரிபெரல்"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"புளூடூத்"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"வைஃபை முடக்கப்பட்டது."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"வைஃபை துண்டிக்கப்பட்டது."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"வைஃபை சிக்னல்: ஒரு கோடு."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"புளூடூத் ஆடியோ LDAC கோடெக்: வீடியோவின் தரம்"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"புளூடூத் ஆடியோ LDAC கோடெக்கைத் தேர்ந்தெடுக்கவும்:\nவீடியோவின் தரம்"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ஸ்ட்ரீமிங்: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"TLS வழியாக DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"இயக்கப்பட்டிருந்தால், போர்ட் 853 இல் TLS வழியாக DNSஐ முயலவும்."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"தனிப்பட்ட DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"தனிப்பட்ட DNS பயன்முறையைத் தேர்ந்தெடுக்கவும்"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"ஆஃப்"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"சந்தர்ப்பவாதம்"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"தனிப்பட்ட DNS வழங்குநரின் ஹோஸ்ட் பெயர்"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS வழங்குநரின் ஹோஸ்ட் பெயரை உள்ளிடவும்"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"வயர்லெஸ் காட்சி சான்றுக்கான விருப்பங்களைக் காட்டு"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wifi நுழைவு அளவை அதிகரித்து, வைஃபை தேர்வியில் ஒவ்வொன்றிற்கும் SSID RSSI ஐ காட்டுக"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"இயக்கப்பட்டதும், வைஃபை சிக்னல் குறையும் போது, வைஃபை முழுமையாக ஒத்துழைக்காமல் இருந்தால் மொபைல் தரவிற்கு மாறும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 0c28ef2..b6f1bd2 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"పిన్ లేదా పాస్కీ చెల్లని కారణంగా <xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో జత చేయడం సాధ్యపడలేదు."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో కమ్యూనికేట్ చేయడం సాధ్యపడదు."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> జత చేయడాన్ని తిరస్కరించింది."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"కంప్యూటర్"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"హెడ్సెట్"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"ఫోన్"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"ప్రతిబింబనం"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"హెడ్ఫోన్"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"ఇన్పుట్ అనుబంధ పరికరం"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"బ్లూటూత్"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi ఆఫ్లో ఉంది."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi డిస్కనెక్ట్ చేయబడింది."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi సిగ్నల్ ఒక బార్ ఉంది."</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"బ్లూటూత్ ఆడియో LDAC కోడెక్: ప్లేబ్యాక్ నాణ్యత"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"బ్లూటూత్ ఆడియో LDAC కోడెక్ని ఎంచుకోండి:\nప్లేబ్యాక్ నాణ్యత"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ప్రసారం చేస్తోంది: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"TLSపై DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"ప్రారంభించబడితే, పోర్ట్ 853లో TLSపై DNSని ప్రయత్నించండి."</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"ప్రైవేట్ DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"ప్రైవేట్ DNS మోడ్ను ఎంచుకోండి"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"ఆఫ్"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"అవకాశవాదం"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"ప్రైవేట్ DNS ప్రదాత హోస్ట్పేరు"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ప్రదాత యొక్క హోస్ట్పేరును నమోదు చేయండి"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"వైర్లెస్ ప్రదర్శన సర్టిఫికెట్ కోసం ఎంపికలను చూపు"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ఎంపికలో SSID RSSI ప్రకారం చూపబడే Wi‑Fi లాగింగ్ స్థాయిని పెంచండి"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ప్రారంభించబడినప్పుడు, Wi‑Fi సిగ్నల్ బలహీనంగా ఉంటే డేటా కనెక్షన్ను మొబైల్కి మార్చేలా Wi‑Fi చురుగ్గా వ్యవహరిస్తుంది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 5ff39b6..2db6178 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ไม่สามารถจับคู่กับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ได้เพราะ PIN หรือรหัสผ่านไม่ถูกต้อง"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"ไม่สามารถเชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ปฏิเสธการจับคู่อุปกรณ์"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"คอมพิวเตอร์"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ชุดหูฟัง"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"โทรศัพท์"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"การถ่ายภาพ"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"หูฟัง"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"อุปกรณ์อินพุต"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"บลูทูธ"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi ปิดอยู่"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"ไม่ได้เชื่อมต่อ Wi-Fi"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"สัญญาณ Wi-Fi 1 ขีด"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC: คุณภาพการเล่น"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"เลือกตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC:\nคุณภาพการเล่น"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"สตรีมมิง: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"DNS ผ่าน TLS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"หากเปิดใช้แล้ว ให้ลองใช้ DNS ผ่าน TLS บนพอร์ต 853"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS ส่วนตัว"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"เลือกโหมด DNS ส่วนตัว"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"ปิด"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ที่ใช้โอกาส"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"ชื่อโฮสต์ของผู้ให้บริการ DNS ส่วนตัว"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ป้อนชื่อโฮสต์ของผู้ให้บริการ DNS"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"แสดงตัวเลือกสำหรับการรับรองการแสดงผล แบบไร้สาย"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"เมื่อเปิดใช้แล้ว Wi-Fi จะส่งผ่านการเชื่อมต่อข้อมูลไปยังเครือข่ายมือถือเมื่อสัญญาณ Wi-Fi อ่อน"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 782bb6f..3b6ab4a5 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"غلط PIN یا پاس کلید کی وجہ سے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> کے ساتھ جوڑا نہیں بنا سکا۔"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> کے ساتھ مواصلت نہیں ہو سکتی۔"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> نے جوڑا بنانے کو مسترد کر دیا۔"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"کمپیوٹر"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"ہیڈ سیٹ"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"فون"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"امیجنگ"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"ہیڈ فون"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"ان پٹ پیریفرل"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"بلوٹوتھ"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi آف ہے۔"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi غیر منسلک ہو گیا۔"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi ایک بار۔"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"بلوٹوتھ آڈیو LDAC کوڈیک: پلے بیک کا معیار"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"بلوٹوتھ آڈیو LDAC کوڈیک منتخب کریں:\nپلے بیک کا معیار"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"سلسلہ بندی: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"TLS پر DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"فعال ہونے پر پورٹ 853 پر TLS پر DNS استعمال کرنے کی کوشش کریں۔"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"نجی DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"نجی DNS وضع منتخب کریں"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"آف"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"موقع پرست"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"نجی DNS فراہم کنندہ میزبان کا نام"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS فراہم کنندہ کے میزبان کا نام درج کریں"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"وائرلیس ڈسپلے سرٹیفیکیشن کیلئے اختیارات دکھائیں"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi لاگنگ لیول میں اضافہ کریں، Wi‑Fi منتخب کنندہ میں فی SSID RSSI دکھائیں"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"فعال کئے جانے پر، جب Wi‑Fi سگنل کمزور ہوگا، تو Wi‑Fi موبائل پر ڈیٹا کنکشن بھیجنے کیلئے مزید جارحانہ کارروائی کرے گا"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 95e4455..4b3d2d4 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasiga ulanib bo‘lmadi, chunki PIN-kod yoki parol noto‘g‘ri kiritildi."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>” qurilmasi bilan aloqa o‘rnatib bo‘lmayapti."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> biriktirish so‘rovini rad qildi."</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Kompyuter"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Audio moslama"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Telefon"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Kamera"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Quloqchin"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Kiritish qurilmasi"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi o‘chiq."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi o‘chiq."</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi: bitta ustun"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"LDAC audiokodeki bilan ijro etish sifati (Bluetooth orqali)"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"LDAC audiokodeki:\nijro sifati"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Translatsiya: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"TLS orqali DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"Yoqilgan bo‘lsa, TLS orqali DNS serverini 853-port yordamida sozlab ko‘ring"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Shaxsiy DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Shaxsiy DNS rejimini tanlang"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"O‘chiq"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Moslashuvchan"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"Shaxsiy DNS provayderining host nomi"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS provayderining host nomini kiriting"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Simsiz monitorlarni sertifikatlash parametrini ko‘rsatish"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi ulanishini tanlashda har bir SSID uchun jurnalda ko‘rsatilsin"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Agar ushbu funksiya yoqilsa, Wi-Fi signali past bo‘lganda internetga ulanish majburiy ravishda mobil internetga o‘tkaziladi"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 094bb90..dd58cc9 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"PIN码或配对密钥不正确,无法与<xliff:g id="DEVICE_NAME">%1$s</xliff:g>配对。"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"无法与“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”进行通信。"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> 已拒绝配对。"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"计算机"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"耳麦"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"手机"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"成像设备"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"耳机"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"外围输入设备"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"蓝牙"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"WLAN 已关闭。"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"WLAN 连接已断开。"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"WLAN 信号强度为一格。"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"蓝牙音频 LDAC 编解码器:播放质量"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"选择蓝牙音频 LDAC 编解码器:\n播放质量"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"正在流式传输:<xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"通过传输层安全协议 (TLS) 执行 DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"如果启用,即可在端口 853 上尝试“通过传输层安全协议 (TLS) 执行 DNS”。"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"私人 DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"选择私人 DNS 模式"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"关闭"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"随机"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"私人 DNS 提供商主机名"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"输入 DNS 提供商的主机名"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"显示无线显示认证选项"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"提升WLAN日志记录级别(在WLAN选择器中显示每个SSID的RSSI)"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"开启此设置后,系统会在 WLAN 信号较弱时,主动将网络模式从 WLAN 网络切换到移动数据网络"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 2554388..2215fe5 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -99,6 +99,13 @@
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 碼或密碼金鑰不正確。"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 通訊。"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」拒絕配對要求。"</string>
+ <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"電腦"</string>
+ <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"免持耳機"</string>
+ <string name="bluetooth_talkback_phone" msgid="4260255181240622896">"電話"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"顯像裝置"</string>
+ <string name="bluetooth_talkback_headphone" msgid="26580326066627664">"頭戴式耳機"</string>
+ <string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"周邊輸入裝置"</string>
+ <string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"藍牙"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"已關閉 Wi-Fi。"</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi 連線已中斷。"</string>
<string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi 訊號強度一格。"</string>
@@ -209,8 +216,12 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"藍牙音訊 LDAC 轉碼器:播放品質"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"選取藍牙音訊 LDAC 轉碼器:\n播放品質"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"串流中:<xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="dns_tls" msgid="6773814174391131955">"透過傳輸層安全標準 (TLS) 執行 DNS"</string>
- <string name="dns_tls_summary" msgid="3692494150251071380">"如果啟用這個選項,即可在通訊埠 853 上嘗試透過傳輸層安全標準 (TLS) 執行 DNS。"</string>
+ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"私人 DNS"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"選取私人 DNS 模式"</string>
+ <string name="private_dns_mode_off" msgid="8236575187318721684">"停用"</string>
+ <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"隨機"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"私人 DNS 供應商主機名稱"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"輸入 DNS 供應商的主機名稱"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
<string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"啟用時,Wi-Fi 連線在訊號不穩的情況下會更積極轉換成行動數據連線"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 4bb16ff..d82db41 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -77,7 +77,7 @@
<!-- Summary for the network but no internet connection was detected. -->
<string name="wifi_no_internet_no_reconnect">Won\'t automatically connect</string>
<!-- Summary for the remembered network but no internet connection was detected. -->
- <string name="wifi_no_internet">No Internet access</string>
+ <string name="wifi_no_internet">No internet access</string>
<!-- Summary for saved networks -->
<string name="saved_network">Saved by <xliff:g id="name">%1$s</xliff:g></string>
@@ -95,7 +95,7 @@
<string name="certinstaller_package" translatable="false">com.android.certinstaller</string>
<!-- Summary for Connected wifi network without internet -->
- <string name="wifi_connected_no_internet">Connected, no Internet</string>
+ <string name="wifi_connected_no_internet">Connected, no internet</string>
<!-- Summary for networks failing to connect due to association rejection status 17, AP full -->
<string name="wifi_ap_unable_to_handle_new_sta">Access point temporarily full</string>
@@ -192,14 +192,14 @@
<!-- Bluetooth settings. Connection options screen. The summary for the HID checkbox preference when HID is connected. -->
<string name="bluetooth_hid_profile_summary_connected">Connected to input device</string>
<!-- Bluetooth settings. Connection options screen. The summary for the checkbox preference when PAN is connected (user role). [CHAR LIMIT=25]-->
- <string name="bluetooth_pan_user_profile_summary_connected">Connected to device for Internet access</string>
+ <string name="bluetooth_pan_user_profile_summary_connected">Connected to device for internet access</string>
<!-- Bluetooth settings. Connection options screen. The summary for the checkbox preference when PAN is connected (NAP role). [CHAR LIMIT=25]-->
- <string name="bluetooth_pan_nap_profile_summary_connected">Sharing local Internet connection with device</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected">Sharing local internet connection with device</string>
<!-- Bluetooth settings. Connection options screen. The summary
for the PAN checkbox preference that describes how checking it
will set the PAN profile as preferred. -->
- <string name="bluetooth_pan_profile_summary_use_for">Use for Internet access</string>
+ <string name="bluetooth_pan_profile_summary_use_for">Use for internet access</string>
<!-- Bluetooth settings. Connection options screen. The summary for the map checkbox preference that describes how checking it will set the map profile as preferred. -->
<string name="bluetooth_map_profile_summary_use_for">Use for map</string>
<!-- Bluetooth settings. Connection options screen. The summary for the sap checkbox preference that describes how checking it will set the sap profile as preferred. -->
@@ -732,6 +732,11 @@
<!-- UI debug setting: profile time taken by hardware acceleration to render apps [CHAR LIMIT=25] -->
<string name="track_frame_time">Profile GPU rendering</string>
+ <!-- UI debug setting: enable gpu debug layers [CHAR LIMIT=25] -->
+ <string name="enable_gpu_debug_layers">Enable GPU debug layers</string>
+ <!-- UI debug setting: enable gpu debug layers summary [CHAR LIMIT=50] -->
+ <string name="enable_gpu_debug_layers_summary">Allow loading GPU debug layers for debug apps</string>
+
<!-- UI debug setting: scaling factor for window animations [CHAR LIMIT=25] -->
<string name="window_animation_scale_title">Window animation scale</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
index 90124f1..8e29e50 100644
--- a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreference.java
@@ -49,8 +49,13 @@
}
public EditText getEditText() {
- return mFragment != null ? (EditText) mFragment.getDialog().findViewById(android.R.id.edit)
- : null;
+ if (mFragment != null) {
+ final Dialog dialog = mFragment.getDialog();
+ if (dialog != null) {
+ return (EditText) dialog.findViewById(android.R.id.edit);
+ }
+ }
+ return null;
}
public boolean isDialogOpen() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
index 78a9064..f2cd103 100644
--- a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
@@ -22,12 +22,15 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
+import android.system.Os;
+import android.system.StructUtsname;
import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
+import android.support.annotation.VisibleForTesting;
import java.io.BufferedReader;
import java.io.FileReader;
@@ -45,7 +48,6 @@
public class DeviceInfoUtils {
private static final String TAG = "DeviceInfoUtils";
- private static final String FILENAME_PROC_VERSION = "/proc/version";
private static final String FILENAME_MSV = "/sys/board_properties/soc/msv";
/**
@@ -63,43 +65,36 @@
}
}
- public static String getFormattedKernelVersion() {
- try {
- return formatKernelVersion(readLine(FILENAME_PROC_VERSION));
- } catch (IOException e) {
- Log.e(TAG, "IO Exception when getting kernel version for Device Info screen",
- e);
-
- return "Unavailable";
- }
+ public static String getFormattedKernelVersion(Context context) {
+ return formatKernelVersion(context, Os.uname());
}
- public static String formatKernelVersion(String rawKernelVersion) {
- // Example (see tests for more):
- // Linux version 4.9.29-g958411d (android-build@xyz) (Android clang version 3.8.256229 \
- // (based on LLVM 3.8.256229)) #1 SMP PREEMPT Wed Jun 7 00:06:03 CST 2017
- // Linux version 4.9.29-geb63318482a7 (android-build@xyz) (gcc version 4.9.x 20150123 \
- // (prerelease) (GCC) ) #1 SMP PREEMPT Thu Jun 1 03:41:57 UTC 2017
- final String PROC_VERSION_REGEX =
- "Linux version (\\S+) " + /* group 1: "3.0.31-g6fb96c9" */
- "\\((\\S+?)\\) " + /* group 2: "(x@y.com) " */
- "\\((.+?)\\) " + /* group 3: kernel toolchain version information */
- "(#\\d+) " + /* group 4: "#1" */
- "(?:.*?)?" + /* ignore: optional SMP, PREEMPT, and any CONFIG_FLAGS */
- "((Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)"; /* group 5: "Thu Jun 28 11:02:39 PDT 2012" */
-
- Matcher m = Pattern.compile(PROC_VERSION_REGEX).matcher(rawKernelVersion);
- if (!m.matches()) {
- Log.e(TAG, "Regex did not match on /proc/version: " + rawKernelVersion);
- return "Unavailable";
- } else if (m.groupCount() < 4) {
- Log.e(TAG, "Regex match on /proc/version only returned " + m.groupCount()
- + " groups");
- return "Unavailable";
+ @VisibleForTesting
+ static String formatKernelVersion(Context context, StructUtsname uname) {
+ if (uname == null) {
+ return context.getString(R.string.status_unavailable);
}
- return m.group(1) + " ("+ m.group(3) + ")\n" + // 3.0.31-g6fb96c9 (toolchain version)
- m.group(2) + " " + m.group(4) + "\n" + // x@y.com #1
- m.group(5); // Thu Jun 28 11:02:39 PDT 2012
+ // Example:
+ // 4.9.29-g958411d
+ // #1 SMP PREEMPT Wed Jun 7 00:06:03 CST 2017
+ final String VERSION_REGEX =
+ "(#\\d+) " + /* group 1: "#1" */
+ "(?:.*?)?" + /* ignore: optional SMP, PREEMPT, and any CONFIG_FLAGS */
+ "((Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)"; /* group 2: "Thu Jun 28 11:02:39 PDT 2012" */
+ Matcher m = Pattern.compile(VERSION_REGEX).matcher(uname.version);
+ if (!m.matches()) {
+ Log.e(TAG, "Regex did not match on uname version " + uname.version);
+ return context.getString(R.string.status_unavailable);
+ }
+
+ // Example output:
+ // 4.9.29-g958411d
+ // #1 Wed Jun 7 00:06:03 CST 2017
+ return new StringBuilder().append(uname.release)
+ .append("\n")
+ .append(m.group(1))
+ .append(" ")
+ .append(m.group(2)).toString();
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/Lifecycle.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/Lifecycle.java
index b2351a9..451e561 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/Lifecycle.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/Lifecycle.java
@@ -15,11 +15,18 @@
*/
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
+
import android.annotation.UiThread;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.LifecycleRegistry;
+import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -44,18 +51,46 @@
/**
* Dispatcher for lifecycle events.
*/
-public class Lifecycle {
+public class Lifecycle extends LifecycleRegistry {
+ private static final String TAG = "LifecycleObserver";
- protected final List<LifecycleObserver> mObservers = new ArrayList<>();
+ private final List<LifecycleObserver> mObservers = new ArrayList<>();
+ private final LifecycleProxy mProxy = new LifecycleProxy();
+
+ /**
+ * Creates a new LifecycleRegistry for the given provider.
+ * <p>
+ * You should usually create this inside your LifecycleOwner class's constructor and hold
+ * onto the same instance.
+ *
+ * @param provider The owner LifecycleOwner
+ */
+ public Lifecycle(@NonNull LifecycleOwner provider) {
+ super(provider);
+ addObserver(mProxy);
+ }
/**
* Registers a new observer of lifecycle events.
*/
@UiThread
- public <T extends LifecycleObserver> T addObserver(T observer) {
+ @Override
+ public void addObserver(android.arch.lifecycle.LifecycleObserver observer) {
ThreadUtils.ensureMainThread();
- mObservers.add(observer);
- return observer;
+ super.addObserver(observer);
+ if (observer instanceof LifecycleObserver) {
+ mObservers.add((LifecycleObserver) observer);
+ }
+ }
+
+ @UiThread
+ @Override
+ public void removeObserver(android.arch.lifecycle.LifecycleObserver observer) {
+ ThreadUtils.ensureMainThread();
+ super.removeObserver(observer);
+ if (observer instanceof LifecycleObserver) {
+ mObservers.remove(observer);
+ }
}
public void onAttach(Context context) {
@@ -67,6 +102,8 @@
}
}
+ // This method is not called from the proxy because it does not have access to the
+ // savedInstanceState
public void onCreate(Bundle savedInstanceState) {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
@@ -76,7 +113,7 @@
}
}
- public void onStart() {
+ private void onStart() {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
if (observer instanceof OnStart) {
@@ -94,7 +131,7 @@
}
}
- public void onResume() {
+ private void onResume() {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
if (observer instanceof OnResume) {
@@ -103,7 +140,7 @@
}
}
- public void onPause() {
+ private void onPause() {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
if (observer instanceof OnPause) {
@@ -121,7 +158,7 @@
}
}
- public void onStop() {
+ private void onStop() {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
if (observer instanceof OnStop) {
@@ -130,7 +167,7 @@
}
}
- public void onDestroy() {
+ private void onDestroy() {
for (int i = 0, size = mObservers.size(); i < size; i++) {
final LifecycleObserver observer = mObservers.get(i);
if (observer instanceof OnDestroy) {
@@ -168,4 +205,34 @@
}
return false;
}
+
+ private class LifecycleProxy
+ implements android.arch.lifecycle.LifecycleObserver {
+ @OnLifecycleEvent(ON_ANY)
+ public void onLifecycleEvent(LifecycleOwner owner, Event event) {
+ switch (event) {
+ case ON_CREATE:
+ // onCreate is called directly since we don't have savedInstanceState here
+ break;
+ case ON_START:
+ onStart();
+ break;
+ case ON_RESUME:
+ onResume();
+ break;
+ case ON_PAUSE:
+ onPause();
+ break;
+ case ON_STOP:
+ onStop();
+ break;
+ case ON_DESTROY:
+ onDestroy();
+ break;
+ case ON_ANY:
+ Log.wtf(TAG, "Should not receive an 'ANY' event!");
+ break;
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/LifecycleObserver.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/LifecycleObserver.java
index 6c41072..ec8a8b5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/LifecycleObserver.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/LifecycleObserver.java
@@ -17,6 +17,9 @@
/**
* Observer of lifecycle events.
+ * @deprecated use {@link android.arch.lifecycle.LifecycleObserver} instead
*/
-public interface LifecycleObserver {
+@Deprecated
+public interface LifecycleObserver extends
+ android.arch.lifecycle.LifecycleObserver {
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
index 727bec7..8b062f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
@@ -15,8 +15,16 @@
*/
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
import android.annotation.Nullable;
import android.app.Activity;
+import android.arch.lifecycle.LifecycleOwner;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.view.Menu;
@@ -25,17 +33,19 @@
/**
* {@link Activity} that has hooks to observe activity lifecycle events.
*/
-public class ObservableActivity extends Activity {
+public class ObservableActivity extends Activity implements LifecycleOwner {
- private final Lifecycle mLifecycle = new Lifecycle();
+ private final Lifecycle mLifecycle = new Lifecycle(this);
- protected Lifecycle getLifecycle() {
+ public Lifecycle getLifecycle() {
return mLifecycle;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mLifecycle.onAttach(this);
+ mLifecycle.onCreate(savedInstanceState);
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
super.onCreate(savedInstanceState);
}
@@ -43,36 +53,38 @@
public void onCreate(@Nullable Bundle savedInstanceState,
@Nullable PersistableBundle persistentState) {
mLifecycle.onAttach(this);
+ mLifecycle.onCreate(savedInstanceState);
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
super.onCreate(savedInstanceState, persistentState);
}
@Override
protected void onStart() {
- mLifecycle.onStart();
+ mLifecycle.handleLifecycleEvent(ON_START);
super.onStart();
}
@Override
protected void onResume() {
- mLifecycle.onResume();
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
super.onResume();
}
@Override
protected void onPause() {
- mLifecycle.onPause();
+ mLifecycle.handleLifecycleEvent(ON_PAUSE);
super.onPause();
}
@Override
protected void onStop() {
- mLifecycle.onStop();
+ mLifecycle.handleLifecycleEvent(ON_STOP);
super.onStop();
}
@Override
protected void onDestroy() {
- mLifecycle.onDestroy();
+ mLifecycle.handleLifecycleEvent(ON_DESTROY);
super.onDestroy();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
index 315bedc..dc95384 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
@@ -15,9 +15,17 @@
*/
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
import android.app.DialogFragment;
+import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
-import android.support.annotation.VisibleForTesting;
+import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -25,9 +33,9 @@
/**
* {@link DialogFragment} that has hooks to observe fragment lifecycle events.
*/
-public class ObservableDialogFragment extends DialogFragment {
+public class ObservableDialogFragment extends DialogFragment implements LifecycleOwner {
- protected final Lifecycle mLifecycle = createLifecycle();
+ protected final Lifecycle mLifecycle = new Lifecycle(this);
@Override
public void onAttach(Context context) {
@@ -36,32 +44,39 @@
}
@Override
+ public void onCreate(Bundle savedInstanceState) {
+ mLifecycle.onCreate(savedInstanceState);
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
public void onStart() {
- mLifecycle.onStart();
+ mLifecycle.handleLifecycleEvent(ON_START);
super.onStart();
}
@Override
public void onResume() {
- mLifecycle.onResume();
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
super.onResume();
}
@Override
public void onPause() {
- mLifecycle.onPause();
+ mLifecycle.handleLifecycleEvent(ON_PAUSE);
super.onPause();
}
@Override
public void onStop() {
- mLifecycle.onStop();
+ mLifecycle.handleLifecycleEvent(ON_STOP);
super.onStop();
}
@Override
public void onDestroy() {
- mLifecycle.onDestroy();
+ mLifecycle.handleLifecycleEvent(ON_DESTROY);
super.onDestroy();
}
@@ -86,9 +101,8 @@
return lifecycleHandled;
}
- @VisibleForTesting(otherwise = VisibleForTesting.NONE)
- /** @return a new lifecycle. */
- public static Lifecycle createLifecycle() {
- return new Lifecycle();
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
index 3a00eba..925eda6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
@@ -16,19 +16,27 @@
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
import android.annotation.CallSuper;
import android.app.Fragment;
+import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-public class ObservableFragment extends Fragment {
+public class ObservableFragment extends Fragment implements LifecycleOwner {
- private final Lifecycle mLifecycle = new Lifecycle();
+ private final Lifecycle mLifecycle = new Lifecycle(this);
- protected Lifecycle getLifecycle() {
+ public Lifecycle getLifecycle() {
return mLifecycle;
}
@@ -43,6 +51,7 @@
@Override
public void onCreate(Bundle savedInstanceState) {
mLifecycle.onCreate(savedInstanceState);
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
super.onCreate(savedInstanceState);
}
@@ -56,35 +65,35 @@
@CallSuper
@Override
public void onStart() {
- mLifecycle.onStart();
+ mLifecycle.handleLifecycleEvent(ON_START);
super.onStart();
}
@CallSuper
@Override
- public void onStop() {
- mLifecycle.onStop();
- super.onStop();
- }
-
- @CallSuper
- @Override
public void onResume() {
- mLifecycle.onResume();
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
super.onResume();
}
@CallSuper
@Override
public void onPause() {
- mLifecycle.onPause();
+ mLifecycle.handleLifecycleEvent(ON_PAUSE);
super.onPause();
}
@CallSuper
@Override
+ public void onStop() {
+ mLifecycle.handleLifecycleEvent(ON_STOP);
+ super.onStop();
+ }
+
+ @CallSuper
+ @Override
public void onDestroy() {
- mLifecycle.onDestroy();
+ mLifecycle.handleLifecycleEvent(ON_DESTROY);
super.onDestroy();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
index 76e5c85..abd7755 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
@@ -16,7 +16,15 @@
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
import android.annotation.CallSuper;
+import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
import android.os.Bundle;
import android.support.v14.preference.PreferenceFragment;
@@ -28,11 +36,12 @@
/**
* {@link PreferenceFragment} that has hooks to observe fragment lifecycle events.
*/
-public abstract class ObservablePreferenceFragment extends PreferenceFragment {
+public abstract class ObservablePreferenceFragment extends PreferenceFragment
+ implements LifecycleOwner {
- private final Lifecycle mLifecycle = new Lifecycle();
+ private final Lifecycle mLifecycle = new Lifecycle(this);
- protected Lifecycle getLifecycle() {
+ public Lifecycle getLifecycle() {
return mLifecycle;
}
@@ -47,6 +56,7 @@
@Override
public void onCreate(Bundle savedInstanceState) {
mLifecycle.onCreate(savedInstanceState);
+ mLifecycle.handleLifecycleEvent(ON_CREATE);
super.onCreate(savedInstanceState);
}
@@ -66,35 +76,35 @@
@CallSuper
@Override
public void onStart() {
- mLifecycle.onStart();
+ mLifecycle.handleLifecycleEvent(ON_START);
super.onStart();
}
@CallSuper
@Override
- public void onStop() {
- mLifecycle.onStop();
- super.onStop();
- }
-
- @CallSuper
- @Override
public void onResume() {
- mLifecycle.onResume();
+ mLifecycle.handleLifecycleEvent(ON_RESUME);
super.onResume();
}
@CallSuper
@Override
public void onPause() {
- mLifecycle.onPause();
+ mLifecycle.handleLifecycleEvent(ON_PAUSE);
super.onPause();
}
@CallSuper
@Override
+ public void onStop() {
+ mLifecycle.handleLifecycleEvent(ON_STOP);
+ super.onStop();
+ }
+
+ @CallSuper
+ @Override
public void onDestroy() {
- mLifecycle.onDestroy();
+ mLifecycle.handleLifecycleEvent(ON_DESTROY);
super.onDestroy();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnAttach.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnAttach.java
index 152cbac..e28c387 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnAttach.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnAttach.java
@@ -17,6 +17,10 @@
import android.content.Context;
+/**
+ * @deprecated pass {@link Context} in constructor instead
+ */
+@Deprecated
public interface OnAttach {
void onAttach(Context context);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnCreate.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnCreate.java
index 44cbf8d..ad7068e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnCreate.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnCreate.java
@@ -16,8 +16,14 @@
package com.android.settingslib.core.lifecycle.events;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
import android.os.Bundle;
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event) }
+ */
+@Deprecated
public interface OnCreate {
void onCreate(Bundle savedInstanceState);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnDestroy.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnDestroy.java
index ffa3d16..c37286e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnDestroy.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnDestroy.java
@@ -15,6 +15,13 @@
*/
package com.android.settingslib.core.lifecycle.events;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event) }
+ */
+@Deprecated
public interface OnDestroy {
void onDestroy();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnPause.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnPause.java
index 4a71105..a5ab39c4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnPause.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnPause.java
@@ -15,6 +15,13 @@
*/
package com.android.settingslib.core.lifecycle.events;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event) }
+ */
+@Deprecated
public interface OnPause {
void onPause();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnResume.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnResume.java
index 8dd24e9..1effba4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnResume.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnResume.java
@@ -15,6 +15,13 @@
*/
package com.android.settingslib.core.lifecycle.events;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event)}
+ */
+@Deprecated
public interface OnResume {
void onResume();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStart.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStart.java
index c88ddaa..07b8460 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStart.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStart.java
@@ -15,7 +15,13 @@
*/
package com.android.settingslib.core.lifecycle.events;
-public interface OnStart {
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event) }
+ */
+@Deprecated
+public interface OnStart {
void onStart();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStop.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStop.java
index 32f61d9..d6a5967 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStop.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/events/OnStop.java
@@ -15,7 +15,13 @@
*/
package com.android.settingslib.core.lifecycle.events;
-public interface OnStop {
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.OnLifecycleEvent;
+/**
+ * @deprecated use {@link OnLifecycleEvent(Lifecycle.Event) }
+ */
+@Deprecated
+public interface OnStop {
void onStop();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
index 17c7d13..7e37493 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
@@ -16,6 +16,7 @@
package com.android.settingslib;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -31,6 +32,7 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsLibRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -59,6 +61,16 @@
verify(editText).requestFocus();
}
+ @Test
+ public void getEditText_noDialog_shouldNotCrash() {
+ ReflectionHelpers.setField(mPreference, "mFragment",
+ mock(CustomEditTextPreference.CustomPreferenceDialogFragment.class));
+
+ mPreference.getEditText();
+
+ // no crash
+ }
+
private static class TestPreference extends CustomEditTextPreference {
public TestPreference(Context context) {
super(context);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
new file mode 100644
index 0000000..82604f7
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.system.StructUtsname;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DeviceInfoUtilsTest {
+
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ mContext = RuntimeEnvironment.application;
+ }
+
+ @Test
+ public void formatKernelVersion_regularInputVersion_shouldStripOptionalValues() {
+ final String sysName = "Linux";
+ final String nodeName = "localhost";
+ final String release = "4.4.88-g134be430baab";
+ final String version = "#1 SMP PREEMPT Tue Dec 31 12:00:00 UTC 2017";
+ final String machine = "aarch64";
+ final StructUtsname uname = new StructUtsname(sysName, nodeName, release, version, machine);
+
+ final String expected = release + "\n" + "#1 Tue Dec 31 12:00:00 UTC 2017";
+
+ assertThat(DeviceInfoUtils.formatKernelVersion(mContext, uname)).isEqualTo(expected);
+ }
+
+ @Test
+ public void formatKernelVersion_nonRegularInputVersion_shouldBeUnavailable() {
+ final String sysName = "Linux";
+ final String nodeName = "localhost";
+ final String release = "4.4.88-g134be430baab";
+ final String version = "%@%!asd%#@!$" + "\n " + "fasdfasdfa13ta";
+ final String machine = "aarch64";
+ final StructUtsname uname = new StructUtsname(sysName, nodeName, release, version, machine);
+
+ final String expected = mContext.getString(R.string.status_unavailable);
+
+ assertThat(DeviceInfoUtils.formatKernelVersion(mContext, uname)).isEqualTo(expected);
+ }
+
+ @Test
+ public void formatKernelVersion_nullInputVersion_shouldBeUnavailable() {
+ final String expected = mContext.getString(R.string.status_unavailable);
+
+ assertThat(DeviceInfoUtils.formatKernelVersion(mContext, null)).isEqualTo(expected);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index 1290391..adb4832 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -15,6 +15,10 @@
*/
package com.android.settingslib.core.lifecycle;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+
+import static com.google.common.truth.Truth.assertThat;
+
import android.content.Context;
import android.view.Menu;
import android.view.MenuInflater;
@@ -32,6 +36,7 @@
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
@@ -39,12 +44,12 @@
import org.robolectric.android.controller.FragmentController;
import org.robolectric.annotation.Config;
-import static com.google.common.truth.Truth.assertThat;
-
@RunWith(SettingsLibRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class LifecycleTest {
+ private Lifecycle mLifecycle;
+
public static class TestDialogFragment extends ObservableDialogFragment {
final TestObserver mFragObserver;
@@ -139,6 +144,11 @@
}
}
+ @Before
+ public void setUp() {
+ mLifecycle = new Lifecycle(() -> mLifecycle);
+ }
+
@Test
public void runThroughActivityLifecycles_shouldObserveEverything() {
ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class);
@@ -212,9 +222,8 @@
@Test
public void addObserverDuringObserve_shoudNotCrash() {
- Lifecycle lifecycle = new Lifecycle();
- lifecycle.addObserver(new OnStartObserver(lifecycle));
- lifecycle.onStart();
+ mLifecycle.addObserver(new OnStartObserver(mLifecycle));
+ mLifecycle.handleLifecycleEvent(ON_START);
}
private static class OptionItemAccepter implements LifecycleObserver, OnOptionsItemSelected {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
index 3f0eca5..5d5733e4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
@@ -44,7 +44,7 @@
shadows = SystemPropertiesTestImpl.class)
public class LogpersistPreferenceControllerTest {
- private Lifecycle mLifecycle = new Lifecycle();
+ private Lifecycle mLifecycle;
@Mock
private ListPreference mListPreference;
@@ -57,6 +57,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
SystemProperties.set("ro.debuggable", "1");
+ mLifecycle = new Lifecycle(() -> mLifecycle);
mController = new AbstractLogpersistPreferenceController(RuntimeEnvironment.application,
mLifecycle) {
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
index 97fda99..75b6c5f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
@@ -16,6 +16,14 @@
package com.android.settingslib.widget;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.support.v14.preference.PreferenceFragment;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen;
@@ -32,13 +40,6 @@
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
@RunWith(SettingsLibRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class FooterPreferenceMixinTest {
@@ -54,7 +55,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mLifecycle = new Lifecycle();
+ mLifecycle = new Lifecycle(() -> mLifecycle);
when(mFragment.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
when(mFragment.getPreferenceManager().getContext())
.thenReturn(ShadowApplication.getInstance().getApplicationContext());
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 1557d91..7428149 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1836,20 +1836,10 @@
}
if (upgradeVersion < 116) {
- if (mUserHandle == UserHandle.USER_SYSTEM) {
- db.beginTransaction();
- SQLiteStatement stmt = null;
- try {
- stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)"
- + " VALUES(?,?);");
- loadSetting(stmt, Settings.Global.ENHANCED_4G_MODE_ENABLED,
- ImsConfig.FeatureValueConstants.ON);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- if (stmt != null) stmt.close();
- }
- }
+ /*
+ * To control the default value by carrier config manager, initializing
+ * ENHANCED_4G_MODE_ENABLED has been removed.
+ */
upgradeVersion = 116;
}
@@ -2633,9 +2623,6 @@
loadSetting(stmt, Settings.Global.DEVICE_NAME, getDefaultDeviceName());
- loadSetting(stmt, Settings.Global.ENHANCED_4G_MODE_ENABLED,
- ImsConfig.FeatureValueConstants.ON);
-
/*
* IMPORTANT: Do not add any more upgrade steps here as the global,
* secure, and system settings are no longer stored in a database
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 67fb4d9..ba1c9e3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -37,9 +37,11 @@
// Global settings
SettingsState globalSettings = settingsRegistry.getSettingsLocked(
SettingsProvider.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
- long globalSettingsToken = proto.start(SettingsServiceDumpProto.GLOBAL_SETTINGS);
- dumpProtoGlobalSettingsLocked(globalSettings, proto);
- proto.end(globalSettingsToken);
+ if (globalSettings != null) {
+ long globalSettingsToken = proto.start(SettingsServiceDumpProto.GLOBAL_SETTINGS);
+ dumpProtoGlobalSettingsLocked(globalSettings, proto);
+ proto.end(globalSettingsToken);
+ }
// Per-user settings
SparseBooleanArray users = settingsRegistry.getKnownUsersLocked();
@@ -67,19 +69,26 @@
SettingsState secureSettings = settingsRegistry.getSettingsLocked(
SettingsProvider.SETTINGS_TYPE_SECURE, user.getIdentifier());
- long secureSettingsToken = proto.start(UserSettingsProto.SECURE_SETTINGS);
- dumpProtoSecureSettingsLocked(secureSettings, proto);
- proto.end(secureSettingsToken);
+ if (secureSettings != null) {
+ long secureSettingsToken = proto.start(UserSettingsProto.SECURE_SETTINGS);
+ dumpProtoSecureSettingsLocked(secureSettings, proto);
+ proto.end(secureSettingsToken);
+ }
SettingsState systemSettings = settingsRegistry.getSettingsLocked(
SettingsProvider.SETTINGS_TYPE_SYSTEM, user.getIdentifier());
- long systemSettingsToken = proto.start(UserSettingsProto.SYSTEM_SETTINGS);
- dumpProtoSystemSettingsLocked(systemSettings, proto);
- proto.end(systemSettingsToken);
+ if (systemSettings != null) {
+ long systemSettingsToken = proto.start(UserSettingsProto.SYSTEM_SETTINGS);
+ dumpProtoSystemSettingsLocked(systemSettings, proto);
+ proto.end(systemSettingsToken);
+ }
}
private static void dumpProtoGlobalSettingsLocked(
@NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+ s.dumpHistoricalOperations(p, GlobalSettingsProto.HISTORICAL_OPERATIONS);
+
+ // This uses the same order as in Settings.Global.
dumpSetting(s, p,
Settings.Global.ADD_USERS_WHEN_LOCKED,
GlobalSettingsProto.ADD_USERS_WHEN_LOCKED);
@@ -114,6 +123,9 @@
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
GlobalSettingsProto.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
dumpSetting(s, p,
+ Settings.Global.BLUETOOTH_CLASS_OF_DEVICE,
+ GlobalSettingsProto.BLUETOOTH_CLASS_OF_DEVICE);
+ dumpSetting(s, p,
Settings.Global.BLUETOOTH_DISABLED_PROFILES,
GlobalSettingsProto.BLUETOOTH_DISABLED_PROFILES);
dumpSetting(s, p,
@@ -194,6 +206,7 @@
dumpSetting(s, p,
Settings.Global.CDMA_SUBSCRIPTION_MODE,
GlobalSettingsProto.CDMA_SUBSCRIPTION_MODE);
+ // Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA intentionally excluded.
dumpSetting(s, p,
Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
GlobalSettingsProto.DATA_ACTIVITY_TIMEOUT_MOBILE);
@@ -209,6 +222,10 @@
dumpSetting(s, p,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
GlobalSettingsProto.FORCE_ALLOW_ON_EXTERNAL);
+ // Settings.Global.DEFAULT_SM_DP_PLUS intentionally excluded.
+ dumpSetting(s, p,
+ Settings.Global.EUICC_PROVISIONED,
+ GlobalSettingsProto.EUICC_PROVISIONED);
dumpSetting(s, p,
Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
GlobalSettingsProto.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
@@ -236,6 +253,7 @@
dumpSetting(s, p,
Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
GlobalSettingsProto.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
+ // Settings.Global.INSTALL_NON_MARKET_APPS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Global.HDMI_CONTROL_ENABLED,
GlobalSettingsProto.HDMI_CONTROL_ENABLED);
@@ -249,6 +267,21 @@
Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
GlobalSettingsProto.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED);
dumpSetting(s, p,
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
+ GlobalSettingsProto.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS);
+ dumpSetting(s, p,
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
+ GlobalSettingsProto.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
+ dumpSetting(s, p,
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ GlobalSettingsProto.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+ dumpSetting(s, p,
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS,
+ GlobalSettingsProto.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS);
+ dumpSetting(s, p,
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ GlobalSettingsProto.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+ dumpSetting(s, p,
Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
GlobalSettingsProto.MHL_INPUT_SWITCHING_ENABLED);
dumpSetting(s, p,
@@ -279,6 +312,9 @@
Settings.Global.NETSTATS_SAMPLE_ENABLED,
GlobalSettingsProto.NETSTATS_SAMPLE_ENABLED);
dumpSetting(s, p,
+ Settings.Global.NETSTATS_AUGMENT_ENABLED,
+ GlobalSettingsProto.NETSTATS_AUGMENT_ENABLED);
+ dumpSetting(s, p,
Settings.Global.NETSTATS_DEV_BUCKET_DURATION,
GlobalSettingsProto.NETSTATS_DEV_BUCKET_DURATION);
dumpSetting(s, p,
@@ -420,6 +456,9 @@
Settings.Global.TETHER_DUN_APN,
GlobalSettingsProto.TETHER_DUN_APN);
dumpSetting(s, p,
+ Settings.Global.TETHER_OFFLOAD_DISABLED,
+ GlobalSettingsProto.TETHER_OFFLOAD_DISABLED);
+ dumpSetting(s, p,
Settings.Global.CARRIER_APP_WHITELIST,
GlobalSettingsProto.CARRIER_APP_WHITELIST);
dumpSetting(s, p,
@@ -450,6 +489,15 @@
Settings.Global.NETWORK_AVOID_BAD_WIFI,
GlobalSettingsProto.NETWORK_AVOID_BAD_WIFI);
dumpSetting(s, p,
+ Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE,
+ GlobalSettingsProto.NETWORK_METERED_MULTIPATH_PREFERENCE);
+ dumpSetting(s, p,
+ Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
+ GlobalSettingsProto.NETWORK_WATCHLIST_LAST_REPORT_TIME);
+ dumpSetting(s, p,
+ Settings.Global.WIFI_BADGING_THRESHOLDS,
+ GlobalSettingsProto.WIFI_BADGING_THRESHOLDS);
+ dumpSetting(s, p,
Settings.Global.WIFI_DISPLAY_ON,
GlobalSettingsProto.WIFI_DISPLAY_ON);
dumpSetting(s, p,
@@ -489,12 +537,30 @@
Settings.Global.WIFI_WAKEUP_ENABLED,
GlobalSettingsProto.WIFI_WAKEUP_ENABLED);
dumpSetting(s, p,
+ Settings.Global.WIFI_WAKEUP_AVAILABLE,
+ GlobalSettingsProto.WIFI_WAKEUP_AVAILABLE);
+ dumpSetting(s, p,
+ Settings.Global.NETWORK_SCORING_UI_ENABLED,
+ GlobalSettingsProto.NETWORK_SCORING_UI_ENABLED);
+ dumpSetting(s, p,
+ Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
+ GlobalSettingsProto.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS);
+ dumpSetting(s, p,
Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
GlobalSettingsProto.NETWORK_RECOMMENDATIONS_ENABLED);
dumpSetting(s, p,
Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE,
GlobalSettingsProto.NETWORK_RECOMMENDATIONS_PACKAGE);
dumpSetting(s, p,
+ Settings.Global.USE_OPEN_WIFI_PACKAGE,
+ GlobalSettingsProto.USE_OPEN_WIFI_PACKAGE);
+ dumpSetting(s, p,
+ Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS,
+ GlobalSettingsProto.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS);
+ dumpSetting(s, p,
+ Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS,
+ GlobalSettingsProto.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS);
+ dumpSetting(s, p,
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
GlobalSettingsProto.BLE_SCAN_ALWAYS_AVAILABLE);
dumpSetting(s, p,
@@ -612,6 +678,12 @@
Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
GlobalSettingsProto.SYS_STORAGE_FULL_THRESHOLD_BYTES);
dumpSetting(s, p,
+ Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE,
+ GlobalSettingsProto.SYS_STORAGE_CACHE_PERCENTAGE);
+ dumpSetting(s, p,
+ Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
+ GlobalSettingsProto.SYS_STORAGE_CACHE_MAX_BYTES);
+ dumpSetting(s, p,
Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
GlobalSettingsProto.SYNC_MAX_RETRY_DELAY_IN_SECONDS);
dumpSetting(s, p,
@@ -627,6 +699,9 @@
Settings.Global.CAPTIVE_PORTAL_MODE,
GlobalSettingsProto.CAPTIVE_PORTAL_MODE);
dumpSetting(s, p,
+ Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED,
+ GlobalSettingsProto.CAPTIVE_PORTAL_DETECTION_ENABLED);
+ dumpSetting(s, p,
Settings.Global.CAPTIVE_PORTAL_SERVER,
GlobalSettingsProto.CAPTIVE_PORTAL_SERVER);
dumpSetting(s, p,
@@ -639,6 +714,9 @@
Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL,
GlobalSettingsProto.CAPTIVE_PORTAL_FALLBACK_URL);
dumpSetting(s, p,
+ Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS,
+ GlobalSettingsProto.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS);
+ dumpSetting(s, p,
Settings.Global.CAPTIVE_PORTAL_USE_HTTPS,
GlobalSettingsProto.CAPTIVE_PORTAL_USE_HTTPS);
dumpSetting(s, p,
@@ -684,6 +762,12 @@
Settings.Global.DEFAULT_DNS_SERVER,
GlobalSettingsProto.DEFAULT_DNS_SERVER);
dumpSetting(s, p,
+ Settings.Global.PRIVATE_DNS_MODE,
+ GlobalSettingsProto.PRIVATE_DNS_MODE);
+ dumpSetting(s, p,
+ Settings.Global.PRIVATE_DNS_SPECIFIER,
+ GlobalSettingsProto.PRIVATE_DNS_SPECIFIER);
+ dumpSetting(s, p,
Settings.Global.BLUETOOTH_HEADSET_PRIORITY_PREFIX,
GlobalSettingsProto.BLUETOOTH_HEADSET_PRIORITY_PREFIX);
dumpSetting(s, p,
@@ -717,12 +801,27 @@
Settings.Global.BLUETOOTH_PAN_PRIORITY_PREFIX,
GlobalSettingsProto.BLUETOOTH_PAN_PRIORITY_PREFIX);
dumpSetting(s, p,
+ Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
+ GlobalSettingsProto.ACTIVITY_MANAGER_CONSTANTS);
+ dumpSetting(s, p,
Settings.Global.DEVICE_IDLE_CONSTANTS,
GlobalSettingsProto.DEVICE_IDLE_CONSTANTS);
dumpSetting(s, p,
+ Settings.Global.BATTERY_SAVER_CONSTANTS,
+ GlobalSettingsProto.BATTERY_SAVER_CONSTANTS);
+ dumpSetting(s, p,
+ Settings.Global.ANOMALY_DETECTION_CONSTANTS,
+ GlobalSettingsProto.ANOMALY_DETECTION_CONSTANTS);
+ dumpSetting(s, p,
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS,
+ GlobalSettingsProto.ALWAYS_ON_DISPLAY_CONSTANTS);
+ dumpSetting(s, p,
Settings.Global.APP_IDLE_CONSTANTS,
GlobalSettingsProto.APP_IDLE_CONSTANTS);
dumpSetting(s, p,
+ Settings.Global.POWER_MANAGER_CONSTANTS,
+ GlobalSettingsProto.POWER_MANAGER_CONSTANTS);
+ dumpSetting(s, p,
Settings.Global.ALARM_MANAGER_CONSTANTS,
GlobalSettingsProto.ALARM_MANAGER_CONSTANTS);
dumpSetting(s, p,
@@ -732,6 +831,12 @@
Settings.Global.SHORTCUT_MANAGER_CONSTANTS,
GlobalSettingsProto.SHORTCUT_MANAGER_CONSTANTS);
dumpSetting(s, p,
+ Settings.Global.DEVICE_POLICY_CONSTANTS,
+ GlobalSettingsProto.DEVICE_POLICY_CONSTANTS);
+ dumpSetting(s, p,
+ Settings.Global.TEXT_CLASSIFIER_CONSTANTS,
+ GlobalSettingsProto.TEXT_CLASSIFIER_CONSTANTS);
+ dumpSetting(s, p,
Settings.Global.WINDOW_ANIMATION_SCALE,
GlobalSettingsProto.WINDOW_ANIMATION_SCALE);
dumpSetting(s, p,
@@ -765,6 +870,16 @@
Settings.Global.WAIT_FOR_DEBUGGER,
GlobalSettingsProto.WAIT_FOR_DEBUGGER);
dumpSetting(s, p,
+ Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
+ GlobalSettingsProto.ENABLE_GPU_DEBUG_LAYERS);
+ dumpSetting(s, p,
+ Settings.Global.GPU_DEBUG_APP,
+ GlobalSettingsProto.GPU_DEBUG_APP);
+ dumpSetting(s, p,
+ Settings.Global.GPU_DEBUG_LAYERS,
+ GlobalSettingsProto.GPU_DEBUG_LAYERS);
+ // Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
+ dumpSetting(s, p,
Settings.Global.LOW_POWER_MODE,
GlobalSettingsProto.LOW_POWER_MODE);
dumpSetting(s, p,
@@ -819,6 +934,18 @@
Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
GlobalSettingsProto.INTENT_FIREWALL_UPDATE_METADATA_URL);
dumpSetting(s, p,
+ Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
+ GlobalSettingsProto.LANG_ID_UPDATE_CONTENT_URL);
+ dumpSetting(s, p,
+ Settings.Global.LANG_ID_UPDATE_METADATA_URL,
+ GlobalSettingsProto.LANG_ID_UPDATE_METADATA_URL);
+ dumpSetting(s, p,
+ Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
+ GlobalSettingsProto.SMART_SELECTION_UPDATE_CONTENT_URL);
+ dumpSetting(s, p,
+ Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
+ GlobalSettingsProto.SMART_SELECTION_UPDATE_METADATA_URL);
+ dumpSetting(s, p,
Settings.Global.SELINUX_STATUS,
GlobalSettingsProto.SELINUX_STATUS);
dumpSetting(s, p,
@@ -882,11 +1009,8 @@
Settings.Global.ENABLE_EPHEMERAL_FEATURE,
GlobalSettingsProto.ENABLE_EPHEMERAL_FEATURE);
dumpSetting(s, p,
- Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
- GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
- dumpSetting(s, p,
- Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
- GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
+ Settings.Global.INSTANT_APP_DEXOPT_ENABLED,
+ GlobalSettingsProto.INSTANT_APP_DEXOPT_ENABLED);
dumpSetting(s, p,
Settings.Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
GlobalSettingsProto.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
@@ -894,6 +1018,12 @@
Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
GlobalSettingsProto.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
dumpSetting(s, p,
+ Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+ GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
+ dumpSetting(s, p,
+ Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
+ GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
+ dumpSetting(s, p,
Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
GlobalSettingsProto.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD);
dumpSetting(s, p,
@@ -909,12 +1039,30 @@
Settings.Global.DEVICE_DEMO_MODE,
GlobalSettingsProto.DEVICE_DEMO_MODE);
dumpSetting(s, p,
+ Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
+ GlobalSettingsProto.NETWORK_ACCESS_TIMEOUT_MS);
+ dumpSetting(s, p,
Settings.Global.DATABASE_DOWNGRADE_REASON,
GlobalSettingsProto.DATABASE_DOWNGRADE_REASON);
dumpSetting(s, p,
+ Settings.Global.DATABASE_CREATION_BUILDID,
+ GlobalSettingsProto.DATABASE_CREATION_BUILDID);
+ dumpSetting(s, p,
Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
GlobalSettingsProto.CONTACTS_DATABASE_WAL_ENABLED);
dumpSetting(s, p,
+ Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
+ GlobalSettingsProto.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED);
+ dumpSetting(s, p,
+ Settings.Global.BACKUP_REFACTORED_SERVICE_DISABLED,
+ GlobalSettingsProto.BACKUP_REFACTORED_SERVICE_DISABLED);
+ dumpSetting(s, p,
+ Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
+ GlobalSettingsProto.EUICC_FACTORY_RESET_TIMEOUT_MILLIS);
+ dumpSetting(s, p,
+ Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD,
+ GlobalSettingsProto.STORAGE_SETTINGS_CLOBBER_THRESHOLD);
+ dumpSetting(s, p,
Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
GlobalSettingsProto.MULTI_SIM_VOICE_CALL_SUBSCRIPTION);
dumpSetting(s, p,
@@ -932,6 +1080,7 @@
dumpSetting(s, p,
Settings.Global.NEW_CONTACT_AGGREGATOR,
GlobalSettingsProto.NEW_CONTACT_AGGREGATOR);
+ // Settings.Global.CONTACT_METADATA_SYNC intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Global.CONTACT_METADATA_SYNC_ENABLED,
GlobalSettingsProto.CONTACT_METADATA_SYNC_ENABLED);
@@ -942,8 +1091,31 @@
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
GlobalSettingsProto.MAX_NOTIFICATION_ENQUEUE_RATE);
dumpSetting(s, p,
+ Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
+ GlobalSettingsProto.SHOW_NOTIFICATION_CHANNEL_WARNINGS);
+ dumpSetting(s, p,
Settings.Global.CELL_ON,
GlobalSettingsProto.CELL_ON);
+ dumpSetting(s, p,
+ Settings.Global.SHOW_TEMPERATURE_WARNING,
+ GlobalSettingsProto.SHOW_TEMPERATURE_WARNING);
+ dumpSetting(s, p,
+ Settings.Global.WARNING_TEMPERATURE,
+ GlobalSettingsProto.WARNING_TEMPERATURE);
+ dumpSetting(s, p,
+ Settings.Global.ENABLE_DISKSTATS_LOGGING,
+ GlobalSettingsProto.ENABLE_DISKSTATS_LOGGING);
+ dumpSetting(s, p,
+ Settings.Global.ENABLE_CACHE_QUOTA_CALCULATION,
+ GlobalSettingsProto.ENABLE_CACHE_QUOTA_CALCULATION);
+ dumpSetting(s, p,
+ Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
+ GlobalSettingsProto.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE);
+ // The list of snooze options for notifications. This is encoded as a key=value list,
+ // separated by commas.
+ dumpSetting(s, p,
+ Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+ GlobalSettingsProto.NOTIFICATION_SNOOZE_OPTIONS);
}
/** Dump a single {@link SettingsState.Setting} to a proto buf */
@@ -966,9 +1138,19 @@
static void dumpProtoSecureSettingsLocked(
@NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+ s.dumpHistoricalOperations(p, SecureSettingsProto.HISTORICAL_OPERATIONS);
+
+ // This uses the same order as in Settings.Secure.
+
+ // Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED intentionally excluded since it's deprecated.
+ // Settings.Secure.BUGREPORT_IN_POWER_MENU intentionally excluded since it's deprecated.
+ // Settings.Secure.ADB_ENABLED intentionally excluded since it's deprecated.
+ // Settings.Secure.ALLOW_MOCK_LOCATION intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.ANDROID_ID,
SecureSettingsProto.ANDROID_ID);
+ // Settings.Secure.BLUETOOTH_ON intentionally excluded since it's deprecated.
+ // Settings.Secure.DATA_ROAMING intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.DEFAULT_INPUT_METHOD,
SecureSettingsProto.DEFAULT_INPUT_METHOD);
@@ -987,9 +1169,16 @@
dumpSetting(s, p,
Settings.Secure.AUTOFILL_SERVICE,
SecureSettingsProto.AUTOFILL_SERVICE);
+ // Settings.Secure.DEVICE_PROVISIONED intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.USER_SETUP_COMPLETE,
SecureSettingsProto.USER_SETUP_COMPLETE);
+ // Whether the current user has been set up via setup wizard (0 = false, 1 = true). This
+ // value differs from USER_SETUP_COMPLETE in that it can be reset back to 0 in case
+ // SetupWizard has been re-enabled on TV devices.
+ dumpSetting(s, p,
+ Settings.Secure.TV_USER_SETUP_COMPLETE,
+ SecureSettingsProto.TV_USER_SETUP_COMPLETE);
dumpSetting(s, p,
Settings.Secure.COMPLETED_CATEGORY_PREFIX,
SecureSettingsProto.COMPLETED_CATEGORY_PREFIX);
@@ -1002,6 +1191,7 @@
dumpSetting(s, p,
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
SecureSettingsProto.SHOW_IME_WITH_HARD_KEYBOARD);
+ // Settings.Secure.HTTP_PROXY intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.ALWAYS_ON_VPN_APP,
SecureSettingsProto.ALWAYS_ON_VPN_APP);
@@ -1012,17 +1202,33 @@
Settings.Secure.INSTALL_NON_MARKET_APPS,
SecureSettingsProto.INSTALL_NON_MARKET_APPS);
dumpSetting(s, p,
+ Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED,
+ SecureSettingsProto.UNKNOWN_SOURCES_DEFAULT_REVERSED);
+ // Settings.Secure.LOCATION_PROVIDERS_ALLOWED intentionally excluded since it's deprecated.
+ dumpSetting(s, p,
Settings.Secure.LOCATION_MODE,
SecureSettingsProto.LOCATION_MODE);
dumpSetting(s, p,
Settings.Secure.LOCATION_PREVIOUS_MODE,
SecureSettingsProto.LOCATION_PREVIOUS_MODE);
+ // Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
SecureSettingsProto.LOCK_TO_APP_EXIT_LOCKED);
+ // Settings.Secure.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_PATTERN_VISIBLE intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_PATTERN_TACTICLE_FEEDBACK_ENABLED intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
SecureSettingsProto.LOCK_SCREEN_LOCK_AFTER_TIMEOUT);
+ // Settings.Secure.LOCK_SCREEN_OWNER_INFO intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_SCREEN_STICKY_APPWIDGET intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED intentionally excluded since it's deprecated.
+ dumpSetting(s, p,
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ SecureSettingsProto.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
dumpSetting(s, p,
Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
SecureSettingsProto.LOCK_SCREEN_ALLOW_REMOTE_INPUT);
@@ -1032,6 +1238,8 @@
dumpSetting(s, p,
Settings.Secure.TRUST_AGENTS_INITIALIZED,
SecureSettingsProto.TRUST_AGENTS_INITIALIZED);
+ // Settings.Secure.LOGGING_ID intentionally excluded since it's deprecated.
+ // Settings.Secure.NETWORK_PREFERENCE intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.PARENTAL_CONTROL_ENABLED,
SecureSettingsProto.PARENTAL_CONTROL_ENABLED);
@@ -1044,10 +1252,27 @@
dumpSetting(s, p,
Settings.Secure.SETTINGS_CLASSNAME,
SecureSettingsProto.SETTINGS_CLASSNAME);
+ // Settings.Secure.USB_MASS_STORAGE_ENABLED intentionally excluded since it's deprecated.
+ // Settings.Secure.USE_GOOGLE_MAIL intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_ENABLED,
SecureSettingsProto.ACCESSIBILITY_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED,
+ SecureSettingsProto.ACCESSIBILITY_SHORTCUT_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
+ SecureSettingsProto.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+ SecureSettingsProto.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ SecureSettingsProto.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ SecureSettingsProto.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
+ dumpSetting(s, p,
Settings.Secure.TOUCH_EXPLORATION_ENABLED,
SecureSettingsProto.TOUCH_EXPLORATION_ENABLED);
dumpSetting(s, p,
@@ -1066,9 +1291,15 @@
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+ SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+ dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
+ SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE);
+ dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
SecureSettingsProto.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
dumpSetting(s, p,
@@ -1134,6 +1365,7 @@
dumpSetting(s, p,
Settings.Secure.DISPLAY_DENSITY_FORCED,
SecureSettingsProto.DISPLAY_DENSITY_FORCED);
+ // Settings.Secure.TTS_USE_DEFAULTS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.TTS_DEFAULT_RATE,
SecureSettingsProto.TTS_DEFAULT_RATE);
@@ -1143,15 +1375,37 @@
dumpSetting(s, p,
Settings.Secure.TTS_DEFAULT_SYNTH,
SecureSettingsProto.TTS_DEFAULT_SYNTH);
+ // Settings.Secure.TTS_DEFAULT_LANG intentionally excluded since it's deprecated.
+ // Settings.Secure.TTS_DEFAULT_COUNTRY intentionally excluded since it's deprecated.
+ // Settings.Secure.TTS_DEFAULT_VARIANT intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.TTS_DEFAULT_LOCALE,
SecureSettingsProto.TTS_DEFAULT_LOCALE);
dumpSetting(s, p,
Settings.Secure.TTS_ENABLED_PLUGINS,
SecureSettingsProto.TTS_ENABLED_PLUGINS);
+ // Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_NUM_OPEN_NETWORKS_KEPT intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_ON intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_AP_COUNT intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_MAX_AP_CHECKS intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_ON intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_WATCH_LIST intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_PING_COUNT intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS);
+ // Settings.Secure.BACKGROUND_DATA intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
SecureSettingsProto.ALLOWED_GEOLOCATION_ORIGINS);
@@ -1179,6 +1433,7 @@
dumpSetting(s, p,
Settings.Secure.LAST_SETUP_SHOWN,
SecureSettingsProto.LAST_SETUP_SHOWN);
+ // Settings.Secure.WIFI_IDLE_MS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY,
SecureSettingsProto.SEARCH_GLOBAL_SEARCH_ACTIVITY);
@@ -1285,6 +1540,9 @@
Settings.Secure.DOZE_PULSE_ON_PICK_UP,
SecureSettingsProto.DOZE_PULSE_ON_PICK_UP);
dumpSetting(s, p,
+ Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
+ SecureSettingsProto.DOZE_PULSE_ON_LONG_PRESS);
+ dumpSetting(s, p,
Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
SecureSettingsProto.DOZE_PULSE_ON_DOUBLE_TAP);
dumpSetting(s, p,
@@ -1351,6 +1609,9 @@
Settings.Secure.PAYMENT_SERVICE_SEARCH_URI,
SecureSettingsProto.PAYMENT_SERVICE_SEARCH_URI);
dumpSetting(s, p,
+ Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
+ SecureSettingsProto.AUTOFILL_SERVICE_SEARCH_URI);
+ dumpSetting(s, p,
Settings.Secure.SKIP_FIRST_USE_HINTS,
SecureSettingsProto.SKIP_FIRST_USE_HINTS);
dumpSetting(s, p,
@@ -1387,18 +1648,42 @@
Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
SecureSettingsProto.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED,
+ SecureSettingsProto.CAMERA_LIFT_TRIGGER_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_GESTURE_ENABLED,
+ SecureSettingsProto.ASSIST_GESTURE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
+ SecureSettingsProto.ASSIST_GESTURE_SENSITIVITY);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
+ SecureSettingsProto.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
+ SecureSettingsProto.ASSIST_GESTURE_WAKE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
+ SecureSettingsProto.ASSIST_GESTURE_SETUP_COMPLETE);
+ dumpSetting(s, p,
Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
SecureSettingsProto.NIGHT_DISPLAY_ACTIVATED);
dumpSetting(s, p,
Settings.Secure.NIGHT_DISPLAY_AUTO_MODE,
SecureSettingsProto.NIGHT_DISPLAY_AUTO_MODE);
dumpSetting(s, p,
+ Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
+ SecureSettingsProto.NIGHT_DISPLAY_COLOR_TEMPERATURE);
+ dumpSetting(s, p,
Settings.Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_START_TIME);
dumpSetting(s, p,
Settings.Secure.NIGHT_DISPLAY_CUSTOM_END_TIME,
SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_END_TIME);
dumpSetting(s, p,
+ Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ SecureSettingsProto.NIGHT_DISPLAY_LAST_ACTIVATED_TIME);
+ dumpSetting(s, p,
Settings.Secure.ENABLED_VR_LISTENERS,
SecureSettingsProto.ENABLED_VR_LISTENERS);
dumpSetting(s, p,
@@ -1423,6 +1708,9 @@
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_LAST_RUN);
dumpSetting(s, p,
+ Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY,
+ SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY);
+ dumpSetting(s, p,
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
SecureSettingsProto.SYSTEM_NAVIGATION_KEYS_ENABLED);
dumpSetting(s, p,
@@ -1438,33 +1726,76 @@
Settings.Secure.DEVICE_PAIRED,
SecureSettingsProto.DEVICE_PAIRED);
dumpSetting(s, p,
+ Settings.Secure.PACKAGE_VERIFIER_STATE,
+ SecureSettingsProto.PACKAGE_VERIFIER_STATE);
+ dumpSetting(s, p,
+ Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
+ SecureSettingsProto.CMAS_ADDITIONAL_BROADCAST_PKG);
+ dumpSetting(s, p,
Settings.Secure.NOTIFICATION_BADGING,
SecureSettingsProto.NOTIFICATION_BADGING);
dumpSetting(s, p,
+ Settings.Secure.QS_AUTO_ADDED_TILES,
+ SecureSettingsProto.QS_AUTO_ADDED_TILES);
+ dumpSetting(s, p,
+ Settings.Secure.LOCKDOWN_IN_POWER_MENU,
+ SecureSettingsProto.LOCKDOWN_IN_POWER_MENU);
+ dumpSetting(s, p,
Settings.Secure.BACKUP_MANAGER_CONSTANTS,
SecureSettingsProto.BACKUP_MANAGER_CONSTANTS);
}
private static void dumpProtoSystemSettingsLocked(
@NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+ s.dumpHistoricalOperations(p, SystemSettingsProto.HISTORICAL_OPERATIONS);
+
+ // This uses the same order as in Settings.System.
+
+ // Settings.System.STAY_ON_WHILE_PLUGGED_IN intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.END_BUTTON_BEHAVIOR,
SystemSettingsProto.END_BUTTON_BEHAVIOR);
dumpSetting(s, p,
Settings.System.ADVANCED_SETTINGS,
SystemSettingsProto.ADVANCED_SETTINGS);
+ // Settings.System.AIRPLANE_MODE_ON intentionally excluded since it's deprecated.
+ // Settings.System.RADIO_BLUETOOTH intentionally excluded since it's deprecated.
+ // Settings.System.RADIO_WIFI intentionally excluded since it's deprecated.
+ // Settings.System.RADIO_WIMAX intentionally excluded since it's deprecated.
+ // Settings.System.RADIO_CELL intentionally excluded since it's deprecated.
+ // Settings.System.RADIO_NFC intentionally excluded since it's deprecated.
+ // Settings.System.AIRPLANE_MODE_RADIOS intentionally excluded since it's deprecated.
+ // Settings.System.AIRPLANE_MODE_TOGGLABLE_RADIOS intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_SLEEP_POLICY intentionally excluded since it's deprecated.
+ // Settings.System.MODE_RINGER intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_USE_STATIC_IP intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_STATIC_IP intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_STATIC_GATEWAY intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_STATIC_NETMASK intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_STATIC_DNS1 intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_STATIC_DNS2 intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.BLUETOOTH_DISCOVERABILITY,
SystemSettingsProto.BLUETOOTH_DISCOVERABILITY);
dumpSetting(s, p,
Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
SystemSettingsProto.BLUETOOTH_DISCOVERABILITY_TIMEOUT);
+ // Settings.System.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
+ // Settings.System.LOCK_PATTERN_VISIBLE intentionally excluded since it's deprecated.
+ // Settings.System.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED intentionally excluded since it's deprecated.
+ // Settings.System.NEXT_ALARM_FORMATTED intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.FONT_SCALE,
SystemSettingsProto.FONT_SCALE);
dumpSetting(s, p,
Settings.System.SYSTEM_LOCALES,
SystemSettingsProto.SYSTEM_LOCALES);
+ // Settings.System.DEBUG_APP intentionally excluded since it's deprecated.
+ // Settings.System.WAIT_FOR_DEBUGGER intentionally excluded since it's deprecated.
+ // Settings.System.DIM_SCREEN intentionally excluded since it's deprecated.
+ dumpSetting(s, p,
+ Settings.System.DISPLAY_COLOR_MODE,
+ SystemSettingsProto.DISPLAY_COLOR_MODE);
dumpSetting(s, p,
Settings.System.SCREEN_OFF_TIMEOUT,
SystemSettingsProto.SCREEN_OFF_TIMEOUT);
@@ -1480,6 +1811,8 @@
dumpSetting(s, p,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
SystemSettingsProto.SCREEN_AUTO_BRIGHTNESS_ADJ);
+ // Settings.System.SHOW_PROCESSES intentionally excluded since it's deprecated.
+ // Settings.System.ALWAYS_FINISH_ACTIVITIES intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
SystemSettingsProto.MODE_RINGER_STREAMS_AFFECTED);
@@ -1514,11 +1847,15 @@
Settings.System.VOLUME_BLUETOOTH_SCO,
SystemSettingsProto.VOLUME_BLUETOOTH_SCO);
dumpSetting(s, p,
+ Settings.System.VOLUME_ACCESSIBILITY,
+ SystemSettingsProto.VOLUME_ACCESSIBILITY);
+ dumpSetting(s, p,
Settings.System.VOLUME_MASTER,
SystemSettingsProto.VOLUME_MASTER);
dumpSetting(s, p,
Settings.System.MASTER_MONO,
SystemSettingsProto.MASTER_MONO);
+ // Settings.System.NOTIFICATIONS_USE_RING_VOLUME intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.VIBRATE_IN_SILENT,
SystemSettingsProto.VIBRATE_IN_SILENT);
@@ -1561,6 +1898,9 @@
dumpSetting(s, p,
Settings.System.SHOW_GTALK_SERVICE_STATUS,
SystemSettingsProto.SHOW_GTALK_SERVICE_STATUS);
+ // Settings.System.WALLPAPER_ACTIVITY intentionally excluded since it's deprecated.
+ // Settings.System.AUTO_TIME intentionally excluded since it's deprecated.
+ // Settings.System.AUTO_TIME_ZONE intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.TIME_12_24,
SystemSettingsProto.TIME_12_24);
@@ -1570,6 +1910,9 @@
dumpSetting(s, p,
Settings.System.SETUP_WIZARD_HAS_RUN,
SystemSettingsProto.SETUP_WIZARD_HAS_RUN);
+ // Settings.System.WINDOW_ANIMATION_SCALE intentionally excluded since it's deprecated.
+ // Settings.System.TRANSITION_ANIMATION_SCALE intentionally excluded since it's deprecated.
+ // Settings.System.ANIMATOR_ANIMATION_SCALE intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.ACCELEROMETER_ROTATION,
SystemSettingsProto.ACCELEROMETER_ROTATION);
@@ -1600,6 +1943,7 @@
dumpSetting(s, p,
Settings.System.HAPTIC_FEEDBACK_ENABLED,
SystemSettingsProto.HAPTIC_FEEDBACK_ENABLED);
+ // Settings.System.SHOW_WEB_SUGGESTIONS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.NOTIFICATION_LIGHT_PULSE,
SystemSettingsProto.NOTIFICATION_LIGHT_PULSE);
@@ -1612,12 +1956,21 @@
dumpSetting(s, p,
Settings.System.WINDOW_ORIENTATION_LISTENER_LOG,
SystemSettingsProto.WINDOW_ORIENTATION_LISTENER_LOG);
+ // Settings.System.POWER_SOUNDS_ENABLED intentionally excluded since it's deprecated.
+ // Settings.System.DOCK_SOUNDS_ENABLED intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
SystemSettingsProto.LOCKSCREEN_SOUNDS_ENABLED);
dumpSetting(s, p,
Settings.System.LOCKSCREEN_DISABLED,
SystemSettingsProto.LOCKSCREEN_DISABLED);
+ // Settings.System.LOW_BATTERY_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.DESK_DOCK_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.DESK_UNDOCK_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.CAR_DOCK_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.CAR_UNDOCK_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.LOCK_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.UNLOCK_SOUND intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.SIP_RECEIVE_CALLS,
SystemSettingsProto.SIP_RECEIVE_CALLS);
@@ -1630,6 +1983,7 @@
dumpSetting(s, p,
Settings.System.SIP_ADDRESS_ONLY,
SystemSettingsProto.SIP_ADDRESS_ONLY);
+ // Settings.System.SIP_ASK_ME_EACH_TIME intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.POINTER_SPEED,
SystemSettingsProto.POINTER_SPEED);
@@ -1640,7 +1994,12 @@
Settings.System.EGG_MODE,
SystemSettingsProto.EGG_MODE);
dumpSetting(s, p,
+ Settings.System.SHOW_BATTERY_PERCENT,
+ SystemSettingsProto.SHOW_BATTERY_PERCENT);
+ dumpSetting(s, p,
Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
+ // The rest of the settings were moved to Settings.Secure, and are thus excluded here since
+ // they're deprecated from Settings.System.
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 36f9b84..258c96c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.backup.BackupManager;
@@ -657,7 +658,6 @@
synchronized (mLock) {
SettingsProtoDumpUtil.dumpProtoLocked(mSettingsRegistry, proto);
-
}
proto.flush();
@@ -2284,6 +2284,7 @@
return users;
}
+ @Nullable
public SettingsState getSettingsLocked(int type, int userId) {
final int key = makeKey(type, userId);
return peekSettingsStateLocked(key);
@@ -2578,6 +2579,7 @@
ssaidSettings.deleteSettingLocked(Integer.toString(uid));
}
+ @Nullable
private SettingsState peekSettingsStateLocked(int key) {
SettingsState settingsState = mSettingsStates.get(key);
if (settingsState != null) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4151ada..f901bca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -434,8 +434,9 @@
* Dump historical operations as a proto buf.
*
* @param proto The proto buf stream to dump to
+ * @param fieldId The repeated field ID to use to save an operation to.
*/
- void dumpProtoHistoricalOperations(@NonNull ProtoOutputStream proto) {
+ void dumpHistoricalOperations(@NonNull ProtoOutputStream proto, long fieldId) {
synchronized (mLock) {
if (mHistoricalOperations == null) {
return;
@@ -448,7 +449,8 @@
index = operationCount + index;
}
HistoricalOperation operation = mHistoricalOperations.get(index);
- long settingsOperationToken = proto.start(GlobalSettingsProto.HISTORICAL_OP);
+
+ final long token = proto.start(fieldId);
proto.write(SettingsOperationProto.TIMESTAMP, operation.mTimestamp);
proto.write(SettingsOperationProto.OPERATION, operation.mOperation);
if (operation.mSetting != null) {
@@ -457,7 +459,7 @@
// add is what the current data is).
proto.write(SettingsOperationProto.SETTING, operation.mSetting.getName());
}
- proto.end(settingsOperationToken);
+ proto.end(token);
}
}
}
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
index 3f3dd2a..e4dcbee 100644
--- a/packages/Shell/res/values-sw/strings.xml
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -24,10 +24,10 @@
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Tafadhali subiri…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Tutatuma ripoti ya hitilafu kwenye simu baada ya muda mfupi"</string>
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Chagua kushiriki ripoti ya hitilafu"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Gonga ili ushiriki ripoti yako ya hitilafu"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Gusa ili ushiriki ripoti yako ya hitilafu"</string>
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Chagua kushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Gonga ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Gonga ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Gusa ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Gusa ili ushiriki ripoti yako ya hitilafu bila picha ya skrini au usubiri picha ya skrini itayarishwe"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Ripoti za hitilafu zinajumuisha data kutoka faili za kumbukumbu mbalimbali zilizo kwenye mfumo, ambazo huenda zinajumuisha data ambayo unachukulia kuwa nyeti (kama vile matumizi ya programu na maelezo kuhusu data ilipo). Shiriki ripoti za hitilafu na watu na programu unazoamini pekee."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Usionyeshe tena"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Ripoti za hitilafu"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 04ce752..839fb87 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -117,8 +117,8 @@
<item quantity="other">رمز PUK لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
<item quantity="one">رمز PUK لشريحة SIM غير صالح، ويتبقى لديك محاولة واحدة (<xliff:g id="NUMBER_0">%d</xliff:g>)، تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
</plurals>
- <string name="kg_password_pin_failed" msgid="8769990811451236223">"أخفقت عملية \"رقم التعريف الشخصي\" لشريحة SIM"</string>
- <string name="kg_password_puk_failed" msgid="1331621440873439974">"أخفقت عملية PUK لشريحة SIM"</string>
+ <string name="kg_password_pin_failed" msgid="8769990811451236223">"تعذّر إتمام عملية \"رقم التعريف الشخصي\" لشريحة SIM"</string>
+ <string name="kg_password_puk_failed" msgid="1331621440873439974">"تعذّر إتمام عملية PUK لشريحة SIM"</string>
<string name="kg_pin_accepted" msgid="7637293533973802143">"تم قبول الرمز"</string>
<string name="keyguard_carrier_default" msgid="4274828292998453695">"لا تتوفر خدمة."</string>
<string name="accessibility_ime_switch_button" msgid="2695096475319405612">"تبديل أسلوب الإدخال"</string>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index b33c523..359271d 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Увядзіце пароль для разблакіравання"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Каб разблакіраваць, увядзіце PIN-код"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Няправільны PIN-код."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Несапраўдная картка."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Зараджаны"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Ідзе зарадка"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Зараджаецца хутка"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index d0d905b..e511940 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -29,6 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Type password to unlock"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Type PIN to unlock"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Charging"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Charging rapidly"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index d0d905b..e511940 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -29,6 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Type password to unlock"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Type PIN to unlock"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Charging"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Charging rapidly"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index d0d905b..e511940 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -29,6 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Type password to unlock"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Type PIN to unlock"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Charging"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Charging rapidly"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index d0d905b..e511940 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -29,6 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Type password to unlock"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Type PIN to unlock"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Charging"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Charging rapidly"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index b8689ee..dc2a441 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Poista lukitus antamalla salasana."</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Poista lukitus antamalla PIN-koodi."</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Väärä PIN-koodi"</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Virheellinen kortti"</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Ladattu"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Ladataan"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Nopea lataus käynnissä"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index e70dca3..8e614e0 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Entrez le mot de passe pour déverrouiller le clavier."</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Entrez le NIP pour déverrouiller le clavier."</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"NIP erroné."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cette carte n\'est pas valide."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Chargé"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Pile en cours de charge"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Charge rapide"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 4fe50ed..7be5f41 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Escribe o contrasinal para desbloquear"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Escribe o PIN para desbloquear"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorrecto"</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"A tarxeta non é válida."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Cargada"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Cargando"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Cargando rapidamente"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 72d9b75..48aa75b 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"અનલૉક કરવા માટે પાસવર્ડ લખો"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"અનલૉક કરવા માટે પિન લખો"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ખોટો પિન કોડ."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"અમાન્ય કાર્ડ."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"ચાર્જ થઈ ગયું"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"ચાર્જ થઈ રહ્યું છે"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"ઝડપથી ચાર્જ થઈ રહ્યું છે"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index 5b94468..093c5cb 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಪಾಸ್ವರ್ಡ್ ಟೈಪ್ ಮಾಡಿ"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಪಿನ್ ಟೈಪ್ ಮಾಡಿ"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ತಪ್ಪಾದ ಪಿನ್ ಕೋಡ್."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ಅಮಾನ್ಯ ಕಾರ್ಡ್."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"ವೇಗವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index b00584f..0b21231 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"അൺലോക്കുചെയ്യുന്നതിന് പാസ്വേഡ് ടൈപ്പുചെയ്യുക"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"അൺലോക്കുചെയ്യുന്നതിന് പിൻ ടൈപ്പുചെയ്യുക"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"പിൻ കോഡ് തെറ്റാണ്."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"അസാധുവായ കാർഡ്."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"ചാർജായി"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"ചാർജ്ജുചെയ്യുന്നു"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"വേഗത്തിൽ ചാർജുചെയ്യുന്നു"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index bedcf17..0bab97d 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਪਾਸਵਰਡ ਟਾਈਪ ਕਰੋ"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਪਿੰਨ ਟਾਈਪ ਕਰੋ"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ਗਲਤ ਪਿੰਨ ਕੋਡ।"</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ਅਵੈਧ ਕਾਰਡ।"</string>
<string name="keyguard_charged" msgid="2222329688813033109">"ਚਾਰਜ ਹੋ ਗਿਆ"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 60c6f49..362ffcf 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Digite a senha para desbloquear"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Insira o PIN para desbloquear"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorreto."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cartão inválido."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Carregada"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Carregando"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Carregando rapidamente"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 60c6f49..362ffcf 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Digite a senha para desbloquear"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Insira o PIN para desbloquear"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorreto."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cartão inválido."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Carregada"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Carregando"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Carregando rapidamente"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index 743bbc1..d0dc17e 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Introduceți parola pentru a debloca"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Introduceți codul PIN pentru a debloca"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Cod PIN incorect."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Card nevalid"</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Încărcată"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Se încarcă"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Se încarcă rapid"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index 7ee0902..ff75819 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Andika nenosiri ili ufungue"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Andika PIN ili ufungue"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nambari ya PIN si sahihi."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Kadi si Sahihi."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Betri imejaa"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Inachaji"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Inachaji kwa kasi"</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index bbcc57e..73f7094 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"అన్లాక్ చేయడానికి పాస్వర్డ్ను టైప్ చేయండి"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"అన్లాక్ చేయడానికి పిన్ టైప్ చేయండి"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"పిన్ కోడ్ తప్పు."</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"చెల్లని కార్డ్."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"ఛార్జ్ చేయబడింది"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"ఛార్జ్ అవుతోంది"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"వేగంగా ఛార్జ్ అవుతోంది"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index 4f09c98..3bb59f0 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"غیر مقفل کرنے کیلئے پاس ورڈ ٹائپ کریں"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"غیر مقفل کرنے کیلئے PIN ٹائپ کریں"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"غلط PIN کوڈ۔"</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"غلط کارڈ۔"</string>
<string name="keyguard_charged" msgid="2222329688813033109">"چارج ہوگئی"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"چارج ہو رہا ہے"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"تیزی سے چارج ہو رہا ہے"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index 30737cc..d523c20 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -29,8 +29,7 @@
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"输入密码即可解锁"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"输入 PIN 码即可解锁"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN 码有误。"</string>
- <!-- no translation found for keyguard_sim_error_message_short (592109500618448312) -->
- <skip />
+ <string name="keyguard_sim_error_message_short" msgid="592109500618448312">"SIM 卡无效。"</string>
<string name="keyguard_charged" msgid="2222329688813033109">"已充满电"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"正在充电"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"正在快速充电"</string>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 3d9a2dc..fac254a 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -13,30 +13,36 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout
+<com.android.systemui.HardwareUiLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/volume_dialog"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/volume_dialog_margin_bottom"
- android:background="@drawable/volume_dialog_background"
- android:paddingTop="@dimen/volume_dialog_padding_top"
+ android:layout_height="match_parent"
android:theme="@style/qs_theme"
- android:translationZ="4dp" >
-
- <LinearLayout
- android:id="@+id/volume_dialog_content"
- android:layout_width="match_parent"
+ android:clipChildren="false" >
+ <RelativeLayout
+ android:id="@+id/volume_dialog"
+ android:layout_width="@dimen/volume_dialog_panel_width"
android:layout_height="wrap_content"
- android:orientation="vertical" >
+ android:layout_gravity="center_vertical|end"
+ android:paddingTop="@dimen/volume_row_padding_bottom"
+ android:layout_margin="12dp"
+ android:background="?android:attr/actionBarItemBackground"
+ android:translationZ="8dp" >
<LinearLayout
- android:id="@+id/volume_dialog_rows"
+ android:id="@+id/volume_dialog_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
- <!-- volume rows added and removed here! :-) -->
- </LinearLayout>
- </LinearLayout>
-</RelativeLayout>
+ <LinearLayout
+ android:id="@+id/volume_dialog_rows"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+ <!-- volume rows added and removed here! :-) -->
+ </LinearLayout>
+ </LinearLayout>
+
+ </RelativeLayout>
+</com.android.systemui.HardwareUiLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index 7328d05..bf76e78 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -19,8 +19,8 @@
android:layout_height="@dimen/volume_row_height"
android:clipChildren="false"
android:clipToPadding="false"
- android:orientation="vertical"
- android:paddingBottom="@dimen/volume_row_padding_bottom" >
+ android:theme="@style/qs_theme"
+ android:orientation="vertical" >
<TextView
android:id="@+id/volume_row_header"
@@ -28,7 +28,8 @@
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
- android:textAppearance="@style/TextAppearance.Volume.Header"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall"
android:paddingStart="@dimen/volume_row_header_padding_start" />
<LinearLayout
@@ -53,4 +54,9 @@
android:paddingStart="@dimen/volume_row_slider_padding_start"/>
</LinearLayout>
+ <Space
+ android:id="@+id/spacer"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/volume_row_padding_bottom"/>
+
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-ar/config.xml b/packages/SystemUI/res/values-ar/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-ar/config.xml
+++ b/packages/SystemUI/res/values-ar/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index b5ac31a..d642118 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -55,15 +55,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"تم إنشاء الاتصال بالإنترنت عن طريق البلوتوث."</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"إعداد أسلوب الإدخال"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"لوحة مفاتيح فعلية"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"هل تريد السماح للتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى جهاز USB؟"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"هل تريد السماح للتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى ملحق USB؟"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"هل تريد فتح <xliff:g id="ACTIVITY">%1$s</xliff:g> عند توصيل جهاز USB هذا؟"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"هل تريد فتح <xliff:g id="ACTIVITY">%1$s</xliff:g> عند توصيل ملحق USB هذا؟"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"هل تريد السماح لتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى <xliff:g id="USB_DEVICE">%2$s</xliff:g>؟"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"هل تريد السماح لتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>؟"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"هل تريد فتح <xliff:g id="APPLICATION">%1$s</xliff:g> للتعامل مع <xliff:g id="USB_DEVICE">%2$s</xliff:g>؟"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"هل تريد فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> للتعامل مع <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>؟"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"لا يعمل أي تطبيق مثبت مع ملحق UEB هذا. مزيد من المعلومات عن هذا الملحق على <xliff:g id="URL">%1$s</xliff:g>."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"ملحق USB"</string>
<string name="label_view" msgid="6304565553218192990">"عرض"</string>
- <string name="always_use_device" msgid="1450287437017315906">"الاستخدام بشكل افتراضي لجهاز USB هذا"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"الاستخدام بشكل افتراضي لملحق USB هذا"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> دائمًا عند توصيل <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> دائمًا عند توصيل <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"هل تريد السماح بتصحيح أخطاء USB؟"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"الملف المرجعي الرئيسي لـ RSA في هذا الكمبيوتر هو:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"السماح دائمًا من هذا الكمبيوتر"</string>
diff --git a/packages/SystemUI/res/values-be/config.xml b/packages/SystemUI/res/values-be/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-be/config.xml
+++ b/packages/SystemUI/res/values-be/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 6de7852..e2ee4ea 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -53,15 +53,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Прывязаныя праз Bluetooth"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Налада метадаў уводу"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Фізічная клавіятура"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Дазволіць праграме <xliff:g id="APPLICATION">%1$s</xliff:g> атрымлiваць доступ да прылады USB?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Дазволіць праграме <xliff:g id="APPLICATION">%1$s</xliff:g> доступ да USB-аксесуара?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Адкрыць <xliff:g id="ACTIVITY">%1$s</xliff:g>, калі гэтая USB-прылада падлучаная?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Адкрыць <xliff:g id="ACTIVITY">%1$s</xliff:g>, калі гэтая USB-прылада падлучаная?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Няма ўсталяв. прыкл. для працы з гэтай прыл. USB. Больш падраб. пра гэтую прыл.: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-прылада"</string>
<string name="label_view" msgid="6304565553218192990">"Прагляд"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Выкарыстоўваць налады па змаўчанні для дадзенай USB-прылады"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Выкарыстоўваць налады па змаўчанні для дадзенай USB-прылады"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"Дазволіць адладку USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"Адбiтак ключа RSA на гэтым камп\'ютары:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Заўсёды дазваляць з гэтага камп\'ютара"</string>
diff --git a/packages/SystemUI/res/values-bn/config.xml b/packages/SystemUI/res/values-bn/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-bn/config.xml
+++ b/packages/SystemUI/res/values-bn/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index ab1b0a1..58bddb0 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"ব্লুটুথ টিথার করা হয়েছে"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ইনপুট পদ্ধতিগুলি সেট আপ করুন"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ফিজিক্যাল কীবোর্ড"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"এই <xliff:g id="APPLICATION">%1$s</xliff:g> অ্যাপ্লিকেশানটিকে কি USB ডিভাইস অ্যাক্সেস করা অনুমতি দেবেন?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"এই <xliff:g id="APPLICATION">%1$s</xliff:g> অ্যাপ্লিকেশানটিকে কি USB যন্ত্রাংশ অ্যাক্সেস করার অনুমতি দেবেন?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"যখন এই USB ডিভাইসটি সংযুক্ত থাকে তখন কি <xliff:g id="ACTIVITY">%1$s</xliff:g> খুলবেন?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"যখন এই USB যন্ত্রাংশটি সংযুক্ত থাকে তখন কি <xliff:g id="ACTIVITY">%1$s</xliff:g> খুলবেন?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ইনস্টল থাকা কোনো অ্যাপ্লিকেশান এই USB যন্ত্রাংশের সাথে কাজ করে না৷ <xliff:g id="URL">%1$s</xliff:g> এ এই যন্ত্রাংশের সম্পর্কে আরও জানুন৷"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB যন্ত্রাংশ"</string>
<string name="label_view" msgid="6304565553218192990">"দেখুন"</string>
- <string name="always_use_device" msgid="1450287437017315906">"এই USB ডিভাইসের জন্য এটি ডিফল্টরুপে ব্যবহার করুন"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"এই USB আনুষঙ্গিকের জন্য এটি ডিফল্টরুপে ব্যবহার করুন"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"USB ডিবাগিং মঞ্জুর করবেন?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"কম্পিউটারের RSA কী আঙ্গুলের ছাপ হল:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"এই কম্পিউটার থেকে সর্বদা অনুমতি দিন"</string>
diff --git a/packages/SystemUI/res/values-ca/config.xml b/packages/SystemUI/res/values-ca/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-ca/config.xml
+++ b/packages/SystemUI/res/values-ca/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 28b0ffe..6db3e38 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Xarxa compartida per Bluetooth"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configura els mètodes d\'entrada"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclat físic"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Vols permetre que l\'aplicació <xliff:g id="APPLICATION">%1$s</xliff:g> accedeixi al dispositiu USB?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Vols permetre que l\'aplicació <xliff:g id="APPLICATION">%1$s</xliff:g> accedeixi a l\'accessori USB?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Vols que s\'obri <xliff:g id="ACTIVITY">%1$s</xliff:g> quan aquest dispositiu USB estigui connectat?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Vols que s\'obri <xliff:g id="ACTIVITY">%1$s</xliff:g> quan aquest accessori USB estigui connectat?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Les aplicacions instal·lades no funcionen amb l\'accessori USB. Més informació: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accessori USB"</string>
<string name="label_view" msgid="6304565553218192990">"Mostra"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Utilitza de manera predet. per al dispositiu USB"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Utilitza de manera predet. per a l\'accessori USB"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"Vols permetre la depuració USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"L\'empremta digital de la clau de l\'RSA de l\'equip és:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Dona sempre permís des d\'aquest equip"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/config.xml b/packages/SystemUI/res/values-en-rAU/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-en-rAU/config.xml
+++ b/packages/SystemUI/res/values-en-rAU/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index fe17a2d..76b21b5 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -51,15 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth tethered"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB device?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB accessory?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB device is connected?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB accessory is connected?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB accessory"</string>
<string name="label_view" msgid="6304565553218192990">"View"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Use by default for this USB device"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Use by default for this USB accessory"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"Always open <xliff:g id="APPLICATION">%1$s</xliff:g> when <xliff:g id="USB_DEVICE">%2$s</xliff:g> is connected"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Always open <xliff:g id="APPLICATION">%1$s</xliff:g> when <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> is connected"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Allow USB debugging?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"The computer\'s RSA key fingerprint is:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Always allow from this computer"</string>
@@ -180,7 +180,6 @@
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
<string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
<string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Quick settings."</string>
@@ -343,8 +342,6 @@
<string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
<string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
<string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
- <string-array name="recents_blacklist_array">
- </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> until full"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/config.xml b/packages/SystemUI/res/values-en-rCA/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-en-rCA/config.xml
+++ b/packages/SystemUI/res/values-en-rCA/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 1ff0ee4..1bc1197 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -51,15 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth tethered"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB device?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB accessory?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB device is connected?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB accessory is connected?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB accessory"</string>
<string name="label_view" msgid="6304565553218192990">"View"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Use by default for this USB device"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Use by default for this USB accessory"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"Always open <xliff:g id="APPLICATION">%1$s</xliff:g> when <xliff:g id="USB_DEVICE">%2$s</xliff:g> is connected"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Always open <xliff:g id="APPLICATION">%1$s</xliff:g> when <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> is connected"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Allow USB debugging?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"The computer\'s RSA key fingerprint is:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Always allow from this computer"</string>
@@ -180,7 +180,6 @@
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
<string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
<string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Quick settings."</string>
@@ -343,8 +342,6 @@
<string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
<string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
<string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
- <string-array name="recents_blacklist_array">
- </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> until full"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/config.xml b/packages/SystemUI/res/values-en-rGB/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-en-rGB/config.xml
+++ b/packages/SystemUI/res/values-en-rGB/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index fe17a2d..76b21b5 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -51,15 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth tethered"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB device?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB accessory?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB device is connected?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB accessory is connected?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB accessory"</string>
<string name="label_view" msgid="6304565553218192990">"View"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Use by default for this USB device"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Use by default for this USB accessory"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"Always open <xliff:g id="APPLICATION">%1$s</xliff:g> when <xliff:g id="USB_DEVICE">%2$s</xliff:g> is connected"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Always open <xliff:g id="APPLICATION">%1$s</xliff:g> when <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> is connected"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Allow USB debugging?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"The computer\'s RSA key fingerprint is:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Always allow from this computer"</string>
@@ -180,7 +180,6 @@
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
<string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
<string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Quick settings."</string>
@@ -343,8 +342,6 @@
<string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
<string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
<string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
- <string-array name="recents_blacklist_array">
- </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> until full"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/config.xml b/packages/SystemUI/res/values-en-rIN/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-en-rIN/config.xml
+++ b/packages/SystemUI/res/values-en-rIN/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index fe17a2d..76b21b5 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -51,15 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth tethered"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB device?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB accessory?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB device is connected?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB accessory is connected?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB accessory"</string>
<string name="label_view" msgid="6304565553218192990">"View"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Use by default for this USB device"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Use by default for this USB accessory"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"Always open <xliff:g id="APPLICATION">%1$s</xliff:g> when <xliff:g id="USB_DEVICE">%2$s</xliff:g> is connected"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Always open <xliff:g id="APPLICATION">%1$s</xliff:g> when <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> is connected"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Allow USB debugging?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"The computer\'s RSA key fingerprint is:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Always allow from this computer"</string>
@@ -180,7 +180,6 @@
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
<string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
<string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Quick settings."</string>
@@ -343,8 +342,6 @@
<string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
<string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
<string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
- <string-array name="recents_blacklist_array">
- </string-array>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Charging"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> until full"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/config.xml b/packages/SystemUI/res/values-en-rXC/config.xml
index 5309563..f015d9b 100644
--- a/packages/SystemUI/res/values-en-rXC/config.xml
+++ b/packages/SystemUI/res/values-en-rXC/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index e1326de..131e199 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -51,15 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth tethered"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB device?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Allow the app <xliff:g id="APPLICATION">%1$s</xliff:g> to access the USB accessory?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB device is connected?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Open <xliff:g id="ACTIVITY">%1$s</xliff:g> when this USB accessory is connected?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB accessory"</string>
<string name="label_view" msgid="6304565553218192990">"View"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Use by default for this USB device"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Use by default for this USB accessory"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"Always open <xliff:g id="APPLICATION">%1$s</xliff:g> when <xliff:g id="USB_DEVICE">%2$s</xliff:g> is connected"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Always open <xliff:g id="APPLICATION">%1$s</xliff:g> when <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> is connected"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Allow USB debugging?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"The computer\'s RSA key fingerprint is:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Always allow from this computer"</string>
diff --git a/packages/SystemUI/res/values-fi/config.xml b/packages/SystemUI/res/values-fi/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-fi/config.xml
+++ b/packages/SystemUI/res/values-fi/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 7b6dc54..65d80a9 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth yhdistetty"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Määritä syöttötavat"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fyysinen näppäimistö"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Annetaanko sovellukselle <xliff:g id="APPLICATION">%1$s</xliff:g> lupa käyttää USB-laitetta?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Annetaanko sovellukselle <xliff:g id="APPLICATION">%1$s</xliff:g> lupa käyttää USB-lisälaitetta?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Avataanko <xliff:g id="ACTIVITY">%1$s</xliff:g> tämän USB-laitteen ollessa kytkettynä?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Avataanko <xliff:g id="ACTIVITY">%1$s</xliff:g> tämän USB-lisälaitteen ollessa kytkettynä?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Asennetut sov. eivät toimi tämän USB-laitteen kanssa. Lisätietoja laitteesta: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-lisälaite"</string>
<string name="label_view" msgid="6304565553218192990">"Näytä"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Käytä oletuksena tällä USB-laitteella"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Käytä oletuksena tällä USB-lisälaitteella"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"Sallitaanko USB-vianetsintä?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"Tietokoneen RSA-avaintunnistetiedosto on:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Salli aina tällä tietokoneella"</string>
@@ -71,7 +77,7 @@
<string name="screenshot_saving_title" msgid="8242282144535555697">"Tallennetaan kuvakaappausta..."</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"Kuvakaappausta tallennetaan."</string>
<string name="screenshot_saved_title" msgid="6461865960961414961">"Kuvakaappaus tallennettu"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Tarkastele kuvakaappausta napauttamalla."</string>
+ <string name="screenshot_saved_text" msgid="2685605830386712477">"Tarkastele kuvakaappausta napauttamalla"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Kuvakaappausta ei voitu tallentaa"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Kuvakaappausta tallennettaessa tapahtui virhe."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Kuvakaappauksen tallentaminen epäonnistui, sillä tallennustilaa ei ole riittävästi."</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/config.xml b/packages/SystemUI/res/values-fr-rCA/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-fr-rCA/config.xml
+++ b/packages/SystemUI/res/values-fr-rCA/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 2344c00..6e1feea 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -51,15 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Connexion Bluetooth partagée"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurer les modes de saisie"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Clavier physique"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Autoriser l\'application <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder au périphérique USB?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Autoriser l\'application <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à l\'accessoire USB?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Ouvrir <xliff:g id="ACTIVITY">%1$s</xliff:g> lors de la connexion de ce périphérique USB?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Ouvrir <xliff:g id="ACTIVITY">%1$s</xliff:g> lors de la connexion de cet accessoire USB?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour utiliser <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour utiliser <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Aucune application installée compatible avec accessoire USB. En savoir plus sur <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accessoire USB"</string>
<string name="label_view" msgid="6304565553218192990">"Afficher"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Utiliser par défaut pour ce périphérique USB"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Utiliser par défaut pour cet accessoire USB"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"Toujours ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> lorsque <xliff:g id="USB_DEVICE">%2$s</xliff:g> est connecté"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Toujours ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> lorsque <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> est connecté"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Autoriser le débogage USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"Empreinte numérique de la clé RSA de l\'ordinateur : \n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Toujours autoriser sur cet ordinateur"</string>
@@ -71,7 +71,7 @@
<string name="screenshot_saving_title" msgid="8242282144535555697">"Enregistrement capture écran…"</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"Enregistrement de la capture d\'écran en cours…"</string>
<string name="screenshot_saved_title" msgid="6461865960961414961">"Capture d\'écran réussie"</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Touchez pour afficher votre saisie d\'écran."</string>
+ <string name="screenshot_saved_text" msgid="2685605830386712477">"Touchez pour afficher votre capture d\'écran."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Impossible de réaliser une capture d\'écran"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Une erreur s\'est produite lors de l\'enregistrement de la saisie d\'écran."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Impossible d\'enregistrer la saisie d\'écran, car l\'espace de stockage est limité."</string>
diff --git a/packages/SystemUI/res/values-gl/config.xml b/packages/SystemUI/res/values-gl/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-gl/config.xml
+++ b/packages/SystemUI/res/values-gl/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 7f11ba2..94c525a 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Conexión compartida por Bluetooth"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de entrada"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Queres permitir que a aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda ao dispositivo USB?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Queres permitir que a aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda ao accesorio USB?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Queres abrir <xliff:g id="ACTIVITY">%1$s</xliff:g> cando este dispositivo USB estea conectado?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Queres abrir <xliff:g id="ACTIVITY">%1$s</xliff:g> cando este accesorio USB estea conectado?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Ningunha aplicación instalada funciona co accesorio USB. Máis información: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accesorio USB"</string>
<string name="label_view" msgid="6304565553218192990">"Ver"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Usar de forma predeterminada para dispositivo USB"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Usar de forma predeterminada para este accesorio USB"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"Permitir a depuración de erros de USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"A impresión dixital da clave de RSA do ordenador é:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Permitir sempre desde este ordenador"</string>
diff --git a/packages/SystemUI/res/values-gu/config.xml b/packages/SystemUI/res/values-gu/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-gu/config.xml
+++ b/packages/SystemUI/res/values-gu/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 2dbd57b..b13011a 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"બ્લૂટૂથ ટિથર કર્યું"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ઇનપુટ પદ્ધતિઓ સેટ કરો"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ભૌતિક કીબોર્ડ"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"<xliff:g id="APPLICATION">%1$s</xliff:g> એપ્લિકેશનને USB ઉપકરણ અૅક્સેસ કરવાની મંજૂરી આપીએ?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"<xliff:g id="APPLICATION">%1$s</xliff:g> એપ્લિકેશનને USB ઍક્સેસરી અૅક્સેસ કરવાની મંજૂરી આપીએ?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"જ્યારે આ USB ઉપકરણ કનેક્ટ હોય ત્યારે <xliff:g id="ACTIVITY">%1$s</xliff:g> ખોલીએ?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"જ્યારે આ USB ઍક્સેસરી કનેક્ટ હોય ત્યારે <xliff:g id="ACTIVITY">%1$s</xliff:g> ખોલીએ?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"કોઈપણ ઇન્સ્ટોલ કરેલી ઍપ્લિકેશનો આ USB ઍક્સેસરી સાથે કામ કરતી નથી. આ ઍક્સેસરી વિશે <xliff:g id="URL">%1$s</xliff:g> પર વધુ જાણો."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ઍક્સેસરી"</string>
<string name="label_view" msgid="6304565553218192990">"જુઓ"</string>
- <string name="always_use_device" msgid="1450287437017315906">"આ USB ઉપકરણ માટે ડિફોલ્ટ તરીકે ઉપયોગમાં લો"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"આ USB ઍક્સેસરી માટે ડિફોલ્ટ તરીકે ઉપયોગમાં લો"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"USB ડિબગિંગને મંજૂરી આપીએ?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"કમ્પ્યુટરની RSA મુખ્ય ફિંગરપ્રિંટ આ છે:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"હંમેશા આ કમ્પ્યુટરથી મંજૂરી આપો"</string>
diff --git a/packages/SystemUI/res/values-hi/config.xml b/packages/SystemUI/res/values-hi/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-hi/config.xml
+++ b/packages/SystemUI/res/values-hi/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 93dadd2..ef66a35 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"ब्लूटूथ टीदर किया गया"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"इनपुट का तरीका सेट करें"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"भौतिक कीबोर्ड"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"ऐप्स <xliff:g id="APPLICATION">%1$s</xliff:g> को USB डिवाइस तक पहुंचने दें?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"ऐप्स <xliff:g id="APPLICATION">%1$s</xliff:g> को USB सहायक डिवाइस तक पहुंचने दें?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"जब यह USB डिवाइस कनेक्ट किया जाए, तब <xliff:g id="ACTIVITY">%1$s</xliff:g> को खोलें?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"जब यह USB एसेसरी कनेक्ट की जाए, तब <xliff:g id="ACTIVITY">%1$s</xliff:g> को खोलें?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"इस USB सहायक डिवाइस के साथ कोई भी इंस्टॉल ऐप्स काम नहीं करता. इस सहायक डिवाइस के बारे में यहां अधिक जानें: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB सहायक साधन"</string>
<string name="label_view" msgid="6304565553218192990">"देखें"</string>
- <string name="always_use_device" msgid="1450287437017315906">"इस USB डिवाइस के लिए डिफ़ॉल्ट रूप से उपयोग करें"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"इस USB एसेसरी के लिए डिफ़ॉल्ट रूप से उपयोग करें"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"USB डीबगिंग करने दें?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"कंप्यूटर का RSA कुंजी फ़िंगरप्रिंट है:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"इस कंप्यूटर से हमेशा अनुमति दें"</string>
diff --git a/packages/SystemUI/res/values-iw/config.xml b/packages/SystemUI/res/values-iw/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-iw/config.xml
+++ b/packages/SystemUI/res/values-iw/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index eb85099..dcef8c6 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -53,15 +53,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth קשור"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"הגדר שיטות קלט"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"מקלדת פיזית"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"לאפשר לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> גישה להתקן ה-USB?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"לאפשר לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> גישה לאביזר ה-USB?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"האם לפתוח את <xliff:g id="ACTIVITY">%1$s</xliff:g> כאשר מכשיר USB זה מחובר?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"האם לפתוח את <xliff:g id="ACTIVITY">%1$s</xliff:g> כאשר אביזר USB זה מחובר?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"אין אפליקציות מותקנות הפועלות עם אביזר ה-USB. למידע נוסף על אביזר זה היכנס לכתובת <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"אביזר USB"</string>
<string name="label_view" msgid="6304565553218192990">"הצג"</string>
- <string name="always_use_device" msgid="1450287437017315906">"השתמש כברירת מחדל עבור מכשיר USB זה"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"השתמש כברירת מחדל עבור אביזר USB זה"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"האם לאפשר ניפוי באגים ב-USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"טביעת האצבע של מפתח ה-RSA של המחשב היא:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"אפשר תמיד ממחשב זה"</string>
diff --git a/packages/SystemUI/res/values-kn/config.xml b/packages/SystemUI/res/values-kn/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-kn/config.xml
+++ b/packages/SystemUI/res/values-kn/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index ca1a650..7ba785b 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"ಬ್ಲೂಟೂತ್ ವ್ಯಾಪ್ತಿ ತಲುಪಿದೆ"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ಇನ್ಪುಟ್ ವಿಧಾನಗಳನ್ನು ಹೊಂದಿಸು"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"USB ಸಾಧನವನ್ನು ಪ್ರವೇಶಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುವುದೇ?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"USB ಪರಿಕರವನ್ನು ಪ್ರವೇಶಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುವುದೇ?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"ಈ USB ಸಾಧನ ಸಂಪರ್ಕಗೊಂಡಿರುವಾಗ <xliff:g id="ACTIVITY">%1$s</xliff:g> ತೆರೆಯುವುದೇ?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"USB ಪರಿಕರವು ಸಂಪರ್ಕಗೊಂಡಿರುವಾಗ <xliff:g id="ACTIVITY">%1$s</xliff:g> ತೆರೆಯುವುದೇ?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ಆಪ್ಗಳು USB ಪರಿಕರದಲ್ಲಿ ಕಾರ್ಯನಿರ್ವಹಿಸುವುದಿಲ್ಲ. ಆ ಬಗ್ಗೆ <xliff:g id="URL">%1$s</xliff:g> ನಲ್ಲಿ ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ಪರಿಕರ"</string>
<string name="label_view" msgid="6304565553218192990">"ವೀಕ್ಷಿಸು"</string>
- <string name="always_use_device" msgid="1450287437017315906">"ಈ USB ಪರಿಕರಕ್ಕಾಗಿ ಡಿಫಾಲ್ಟ್ ಆಗಿ ಬಳಸಿ"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"ಈ USB ಪರಿಕರಕ್ಕಾಗಿ ಡಿಫಾಲ್ಟ್ ಆಗಿ ಬಳಸಿ"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ಅನುಮತಿಸುವುದೇ?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"ಕಂಪ್ಯೂಟರ್ನ RSA ಕೀ ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಹೀಗಿದೆ :\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"ಈ ಕಂಪ್ಯೂಟರ್ನಿಂದ ಯಾವಾಗಲೂ ಅನುಮತಿಸಿ"</string>
diff --git a/packages/SystemUI/res/values-ml/config.xml b/packages/SystemUI/res/values-ml/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-ml/config.xml
+++ b/packages/SystemUI/res/values-ml/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 855ae6a..5522e51 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"ബ്ലൂടൂത്ത് ടെതർ ചെയ്തു"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ടൈപ്പുചെയ്യൽ രീതികൾ സജ്ജീകരിക്കുക"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ഫിസിക്കൽ കീബോർഡ്"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"USB ഉപകരണം ആക്സസ്സ് ചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g> എന്ന അപ്ളിക്കേഷനെ അനുവദിക്കണോ?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"USB ആക്സസ്സറി ആക്സസ്സുചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g> എന്ന അപ്ലിക്കേഷനെ അനുവദിക്കണോ?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"ഈ USB ഉപകരണം കണക്റ്റുചെയ്തിരിക്കുമ്പോൾ <xliff:g id="ACTIVITY">%1$s</xliff:g> തുറക്കണോ?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"ഈ USB ആക്സസ്സറി കണക്റ്റുചെയ്തിരിക്കുമ്പോൾ <xliff:g id="ACTIVITY">%1$s</xliff:g> തുറക്കണോ?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ഈ USB ആക്സസ്സറിയിൽ ഇൻസ്റ്റാളുചെയ്തവയൊന്നും പ്രവർത്തിക്കുന്നില്ല. <xliff:g id="URL">%1$s</xliff:g>-ൽ ഇതേക്കുറിച്ച് കൂടുതലറിയുക"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ആക്സസ്സറി"</string>
<string name="label_view" msgid="6304565553218192990">"കാണുക"</string>
- <string name="always_use_device" msgid="1450287437017315906">"ഈ USB ഉപകരണത്തിനായി സ്ഥിരമായി ഉപയോഗിക്കുക"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"ഈ USB ആക്സസ്സറിക്കായി സ്ഥിരമായി ഉപയോഗിക്കുക"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"USB ഡീബഗ്ഗിംഗ് അനുവദിക്കണോ?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"ഈ കമ്പ്യൂട്ടറിന്റെ RSA കീ ഫിംഗർപ്രിന്റ് ഇതാണ്:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"ഈ കമ്പ്യൂട്ടറിൽ നിന്ന് എല്ലായ്പ്പോഴും അനുവദിക്കുക"</string>
diff --git a/packages/SystemUI/res/values-mr/config.xml b/packages/SystemUI/res/values-mr/config.xml
index 9857f13..477f219 100644
--- a/packages/SystemUI/res/values-mr/config.xml
+++ b/packages/SystemUI/res/values-mr/config.xml
@@ -22,7 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for config_overviewServiceComponent (2288311504315574053) -->
- <skip />
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 2a9ec0c..b03d6bf 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"ब्लूटूथ टेदर केले"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"इनपुट पद्धती सेट करा"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"वास्तविक कीबोर्ड"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"USB डिव्हाइसवर प्रवेश करण्यासाठी <xliff:g id="APPLICATION">%1$s</xliff:g> अॅप ला अनुमती द्यायची?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"अॅप <xliff:g id="APPLICATION">%1$s</xliff:g> ला USB उपसाधनात प्रवेश करण्याची अनुमती द्यायची?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"हे USB डिव्हाइस कनेक्ट केलेले असते तेव्हा <xliff:g id="ACTIVITY">%1$s</xliff:g> उघडायचे?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"हे USB उपसाधन कनेक्ट केलेले असते तेव्हा <xliff:g id="ACTIVITY">%1$s</xliff:g> उघडायचे?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"इंस्टॉल केलेले अॅप्स या USB उपसाधनासह कार्य करत नाहीत. <xliff:g id="URL">%1$s</xliff:g> येथे या उपसाधनाविषयी अधिक जाणून घ्या"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB उपसाधन"</string>
<string name="label_view" msgid="6304565553218192990">"पहा"</string>
- <string name="always_use_device" msgid="1450287437017315906">"या USB डीव्हाइससाठी डीफॉल्टनुसार वापरा"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"या USB अॅक्सेसरीसाठी डीफॉल्टनुसार वापरा"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"USB डीबग करण्यास अनुमती द्यायची?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"संगणकाची RSA की फिंगरप्रिंट ही आहे:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"या संगणकावरून नेहमी अनुमती द्या"</string>
diff --git a/packages/SystemUI/res/values-ne/config.xml b/packages/SystemUI/res/values-ne/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-ne/config.xml
+++ b/packages/SystemUI/res/values-ne/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index b2bf111..e994174 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"ब्लुटुथ टेथर भयो"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"इनपुट विधिहरू सेटअप गर्नुहोस्"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"वास्तविक किबोर्ड"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"<xliff:g id="APPLICATION">%1$s</xliff:g> USB उपकरणलाई पहुँच दिनको लागि अनुप्रयोगलाई अनुमति दिने हो?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"<xliff:g id="APPLICATION">%1$s</xliff:g> USB पाटपुर्जालाई पहुँच दिनको लागि अनुप्रयोगलाई अनुमति दिने हो?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"यो USB उपकरण जोडिएको बेला <xliff:g id="ACTIVITY">%1$s</xliff:g> खोल्ने हो?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"यो USB सहायक जडान हुँदा <xliff:g id="ACTIVITY">%1$s</xliff:g> खोल्ने हो?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"यस USB उपकरणसँग स्थापित अनुप्रयोग काम गर्दैन। यस उपकरणको बारेमा <xliff:g id="URL">%1$s</xliff:g> मा धेरै जान्नुहोस्"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB सहयोगी"</string>
<string name="label_view" msgid="6304565553218192990">"दृश्य"</string>
- <string name="always_use_device" msgid="1450287437017315906">"यो USB उपकरणको लागि पूर्वनिर्धारितबाट प्रयोग गर्नुहोस्"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"यस USB सहायक सामानको लागि पूर्वनिर्धारितद्वारा प्रयोग गर्नुहोस्"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"USB डिबग गर्नको लागि अनुमति दिने हो?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"कम्प्युटरको RSA कुञ्जी औंलाछाप:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"यो कम्प्युटरबाट सधैँ अनुमति दिनुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/config.xml b/packages/SystemUI/res/values-nl/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-nl/config.xml
+++ b/packages/SystemUI/res/values-nl/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 815e5f9..640b5d0 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -51,15 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth getetherd"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Invoermethoden instellen"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fysiek toetsenbord"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"De app <xliff:g id="APPLICATION">%1$s</xliff:g> toegang geven tot het USB-apparaat?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"De app <xliff:g id="APPLICATION">%1$s</xliff:g> toegang geven tot het USB-accessoire?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"<xliff:g id="ACTIVITY">%1$s</xliff:g> openen wanneer dit USB-apparaat wordt aangesloten?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"<xliff:g id="ACTIVITY">%1$s</xliff:g> openen wanneer dit USB-accessoire wordt aangesloten?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> toegang geven tot <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> toegang geven tot <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="APPLICATION">%1$s</xliff:g> openen om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te verwerken?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="APPLICATION">%1$s</xliff:g> openen om <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> te verwerken?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Er werken geen geïnstalleerde apps met dit USB-accessoire. Meer informatie op: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-accessoire"</string>
<string name="label_view" msgid="6304565553218192990">"Weergeven"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Standaard gebruiken voor dit USB-apparaat"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Standaard gebruiken voor dit USB-accessoire"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"<xliff:g id="APPLICATION">%1$s</xliff:g> altijd openen wanneer <xliff:g id="USB_DEVICE">%2$s</xliff:g> is verbonden"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"<xliff:g id="APPLICATION">%1$s</xliff:g> altijd openen wanneer <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> is verbonden"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"USB-foutopsporing toestaan?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"De vingerafdruk voor de RSA-sleutel van de computer is:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Altijd toestaan vanaf deze computer"</string>
diff --git a/packages/SystemUI/res/values-pa/config.xml b/packages/SystemUI/res/values-pa/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-pa/config.xml
+++ b/packages/SystemUI/res/values-pa/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index cec5c96..48be371 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth ਟੀਥਰ ਕੀਤੀ"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ਇਨਪੁਟ ਵਿਧੀਆਂ ਸੈਟ ਅਪ ਕਰੋ"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ਫਿਜੀਕਲ ਕੀ-ਬੋਰਡ"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"ਕੀ ਐਪ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨੂੰ USB ਡੀਵਾਈਸ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"ਕੀ ਐਪ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨੂੰ USB ਐਕਸੈੱਸਰੀ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"ਕੀ ਜਦੋਂ ਇਹ USB ਡੀਵਾਈਸ ਕਨੈਕਟ ਕੀਤੀ ਜਾਂਦੀ ਹੈ ਤਾਂ <xliff:g id="ACTIVITY">%1$s</xliff:g> ਨੂੰ ਖੋਲ੍ਹਣਾ ਹੈ?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"ਕੀ ਜਦੋਂ ਇਹ USB ਐਕਸੈਸਰੀ ਕਨੈਕਟ ਕੀਤੀ ਜਾਂਦੀ ਹੈ ਤਾਂ <xliff:g id="ACTIVITY">%1$s</xliff:g> ਨੂੰ ਖੋਲ੍ਹਣਾ ਹੈ?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ਕੋਈ ਇੰਸਟੌਲ ਕੀਤੇ ਐਪਸ ਇਸ USB ਐਕਸੈਸਰੀ ਨਾਲ ਕੰਮ ਨਹੀਂ ਕਰਦੇ। <xliff:g id="URL">%1$s</xliff:g> ਤੇ ਇਸ ਐਕਸੈਸਰੀ ਬਾਰੇ ਹੋਰ ਜਾਣੋ"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ਐਕਸੈਸਰੀ"</string>
<string name="label_view" msgid="6304565553218192990">"ਦੇਖੋ"</string>
- <string name="always_use_device" msgid="1450287437017315906">"ਇਸ USB ਡੀਵਾਈਸ ਲਈ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਤੌਰ \'ਤੇ ਵਰਤੋ"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"ਇਸ USB ਐਕਸਸੈਰੀ ਲਈ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਤੌਰ \'ਤੇ ਵਰਤੋ"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"ਕੀ USB ਡੀਬਗਿੰਗ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"ਕੰਪਿਊਟਰ ਦਾ RSA ਕੁੰਜੀ ਫਿੰਗਰਪ੍ਰਿੰਟ \n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"ਹਮੇਸ਼ਾਂ ਇਸ ਕੰਪਿਊਟਰ ਤੋਂ ਆਗਿਆ ਦਿਓ"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/config.xml b/packages/SystemUI/res/values-pt-rBR/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-pt-rBR/config.xml
+++ b/packages/SystemUI/res/values-pt-rBR/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 4b72c29..ce457be 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -51,15 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth vinculado"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de entrada"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse o dispositivo USB?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse o acessório USB?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Abrir <xliff:g id="ACTIVITY">%1$s</xliff:g> quando este dispositivo USB estiver conectado?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Abrir <xliff:g id="ACTIVITY">%1$s</xliff:g> quando este acessório USB estiver conectado?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> para lidar com o <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> para lidar com o <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Nenhum apl. instalado funciona com o USB. Saiba mais sobre o acessório em <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Acessório USB"</string>
<string name="label_view" msgid="6304565553218192990">"Visualizar"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Usar por padrão para este dispositivo USB"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Usar por padrão para este acessório USB"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"Sempre abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> quando <xliff:g id="USB_DEVICE">%2$s</xliff:g> for conectado"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Sempre abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> quando <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> for conectado"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Permitir a depuração USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"A impressão digital da chave RSA deste computador é:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Sempre permitir a partir deste computador"</string>
diff --git a/packages/SystemUI/res/values-pt/config.xml b/packages/SystemUI/res/values-pt/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-pt/config.xml
+++ b/packages/SystemUI/res/values-pt/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 4b72c29..ce457be 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -51,15 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth vinculado"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de entrada"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse o dispositivo USB?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse o acessório USB?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Abrir <xliff:g id="ACTIVITY">%1$s</xliff:g> quando este dispositivo USB estiver conectado?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Abrir <xliff:g id="ACTIVITY">%1$s</xliff:g> quando este acessório USB estiver conectado?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> para lidar com o <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> para lidar com o <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Nenhum apl. instalado funciona com o USB. Saiba mais sobre o acessório em <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Acessório USB"</string>
<string name="label_view" msgid="6304565553218192990">"Visualizar"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Usar por padrão para este dispositivo USB"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Usar por padrão para este acessório USB"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"Sempre abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> quando <xliff:g id="USB_DEVICE">%2$s</xliff:g> for conectado"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Sempre abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> quando <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> for conectado"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Permitir a depuração USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"A impressão digital da chave RSA deste computador é:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Sempre permitir a partir deste computador"</string>
diff --git a/packages/SystemUI/res/values-ro/config.xml b/packages/SystemUI/res/values-ro/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-ro/config.xml
+++ b/packages/SystemUI/res/values-ro/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 4800578..c7822e0d 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -52,15 +52,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Conectat prin tethering prin Bluetooth"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Setați metode introducere text"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Tastatură fizică"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Permiteți aplicației <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze dispozitivul USB?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Permiteți aplicației <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze accesoriul USB?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Deschideți <xliff:g id="ACTIVITY">%1$s</xliff:g> la conectarea acestui dispozitiv USB?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Deschideți <xliff:g id="ACTIVITY">%1$s</xliff:g> la conectarea acestui accesoriu USB?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Permiteți <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Permiteți <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Aplic. instal. nu funcț. cu acest acces. USB. Aflați despre acest accesoriu la <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accesoriu USB"</string>
<string name="label_view" msgid="6304565553218192990">"Afișați"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Utilizați în mod prestabilit pt. acest dispoz. USB"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Utiliz. în mod prestabilit pt. acest accesoriu USB"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"Deschideți întotdeauna <xliff:g id="APPLICATION">%1$s</xliff:g> când este conectat <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Deschideți întotdeauna <xliff:g id="APPLICATION">%1$s</xliff:g> când este conectat <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Permiteți remedierea erorilor prin USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"Amprenta digitală din cheia RSA a computerului este:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Permiteți întotdeauna de pe acest computer"</string>
diff --git a/packages/SystemUI/res/values-sw/config.xml b/packages/SystemUI/res/values-sw/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-sw/config.xml
+++ b/packages/SystemUI/res/values-sw/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 524faea..5853d93 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -51,15 +51,15 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth imefungwa"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Weka mbinu za ingizo"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Kibodi halisi"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"Ruhusu programu <xliff:g id="APPLICATION">%1$s</xliff:g> kufikia kifaa cha USB?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Ruhusu programu <xliff:g id="APPLICATION">%1$s</xliff:g> kufikia kifaa cha ziada cha USB?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Je, ungetaka kufungua <xliff:g id="ACTIVITY">%1$s</xliff:g>wakati kifaa cha USB kimeunganishwa?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Je, ungetaka kufungua <xliff:g id="ACTIVITY">%1$s</xliff:g>wakati kifaa cha USB kimeunganishwa?"</string>
+ <string name="usb_device_permission_prompt" msgid="1825685909587559679">"Ungependa kuruhusu <xliff:g id="APPLICATION">%1$s</xliff:g> ifikie <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Ungependa kuruhusu <xliff:g id="APPLICATION">%1$s</xliff:g> ifikie <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Ungependa kufungua <xliff:g id="APPLICATION">%1$s</xliff:g> ili itumie <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Ungependa kufungua <xliff:g id="APPLICATION">%1$s</xliff:g> ili itumie <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Hakuna programu zilizosakinishwa zinazofanya kazi na kifaa hiki cha USB. Pata maelezo zaidi kuhusu kifaa hiki kwenye <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Kifaa cha Usb"</string>
<string name="label_view" msgid="6304565553218192990">"Ona"</string>
- <string name="always_use_device" msgid="1450287437017315906">"Kwa kifaa hiki cha USB tumia chaguo-msingi"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"Tumia kama chaguo-msingi ya kifuasi hiki cha USB"</string>
+ <string name="always_use_device" msgid="4015357883336738417">"Fungua <xliff:g id="APPLICATION">%1$s</xliff:g> kila wakati <xliff:g id="USB_DEVICE">%2$s</xliff:g> inaunganishwa"</string>
+ <string name="always_use_accessory" msgid="3257892669444535154">"Fungua <xliff:g id="APPLICATION">%1$s</xliff:g> kila wakati <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> inaunganishwa"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Ruhusu utatuaji wa USB?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"Alama ya kidole ya kitufe cha RSA ya kompyuta ni:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Ruhusu kutoka kwenye kompyuta hii kila wakati"</string>
@@ -71,7 +71,7 @@
<string name="screenshot_saving_title" msgid="8242282144535555697">"Inahifadhi picha ya skrini..."</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"Picha ya skrini inahifadhiwa"</string>
<string name="screenshot_saved_title" msgid="6461865960961414961">"Picha ya skrini imenaswa."</string>
- <string name="screenshot_saved_text" msgid="2685605830386712477">"Gonga ili utazame picha ya skrini uliyohifadhi."</string>
+ <string name="screenshot_saved_text" msgid="2685605830386712477">"Gusa ili utazame picha ya skrini uliyohifadhi."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Haikuweza kunasa picha ya skrini"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Hitilafu imetokea wakati wa kuhifadhi picha ya skrini."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Haina nafasi ya kutosha kuhifadhi picha ya skrini."</string>
@@ -357,7 +357,7 @@
<string name="zen_silence_introduction" msgid="3137882381093271568">"Hatua hii huzuia sauti na mitetemo YOTE, ikiwa ni pamoja na ile inayotokana na kengele, muziki, video na michezo."</string>
<string name="keyguard_more_overflow_text" msgid="9195222469041601365">"<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>+"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Arifa zisizo za dharura sana ziko hapo chini"</string>
- <string name="notification_tap_again" msgid="7590196980943943842">"Gonga tena ili ufungue"</string>
+ <string name="notification_tap_again" msgid="7590196980943943842">"Gusa tena ili ufungue"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Telezesha kidole ili ufungue"</string>
<string name="do_disclosure_generic" msgid="5615898451805157556">"Kifaa hiki kinasimamiwa na shirika lako"</string>
<string name="do_disclosure_with_name" msgid="5640615509915445501">"Kifaa hiki kinadhibitiwa na <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
@@ -494,11 +494,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Masafa ya ishara ya kampuni ya simu"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Zana za walio na matatizo ya kuona au kusikia"</string>
- <string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Gonga ili urejeshe."</string>
- <string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Gonga ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
- <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Gonga ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
- <string name="volume_stream_content_description_vibrate_a11y" msgid="6427727603978431301">"%1$s. Gonga ili uweke mtetemo."</string>
- <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Gonga ili usitishe."</string>
+ <string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Gusa ili urejeshe."</string>
+ <string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Gusa ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
+ <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Gusa ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
+ <string name="volume_stream_content_description_vibrate_a11y" msgid="6427727603978431301">"%1$s. Gusa ili uweke mtetemo."</string>
+ <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s. Gusa ili usitishe."</string>
<string name="volume_dialog_accessibility_shown_message" msgid="1834631467074259998">"Inaonyesha %s ya vidhibiti vya sauti. Telezesha kidole juu ili uondoe."</string>
<string name="volume_dialog_accessibility_dismissed_message" msgid="51543526013711399">"Imeficha vidhibiti vya sauti"</string>
<string name="system_ui_tuner" msgid="708224127392452018">"Kirekebishi cha kiolesura cha mfumo"</string>
@@ -694,9 +694,9 @@
<string name="accessibility_action_divider_top_50" msgid="6385859741925078668">"Juu 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="6201455163864841205">"Juu 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"Skrini nzima ya chini"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Gonga mara mbili ili ubadilishe."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gonga mara mbili ili uongeze."</string>
- <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>. Gonga mara mbili ili uchague."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Gusa mara mbili ili ubadilishe."</string>
+ <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gusa mara mbili ili uongeze."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>. Gusa mara mbili ili uchague."</string>
<string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Hamisha <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Ondoa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> imeongezwa kwenye nafasi ya <xliff:g id="POSITION">%2$d</xliff:g>"</string>
@@ -770,7 +770,7 @@
<string name="qs_dnd_keep" msgid="1825009164681928736">"Usibadilishe"</string>
<string name="qs_dnd_replace" msgid="8019520786644276623">"Badilisha"</string>
<string name="running_foreground_services_title" msgid="381024150898615683">"Programu zinatumika chinichini"</string>
- <string name="running_foreground_services_msg" msgid="6326247670075574355">"Gonga ili upate maelezo kuhusu betri na matumizi ya data"</string>
+ <string name="running_foreground_services_msg" msgid="6326247670075574355">"Gusa ili upate maelezo kuhusu betri na matumizi ya data"</string>
<string name="data_usage_disable_mobile" msgid="5116269981510015864">"Ungependa kuzima data ya mtandao wa simu?"</string>
<string name="touch_filtered_warning" msgid="8671693809204767551">"Kwa sababu programu nyingine inazuia ombi la ruhusa, hatuwezi kuthibitisha jibu lako katika Mipangilio."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/config.xml b/packages/SystemUI/res/values-ta/config.xml
index 9857f13..477f219 100644
--- a/packages/SystemUI/res/values-ta/config.xml
+++ b/packages/SystemUI/res/values-ta/config.xml
@@ -22,7 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for config_overviewServiceComponent (2288311504315574053) -->
- <skip />
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index ada5d4b..02d6613 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"புளூடூத் இணைக்கப்பட்டது"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"உள்ளீட்டு முறைகளை அமை"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"கைமுறை விசைப்பலகை"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"USB சாதனத்தை அணுக <xliff:g id="APPLICATION">%1$s</xliff:g> பயன்பாட்டை அனுமதிக்கவா?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"USB இன் துணைக்கருவியை அணுக <xliff:g id="APPLICATION">%1$s</xliff:g> பயன்பாட்டை அனுமதிக்கவா?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"இந்த USB சாதனம் இணைக்கப்பட்டுள்ளபோது <xliff:g id="ACTIVITY">%1$s</xliff:g> ஐத் திறக்கவா?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"இந்த USB துணைக்கருவி இணைக்கப்பட்டுள்ளபோது <xliff:g id="ACTIVITY">%1$s</xliff:g> ஐத் திறக்கவா?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"நிறுவிய பயன்பாடுகள் எதுவும், USB துணைக்கருவியுடன் இயங்காது. <xliff:g id="URL">%1$s</xliff:g> இல் துணைக்கருவி குறித்து மேலும் அறிக"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB துணைக்கருவி"</string>
<string name="label_view" msgid="6304565553218192990">"காட்சி"</string>
- <string name="always_use_device" msgid="1450287437017315906">"இந்த USB சாதனத்திற்கு இயல்பாகப் பயன்படுத்து"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"USB துணைக்கருவிக்கு இயல்பாகப் பயன்படுத்து"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"USB பிழைத்திருத்தத்தை அனுமதிக்கவா?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"பின்வருவது கணினியின் RSA விசை கைரேகையாகும்:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"இந்தக் கணினியிலிருந்து எப்போதும் அனுமதி"</string>
diff --git a/packages/SystemUI/res/values-te/config.xml b/packages/SystemUI/res/values-te/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-te/config.xml
+++ b/packages/SystemUI/res/values-te/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index f23e169..0652de9 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"బ్లూటూత్ టీథర్ చేయబడింది"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ఇన్పుట్ పద్ధతులను సెటప్ చేయండి"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"భౌతిక కీబోర్డ్"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"USB పరికరాన్ని యాక్సెస్ చేయడానికి యాప్ <xliff:g id="APPLICATION">%1$s</xliff:g> అనుమతించాలా?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"USB ఉపకరణాన్ని యాక్సెస్ చేయడానికి యాప్ <xliff:g id="APPLICATION">%1$s</xliff:g>ను అనుమతించాలా?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"ఈ USB పరికరం కనెక్ట్ చేయబడినప్పుడు <xliff:g id="ACTIVITY">%1$s</xliff:g>ని తెరవాలా?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"ఈ USB ఉపకరణం కనెక్ట్ చేయబడినప్పుడు <xliff:g id="ACTIVITY">%1$s</xliff:g>ని తెరవాలా?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ఈ USB ఉపకరణంతో ఇన్స్టాల్ చేయబడిన అనువర్తనాలు ఏవీ పని చేయవు. ఈ ఉపకరణం గురించి <xliff:g id="URL">%1$s</xliff:g>లో మరింత తెలుసుకోండి"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ఉపకరణం"</string>
<string name="label_view" msgid="6304565553218192990">"వీక్షించండి"</string>
- <string name="always_use_device" msgid="1450287437017315906">"ఈ USB పరికరానికి డిఫాల్ట్గా ఉపయోగించండి"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"ఈ USB ఉపకరణానికి డిఫాల్ట్గా ఉపయోగించండి"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"USB డీబగ్గింగ్ను అనుమతించాలా?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"ఇది కంప్యూటర్ యొక్క RSA కీ వేలిముద్ర:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"ఈ కంప్యూటర్ నుండి ఎల్లప్పుడూ అనుమతించు"</string>
diff --git a/packages/SystemUI/res/values-ur/config.xml b/packages/SystemUI/res/values-ur/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-ur/config.xml
+++ b/packages/SystemUI/res/values-ur/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 7f17182..68db1bf 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"بلوٹوتھ مربوط کر دیا گیا"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ان پٹ کے طریقوں کو ترتیب دیں"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"طبعی کی بورڈ"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"ایپ <xliff:g id="APPLICATION">%1$s</xliff:g> کو USB آلہ تک رسائی کی اجازت دیں؟"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"<xliff:g id="APPLICATION">%1$s</xliff:g> ایپ کو USB لازمہ تک رسائی حاصل کرنے کی اجازت دیں؟"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"اس USB آلہ کے مربوط ہونے پر <xliff:g id="ACTIVITY">%1$s</xliff:g> کھولیں؟"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"اس USB لوازم کے مربوط ہونے پر <xliff:g id="ACTIVITY">%1$s</xliff:g> کھولیں؟"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"اس USB لوازم کے ساتھ کوئی انسٹال کردہ ایپس کام نہیں کرتی ہیں۔ <xliff:g id="URL">%1$s</xliff:g> پر مزید جانیں"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB لوازم"</string>
<string name="label_view" msgid="6304565553218192990">"دیکھیں"</string>
- <string name="always_use_device" msgid="1450287437017315906">"اس USB آلہ کیلئے بطور ڈیفالٹ استعمال کریں"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"اس USB لوازم کیلئے بطور ڈیفالٹ استعمال کریں"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"USB ڈیبگ کرنے کی اجازت دیں؟"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"کمپیوٹر کے RSA کا کلیدی فنگر پرنٹ ہے:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"اس کمپیوٹر سے ہمیشہ اجازت دیں"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/config.xml b/packages/SystemUI/res/values-zh-rCN/config.xml
index 5309563..477f219 100644
--- a/packages/SystemUI/res/values-zh-rCN/config.xml
+++ b/packages/SystemUI/res/values-zh-rCN/config.xml
@@ -22,5 +22,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_overviewServiceComponent" msgid="2288311504315574053">"com.android.launcher3/com.android.quickstep.TouchInteractionService"</string>
<string name="doze_pickup_subtype_performs_proximity_check" msgid="533127617385956583"></string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index e179c5d..74afc22 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -51,15 +51,21 @@
<string name="bluetooth_tethered" msgid="7094101612161133267">"已通过蓝牙共享网络"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"设置输入法"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"物理键盘"</string>
- <string name="usb_device_permission_prompt" msgid="834698001271562057">"允许应用“<xliff:g id="APPLICATION">%1$s</xliff:g>”访问该USB设备吗?"</string>
- <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"允许应用“<xliff:g id="APPLICATION">%1$s</xliff:g>”访问该USB配件吗?"</string>
- <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"要在连接此USB设备时打开<xliff:g id="ACTIVITY">%1$s</xliff:g>吗?"</string>
- <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"要在连接此USB配件时打开<xliff:g id="ACTIVITY">%1$s</xliff:g>吗?"</string>
+ <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
+ <skip />
+ <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
+ <skip />
+ <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
+ <skip />
+ <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
+ <skip />
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"未安装此USB配件适用的应用。要了解此配件的详情,请访问:<xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB配件"</string>
<string name="label_view" msgid="6304565553218192990">"查看"</string>
- <string name="always_use_device" msgid="1450287437017315906">"默认情况下用于该USB设备"</string>
- <string name="always_use_accessory" msgid="1210954576979621596">"默认情况下用于该USB配件"</string>
+ <!-- no translation found for always_use_device (4015357883336738417) -->
+ <skip />
+ <!-- no translation found for always_use_accessory (3257892669444535154) -->
+ <skip />
<string name="usb_debugging_title" msgid="4513918393387141949">"允许 USB 调试吗?"</string>
<string name="usb_debugging_message" msgid="2220143855912376496">"这台计算机的 RSA 密钥指纹如下:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"一律允许使用这台计算机进行调试"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c678111f..f0bad2a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -240,7 +240,7 @@
<!-- The width of the panel that holds the quick settings. -->
<dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
- <dimen name="volume_dialog_panel_width">@dimen/standard_notification_panel_width</dimen>
+ <dimen name="volume_dialog_panel_width">315dp</dimen>
<!-- Gravity for the notification panel -->
<integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1536b64..b132160 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -124,16 +124,16 @@
<string name="status_bar_use_physical_keyboard">Physical keyboard</string>
<!-- Prompt for the USB device permission dialog [CHAR LIMIT=80] -->
- <string name="usb_device_permission_prompt">Allow the app <xliff:g id="application">%1$s</xliff:g> to access the USB device?</string>
+ <string name="usb_device_permission_prompt">Allow <xliff:g id="application">%1$s</xliff:g> to access <xliff:g id="usb_device">%2$s</xliff:g>?</string>
<!-- Prompt for the USB accessory permission dialog [CHAR LIMIT=80] -->
- <string name="usb_accessory_permission_prompt">Allow the app <xliff:g id="application">%1$s</xliff:g> to access the USB accessory?</string>
+ <string name="usb_accessory_permission_prompt">Allow <xliff:g id="application">%1$s</xliff:g> to access <xliff:g id="usb_accessory">%2$s</xliff:g>?</string>
<!-- Prompt for the USB device confirm dialog [CHAR LIMIT=80] -->
- <string name="usb_device_confirm_prompt">Open <xliff:g id="activity">%1$s</xliff:g> when this USB device is connected?</string>
+ <string name="usb_device_confirm_prompt">Open <xliff:g id="application">%1$s</xliff:g> to handle <xliff:g id="usb_device">%2$s</xliff:g>?</string>
<!-- Prompt for the USB accessory confirm dialog [CHAR LIMIT=80] -->
- <string name="usb_accessory_confirm_prompt">Open <xliff:g id="activity">%1$s</xliff:g> when this USB accessory is connected?</string>
+ <string name="usb_accessory_confirm_prompt">Open <xliff:g id="application">%1$s</xliff:g> to handle <xliff:g id="usb_accessory">%2$s</xliff:g>?</string>
<!-- Prompt for the USB accessory URI dialog [CHAR LIMIT=80] -->
<string name="usb_accessory_uri_prompt">No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="url">%1$s</xliff:g></string>
@@ -145,10 +145,10 @@
<string name="label_view">View</string>
<!-- Checkbox label for USB device dialogs. [CHAR LIMIT=50] -->
- <string name="always_use_device">Use by default for this USB device</string>
+ <string name="always_use_device">Always open <xliff:g id="application">%1$s</xliff:g> when <xliff:g id="usb_device">%2$s</xliff:g> is connected</string>
- <!-- Checkbox label for USB accessory dialogs. [CHAR LIMIT=50] -->
- <string name="always_use_accessory">Use by default for this USB accessory</string>
+ <!-- Checkbox label for USB accessory dialogs. [CHAR LIMIT=50]-->
+ <string name="always_use_accessory">Always open <xliff:g id="application">%1$s</xliff:g> when <xliff:g id="usb_accessory">%2$s</xliff:g> is connected</string>
<!-- Title of confirmation dialog for USB debugging -->
<string name="usb_debugging_title">Allow USB debugging?</string>
diff --git a/packages/SystemUI/shared/Android.mk b/packages/SystemUI/shared/Android.mk
index e177d1d..5f75f7d 100644
--- a/packages/SystemUI/shared/Android.mk
+++ b/packages/SystemUI/shared/Android.mk
@@ -30,7 +30,7 @@
include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := SharedDummyLib
+LOCAL_PACKAGE_NAME := SystemUISharedLib
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := SystemUISharedLib
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index d82b010..cc4bc58 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -16,8 +16,8 @@
package com.android.systemui.shared.recents;
-import android.graphics.Bitmap;
import android.graphics.Rect;
+import com.android.systemui.shared.system.GraphicBufferCompat;
/**
* Temporary callbacks into SystemUI.
@@ -25,9 +25,8 @@
interface ISystemUiProxy {
/**
- * Proxies SurfaceControl.screenshot().
+ * Proxies SurfaceControl.screenshotToBuffer().
*/
- Bitmap screenshot(in Rect sourceCrop, int width, int height,
- int minLayer, int maxLayer, boolean useIdentityTransform,
- int rotation);
+ GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
+ int maxLayer, boolean useIdentityTransform, int rotation);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
index f92509d..4834bb1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
@@ -45,6 +45,11 @@
*/
public class RecentsTaskLoadPlan {
+ /** The set of conditions to preload tasks. */
+ public static class PreloadOptions {
+ public boolean loadTitles = true;
+ }
+
/** The set of conditions to load tasks. */
public static class Options {
public int runningTaskId = -1;
@@ -80,7 +85,8 @@
* Note: Do not lock, since this can be calling back to the loader, which separately also drives
* this call (callers should synchronize on the loader before making this call).
*/
- public void preloadPlan(RecentsTaskLoader loader, int runningTaskId, int currentUserId) {
+ public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId,
+ int currentUserId) {
Resources res = mContext.getResources();
ArrayList<Task> allTasks = new ArrayList<>();
if (mRawTasks == null) {
@@ -110,9 +116,12 @@
}
// Load the title, icon, and color
- String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
- String titleDescription = loader.getAndUpdateContentDescription(taskKey,
- t.taskDescription);
+ String title = opts.loadTitles
+ ? loader.getAndUpdateActivityTitle(taskKey, t.taskDescription)
+ : "";
+ String titleDescription = opts.loadTitles
+ ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription)
+ : "";
Drawable icon = isStackTask
? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
: null;
@@ -153,7 +162,7 @@
Resources res = mContext.getResources();
// Iterate through each of the tasks and load them according to the load conditions.
- ArrayList<Task> tasks = mStack.getStackTasks();
+ ArrayList<Task> tasks = mStack.getTasks();
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task task = tasks.get(i);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
index 9a991cf..0f68026 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
@@ -32,6 +32,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.Options;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.TaskKeyLruCache.EvictionCallback;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -155,7 +156,7 @@
int currentUserId) {
try {
Trace.beginSection("preloadPlan");
- plan.preloadPlan(this, runningTaskId, currentUserId);
+ plan.preloadPlan(new PreloadOptions(), this, runningTaskId, currentUserId);
} finally {
Trace.endSection();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java
index 693379d..a369397 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java
@@ -19,7 +19,6 @@
import android.content.ComponentName;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.SparseArray;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.utilities.AnimationProps;
@@ -92,7 +91,7 @@
boolean dismissRecentsIfAllRemoved) {
if (mStackTaskList.contains(t)) {
mStackTaskList.remove(t);
- Task newFrontMostTask = getStackFrontMostTask();
+ Task newFrontMostTask = getFrontMostTask();
if (mCb != null) {
// Notify that a task has been removed
mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation,
@@ -183,7 +182,7 @@
// Only callback for the removed tasks after the stack has updated
int removedTaskCount = removedTasks.size();
- Task newFrontMostTask = getStackFrontMostTask();
+ Task newFrontMostTask = getFrontMostTask();
for (int i = 0; i < removedTaskCount; i++) {
mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask,
AnimationProps.IMMEDIATE, false /* fromDockGesture */,
@@ -205,7 +204,7 @@
/**
* Gets the front-most task in the stack.
*/
- public Task getStackFrontMostTask() {
+ public Task getFrontMostTask() {
ArrayList<Task> stackTasks = mStackTaskList.getTasks();
if (stackTasks.isEmpty()) {
return null;
@@ -228,7 +227,7 @@
/**
* Returns the set of "active" (non-historical) tasks in the stack that have been used recently.
*/
- public ArrayList<Task> getStackTasks() {
+ public ArrayList<Task> getTasks() {
return mStackTaskList.getTasks();
}
@@ -242,16 +241,9 @@
}
/**
- * Returns the number of stacktasks.
- */
- public int getTaskCount() {
- return mStackTaskList.size();
- }
-
- /**
* Returns the number of stack tasks.
*/
- public int getStackTaskCount() {
+ public int getTaskCount() {
return mStackTaskList.size();
}
@@ -298,7 +290,7 @@
if (nextLaunchTarget != null) {
return nextLaunchTarget;
}
- return getStackTasks().get(getTaskCount() - 1);
+ return getTasks().get(getTaskCount() - 1);
}
private Task getNextLaunchTargetRaw() {
@@ -306,15 +298,15 @@
if (taskCount == 0) {
return null;
}
- int launchTaskIndex = indexOfStackTask(getLaunchTarget());
+ int launchTaskIndex = indexOfTask(getLaunchTarget());
if (launchTaskIndex != -1 && launchTaskIndex > 0) {
- return getStackTasks().get(launchTaskIndex - 1);
+ return getTasks().get(launchTaskIndex - 1);
}
return null;
}
/** Returns the index of this task in this current task stack */
- public int indexOfStackTask(Task t) {
+ public int indexOfTask(Task t) {
return mStackTaskList.indexOf(t);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AppTrace.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AppTrace.java
new file mode 100644
index 0000000..0241c59
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AppTrace.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.recents.utilities;
+
+import static android.os.Trace.TRACE_TAG_APP;
+
+/**
+ * Helper class for internal trace functions.
+ */
+public class AppTrace {
+
+ /**
+ * Begins a new async trace section with the given {@param key} and {@param cookie}.
+ */
+ public static void start(String key, int cookie) {
+ android.os.Trace.asyncTraceBegin(TRACE_TAG_APP, key, cookie);
+ }
+
+ /**
+ * Begins a new async trace section with the given {@param key}.
+ */
+ public static void start(String key) {
+ android.os.Trace.asyncTraceBegin(TRACE_TAG_APP, key, 0);
+ }
+
+ /**
+ * Ends an existing async trace section with the given {@param key}.
+ */
+ public static void end(String key) {
+ android.os.Trace.asyncTraceEnd(TRACE_TAG_APP, key, 0);
+ }
+
+ /**
+ * Ends an existing async trace section with the given {@param key} and {@param cookie}.
+ */
+ public static void end(String key, int cookie) {
+ android.os.Trace.asyncTraceEnd(TRACE_TAG_APP, key, cookie);
+ }
+
+ /**
+ * Begins a new trace section with the given {@param key}. Can be nested.
+ */
+ public static void beginSection(String key) {
+ android.os.Trace.beginSection(key);
+ }
+
+ /**
+ * Ends an existing trace section started in the last {@link #beginSection(String)}.
+ */
+ public static void endSection() {
+ android.os.Trace.endSection();
+ }
+
+ /**
+ * Traces a counter value.
+ */
+ public static void count(String name, int count) {
+ android.os.Trace.traceCounter(TRACE_TAG_APP, name, count);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java
index b598ec6..45728c4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents.views;
+package com.android.systemui.shared.recents.view;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -24,22 +24,19 @@
import com.android.systemui.shared.recents.utilities.Utilities;
-/* An outline provider that has a clip and outline that can be animated. */
+/**
+ * An outline provider that has a clip and outline that can be animated.
+ */
public class AnimateableViewBounds extends ViewOutlineProvider {
private static final float MIN_ALPHA = 0.1f;
private static final float MAX_ALPHA = 0.8f;
protected View mSourceView;
- @ViewDebug.ExportedProperty(category="recents")
protected Rect mClipRect = new Rect();
- @ViewDebug.ExportedProperty(category="recents")
protected Rect mClipBounds = new Rect();
- @ViewDebug.ExportedProperty(category="recents")
protected Rect mLastClipBounds = new Rect();
- @ViewDebug.ExportedProperty(category="recents")
protected int mCornerRadius;
- @ViewDebug.ExportedProperty(category="recents")
protected float mAlpha = 1f;
public AnimateableViewBounds(View source, int cornerRadius) {
@@ -73,7 +70,7 @@
/**
* Sets the view outline alpha.
*/
- void setAlpha(float alpha) {
+ public void setAlpha(float alpha) {
if (Float.compare(alpha, mAlpha) != 0) {
mAlpha = alpha;
// TODO, If both clip and alpha change in the same frame, only invalidate once
@@ -88,28 +85,43 @@
return mAlpha;
}
- /** Sets the top clip. */
+ /**
+ * Sets the top clip.
+ */
public void setClipTop(int top) {
mClipRect.top = top;
updateClipBounds();
}
- /** Returns the top clip. */
+ /**
+ * @return the top clip.
+ */
public int getClipTop() {
return mClipRect.top;
}
- /** Sets the bottom clip. */
+ /**
+ * Sets the bottom clip.
+ */
public void setClipBottom(int bottom) {
mClipRect.bottom = bottom;
updateClipBounds();
}
- /** Returns the bottom clip. */
+ /**
+ * @return the bottom clip.
+ */
public int getClipBottom() {
return mClipRect.bottom;
}
+ /**
+ * @return the clip bounds.
+ */
+ public Rect getClipBounds() {
+ return mClipBounds;
+ }
+
protected void updateClipBounds() {
mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top),
mSourceView.getWidth() - Math.max(0, mClipRect.right),
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecCompat.java
new file mode 100644
index 0000000..ebdc884
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecCompat.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.recents.view;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.view.AppTransitionAnimationSpec;
+
+/**
+ * Wraps the internal app transition animation spec.
+ */
+public class AppTransitionAnimationSpecCompat {
+
+ private int mTaskId;
+ private Bitmap mBuffer;
+ private Rect mRect;
+
+ public AppTransitionAnimationSpecCompat(int taskId, Bitmap buffer, Rect rect) {
+ mTaskId = taskId;
+ mBuffer = buffer;
+ mRect = rect;
+ }
+
+ public AppTransitionAnimationSpec toAppTransitionAnimationSpec() {
+ return new AppTransitionAnimationSpec(mTaskId,
+ mBuffer != null ? mBuffer.createGraphicBufferHandle() : null, mRect);
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java
index 7a24071..85d362a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AppTransitionAnimationSpecsFuture.java
@@ -32,10 +32,10 @@
public abstract class AppTransitionAnimationSpecsFuture {
private final Handler mHandler;
- private FutureTask<List<AppTransitionAnimationSpec>> mComposeTask = new FutureTask<>(
- new Callable<List<AppTransitionAnimationSpec>>() {
+ private FutureTask<List<AppTransitionAnimationSpecCompat>> mComposeTask = new FutureTask<>(
+ new Callable<List<AppTransitionAnimationSpecCompat>>() {
@Override
- public List<AppTransitionAnimationSpec> call() throws Exception {
+ public List<AppTransitionAnimationSpecCompat> call() throws Exception {
return composeSpecs();
}
});
@@ -48,13 +48,15 @@
if (!mComposeTask.isDone()) {
mHandler.post(mComposeTask);
}
- List<AppTransitionAnimationSpec> specs = mComposeTask.get();
+ List<AppTransitionAnimationSpecCompat> specs = mComposeTask.get();
if (specs == null) {
return null;
}
AppTransitionAnimationSpec[] arr = new AppTransitionAnimationSpec[specs.size()];
- specs.toArray(arr);
+ for (int i = 0; i < specs.size(); i++) {
+ arr[i] = specs.get(i).toAppTransitionAnimationSpec();
+ }
return arr;
} catch (Exception e) {
return null;
@@ -83,5 +85,5 @@
mComposeTask.run();
}
- public abstract List<AppTransitionAnimationSpec> composeSpecs();
+ public abstract List<AppTransitionAnimationSpecCompat> composeSpecs();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
index f8f86f0..ab89043 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/RecentsTransition.java
@@ -42,7 +42,7 @@
*/
public static ActivityOptions createAspectScaleAnimation(Context context, Handler handler,
boolean scaleUp, AppTransitionAnimationSpecsFuture animationSpecsFuture,
- final OnAnimationStartedListener animationStartCallback) {
+ final Runnable animationStartCallback) {
final OnAnimationStartedListener animStartedListener = new OnAnimationStartedListener() {
private boolean mHandled;
@@ -56,7 +56,7 @@
mHandled = true;
if (animationStartCallback != null) {
- animationStartCallback.onAnimationStarted();
+ animationStartCallback.run();
}
}
};
@@ -71,19 +71,14 @@
* Wraps a animation-start callback in a binder that can be called from window manager.
*/
public static IRemoteCallback wrapStartedListener(final Handler handler,
- final OnAnimationStartedListener listener) {
- if (listener == null) {
+ final Runnable animationStartCallback) {
+ if (animationStartCallback == null) {
return null;
}
return new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle data) throws RemoteException {
- handler.post(new Runnable() {
- @Override
- public void run() {
- listener.onAnimationStarted();
- }
- });
+ handler.post(animationStartCallback);
}
};
}
@@ -92,9 +87,9 @@
* @return a {@link GraphicBuffer} with the {@param view} drawn into it. Result can be null if
* we were unable to allocate a hardware bitmap.
*/
- public static GraphicBuffer drawViewIntoGraphicBuffer(int width, int height, final View view,
+ public static Bitmap drawViewIntoHardwareBitmap(int width, int height, final View view,
final float scale, final int eraseColor) {
- final Bitmap hwBitmap = createHardwareBitmap(width, height, new Consumer<Canvas>() {
+ return createHardwareBitmap(width, height, new Consumer<Canvas>() {
@Override
public void accept(Canvas c) {
c.scale(scale, scale);
@@ -106,7 +101,6 @@
}
}
});
- return hwBitmap != null ? hwBitmap.createGraphicBufferHandle() : null;
}
/**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index db1583a..f6fab86 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -29,6 +29,7 @@
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityOptions;
import android.app.AppGlobals;
+import android.app.IAssistDataReceiver;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -39,7 +40,9 @@
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.IconDrawableFactory;
@@ -62,12 +65,14 @@
private final PackageManager mPackageManager;
private final IconDrawableFactory mDrawableFactory;
private final BackgroundExecutor mBackgroundExecutor;
+ private final TaskStackChangeListeners mTaskStackChangeListeners;
private ActivityManagerWrapper() {
final Context context = AppGlobals.getInitialApplication();
mPackageManager = context.getPackageManager();
mDrawableFactory = IconDrawableFactory.newInstance(context);
mBackgroundExecutor = BackgroundExecutor.get();
+ mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper());
}
public static ActivityManagerWrapper getInstance() {
@@ -232,13 +237,54 @@
}
/**
+ * Starts the recents activity. The caller should manage the thread on which this is called.
+ */
+ public void startRecentsActivity(AssistDataReceiverCompat assistDataReceiver, Bundle options,
+ ActivityOptions opts, int userId, Consumer<Boolean> resultCallback,
+ Handler resultCallbackHandler) {
+ Bundle activityOptions = opts != null ? opts.toBundle() : null;
+ try {
+ IAssistDataReceiver receiver = null;
+ if (assistDataReceiver != null) {
+ receiver = new IAssistDataReceiver.Stub() {
+ public void onHandleAssistData(Bundle resultData) {
+ assistDataReceiver.onHandleAssistData(resultData);
+ }
+ public void onHandleAssistScreenshot(Bitmap screenshot) {
+ assistDataReceiver.onHandleAssistScreenshot(screenshot);
+ }
+ };
+ }
+ ActivityManager.getService().startRecentsActivity(receiver, options, activityOptions,
+ userId);
+ if (resultCallback != null) {
+ resultCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ resultCallback.accept(true);
+ }
+ });
+ }
+ } catch (Exception e) {
+ if (resultCallback != null) {
+ resultCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ resultCallback.accept(false);
+ }
+ });
+ }
+ }
+ }
+
+ /**
* Starts a task from Recents.
*
- * @see {@link #startActivityFromRecents(TaskKey, ActivityOptions, int, int, Consumer, Handler)}
+ * @see {@link #startActivityFromRecentsAsync(TaskKey, ActivityOptions, int, int, Consumer, Handler)}
*/
- public void startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options,
+ public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
- startActivityFromRecents(taskKey, options, WINDOWING_MODE_UNDEFINED,
+ startActivityFromRecentsAsync(taskKey, options, WINDOWING_MODE_UNDEFINED,
ACTIVITY_TYPE_UNDEFINED, resultCallback, resultCallbackHandler);
}
@@ -248,7 +294,7 @@
* @param resultCallback The result success callback
* @param resultCallbackHandler The handler to receive the result callback
*/
- public void startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options,
+ public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
int windowingMode, int activityType, Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
@@ -270,30 +316,66 @@
// Execute this from another thread such that we can do other things (like caching the
// bitmap for the thumbnail) while AM is busy starting our activity.
- mBackgroundExecutor.submit(() -> {
- try {
- ActivityManager.getService().startActivityFromRecents(taskKey.id,
- finalOptions == null ? null : finalOptions.toBundle());
- if (resultCallback != null) {
- resultCallbackHandler.post(() -> resultCallback.accept(true));
- }
- } catch (Exception e) {
- if (resultCallback != null) {
- resultCallbackHandler.post(() -> resultCallback.accept(false));
+ mBackgroundExecutor.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityManager.getService().startActivityFromRecents(taskKey.id,
+ finalOptions == null ? null : finalOptions.toBundle());
+ if (resultCallback != null) {
+ resultCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ resultCallback.accept(true);
+ }
+ });
+ }
+ } catch (Exception e) {
+ if (resultCallback != null) {
+ resultCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ resultCallback.accept(false);
+ }
+ });
+ }
}
}
});
}
/**
+ * Registers a task stack listener with the system.
+ * This should be called on the main thread.
+ */
+ public void registerTaskStackListener(TaskStackChangeListener listener) {
+ synchronized (mTaskStackChangeListeners) {
+ mTaskStackChangeListeners.addListener(ActivityManager.getService(), listener);
+ }
+ }
+
+ /**
+ * Unregisters a task stack listener with the system.
+ * This should be called on the main thread.
+ */
+ public void unregisterTaskStackListener(TaskStackChangeListener listener) {
+ synchronized (mTaskStackChangeListeners) {
+ mTaskStackChangeListeners.removeListener(listener);
+ }
+ }
+
+ /**
* Requests that the system close any open system windows (including other SystemUI).
*/
public void closeSystemWindows(String reason) {
- mBackgroundExecutor.submit(() -> {
- try {
- ActivityManager.getService().closeSystemDialogs(reason);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to close system windows", e);
+ mBackgroundExecutor.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityManager.getService().closeSystemDialogs(reason);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to close system windows", e);
+ }
}
});
}
@@ -302,11 +384,14 @@
* Removes a task by id.
*/
public void removeTask(int taskId) {
- mBackgroundExecutor.submit(() -> {
- try {
- ActivityManager.getService().removeTask(taskId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to remove task=" + taskId, e);
+ mBackgroundExecutor.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityManager.getService().removeTask(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to remove task=" + taskId, e);
+ }
}
});
}
diff --git a/core/java/com/android/internal/app/IAssistDataReceiver.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
similarity index 66%
copy from core/java/com/android/internal/app/IAssistDataReceiver.aidl
copy to packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
index 9c9ffef..cd943f6 100644
--- a/core/java/com/android/internal/app/IAssistDataReceiver.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
@@ -11,16 +11,18 @@
* 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.internal.app;
+package com.android.systemui.shared.system;
import android.graphics.Bitmap;
import android.os.Bundle;
-/** @hide */
-oneway interface IAssistDataReceiver {
- void onHandleAssistData(in Bundle resultData);
- void onHandleAssistScreenshot(in Bitmap screenshot);
+/**
+ * Abstract class for assist data receivers.
+ */
+public abstract class AssistDataReceiverCompat {
+ public abstract void onHandleAssistData(Bundle resultData);
+ public abstract void onHandleAssistScreenshot(Bitmap screenshot);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java
index 26e1813..0bd89a7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BackgroundExecutor.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.system;
+import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -37,9 +38,24 @@
}
/**
+ * Runs the given {@param callable} on one of the background executor threads.
+ */
+ public <T> Future<T> submit(Callable<T> callable) {
+ return mExecutorService.submit(callable);
+ }
+
+ /**
* Runs the given {@param runnable} on one of the background executor threads.
*/
public Future<?> submit(Runnable runnable) {
return mExecutorService.submit(runnable);
}
+
+ /**
+ * Runs the given {@param runnable} on one of the background executor threads. Return
+ * {@param result} when the future is resolved.
+ */
+ public <T> Future<T> submit(Runnable runnable, T result) {
+ return mExecutorService.submit(runnable, result);
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ChoreographerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ChoreographerCompat.java
new file mode 100644
index 0000000..4d422bb
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ChoreographerCompat.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.system;
+
+import static android.view.Choreographer.CALLBACK_INPUT;
+
+import android.view.Choreographer;
+
+/**
+ * Wraps the internal choreographer.
+ */
+public class ChoreographerCompat {
+
+ /**
+ * Posts an input callback to the choreographer.
+ */
+ public static void postInputFrame(Choreographer choreographer, Runnable runnable) {
+ choreographer.postCallback(CALLBACK_INPUT, runnable, null);
+ }
+}
diff --git a/core/java/com/android/internal/app/IAssistDataReceiver.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.aidl
similarity index 70%
copy from core/java/com/android/internal/app/IAssistDataReceiver.aidl
copy to packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.aidl
index 9c9ffef..f9450ad 100644
--- a/core/java/com/android/internal/app/IAssistDataReceiver.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.aidl
@@ -14,13 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.app;
+package com.android.systemui.shared.system;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-
-/** @hide */
-oneway interface IAssistDataReceiver {
- void onHandleAssistData(in Bundle resultData);
- void onHandleAssistScreenshot(in Bitmap screenshot);
-}
+parcelable GraphicBufferCompat;
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java
new file mode 100644
index 0000000..66b8fed
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/GraphicBufferCompat.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.system;
+
+import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Wraps the internal graphic buffer.
+ */
+public class GraphicBufferCompat implements Parcelable {
+
+ private GraphicBuffer mBuffer;
+
+ public GraphicBufferCompat(GraphicBuffer buffer) {
+ mBuffer = buffer;
+ }
+
+ public GraphicBufferCompat(Parcel in) {
+ mBuffer = GraphicBuffer.CREATOR.createFromParcel(in);
+ }
+
+ public Bitmap toBitmap() {
+ return mBuffer != null
+ ? Bitmap.createHardwareBitmap(mBuffer)
+ : null;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mBuffer.writeToParcel(dest, flags);
+ }
+
+ public static final Parcelable.Creator<GraphicBufferCompat> CREATOR
+ = new Parcelable.Creator<GraphicBufferCompat>() {
+ public GraphicBufferCompat createFromParcel(Parcel in) {
+ return new GraphicBufferCompat(in);
+ }
+
+ public GraphicBufferCompat[] newArray(int size) {
+ return new GraphicBufferCompat[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 36310f1..17cb0d8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -20,6 +20,8 @@
import android.os.UserHandle;
import android.util.Log;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
/**
* An interface to track task stack changes. Classes should implement this instead of
* {@link android.app.ITaskStackListener} to reduce IPC calls from system services.
@@ -31,7 +33,7 @@
// Main thread callbacks
public void onTaskStackChanged() { }
- public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
+ public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) { }
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
public void onActivityUnpinned() { }
public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 95fc96f..81c37a9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -26,6 +26,8 @@
import android.os.Trace;
import android.util.Log;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
import java.util.ArrayList;
import java.util.List;
@@ -43,6 +45,7 @@
private final List<TaskStackChangeListener> mTmpListeners = new ArrayList<>();
private final Handler mHandler;
+ private boolean mRegistered;
public TaskStackChangeListeners(Looper looper) {
mHandler = new H(looper);
@@ -50,16 +53,21 @@
public void addListener(IActivityManager am, TaskStackChangeListener listener) {
mTaskStackListeners.add(listener);
- if (mTaskStackListeners.size() == 1) {
+ if (!mRegistered) {
// Register mTaskStackListener to IActivityManager only once if needed.
try {
am.registerTaskStackListener(this);
+ mRegistered = true;
} catch (Exception e) {
Log.w(TAG, "Failed to call registerTaskStackListener", e);
}
}
}
+ public void removeListener(TaskStackChangeListener listener) {
+ mTaskStackListeners.remove(listener);
+ }
+
@Override
public void onTaskStackChanged() throws RemoteException {
// Call the task changed callback for the non-ui thread listeners first
@@ -170,7 +178,7 @@
Trace.beginSection("onTaskSnapshotChanged");
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1,
- (TaskSnapshot) msg.obj);
+ new ThumbnailData((TaskSnapshot) msg.obj));
}
Trace.endSection();
break;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
new file mode 100644
index 0000000..1477558
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.graphics.Rect;
+import android.view.WindowManagerGlobal;
+
+public class WindowManagerWrapper {
+
+ private static final String TAG = "WindowManagerWrapper";
+
+ private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
+
+ public static WindowManagerWrapper getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * @return the stable insets for the primary display.
+ */
+ public void getStableInsets(Rect outStableInsets) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().getStableInsets(DEFAULT_DISPLAY,
+ outStableInsets);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 623fe53..41b007a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -78,6 +78,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.google.android.collect.Lists;
@@ -1187,7 +1188,7 @@
mFpm.addLockoutResetCallback(mLockoutResetCallback);
}
- SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
mUserManager = context.getSystemService(UserManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 0cefe44..22922e7 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -16,40 +16,48 @@
package com.android.systemui;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.view.SurfaceControl;
+import com.android.systemui.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.shared.system.GraphicBufferCompat;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
* Class to send information from overview to launcher with a binder.
*/
-public class OverviewProxyService implements CallbackController<OverviewProxyListener> {
+public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
private static final String TAG = "OverviewProxyService";
private static final long BACKOFF_MILLIS = 5000;
private final Context mContext;
private final Handler mHandler;
- private final Runnable mConnectionRunnable = this::startConnectionToCurrentUser;
+ private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
+ private final ComponentName mLauncherComponentName;
private final DeviceProvisionedController mDeviceProvisionedController
= Dependency.get(DeviceProvisionedController.class);
private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
@@ -58,18 +66,26 @@
private int mConnectionBackoffAttempts;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
- public Bitmap screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
- boolean useIdentityTransform, int rotation) {
+ public GraphicBufferCompat screenshot(Rect sourceCrop, int width, int height, int minLayer,
+ int maxLayer, boolean useIdentityTransform, int rotation) {
long token = Binder.clearCallingIdentity();
try {
- return SurfaceControl.screenshot(sourceCrop, width, height, minLayer, maxLayer,
- useIdentityTransform, rotation);
+ return new GraphicBufferCompat(SurfaceControl.screenshotToBuffer(sourceCrop, width,
+ height, minLayer, maxLayer, useIdentityTransform, rotation));
} finally {
Binder.restoreCallingIdentity(token);
}
}
};
+ private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Reconnect immediately, instead of waiting for resume to arrive.
+ startConnectionToCurrentUser();
+ }
+ };
+
private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -102,33 +118,46 @@
@Override
public void onUserSetupChanged() {
if (mDeviceProvisionedController.isCurrentUserSetup()) {
- startConnectionToCurrentUser();
+ internalConnectToCurrentUser();
}
}
@Override
public void onUserSwitched() {
mConnectionBackoffAttempts = 0;
- startConnectionToCurrentUser();
+ internalConnectToCurrentUser();
}
};
// This is the death handler for the binder from the launcher service
- private final IBinder.DeathRecipient mOverviewServiceDeathRcpt = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- startConnectionToCurrentUser();
- }
- };
+ private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
+ = this::startConnectionToCurrentUser;
public OverviewProxyService(Context context) {
mContext = context;
mHandler = new Handler();
mConnectionBackoffAttempts = 0;
+ mLauncherComponentName = ComponentName
+ .unflattenFromString(context.getString(R.string.config_overviewServiceComponent));
mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
+
+ // Listen for the package update changes.
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addDataScheme("package");
+ filter.addDataSchemeSpecificPart(mLauncherComponentName.getPackageName(),
+ PatternMatcher.PATTERN_LITERAL);
+ mContext.registerReceiver(mLauncherAddedReceiver, filter);
}
public void startConnectionToCurrentUser() {
+ if (mHandler.getLooper() != Looper.myLooper()) {
+ mHandler.post(mConnectionRunnable);
+ } else {
+ internalConnectToCurrentUser();
+ }
+ }
+
+ private void internalConnectToCurrentUser() {
disconnectFromLauncherService();
// If user has not setup yet or already connected, do not try to connect
@@ -137,8 +166,7 @@
}
mHandler.removeCallbacks(mConnectionRunnable);
Intent launcherServiceIntent = new Intent();
- launcherServiceIntent.setComponent(ComponentName.unflattenFromString(
- mContext.getString(R.string.config_overviewServiceComponent)));
+ launcherServiceIntent.setComponent(mLauncherComponentName);
boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
UserHandle.getUserHandleForUid(mDeviceProvisionedController.getCurrentUser()));
@@ -180,6 +208,15 @@
}
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(TAG + " state:");
+ pw.print(" mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
+ pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController
+ .isCurrentUserSetup());
+ pw.print(" isConnected="); pw.println(mOverviewProxy != null);
+ }
+
public interface OverviewProxyListener {
void onConnectionChanged(boolean isConnected);
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d82f9cd..00e8b1a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -80,7 +80,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
+import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
import java.util.ArrayList;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 28adca9..a35ba9f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -28,6 +28,7 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlarmManager;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
@@ -1691,6 +1692,8 @@
mUiOffloadThread.submit(() -> {
// If the stream is muted, don't play the sound
if (mAudioManager.isStreamMute(mUiSoundsStreamType)) return;
+ // If DND blocks the sound, don't play the sound
+ if (areSystemSoundsZenModeBlocked(mContext)) return;
int id = mLockSounds.play(soundId,
mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
@@ -1702,6 +1705,25 @@
}
}
+ private boolean areSystemSoundsZenModeBlocked(Context context) {
+ int zenMode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ZEN_MODE, 0);
+
+ switch (zenMode) {
+ case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+ case Settings.Global.ZEN_MODE_ALARMS:
+ return true;
+ case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+ final NotificationManager noMan = (NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ return (noMan.getNotificationPolicy().priorityCategories
+ & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) == 0;
+ case Settings.Global.ZEN_MODE_OFF:
+ default:
+ return false;
+ }
+ }
+
private void playTrustedSound() {
playSound(mTrustedSoundId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index 4119ec0..b9e9e0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -31,6 +31,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
public class WorkLockActivityController {
private static final String TAG = WorkLockActivityController.class.getSimpleName();
@@ -39,15 +40,15 @@
private final IActivityManager mIam;
public WorkLockActivityController(Context context) {
- this(context, SystemServicesProxy.getInstance(context), ActivityManager.getService());
+ this(context, ActivityManagerWrapper.getInstance(), ActivityManager.getService());
}
@VisibleForTesting
- WorkLockActivityController(Context context, SystemServicesProxy ssp, IActivityManager am) {
+ WorkLockActivityController(Context context, ActivityManagerWrapper am, IActivityManager iAm) {
mContext = context;
- mIam = am;
+ mIam = iAm;
- ssp.registerTaskStackListener(mLockListener);
+ am.registerTaskStackListener(mLockListener);
}
private void startWorkChallengeInTask(int taskId, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 7ef0f15..2963506 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -174,7 +174,7 @@
} catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
- SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
mInputConsumerController = new InputConsumerController(mWindowManager);
mMediaController = new PipMediaController(context, mActivityManager);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 5a91def..eef43d2 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -47,6 +47,7 @@
import com.android.systemui.pip.BasePipManager;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -234,7 +235,7 @@
mActivityManager = ActivityManager.getService();
mWindowManager = WindowManagerGlobal.getWindowManagerService();
- SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index c666d57..9aecc68 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -17,7 +17,6 @@
package com.android.systemui.recents;
import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -96,7 +95,6 @@
import com.android.systemui.recents.views.RecentsView;
import com.android.systemui.recents.views.SystemBarScrimViews;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -471,7 +469,7 @@
if (launchState.launchedFromApp) {
Task launchTarget = stack.getLaunchTarget();
int launchTaskIndexInStack = launchTarget != null
- ? stack.indexOfStackTask(launchTarget)
+ ? stack.indexOfTask(launchTarget)
: 0;
MetricsLogger.count(this, "overview_source_app", 1);
// If from an app, track the stack index of the app in the stack (for affiliated tasks)
@@ -517,7 +515,7 @@
// Notify of the config change
Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this);
- int numStackTasks = mRecentsView.getStack().getStackTaskCount();
+ int numStackTasks = mRecentsView.getStack().getTaskCount();
EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
mLastConfig.orientation != newDeviceConfiguration.orientation,
mLastConfig.densityDpi != newDeviceConfiguration.densityDpi, numStackTasks > 0));
@@ -826,7 +824,7 @@
loader.loadTasks(loadPlan, loadOpts);
TaskStack stack = loadPlan.getTaskStack();
- int numStackTasks = stack.getStackTaskCount();
+ int numStackTasks = stack.getTaskCount();
boolean showDeferredAnimation = numStackTasks > 0;
if (sendConfigChangedEvent) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index d2326ce..14fda95 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -50,24 +50,4 @@
launchedViaDragGesture = false;
launchedViaDockGesture = false;
}
-
- /**
- * Returns the task to focus given the current launch state.
- */
- public int getInitialFocusTaskIndex(int numTasks, boolean useGridLayout) {
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (launchedFromApp) {
- if (useGridLayout) {
- // If coming from another app to the grid layout, focus the front most task
- return numTasks - 1;
- }
-
- // If coming from another app, focus the next task
- return Math.max(0, numTasks - 2);
- } else {
- // If coming from home, focus the front most task
- return numTasks - 1;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 0b816b5..3b1b2f9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -25,11 +25,11 @@
import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
-import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -40,7 +40,6 @@
import android.util.Log;
import android.util.MutableBoolean;
import android.util.Pair;
-import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
import android.view.ViewConfiguration;
import android.view.WindowManager;
@@ -85,6 +84,7 @@
import com.android.systemui.recents.views.TaskViewHeader;
import com.android.systemui.recents.views.TaskViewTransform;
import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -159,7 +159,7 @@
mBackgroundLayoutAlgorithm.update(plan.getTaskStack(), EMPTY_SET, launchState);
VisibilityReport visibilityReport =
mBackgroundLayoutAlgorithm.computeStackVisibilityReport(
- stack.getStackTasks());
+ stack.getTasks());
launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1;
launchOpts.numVisibleTasks = visibilityReport.numVisibleTasks;
@@ -200,14 +200,13 @@
}
@Override
- public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+ public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
// Check this is for the right user
if (!checkCurrentUserId(mContext, false /* debug */)) {
return;
}
- EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId,
- new ThumbnailData(snapshot)));
+ EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId, snapshot));
}
}
@@ -221,13 +220,12 @@
// recents. In this case, we defer the toggle state until then and apply it immediately after.
private static boolean mToggleFollowingTransitionStart = true;
- private ActivityOptions.OnAnimationStartedListener mResetToggleFlagListener =
- new OnAnimationStartedListener() {
- @Override
- public void onAnimationStarted() {
- setWaitingForTransitionStart(false);
- }
- };
+ private Runnable mResetToggleFlagListener = new Runnable() {
+ @Override
+ public void run() {
+ setWaitingForTransitionStart(false);
+ }
+ };
protected Context mContext;
protected Handler mHandler;
@@ -270,8 +268,7 @@
// Register the task stack listener
mTaskStackListener = new TaskStackListenerImpl();
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.registerTaskStackListener(mTaskStackListener);
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
// Initialize the static configuration resources
mDummyStackView = new TaskStackView(mContext);
@@ -535,7 +532,7 @@
boolean isRunningTaskInHomeStack =
runningTask.configuration.windowConfiguration.getActivityType()
== ACTIVITY_TYPE_HOME;
- ArrayList<Task> tasks = focusedStack.getStackTasks();
+ ArrayList<Task> tasks = focusedStack.getTasks();
Task toTask = null;
ActivityOptions launchOpts = null;
int taskCount = tasks.size();
@@ -565,7 +562,7 @@
}
// Launch the task
- ActivityManagerWrapper.getInstance().startActivityFromRecents(toTask.key, launchOpts,
+ ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(toTask.key, launchOpts,
null /* resultCallback */, null /* resultCallbackHandler */);
}
@@ -591,7 +588,7 @@
if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) return;
// Find the task in the recents list
- ArrayList<Task> tasks = focusedStack.getStackTasks();
+ ArrayList<Task> tasks = focusedStack.getTasks();
Task toTask = null;
ActivityOptions launchOpts = null;
int taskCount = tasks.size();
@@ -635,7 +632,7 @@
MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
// Launch the task
- ActivityManagerWrapper.getInstance().startActivityFromRecents(toTask.key, launchOpts,
+ ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(toTask.key, launchOpts,
null /* resultListener */, null /* resultCallbackHandler */);
}
@@ -736,7 +733,10 @@
// However, the window bounds include the insets, so we need to subtract them here to make
// them identical.
if (ssp.hasDockedTask()) {
- windowRect.bottom -= systemInsets.bottom;
+ if (systemInsets.bottom < windowRect.height()) {
+ // Only apply inset if it isn't going to cause the rect height to go negative.
+ windowRect.bottom -= systemInsets.bottom;
+ }
systemInsets.bottom = 0;
}
calculateWindowStableInsets(systemInsets, windowRect, displayRect);
@@ -876,12 +876,12 @@
RectF toTaskRect = toTransform.rect;
AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(mHandler) {
@Override
- public List<AppTransitionAnimationSpec> composeSpecs() {
+ public List<AppTransitionAnimationSpecCompat> composeSpecs() {
Rect rect = new Rect();
toTaskRect.round(rect);
- GraphicBuffer thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
- return Lists.newArrayList(new AppTransitionAnimationSpec(toTask.key.id, thumbnail,
- rect));
+ Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
+ return Lists.newArrayList(new AppTransitionAnimationSpecCompat(toTask.key.id,
+ thumbnail, rect));
}
};
@@ -904,7 +904,7 @@
runningTaskOut.copyFrom(launchTask);
} else {
// If no task is specified or we can not find the task just use the front most one
- launchTask = stack.getStackFrontMostTask();
+ launchTask = stack.getFrontMostTask();
runningTaskOut.copyFrom(launchTask);
}
@@ -919,7 +919,7 @@
/**
* Draws the header of a task used for the window animation into a bitmap.
*/
- private GraphicBuffer drawThumbnailTransitionBitmap(Task toTask,
+ private Bitmap drawThumbnailTransitionBitmap(Task toTask,
TaskViewTransform toTransform) {
SystemServicesProxy ssp = Recents.getSystemServices();
int width = (int) toTransform.rect.width();
@@ -929,7 +929,7 @@
boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
mHeaderBar.onTaskViewSizeChanged(width, height);
if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
- return RecentsTransition.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
+ return RecentsTransition.drawViewIntoHardwareBitmap(width, mTaskBarHeight,
null, 1f, 0xFFff0000);
} else {
// Workaround for b/27815919, reset the callback so that we do not trigger an
@@ -942,7 +942,7 @@
disabledInSafeMode);
mHeaderBar.onTaskDataLoaded();
mHeaderBar.setDimAlpha(toTransform.dimAlpha);
- return RecentsTransition.drawViewIntoGraphicBuffer(width, mTaskBarHeight,
+ return RecentsTransition.drawViewIntoHardwareBitmap(width, mTaskBarHeight,
mHeaderBar, 1f, 0);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index a436e17..d89bab7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -114,7 +114,6 @@
UserManager mUm;
Display mDisplay;
String mRecentsPackage;
- private TaskStackChangeListeners mTaskStackChangeListeners;
private int mCurrentUserId;
boolean mIsSafeMode;
@@ -156,7 +155,6 @@
mRecentsPackage = context.getPackageName();
mIsSafeMode = mPm.isSafeMode();
mCurrentUserId = mAm.getCurrentUser();
- mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper());
// Get the dummy thumbnail width/heights
Resources res = context.getResources();
@@ -208,6 +206,8 @@
*
* @param isHomeStackVisible if provided, will return whether the home stack is visible
* regardless of the recents visibility
+ *
+ * TODO(winsonc): Refactor this check to just use the recents activity lifecycle
*/
public boolean isRecentsActivityVisible(MutableBoolean isHomeStackVisible) {
if (mIam == null) return false;
@@ -222,13 +222,13 @@
final WindowConfiguration winConfig = stackInfo.configuration.windowConfiguration;
final int activityType = winConfig.getActivityType();
final int windowingMode = winConfig.getWindowingMode();
- if (activityType == ACTIVITY_TYPE_HOME) {
+ if (homeStackInfo == null && activityType == ACTIVITY_TYPE_HOME) {
homeStackInfo = stackInfo;
- } else if (activityType == ACTIVITY_TYPE_STANDARD
+ } else if (fullscreenStackInfo == null && activityType == ACTIVITY_TYPE_STANDARD
&& (windowingMode == WINDOWING_MODE_FULLSCREEN
|| windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
fullscreenStackInfo = stackInfo;
- } else if (activityType == ACTIVITY_TYPE_RECENTS) {
+ } else if (recentsStackInfo == null && activityType == ACTIVITY_TYPE_RECENTS) {
recentsStackInfo = stackInfo;
}
}
@@ -492,18 +492,6 @@
}
}
- /**
- * Registers a task stack listener with the system.
- * This should be called on the main thread.
- */
- public void registerTaskStackListener(SysUiTaskStackChangeListener listener) {
- if (mIam == null) return;
-
- synchronized (mTaskStackChangeListeners) {
- mTaskStackChangeListeners.addListener(mIam, listener);
- }
- }
-
public void endProlongedAnimations() {
if (mWm == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
index 99390ec..1c47430 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
@@ -23,14 +23,15 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.util.Log;
-import android.view.AppTransitionAnimationSpec;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
import com.android.systemui.shared.recents.view.RecentsTransition;
import java.util.ArrayList;
@@ -54,9 +55,9 @@
/**
* Composes a single animation spec for the given {@link TaskView}
*/
- private static AppTransitionAnimationSpec composeAnimationSpec(TaskStackView stackView,
+ private static AppTransitionAnimationSpecCompat composeAnimationSpec(TaskStackView stackView,
TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
- GraphicBuffer b = null;
+ Bitmap b = null;
if (addHeaderBitmap) {
b = composeHeaderBitmap(taskView, transform);
if (b == null) {
@@ -71,28 +72,28 @@
// force the task thumbnail to full stackView height immediately causing the transition
// jarring.
if (!Recents.getConfiguration().isLowRamDevice && taskView.getTask() !=
- stackView.getStack().getStackFrontMostTask()) {
+ stackView.getStack().getFrontMostTask()) {
taskRect.bottom = taskRect.top + stackView.getMeasuredHeight();
}
- return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
+ return new AppTransitionAnimationSpecCompat(taskView.getTask().key.id, b, taskRect);
}
/**
* Composes the transition spec when docking a task, which includes a full task bitmap.
*/
- public List<AppTransitionAnimationSpec> composeDockAnimationSpec(TaskView taskView,
+ public List<AppTransitionAnimationSpecCompat> composeDockAnimationSpec(TaskView taskView,
Rect bounds) {
mTmpTransform.fillIn(taskView);
Task task = taskView.getTask();
- GraphicBuffer buffer = RecentsTransitionComposer.composeTaskBitmap(taskView, mTmpTransform);
- return Collections.singletonList(new AppTransitionAnimationSpec(task.key.id, buffer,
+ Bitmap buffer = RecentsTransitionComposer.composeTaskBitmap(taskView, mTmpTransform);
+ return Collections.singletonList(new AppTransitionAnimationSpecCompat(task.key.id, buffer,
bounds));
}
/**
* Composes the animation specs for all the tasks in the target stack.
*/
- public List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
+ public List<AppTransitionAnimationSpecCompat> composeAnimationSpecs(final Task task,
final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) {
// Calculate the offscreen task rect (for tasks that are not backed by views)
TaskView taskView = stackView.getChildViewForTask(task);
@@ -110,13 +111,13 @@
|| windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
|| activityType == ACTIVITY_TYPE_ASSISTANT
|| windowingMode == WINDOWING_MODE_UNDEFINED) {
- List<AppTransitionAnimationSpec> specs = new ArrayList<>();
+ List<AppTransitionAnimationSpecCompat> specs = new ArrayList<>();
if (taskView == null) {
specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
} else {
mTmpTransform.fillIn(taskView);
stackLayout.transformToScreenCoordinates(mTmpTransform, windowRect);
- AppTransitionAnimationSpec spec = composeAnimationSpec(stackView, taskView,
+ AppTransitionAnimationSpecCompat spec = composeAnimationSpec(stackView, taskView,
mTmpTransform, true /* addHeaderBitmap */);
if (spec != null) {
specs.add(spec);
@@ -130,12 +131,12 @@
/**
* Composes a single animation spec for the given {@link Task}
*/
- private static AppTransitionAnimationSpec composeOffscreenAnimationSpec(Task task,
+ private static AppTransitionAnimationSpecCompat composeOffscreenAnimationSpec(Task task,
Rect taskRect) {
- return new AppTransitionAnimationSpec(task.key.id, null, taskRect);
+ return new AppTransitionAnimationSpecCompat(task.key.id, null, taskRect);
}
- public static GraphicBuffer composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
+ public static Bitmap composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
float scale = transform.scale;
int fromWidth = (int) (transform.rect.width() * scale);
int fromHeight = (int) (transform.rect.height() * scale);
@@ -143,19 +144,19 @@
Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() +
" at transform: " + transform);
- return RecentsTransition.drawViewIntoGraphicBuffer(1, 1, null, 1f, 0x00ffffff);
+ return RecentsTransition.drawViewIntoHardwareBitmap(1, 1, null, 1f, 0x00ffffff);
} else {
if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
- return RecentsTransition.drawViewIntoGraphicBuffer(fromWidth, fromHeight, null, 1f,
+ return RecentsTransition.drawViewIntoHardwareBitmap(fromWidth, fromHeight, null, 1f,
0xFFff0000);
} else {
- return RecentsTransition.drawViewIntoGraphicBuffer(fromWidth, fromHeight, taskView,
+ return RecentsTransition.drawViewIntoHardwareBitmap(fromWidth, fromHeight, taskView,
scale, 0);
}
}
}
- private static GraphicBuffer composeHeaderBitmap(TaskView taskView,
+ private static Bitmap composeHeaderBitmap(TaskView taskView,
TaskViewTransform transform) {
float scale = transform.scale;
int headerWidth = (int) (transform.rect.width());
@@ -165,10 +166,10 @@
}
if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
- return RecentsTransition.drawViewIntoGraphicBuffer(headerWidth, headerHeight, null, 1f,
+ return RecentsTransition.drawViewIntoHardwareBitmap(headerWidth, headerHeight, null, 1f,
0xFFff0000);
} else {
- return RecentsTransition.drawViewIntoGraphicBuffer(headerWidth, headerHeight,
+ return RecentsTransition.drawViewIntoHardwareBitmap(headerWidth, headerHeight,
taskView.mHeaderView, scale, 0);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index b82f15e..1440fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,7 +16,6 @@
package com.android.systemui.recents.views;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY;
import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
import android.animation.ValueAnimator;
@@ -90,6 +89,7 @@
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -296,7 +296,7 @@
* @return True if it changed.
*/
private boolean updateBusyness() {
- final int taskCount = mTaskStackView.getStack().getStackTaskCount();
+ final int taskCount = mTaskStackView.getStack().getTaskCount();
final float busyness = Math.min(taskCount, BUSY_RECENTS_TASK_COUNT)
/ (float) BUSY_RECENTS_TASK_COUNT;
if (mBusynessFactor == busyness) {
@@ -611,27 +611,23 @@
// Dock the task and launch it
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode)) {
- final OnAnimationStartedListener startedListener =
- new OnAnimationStartedListener() {
- @Override
- public void onAnimationStarted() {
- EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
- // Remove the task and don't bother relaying out, as all the tasks will be
- // relaid out when the stack changes on the multiwindow change event
- getStack().removeTask(event.task, null, true /* fromDockGesture */);
- }
+ final Runnable animStartedListener = () -> {
+ EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
+ // Remove the task and don't bother relaying out, as all the tasks will be
+ // relaid out when the stack changes on the multiwindow change event
+ getStack().removeTask(event.task, null, true /* fromDockGesture */);
};
final Rect taskRect = getTaskRect(event.taskView);
AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(
getHandler()) {
@Override
- public List<AppTransitionAnimationSpec> composeSpecs() {
+ public List<AppTransitionAnimationSpecCompat> composeSpecs() {
return mTransitionHelper.composeDockAnimationSpec(event.taskView, taskRect);
}
};
ssp.overridePendingAppTransitionMultiThumbFuture(future.getFuture(),
- RecentsTransition.wrapStartedListener(getHandler(), startedListener),
+ RecentsTransition.wrapStartedListener(getHandler(), animStartedListener),
true /* scaleUp */);
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
@@ -928,7 +924,7 @@
final TaskStackView stackView, final TaskView taskView,
final boolean screenPinningRequested, final int windowingMode, final int activityType) {
- final ActivityOptions.OnAnimationStartedListener animStartedListener;
+ final Runnable animStartedListener;
final AppTransitionAnimationSpecsFuture transitionFuture;
if (taskView != null) {
@@ -937,16 +933,16 @@
final Rect windowRect = Recents.getSystemServices().getWindowRect();
transitionFuture = new AppTransitionAnimationSpecsFuture(stackView.getHandler()) {
@Override
- public List<AppTransitionAnimationSpec> composeSpecs() {
+ public List<AppTransitionAnimationSpecCompat> composeSpecs() {
return mTransitionHelper.composeAnimationSpecs(task, stackView, windowingMode,
activityType, windowRect);
}
};
- animStartedListener = new OnAnimationStartedListener() {
+ animStartedListener = new Runnable() {
private boolean mHandled;
@Override
- public void onAnimationStarted() {
+ public void run() {
if (mHandled) {
return;
}
@@ -975,11 +971,11 @@
} else {
// This is only the case if the task is not on screen (scrolled offscreen for example)
transitionFuture = null;
- animStartedListener = new OnAnimationStartedListener() {
+ animStartedListener = new Runnable() {
private boolean mHandled;
@Override
- public void onAnimationStarted() {
+ public void run() {
if (mHandled) {
return;
}
@@ -1027,12 +1023,12 @@
private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView,
ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture,
int windowingMode, int activityType) {
- ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key, opts, windowingMode,
- activityType, succeeded -> {
+ ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts,
+ windowingMode, activityType, succeeded -> {
if (succeeded) {
// Keep track of the index of the task launch
int taskIndexFromFront = 0;
- int taskIndex = stack.indexOfStackTask(task);
+ int taskIndex = stack.indexOfTask(task);
if (taskIndex > -1) {
taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 0cfdbde..5c69ae3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -40,7 +40,6 @@
import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
import java.util.ArrayList;
@@ -106,7 +105,7 @@
/** Handles touch events once we have intercepted them */
public boolean onTouchEvent(MotionEvent ev) {
handleTouchEvent(ev);
- if (ev.getAction() == MotionEvent.ACTION_UP && mRv.getStack().getStackTaskCount() == 0) {
+ if (ev.getAction() == MotionEvent.ACTION_UP && mRv.getStack().getTaskCount() == 0) {
EventBus.getDefault().send(new HideRecentsEvent(false, true));
}
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 7827c59..170e39d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -154,7 +154,7 @@
public final void onBusEvent(MultiWindowStateChangedEvent event) {
mHasDockedTasks = event.inMultiWindow;
- animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0);
+ animateScrimToCurrentNavBarState(event.stack.getTaskCount() > 0);
}
public final void onBusEvent(final DragEndEvent event) {
@@ -166,7 +166,7 @@
public final void onBusEvent(final DragEndCancelledEvent event) {
// Restore the scrims to the normal state
- animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0);
+ animateScrimToCurrentNavBarState(event.stack.getTaskCount() > 0);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index 26db26f..67d0978 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -449,7 +449,7 @@
// Get the current set of task transforms
int taskViewCount = mStackView.getTaskViews().size();
- ArrayList<Task> stackTasks = stack.getStackTasks();
+ ArrayList<Task> stackTasks = stack.getTasks();
mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
// Pick up the newly visible views after the scroll
@@ -541,7 +541,7 @@
TaskStackViewScroller stackScroller = mStackView.getScroller();
// Get the current set of task transforms
- ArrayList<Task> stackTasks = newStack.getStackTasks();
+ ArrayList<Task> stackTasks = newStack.getTasks();
mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
// Update the stack
@@ -563,7 +563,7 @@
false /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
// Hide the front most task view until the scroll is complete
- Task frontMostTask = newStack.getStackFrontMostTask();
+ Task frontMostTask = newStack.getFrontMostTask();
final TaskView frontMostTaskView = mStackView.getChildViewForTask(frontMostTask);
final TaskViewTransform frontMostTransform = mTmpFinalTaskTransforms.get(
stackTasks.indexOf(frontMostTask));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index acb058c..600da04 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -438,7 +438,7 @@
mTaskIndexMap.clear();
// Return early if we have no tasks
- ArrayList<Task> tasks = stack.getStackTasks();
+ ArrayList<Task> tasks = stack.getTasks();
if (tasks.isEmpty()) {
mFrontMostTaskP = 0;
mMinScrollP = mMaxScrollP = mInitialScrollP = 0;
@@ -468,7 +468,7 @@
// Calculate the min/max/initial scroll
Task launchTask = stack.getLaunchTarget();
int launchTaskIndex = launchTask != null
- ? stack.indexOfStackTask(launchTask)
+ ? stack.indexOfTask(launchTask)
: mNumStackTasks - 1;
if (getInitialFocusState() == STATE_FOCUSED) {
int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
@@ -557,7 +557,7 @@
}
mUnfocusedRange.offset(0f);
- List<Task> tasks = stack.getStackTasks();
+ List<Task> tasks = stack.getTasks();
int taskCount = tasks.size();
for (int i = taskCount - 1; i >= 0; i--) {
int indexFromFront = taskCount - i - 1;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 428113a..1197501 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -535,7 +535,7 @@
*/
void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
// Get all the task transforms
- ArrayList<Task> tasks = mStack.getStackTasks();
+ ArrayList<Task> tasks = mStack.getTasks();
int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks,
ignoreTaskOverrides);
@@ -557,7 +557,7 @@
// It is possible for the set of lingering TaskViews to differ from the stack if the
// stack was updated before the relayout. If the task view is no longer in the stack,
// then just return it back to the view pool.
- int taskIndex = mStack.indexOfStackTask(task);
+ int taskIndex = mStack.indexOfTask(task);
TaskViewTransform transform = null;
if (taskIndex != -1) {
transform = mCurrentTaskTransforms.get(taskIndex);
@@ -601,7 +601,7 @@
}
} else {
// Reattach it in the right z order
- final int taskIndex = mStack.indexOfStackTask(task);
+ final int taskIndex = mStack.indexOfTask(task);
final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
if (insertIndex != getTaskViews().indexOf(tv)){
detachViewFromParent(tv);
@@ -663,7 +663,7 @@
continue;
}
- int taskIndex = mStack.indexOfStackTask(task);
+ int taskIndex = mStack.indexOfTask(task);
TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
if (animationOverrides != null && animationOverrides.containsKey(task)) {
animation = animationOverrides.get(task);
@@ -866,7 +866,7 @@
int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
Utilities.clamp(focusTaskIndex, 0, mStack.getTaskCount() - 1) : -1;
final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
- mStack.getStackTasks().get(newFocusedTaskIndex) : null;
+ mStack.getTasks().get(newFocusedTaskIndex) : null;
// Reset the last focused task state if changed
if (mFocusedTask != null) {
@@ -953,10 +953,10 @@
public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
boolean cancelWindowAnimations, int timerIndicatorDuration) {
Task focusedTask = getFocusedTask();
- int newIndex = mStack.indexOfStackTask(focusedTask);
+ int newIndex = mStack.indexOfTask(focusedTask);
if (focusedTask != null) {
if (stackTasksOnly) {
- List<Task> tasks = mStack.getStackTasks();
+ List<Task> tasks = mStack.getTasks();
// Try the next task if it is a stack task
int tmpNewIndex = newIndex + (forward ? -1 : 1);
if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
@@ -971,7 +971,7 @@
} else {
// We don't have a focused task
float stackScroll = mStackScroller.getStackScroll();
- ArrayList<Task> tasks = mStack.getStackTasks();
+ ArrayList<Task> tasks = mStack.getTasks();
int taskCount = tasks.size();
if (useGridLayout()) {
// For the grid layout, we directly set focus to the most recently used task
@@ -1060,8 +1060,8 @@
if (taskViewCount > 0) {
TaskView backMostTask = taskViews.get(0);
TaskView frontMostTask = taskViews.get(taskViewCount - 1);
- event.setFromIndex(mStack.indexOfStackTask(backMostTask.getTask()));
- event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
+ event.setFromIndex(mStack.indexOfTask(backMostTask.getTask()));
+ event.setToIndex(mStack.indexOfTask(frontMostTask.getTask()));
event.setContentDescription(frontMostTask.getTask().title);
}
event.setItemCount(mStack.getTaskCount());
@@ -1080,7 +1080,7 @@
// Find the accessibility focused task
Task focusedTask = getAccessibilityFocusedTask();
info.setScrollable(true);
- int focusedTaskIndex = mStack.indexOfStackTask(focusedTask);
+ int focusedTaskIndex = mStack.indexOfTask(focusedTask);
if (focusedTaskIndex > 0 || !mStackActionButtonVisible) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
@@ -1101,7 +1101,7 @@
return true;
}
Task focusedTask = getAccessibilityFocusedTask();
- int taskIndex = mStack.indexOfStackTask(focusedTask);
+ int taskIndex = mStack.indexOfTask(focusedTask);
if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
switch (action) {
case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
@@ -1157,7 +1157,7 @@
* updateLayoutForStack() is called first.
*/
public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
- return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getStackTasks());
+ return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getTasks());
}
/**
@@ -1328,10 +1328,9 @@
// We set the initial focused task view iff the following conditions are satisfied:
// 1. Recents is showing task views in stack layout.
// 2. Recents is launched with ALT + TAB.
- boolean setFocusOnFirstLayout = !useGridLayout() ||
- Recents.getConfiguration().getLaunchState().launchedWithAltTab;
+ boolean setFocusOnFirstLayout = !useGridLayout() || launchState.launchedWithAltTab;
if (setFocusOnFirstLayout) {
- int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount(),
+ int focusedTaskIndex = getInitialFocusTaskIndex(launchState, mStack.getTaskCount(),
useGridLayout());
if (focusedTaskIndex != -1) {
setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
@@ -1502,7 +1501,7 @@
@Override
public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
// Find the index where this task should be placed in the stack
- int taskIndex = mStack.indexOfStackTask(task);
+ int taskIndex = mStack.indexOfTask(task);
int insertIndex = findTaskViewInsertIndex(task, taskIndex);
// Add/attach the view to the hierarchy
@@ -1545,7 +1544,7 @@
}
// Restore the action button visibility if it is the front most task view
- if (mScreenPinningEnabled && tv.getTask() == mStack.getStackFrontMostTask()) {
+ if (mScreenPinningEnabled && tv.getTask() == mStack.getFrontMostTask()) {
tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
}
}
@@ -1669,7 +1668,7 @@
event.packageName, event.userId);
// For other tasks, just remove them directly if they no longer exist
- ArrayList<Task> tasks = mStack.getStackTasks();
+ ArrayList<Task> tasks = mStack.getTasks();
for (int i = tasks.size() - 1; i >= 0; i--) {
final Task t = tasks.get(i);
if (removedComponents.contains(t.key.getComponent())) {
@@ -1692,7 +1691,7 @@
public final void onBusEvent(LaunchMostRecentTaskRequestEvent event) {
if (mStack.getTaskCount() > 0) {
- Task mostRecentTask = mStack.getStackFrontMostTask();
+ Task mostRecentTask = mStack.getFrontMostTask();
launchTask(mostRecentTask);
}
}
@@ -1786,7 +1785,7 @@
public final void onBusEvent(final DismissAllTaskViewsEvent event) {
// Keep track of the tasks which will have their data removed
- ArrayList<Task> tasks = new ArrayList<>(mStack.getStackTasks());
+ ArrayList<Task> tasks = new ArrayList<>(mStack.getTasks());
mAnimationHelper.startDeleteAllTasksAnimation(
getTaskViews(), useGridLayout(), event.getAnimationTrigger());
event.addPostAnimationCallback(new Runnable() {
@@ -1854,7 +1853,7 @@
public final void onBusEvent(NavigateTaskViewEvent event) {
if (useGridLayout()) {
final int taskCount = mStack.getTaskCount();
- final int currentIndex = mStack.indexOfStackTask(getFocusedTask());
+ final int currentIndex = mStack.indexOfTask(getFocusedTask());
final int nextIndex = mLayoutAlgorithm.mTaskGridLayoutAlgorithm.navigateFocus(taskCount,
currentIndex, event.direction);
setFocusedTask(nextIndex, false, true);
@@ -2000,7 +1999,7 @@
if (mFocusedTask != null) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- setFocusedTask(mStack.indexOfStackTask(mFocusedTask),
+ setFocusedTask(mStack.indexOfTask(mFocusedTask),
false /* scrollToTask */, launchState.launchedWithAltTab);
TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
if (mTouchExplorationEnabled && focusedTaskView != null) {
@@ -2131,7 +2130,7 @@
Task tvTask = taskViews.get(i).getTask();
if (tvTask == task) {
foundTaskView = true;
- } else if (taskIndex < mStack.indexOfStackTask(tvTask)) {
+ } else if (taskIndex < mStack.indexOfTask(tvTask)) {
if (foundTaskView) {
return i - 1;
} else {
@@ -2201,6 +2200,25 @@
}
/**
+ * Returns the task to focus given the current launch state.
+ */
+ private int getInitialFocusTaskIndex(RecentsActivityLaunchState launchState, int numTasks,
+ boolean useGridLayout) {
+ if (launchState.launchedFromApp) {
+ if (useGridLayout) {
+ // If coming from another app to the grid layout, focus the front most task
+ return numTasks - 1;
+ }
+
+ // If coming from another app, focus the next task
+ return Math.max(0, numTasks - 2);
+ } else {
+ // If coming from home, focus the front most task
+ return numTasks - 1;
+ }
+ }
+
+ /**
* Updates {@param transforms} to be the same size as {@param tasks}.
*/
private void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index b9ca248..bfaa8cd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -446,7 +446,7 @@
TaskView tv = (TaskView) v;
Task task = tv.getTask();
return !mSwipeHelperAnimations.containsKey(v) &&
- (mSv.getStack().indexOfStackTask(task) != -1);
+ (mSv.getStack().indexOfTask(task) != -1);
}
/**
@@ -476,7 +476,7 @@
mSv.addIgnoreTask(tv.getTask());
// Determine if we are animating the other tasks while dismissing this task
- mCurrentTasks = new ArrayList<Task>(mSv.getStack().getStackTasks());
+ mCurrentTasks = new ArrayList<Task>(mSv.getStack().getTasks());
MutableBoolean isFrontMostTask = new MutableBoolean(false);
Task anchorTask = mSv.findAnchorTask(mCurrentTasks, isFrontMostTask);
TaskStackLayoutAlgorithm layoutAlgorithm = mSv.getStackAlgorithm();
@@ -673,7 +673,7 @@
/** Returns the view at the specified coordinates */
private TaskView findViewAtPoint(int x, int y) {
- List<Task> tasks = mSv.getStack().getStackTasks();
+ List<Task> tasks = mSv.getStack().getTasks();
int taskCount = tasks.size();
for (int i = taskCount - 1; i >= 0; i--) {
TaskView tv = mSv.getChildViewForTask(tasks.get(i));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index b440847..f0278a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -55,6 +55,7 @@
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.recents.view.AnimateableViewBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -683,7 +684,7 @@
}
SystemServicesProxy ssp = Recents.getSystemServices();
boolean inBounds = false;
- Rect clipBounds = new Rect(mViewBounds.mClipBounds);
+ Rect clipBounds = new Rect(mViewBounds.getClipBounds());
if (!clipBounds.isEmpty()) {
// If we are clipping the view to the bounds, manually do the hit test.
clipBounds.scale(getScaleX());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
index a029478..3bdad31 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.views.grid;
import android.view.View;
-import com.android.systemui.recents.views.AnimateableViewBounds;
+import com.android.systemui.shared.recents.view.AnimateableViewBounds;
/* An outline provider for grid-based task views. */
class AnimateableGridViewBounds extends AnimateableViewBounds {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
index 8b4700c..0d51154 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
@@ -19,7 +19,7 @@
import android.content.Context;
import android.util.AttributeSet;
import com.android.systemui.R;
-import com.android.systemui.recents.views.AnimateableViewBounds;
+import com.android.systemui.shared.recents.view.AnimateableViewBounds;
import com.android.systemui.recents.views.TaskView;
public class GridTaskView extends TaskView {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
index 95f1d58..fe6bafb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
@@ -125,7 +125,7 @@
// We're returning from touch mode, set the focus to the previously focused task.
final TaskStack stack = mSv.getStack();
final int taskCount = stack.getTaskCount();
- final int k = stack.indexOfStackTask(mSv.getFocusedTask());
+ final int k = stack.indexOfTask(mSv.getFocusedTask());
final int taskIndexToFocus = k == -1 ? (taskCount - 1) : (k % taskCount);
mSv.setFocusedTask(taskIndexToFocus, false, true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index b2bbe30..826fa6c 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -33,6 +33,7 @@
import com.android.systemui.recents.events.component.ShowUserToastEvent;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.stackdivider.events.StartedDragingEvent;
import com.android.systemui.stackdivider.events.StoppedDragingEvent;
@@ -75,7 +76,7 @@
public ForcedResizableInfoActivityController(Context context) {
mContext = context;
EventBus.getDefault().register(this);
- SystemServicesProxy.getInstance(context).registerTaskStackListener(
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(
new SysUiTaskStackChangeListener() {
@Override
public void onActivityForcedResizable(String packageName, int taskId,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index b585bdf..f451fda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -37,6 +37,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Interpolators;
@@ -75,17 +76,20 @@
private NotificationGuts mNotificationGutsExposed;
private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
private final NotificationInfo.CheckSaveListener mCheckSaveListener;
+ private final OnSettingsClickListener mOnSettingsClickListener;
private String mKeyToRemoveOnGutsClosed;
public NotificationGutsManager(
NotificationPresenter presenter,
NotificationStackScrollLayout stackScroller,
NotificationInfo.CheckSaveListener checkSaveListener,
- Context context) {
+ Context context,
+ OnSettingsClickListener onSettingsClickListener) {
mPresenter = presenter;
mStackScroller = stackScroller;
mCheckSaveListener = checkSaveListener;
mContext = context;
+ mOnSettingsClickListener = onSettingsClickListener;
Resources res = context.getResources();
mNonBlockablePkgs = new HashSet<>();
@@ -189,6 +193,7 @@
onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
guts.resetFalsingCheck();
+ mOnSettingsClickListener.onClick(sbn.getKey());
startAppNotificationSettingsActivity(pkg, appUid, channel);
};
}
@@ -352,4 +357,8 @@
pw.print("mKeyToRemoveOnGutsClosed: ");
pw.println(mKeyToRemoveOnGutsClosed);
}
+
+ public interface OnSettingsClickListener {
+ void onClick(String key);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 41cae6b..5941af2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -85,7 +85,7 @@
public void start() {
super.start();
mTaskStackListener = new TaskStackListenerImpl();
- SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
registerPackageChangeReceivers();
mStackScroller.setScrollingEnabled(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index 4c2a2f5..fc420eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -383,6 +383,12 @@
}
}
+ @Override
+ public void prepareFadeIn() {
+ super.prepareFadeIn();
+ setVisible(true /* visible */, false /* force */);
+ }
+
private void resetTransformedView(View child) {
TransformState ownState = TransformState.createFrom(child, mTransformInfo);
ownState.resetTransformedView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 4180884..bed6d82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -19,6 +19,7 @@
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
@@ -76,6 +77,8 @@
private final GestureDetector mTaskSwitcherDetector;
private final int mScrollTouchSlop;
private final int mMinFlingVelocity;
+ private final Matrix mTransformGlobalMatrix = new Matrix();
+ private final Matrix mTransformLocalMatrix = new Matrix();
private int mTouchDownX;
private int mTouchDownY;
private boolean mDownOnRecents;
@@ -116,27 +119,30 @@
final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
if (overviewProxy != null) {
mNavigationBarView.requestUnbufferedDispatch(event);
+ event.transform(mTransformGlobalMatrix);
try {
overviewProxy.onMotionEvent(event);
return true;
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
+ } finally {
+ event.transform(mTransformLocalMatrix);
}
}
return false;
}
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (!proxyMotionEvents(event)) {
- // If we move more than a fixed amount, then start capturing for the
- // task switcher detector, disabled when proxying motion events to launcher service
- mTaskSwitcherDetector.onTouchEvent(event);
- }
int action = event.getAction();
+ boolean result = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
mTouchDownX = (int) event.getX();
mTouchDownY = (int) event.getY();
+ mTransformGlobalMatrix.set(Matrix.IDENTITY_MATRIX);
+ mTransformLocalMatrix.set(Matrix.IDENTITY_MATRIX);
+ mNavigationBarView.transformMatrixToGlobal(mTransformGlobalMatrix);
+ mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -147,7 +153,7 @@
boolean exceededTouchSlop = xDiff > mScrollTouchSlop && xDiff > yDiff
|| yDiff > mScrollTouchSlop && yDiff > xDiff;
if (exceededTouchSlop) {
- return true;
+ result = true;
}
break;
}
@@ -155,7 +161,12 @@
case MotionEvent.ACTION_UP:
break;
}
- return mDockWindowEnabled && interceptDockWindowEvent(event);
+ if (!proxyMotionEvents(event)) {
+ // If we move more than a fixed amount, then start capturing for the
+ // task switcher detector, disabled when proxying motion events to launcher service
+ mTaskSwitcherDetector.onTouchEvent(event);
+ }
+ return result || (mDockWindowEnabled && interceptDockWindowEvent(event));
}
private boolean interceptDockWindowEvent(MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index ba4ff58..09fe579 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -67,6 +67,7 @@
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.BluetoothController;
@@ -249,7 +250,7 @@
mLocationController.addCallback(this);
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
- SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskListener);
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
// Clear out all old notifications on startup (only present in the case where sysui dies)
NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index e3ed581..1bbb94a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -43,6 +43,7 @@
import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.RemoteInput;
@@ -919,7 +920,14 @@
mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
mGutsManager = new NotificationGutsManager(this, mStackScroller,
- mCheckSaveListener, mContext);
+ mCheckSaveListener, mContext,
+ key -> {
+ try {
+ mBarService.onNotificationSettingsViewed(key);
+ } catch (RemoteException e) {
+ // if we're here we're dead
+ }
+ });
mNotificationPanel.setStatusBar(this);
mNotificationPanel.setGroupManager(mGroupManager);
mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
@@ -1470,6 +1478,11 @@
}
}, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
}
+ try {
+ mBarService.onNotificationDirectReplied(entry.key);
+ } catch (RemoteException e) {
+ // system process is dead if we're here.
+ }
}
});
@@ -1785,9 +1798,14 @@
final int id = n.getId();
final int userId = n.getUserId();
try {
- // TODO: record actual dismissal surface
+ int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+ if (isHeadsUp(n.getKey())) {
+ dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+ } else if (mStackScroller.hasPulsingNotifications()) {
+ dismissalSurface = NotificationStats.DISMISSAL_AOD;
+ }
mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(),
- NotificationStats.DISMISSAL_OTHER);
+ dismissalSurface);
if (FORCE_REMOTE_INPUT_HISTORY
&& mKeysKeptForRemoteInput.contains(n.getKey())) {
mKeysKeptForRemoteInput.remove(n.getKey());
@@ -6838,6 +6856,15 @@
return false;
}
+ if (mIsOccluded && !isDozing()) {
+ boolean devicePublic = isLockscreenPublicMode(mCurrentUserId);
+ boolean userPublic = devicePublic || isLockscreenPublicMode(sbn.getUserId());
+ boolean needsRedaction = needsRedaction(entry);
+ if (userPublic && needsRedaction) {
+ return false;
+ }
+ }
+
if (sbn.getNotification().fullScreenIntent != null) {
if (mAccessibilityManager.isTouchExplorationEnabled()) {
if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 8044b07..efe049a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3066,7 +3066,7 @@
}
if (!childWasSwipedOut) {
Rect clipBounds = child.getClipBounds();
- childWasSwipedOut = clipBounds.height() == 0;
+ childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
}
int animationType = childWasSwipedOut
? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index f91e45d..27bf534 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -169,16 +169,23 @@
protected boolean persistBoolean(boolean value) {
final int desiredState = value ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ boolean shouldSendBroadcast = false;
for (int i = 0; i < mInfo.services.length; i++) {
ComponentName componentName = new ComponentName(mInfo.packageName,
mInfo.services[i].name);
- mPm.setComponentEnabledSetting(componentName, desiredState,
- PackageManager.DONT_KILL_APP);
+
+ if (mPm.getComponentEnabledSetting(componentName) != desiredState) {
+ mPm.setComponentEnabledSetting(componentName, desiredState,
+ PackageManager.DONT_KILL_APP);
+ shouldSendBroadcast = true;
+ }
}
- final String pkg = mInfo.packageName;
- final Intent intent = new Intent(PluginManager.PLUGIN_CHANGED,
- pkg != null ? Uri.fromParts("package", pkg, null) : null);
- getContext().sendBroadcast(intent);
+ if (shouldSendBroadcast) {
+ final String pkg = mInfo.packageName;
+ final Intent intent = new Intent(PluginManager.PLUGIN_CHANGED,
+ pkg != null ? Uri.fromParts("package", pkg, null) : null);
+ getContext().sendBroadcast(intent);
+ }
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
index 3eccccd..e117969 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
@@ -71,10 +71,12 @@
ap.mIcon = mResolveInfo.loadIcon(packageManager);
ap.mTitle = appName;
if (mDevice == null) {
- ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName);
+ ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName,
+ mAccessory.getDescription());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
} else {
- ap.mMessage = getString(R.string.usb_device_confirm_prompt, appName);
+ ap.mMessage = getString(R.string.usb_device_confirm_prompt, appName,
+ mDevice.getProductName());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
}
ap.mPositiveButtonText = getString(android.R.string.ok);
@@ -88,9 +90,11 @@
ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
mAlwaysUse = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
if (mDevice == null) {
- mAlwaysUse.setText(R.string.always_use_accessory);
+ mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
+ mAccessory.getDescription()));
} else {
- mAlwaysUse.setText(R.string.always_use_device);
+ mAlwaysUse.setText(getString(R.string.always_use_device, appName,
+ mDevice.getProductName()));
}
mAlwaysUse.setOnCheckedChangeListener(this);
mClearDefaultHint = (TextView)ap.mView.findViewById(
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 1e69fc5..4606aee 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -16,13 +16,17 @@
package com.android.systemui.usb;
+import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.res.XmlResourceParser;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
@@ -41,8 +45,13 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
+import com.android.internal.util.XmlUtils;
+import android.hardware.usb.AccessoryFilter;
+import android.hardware.usb.DeviceFilter;
import com.android.systemui.R;
+import org.xmlpull.v1.XmlPullParser;
+
public class UsbPermissionActivity extends AlertActivity
implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
@@ -84,10 +93,12 @@
ap.mIcon = aInfo.loadIcon(packageManager);
ap.mTitle = appName;
if (mDevice == null) {
- ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName);
+ ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName,
+ mAccessory.getDescription());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
} else {
- ap.mMessage = getString(R.string.usb_device_permission_prompt, appName);
+ ap.mMessage = getString(R.string.usb_device_permission_prompt, appName,
+ mDevice.getProductName());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
}
ap.mPositiveButtonText = getString(android.R.string.ok);
@@ -95,25 +106,123 @@
ap.mPositiveButtonListener = this;
ap.mNegativeButtonListener = this;
- // add "always use" checkbox
- LayoutInflater inflater = (LayoutInflater)getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
- mAlwaysUse = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
- if (mDevice == null) {
- mAlwaysUse.setText(R.string.always_use_accessory);
- } else {
- mAlwaysUse.setText(R.string.always_use_device);
+ try {
+ PackageInfo packageInfo = packageManager.getPackageInfo(mPackageName,
+ PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+
+ if ((mDevice != null && canBeDefault(mDevice, packageInfo))
+ || (mAccessory != null && canBeDefault(mAccessory, packageInfo))) {
+ // add "open when" checkbox
+ LayoutInflater inflater = (LayoutInflater) getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+ mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+ if (mDevice == null) {
+ mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
+ mAccessory.getDescription()));
+ } else {
+ mAlwaysUse.setText(getString(R.string.always_use_device, appName,
+ mDevice.getProductName()));
+ }
+ mAlwaysUse.setOnCheckedChangeListener(this);
+
+ mClearDefaultHint = (TextView)ap.mView.findViewById(
+ com.android.internal.R.id.clearDefaultHint);
+ mClearDefaultHint.setVisibility(View.GONE);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // ignore
}
- mAlwaysUse.setOnCheckedChangeListener(this);
- mClearDefaultHint = (TextView)ap.mView.findViewById(
- com.android.internal.R.id.clearDefaultHint);
- mClearDefaultHint.setVisibility(View.GONE);
setupAlert();
}
+ /**
+ * Can the app be the default for the USB device. I.e. can the app be launched by default if
+ * the device is plugged in.
+ *
+ * @param device The device the app would be default for
+ * @param packageInfo The package info of the app
+ *
+ * @return {@code true} iff the app can be default
+ */
+ private boolean canBeDefault(@NonNull UsbDevice device, @NonNull PackageInfo packageInfo) {
+ ActivityInfo[] activities = packageInfo.activities;
+ if (activities != null) {
+ int numActivities = activities.length;
+ for (int i = 0; i < numActivities; i++) {
+ ActivityInfo activityInfo = activities[i];
+
+ try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
+ UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+ if (parser == null) {
+ continue;
+ }
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if ("usb-device".equals(parser.getName())) {
+ DeviceFilter filter = DeviceFilter.read(parser);
+ if (filter.matches(device)) {
+ return true;
+ }
+ }
+
+ XmlUtils.nextElement(parser);
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
+ * the accessory is plugged in.
+ *
+ * @param accessory The accessory the app would be default for
+ * @param packageInfo The package info of the app
+ *
+ * @return {@code true} iff the app can be default
+ */
+ private boolean canBeDefault(@NonNull UsbAccessory accessory,
+ @NonNull PackageInfo packageInfo) {
+ ActivityInfo[] activities = packageInfo.activities;
+ if (activities != null) {
+ int numActivities = activities.length;
+ for (int i = 0; i < numActivities; i++) {
+ ActivityInfo activityInfo = activities[i];
+
+ try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
+ UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+ if (parser == null) {
+ continue;
+ }
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if ("usb-accessory".equals(parser.getName())) {
+ AccessoryFilter filter = AccessoryFilter.read(parser);
+ if (filter.matches(accessory)) {
+ return true;
+ }
+ }
+
+ XmlUtils.nextElement(parser);
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+ }
+ }
+ }
+
+ return false;
+ }
+
@Override
public void onDestroy() {
IBinder b = ServiceManager.getService(USB_SERVICE);
@@ -126,7 +235,7 @@
intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
if (mPermissionGranted) {
service.grantDevicePermission(mDevice, mUid);
- if (mAlwaysUse.isChecked()) {
+ if (mAlwaysUse != null && mAlwaysUse.isChecked()) {
final int userId = UserHandle.getUserId(mUid);
service.setDevicePackage(mDevice, mPackageName, userId);
}
@@ -136,7 +245,7 @@
intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
if (mPermissionGranted) {
service.grantAccessoryPermission(mAccessory, mUid);
- if (mAlwaysUse.isChecked()) {
+ if (mAlwaysUse != null && mAlwaysUse.isChecked()) {
final int userId = UserHandle.getUserId(mUid);
service.setAccessoryPackage(mAccessory, mPackageName, userId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SystemUIInterpolators.java b/packages/SystemUI/src/com/android/systemui/volume/SystemUIInterpolators.java
new file mode 100644
index 0000000..5ad8840
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/SystemUIInterpolators.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.animation.TimeInterpolator;
+
+public class SystemUIInterpolators {
+ public static final class LogDecelerateInterpolator implements TimeInterpolator {
+ private final float mBase;
+ private final float mDrift;
+ private final float mTimeScale;
+ private final float mOutputScale;
+
+ public LogDecelerateInterpolator() {
+ this(400f, 1.4f, 0);
+ }
+
+ private LogDecelerateInterpolator(float base, float timeScale, float drift) {
+ mBase = base;
+ mDrift = drift;
+ mTimeScale = 1f / timeScale;
+
+ mOutputScale = 1f / computeLog(1f);
+ }
+
+ private float computeLog(float t) {
+ return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ return computeLog(t) * mOutputScale;
+ }
+ }
+
+ public static final class LogAccelerateInterpolator implements TimeInterpolator {
+ private final int mBase;
+ private final int mDrift;
+ private final float mLogScale;
+
+ public LogAccelerateInterpolator() {
+ this(100, 0);
+ }
+
+ private LogAccelerateInterpolator(int base, int drift) {
+ mBase = base;
+ mDrift = drift;
+ mLogScale = 1f / computeLog(1, mBase, mDrift);
+ }
+
+ private static float computeLog(float t, int base, int drift) {
+ return (float) -Math.pow(base, -t) + 1 + (drift * t);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
+ }
+ }
+
+ public interface Callback {
+ void onAnimatingChanged(boolean animating);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index ee8f18e..4dff9bd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -62,7 +62,6 @@
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
| ActivityInfo.CONFIG_ASSETS_PATHS);
- private final Extension mExtension;
private VolumeDialog mDialog;
private VolumePolicy mVolumePolicy = new VolumePolicy(
DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT, // volumeDownToEnterSilent
@@ -79,7 +78,7 @@
// Allow plugins to reference the VolumeDialogController.
Dependency.get(PluginDependencyProvider.class)
.allowPluginDependency(VolumeDialogController.class);
- mExtension = Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
+ Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
.withPlugin(VolumeDialog.class)
.withDefault(this::createDefault)
.withCallback(dialog -> {
@@ -151,7 +150,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (mConfigChanges.applyNewConfig(mContext.getResources())) {
- mExtension.reload();
+ mController.mCallbacks.onConfigurationChanged();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 1ecaa13..0d41e20 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -19,6 +19,8 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
+import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.animation.ObjectAnimator;
import android.annotation.NonNull;
@@ -26,11 +28,11 @@
import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
@@ -41,12 +43,10 @@
import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings.Global;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
@@ -54,7 +54,6 @@
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
@@ -68,12 +67,13 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
+import com.android.systemui.HardwareUiLayout;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.plugins.VolumeDialogController.StreamState;
-import com.android.systemui.statusbar.policy.ZenModeController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -89,8 +89,6 @@
public class VolumeDialogImpl implements VolumeDialog {
private static final String TAG = Util.logTag(VolumeDialogImpl.class);
- public static final String SHOW_FULL_ZEN = "sysui_show_full_zen";
-
private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
private static final int UPDATE_ANIMATION_DURATION = 80;
@@ -99,6 +97,7 @@
private final VolumeDialogController mController;
private Window mWindow;
+ private HardwareUiLayout mHardwareLayout;
private CustomDialog mDialog;
private ViewGroup mDialogView;
private ViewGroup mDialogRowsView;
@@ -107,16 +106,11 @@
private ConfigurableTexts mConfigurableTexts;
private final SparseBooleanArray mDynamic = new SparseBooleanArray();
private final KeyguardManager mKeyguard;
- private final AudioManager mAudioManager;
private final AccessibilityManager mAccessibilityMgr;
- private int mExpandButtonAnimationDuration;
private final Object mSafetyWarningLock = new Object();
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveSliderTint;
private final ColorStateList mInactiveSliderTint;
- private VolumeDialogMotion mMotion;
- private int mWindowType;
- private final ZenModeController mZenModeController;
private boolean mShowing;
private boolean mShowA11yStream;
@@ -127,19 +121,12 @@
private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
private State mState;
private SafetyWarningDialog mSafetyWarning;
- private Callback mCallback;
- private boolean mPendingStateChanged;
- private boolean mPendingRecheckAll;
- private long mCollapseTime;
private boolean mHovering = false;
- private int mDensity;
public VolumeDialogImpl(Context context) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
- mZenModeController = Dependency.get(ZenModeController.class);
mController = Dependency.get(VolumeDialogController.class);
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAccessibilityMgr =
(AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
@@ -147,18 +134,12 @@
}
public void init(int windowType, Callback callback) {
- mCallback = callback;
- mWindowType = windowType;
-
initDialog();
mAccessibility.init();
mController.addCallback(mControllerCallbackH, mHandler);
mController.getState();
-
- final Configuration currentConfig = mContext.getResources().getConfiguration();
- mDensity = currentConfig.densityDpi;
}
@Override
@@ -177,25 +158,16 @@
mWindow = mDialog.getWindow();
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
- mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
- mDialog.setCanceledOnTouchOutside(true);
- final Resources res = mContext.getResources();
- final WindowManager.LayoutParams lp = mWindow.getAttributes();
- lp.type = mWindowType;
- lp.format = PixelFormat.TRANSLUCENT;
- lp.setTitle(VolumeDialogImpl.class.getSimpleName());
- lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.y = res.getDimensionPixelSize(R.dimen.volume_offset_top);
- lp.gravity = Gravity.TOP;
- lp.windowAnimations = -1;
- mWindow.setAttributes(lp);
- mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
+ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
+ mWindow.addFlags(
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+ mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
mDialog.setContentView(R.layout.volume_dialog);
mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog);
@@ -209,26 +181,11 @@
return true;
}
});
+ mHardwareLayout = HardwareUiLayout.get(mDialogView);
+ mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
mDialogContentView = mDialog.findViewById(R.id.volume_dialog_content);
mDialogRowsView = mDialogContentView.findViewById(R.id.volume_dialog_rows);
- updateWindowWidthH();
-
- mMotion = new VolumeDialogMotion(mDialog, mDialogView, mDialogContentView,
- new VolumeDialogMotion.Callback() {
- @Override
- public void onAnimatingChanged(boolean animating) {
- if (animating) return;
- if (mPendingStateChanged) {
- mHandler.sendEmptyMessage(H.STATE_CHANGED);
- mPendingStateChanged = false;
- }
- if (mPendingRecheckAll) {
- mHandler.sendEmptyMessage(H.RECHECK_ALL);
- mPendingRecheckAll = false;
- }
- }
- });
if (mRows.isEmpty()) {
addRow(AudioManager.STREAM_MUSIC,
@@ -250,28 +207,13 @@
} else {
addExistingRows();
}
- mExpandButtonAnimationDuration = res.getInteger(R.integer.volume_expand_animation_duration);
+ updateRowsH(getActiveRow());
}
private ColorStateList loadColorStateList(int colorResId) {
return ColorStateList.valueOf(mContext.getColor(colorResId));
}
- private void updateWindowWidthH() {
- final ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) mDialogView.getLayoutParams();
- final DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
- if (D.BUG) Log.d(TAG, "updateWindowWidth dm.w=" + dm.widthPixels);
- int w = dm.widthPixels;
- final int max = mContext.getResources()
- .getDimensionPixelSize(R.dimen.volume_dialog_panel_width);
- if (w > max) {
- w = max;
- }
- lp.width = w - lp.getMarginEnd() - lp.getMarginStart();
- mDialogView.setLayoutParams(lp);
- }
-
public void setStreamImportant(int stream, boolean important) {
mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
}
@@ -315,6 +257,7 @@
final VolumeRow row = mRows.get(i);
initRow(row, row.stream, row.iconRes, row.iconMuteRes, row.important);
mDialogRowsView.addView(row.view);
+ updateVolumeRowH(row);
}
}
@@ -341,7 +284,6 @@
writer.print(" mDynamic: "); writer.println(mDynamic);
writer.print(" mAutomute: "); writer.println(mAutomute);
writer.print(" mSilentMode: "); writer.println(mSilentMode);
- writer.print(" mCollapseTime: "); writer.println(mCollapseTime);
writer.print(" mAccessibility.mFeedbackEnabled: ");
writer.println(mAccessibility.mFeedbackEnabled);
}
@@ -364,9 +306,9 @@
row.view = mDialog.getLayoutInflater().inflate(R.layout.volume_dialog_row, null);
row.view.setId(row.stream);
row.view.setTag(row);
- row.header = (TextView) row.view.findViewById(R.id.volume_row_header);
+ row.header = row.view.findViewById(R.id.volume_row_header);
row.header.setId(20 * row.stream);
- row.slider = (SeekBar) row.view.findViewById(R.id.volume_row_slider);
+ row.slider = row.view.findViewById(R.id.volume_row_slider);
row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
row.anim = null;
@@ -447,11 +389,27 @@
rescheduleTimeoutH();
if (mShowing) return;
mShowing = true;
- mMotion.startShow();
+ mHardwareLayout.setTranslationX(getAnimTranslation());
+ mHardwareLayout.setAlpha(0);
+ mHardwareLayout.animate()
+ .alpha(1)
+ .translationX(0)
+ .setDuration(300)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .withEndAction(() -> {
+ mDialog.show();
+ mWindow.getDecorView().requestAccessibilityFocus();
+ })
+ .start();
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
mController.notifyVisible(true);
}
+ private float getAnimTranslation() {
+ return mContext.getResources().getDimension(
+ R.dimen.volume_dialog_panel_width) / 2;
+ }
+
protected void rescheduleTimeoutH() {
mHandler.removeMessages(H.DISMISS);
final int timeout = computeTimeoutH();
@@ -470,14 +428,19 @@
}
protected void dismissH(int reason) {
- if (mMotion.isAnimating()) {
- return;
- }
mHandler.removeMessages(H.DISMISS);
mHandler.removeMessages(H.SHOW);
if (!mShowing) return;
mShowing = false;
- mMotion.startDismiss();
+ mHardwareLayout.setTranslationX(0);
+ mHardwareLayout.setAlpha(1);
+ mHardwareLayout.animate()
+ .alpha(0)
+ .translationX(getAnimTranslation())
+ .setDuration(300)
+ .withEndAction(() -> mDialog.dismiss())
+ .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+ .start();
if (mAccessibilityMgr.isEnabled()) {
AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -497,30 +460,6 @@
}
}
- private void updateDialogBottomMarginH() {
- final long diff = System.currentTimeMillis() - mCollapseTime;
- final boolean collapsing = mCollapseTime != 0 && diff < getConservativeCollapseDuration();
- final ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) mDialogView.getLayoutParams();
- final int bottomMargin = collapsing ? mDialogContentView.getHeight() :
- mContext.getResources().getDimensionPixelSize(R.dimen.volume_dialog_margin_bottom);
- if (bottomMargin != mlp.bottomMargin) {
- if (D.BUG) Log.d(TAG, "bottomMargin " + mlp.bottomMargin + " -> " + bottomMargin);
- mlp.bottomMargin = bottomMargin;
- mDialogView.setLayoutParams(mlp);
- }
- }
-
- private long getConservativeCollapseDuration() {
- return mExpandButtonAnimationDuration * 3;
- }
-
- private void prepareForCollapse() {
- mHandler.removeMessages(H.UPDATE_BOTTOM_MARGIN);
- mCollapseTime = System.currentTimeMillis();
- updateDialogBottomMarginH();
- mHandler.sendEmptyMessageDelayed(H.UPDATE_BOTTOM_MARGIN, getConservativeCollapseDuration());
- }
-
private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) {
boolean isActive = row == activeRow;
if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
@@ -567,13 +506,7 @@
}
private void onStateChangedH(State state) {
- final boolean animating = mMotion.isAnimating();
- if (D.BUG) Log.d(TAG, "onStateChangedH animating=" + animating);
mState = state;
- if (animating) {
- mPendingStateChanged = true;
- return;
- }
mDynamic.clear();
// add any new dynamic rows
for (int i = 0; i < state.states.size(); i++) {
@@ -642,19 +575,13 @@
row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
final int iconRes =
isRingVibrate ? R.drawable.ic_volume_ringer_vibrate
- : isRingSilent || zenMuted ? row.cachedIconRes
+ : isRingSilent || zenMuted ? row.iconMuteRes
: ss.routedToBluetooth ?
(ss.muted ? R.drawable.ic_volume_media_bt_mute
: R.drawable.ic_volume_media_bt)
: mAutomute && ss.level == 0 ? row.iconMuteRes
: (ss.muted ? row.iconMuteRes : row.iconRes);
- if (iconRes != row.cachedIconRes) {
- if (row.cachedIconRes != 0 && isRingVibrate) {
- mController.vibrate();
- }
- row.cachedIconRes = iconRes;
- row.icon.setImageResource(iconRes);
- }
+ row.icon.setImageResource(iconRes);
row.iconState =
iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE
: (iconRes == R.drawable.ic_volume_media_bt_mute || iconRes == row.iconMuteRes)
@@ -864,14 +791,8 @@
@Override
public void onConfigurationChanged() {
- Configuration newConfig = mContext.getResources().getConfiguration();
- final int density = newConfig.densityDpi;
- if (density != mDensity) {
- mDialog.dismiss();
- initDialog();
- mDensity = density;
- }
- updateWindowWidthH();
+ mDialog.dismiss();
+ initDialog();
mConfigurableTexts.update();
}
@@ -908,23 +829,6 @@
}
};
- private final ZenModePanel.Callback mZenPanelCallback = new ZenModePanel.Callback() {
- @Override
- public void onPrioritySettings() {
- mCallback.onZenPrioritySettingsClicked();
- }
-
- @Override
- public void onInteraction() {
- mHandler.sendEmptyMessage(H.RESCHEDULE_TIMEOUT);
- }
-
- @Override
- public void onExpanded(boolean expanded) {
- // noop.
- }
- };
-
private final class H extends Handler {
private static final int SHOW = 1;
private static final int DISMISS = 2;
@@ -933,7 +837,6 @@
private static final int SET_STREAM_IMPORTANT = 5;
private static final int RESCHEDULE_TIMEOUT = 6;
private static final int STATE_CHANGED = 7;
- private static final int UPDATE_BOTTOM_MARGIN = 8;
public H() {
super(Looper.getMainLooper());
@@ -949,14 +852,13 @@
case SET_STREAM_IMPORTANT: setStreamImportantH(msg.arg1, msg.arg2 != 0); break;
case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
case STATE_CHANGED: onStateChangedH(mState); break;
- case UPDATE_BOTTOM_MARGIN: updateDialogBottomMarginH(); break;
}
}
}
- private final class CustomDialog extends Dialog {
+ private final class CustomDialog extends Dialog implements DialogInterface {
public CustomDialog(Context context) {
- super(context);
+ super(context, com.android.systemui.R.style.qs_theme);
}
@Override
@@ -966,26 +868,15 @@
}
@Override
- protected void onStop() {
- super.onStop();
- final boolean animating = mMotion.isAnimating();
- if (D.BUG) Log.d(TAG, "onStop animating=" + animating);
- if (animating) {
- mPendingRecheckAll = true;
- return;
- }
- mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ protected void onStart() {
+ super.setCanceledOnTouchOutside(true);
+ super.onStart();
}
@Override
- public boolean onTouchEvent(MotionEvent event) {
- if (isShowing()) {
- if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
- return true;
- }
- }
- return false;
+ protected void onStop() {
+ super.onStop();
+ mHandler.sendEmptyMessage(H.RECHECK_ALL);
}
@Override
@@ -1128,7 +1019,6 @@
private int iconRes;
private int iconMuteRes;
private boolean important;
- private int cachedIconRes;
private ColorStateList cachedSliderTint;
private int iconState; // from Events
private ObjectAnimator anim; // slider progress animation for non-touch-related updates
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
deleted file mode 100644
index 2b65fbd..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
-import android.content.DialogInterface.OnShowListener;
-import android.os.Handler;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.PathInterpolator;
-
-public class VolumeDialogMotion {
- private static final String TAG = Util.logTag(VolumeDialogMotion.class);
-
- private static final float ANIMATION_SCALE = 1.0f;
- private static final int PRE_DISMISS_DELAY = 50;
-
- private final Dialog mDialog;
- private final View mDialogView;
- private final ViewGroup mContents; // volume rows + zen footer
- private final Handler mHandler = new Handler();
- private final Callback mCallback;
-
- private boolean mAnimating; // show or dismiss animation is running
- private boolean mShowing; // show animation is running
- private boolean mDismissing; // dismiss animation is running
- private ValueAnimator mContentsPositionAnimator;
-
- public VolumeDialogMotion(Dialog dialog, View dialogView, ViewGroup contents,
- Callback callback) {
- mDialog = dialog;
- mDialogView = dialogView;
- mContents = contents;
- mCallback = callback;
- mDialog.setOnDismissListener(new OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- if (D.BUG) Log.d(TAG, "mDialog.onDismiss");
- }
- });
- mDialog.setOnShowListener(new OnShowListener() {
- @Override
- public void onShow(DialogInterface dialog) {
- if (D.BUG) Log.d(TAG, "mDialog.onShow");
- final int h = mDialogView.getHeight();
- mDialogView.setTranslationY(-h);
- startShowAnimation();
- }
- });
- }
-
- public boolean isAnimating() {
- return mAnimating;
- }
-
- private void setShowing(boolean showing) {
- if (showing == mShowing) return;
- mShowing = showing;
- if (D.BUG) Log.d(TAG, "mShowing = " + mShowing);
- updateAnimating();
- }
-
- private void setDismissing(boolean dismissing) {
- if (dismissing == mDismissing) return;
- mDismissing = dismissing;
- if (D.BUG) Log.d(TAG, "mDismissing = " + mDismissing);
- updateAnimating();
- }
-
- private void updateAnimating() {
- final boolean animating = mShowing || mDismissing;
- if (animating == mAnimating) return;
- mAnimating = animating;
- if (D.BUG) Log.d(TAG, "mAnimating = " + mAnimating);
- if (mCallback != null) {
- mCallback.onAnimatingChanged(mAnimating);
- }
- }
-
- public void startShow() {
- if (D.BUG) Log.d(TAG, "startShow");
- if (mShowing) return;
- setShowing(true);
- if (mDismissing) {
- mDialogView.animate().cancel();
- setDismissing(false);
- startShowAnimation();
- return;
- }
- if (D.BUG) Log.d(TAG, "mDialog.show()");
- mDialog.show();
- }
-
- private void startShowAnimation() {
- if (D.BUG) Log.d(TAG, "startShowAnimation");
- mDialogView.animate()
- .translationY(0)
- .setDuration(scaledDuration(300))
- .setInterpolator(new LogDecelerateInterpolator())
- .setListener(null)
- .start();
-
- mContentsPositionAnimator = ValueAnimator.ofFloat(0).setDuration(scaledDuration(400));
- mContentsPositionAnimator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCancelled) return;
- if (D.BUG) Log.d(TAG, "show.onAnimationEnd");
- setShowing(false);
- }
- @Override
- public void onAnimationCancel(Animator animation) {
- if (D.BUG) Log.d(TAG, "show.onAnimationCancel");
- mCancelled = true;
- }
- });
- mContentsPositionAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float v = (Float) animation.getAnimatedValue();
- mContents.setTranslationY(v + -mDialogView.getTranslationY());
- }
- });
- mContentsPositionAnimator.setInterpolator(new LogDecelerateInterpolator());
- mContentsPositionAnimator.start();
-
- mContents.setAlpha(0);
- mContents.animate()
- .alpha(1)
- .setDuration(scaledDuration(150))
- .setInterpolator(new PathInterpolator(0f, 0f, .2f, 1f))
- .start();
- }
-
- public void startDismiss() {
- if (D.BUG) Log.d(TAG, "startDismiss");
- if (mDismissing) return;
- setDismissing(true);
- if (mShowing) {
- mDialogView.animate().cancel();
- if (mContentsPositionAnimator != null) {
- mContentsPositionAnimator.cancel();
- }
- mContents.animate().cancel();
- setShowing(false);
- }
- mDialogView.animate()
- .translationY(-mDialogView.getHeight())
- .setDuration(scaledDuration(250))
- .setInterpolator(new LogAccelerateInterpolator())
- .setUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mContents.setTranslationY(-mDialogView.getTranslationY());
- }
- })
- .setListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCancelled) return;
- if (D.BUG) Log.d(TAG, "dismiss.onAnimationEnd");
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
- mDialog.dismiss();
- setDismissing(false);
- }
- }, PRE_DISMISS_DELAY);
-
- }
- @Override
- public void onAnimationCancel(Animator animation) {
- if (D.BUG) Log.d(TAG, "dismiss.onAnimationCancel");
- mCancelled = true;
- }
- }).start();
- }
-
- private static int scaledDuration(int base) {
- return (int) (base * ANIMATION_SCALE);
- }
-
- public static final class LogDecelerateInterpolator implements TimeInterpolator {
- private final float mBase;
- private final float mDrift;
- private final float mTimeScale;
- private final float mOutputScale;
-
- public LogDecelerateInterpolator() {
- this(400f, 1.4f, 0);
- }
-
- private LogDecelerateInterpolator(float base, float timeScale, float drift) {
- mBase = base;
- mDrift = drift;
- mTimeScale = 1f / timeScale;
-
- mOutputScale = 1f / computeLog(1f);
- }
-
- private float computeLog(float t) {
- return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
- }
-
- @Override
- public float getInterpolation(float t) {
- return computeLog(t) * mOutputScale;
- }
- }
-
- public static final class LogAccelerateInterpolator implements TimeInterpolator {
- private final int mBase;
- private final int mDrift;
- private final float mLogScale;
-
- public LogAccelerateInterpolator() {
- this(100, 0);
- }
-
- private LogAccelerateInterpolator(int base, int drift) {
- mBase = base;
- mDrift = drift;
- mLogScale = 1f / computeLog(1, mBase, mDrift);
- }
-
- private static float computeLog(float t, int base, int drift) {
- return (float) -Math.pow(base, -t) + 1 + (drift * t);
- }
-
- @Override
- public float getInterpolation(float t) {
- return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
- }
- }
-
- public interface Callback {
- void onAnimatingChanged(boolean animating);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index d758314..cf32760 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -41,7 +41,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import org.junit.Before;
import org.junit.Test;
@@ -61,7 +61,7 @@
private static final int TASK_ID = 444;
private @Mock Context mContext;
- private @Mock SystemServicesProxy mSystemServicesProxy;
+ private @Mock ActivityManagerWrapper mActivityManager;
private @Mock IActivityManager mIActivityManager;
private WorkLockActivityController mController;
@@ -77,10 +77,9 @@
// Construct controller. Save the TaskStackListener for injecting events.
final ArgumentCaptor<SysUiTaskStackChangeListener> listenerCaptor =
ArgumentCaptor.forClass(SysUiTaskStackChangeListener.class);
- mController =
- new WorkLockActivityController(mContext, mSystemServicesProxy, mIActivityManager);
+ mController = new WorkLockActivityController(mContext, mActivityManager, mIActivityManager);
- verify(mSystemServicesProxy).registerTaskStackListener(listenerCaptor.capture());
+ verify(mActivityManager).registerTaskStackListener(listenerCaptor.capture());
mTaskStackListener = listenerCaptor.getValue();
}
diff --git a/proto/src/ipconnectivity.proto b/proto/src/ipconnectivity.proto
index 327f17d..0e39446 100644
--- a/proto/src/ipconnectivity.proto
+++ b/proto/src/ipconnectivity.proto
@@ -489,7 +489,7 @@
// Represents statistics from NFLOG wakeup events due to ingress packets.
// Since oc-mr1.
-// Next tag: 8.
+// Next tag: 13.
message WakeupStats {
// The time duration in seconds covered by these stats, for deriving
// exact wakeup rates.
@@ -517,6 +517,24 @@
// The total number of wakeup packets with no associated socket or uid.
optional int64 no_uid_wakeups = 7;
+
+ // Counts of all different ethertype values from wakeup packets received.
+ repeated Pair ethertype_counts = 8;
+
+ // Counts of all different IP next header values from wakeup packets received.
+ repeated Pair ip_next_header_counts = 9;
+
+ // The total number of wakeup packets whose destination hardware address was
+ // a unicast address.
+ optional int64 l2_unicast_count = 10;
+
+ // The total number of wakeup packets whose destination hardware address was
+ // a multicast address.
+ optional int64 l2_multicast_count = 11;
+
+ // The total number of wakeup packets whose destination hardware address was
+ // a broadcast address.
+ optional int64 l2_broadcast_count = 12;
}
// Represents one of the IP connectivity event defined in this file.
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 8e93d19..98efd52 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -171,6 +171,12 @@
TEXT_CLASSIFIER_TYPE_URL = 6;
}
+ // Selection invocation methods. Used as sub-type for TEXT_SELECTION_SESSION events.
+ enum TextSelectionInvocationMethod {
+ TEXT_SELECTION_INVOCATION_MANUAL = 1;
+ TEXT_SELECTION_INVOCATION_LINK = 2;
+ }
+
// Known visual elements: views or controls.
enum View {
// Unknown view
@@ -4690,6 +4696,73 @@
// OS: P
ACTION_PRIVATE_DNS_MODE = 1249;
+ // FIELD: text select start offset in words (as defined by the ICU BreakIterator).
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_RANGE_START = 1250;
+
+ // FIELD: text select end offset in words (as defined by the ICU BreakIterator).
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_RANGE_END = 1251;
+
+ // FIELD: smart text selection start offset in words (as defined by the ICU BreakIterator),
+ // stored as two packed 16bit integers. (start in MSBs, end in LSBs)
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_SMART_RANGE_START = 1252;
+
+ // FIELD: smart text selection end offset in words (as defined by the ICU BreakIterator),
+ // stored as two packed 16bit integers. (start in MSBs, end in LSBs)
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_SMART_RANGE_END = 1253;
+
+ // FIELD: the entity type of the text currently selected.
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_ENTITY_TYPE = 1254;
+
+ // FIELD: the type of widget the selection was made in.
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_WIDGET_TYPE = 1255;
+
+ // FIELD: the name of the text classifier model used.
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_TEXTCLASSIFIER_MODEL = 1256;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > Behavior > Messages
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_ZEN_MODE_MESSAGES = 1257;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > Behavior > Calls
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_ZEN_MODE_CALLS = 1258;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > TURN ON -> Until you turn off
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_ZEN_MODE_TOGGLE_ON_FOREVER = 1259;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > TURN ON -> Time countdown manual rule (ie: for one hour)
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_ZEN_MODE_TOGGLE_ON_COUNTDOWN = 1260;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > TURN ON -> Next Alarm (ie: Until Tue 7:20 AM)
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_ZEN_MODE_TOGGLE_ON_ALARM = 1261;
+
+ // FIELD: the version of the widget the selection was made in.
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_WIDGET_VERSION = 1262;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 9d25055..f5349df 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -364,6 +364,15 @@
// (one value added per unique ESS - potentially multiple counts per single
// scan!)
repeated NumConnectableNetworksBucket observed_hotspot_r2_aps_per_ess_in_scan_histogram = 88;
+
+ // SoftAP event list tracking sessions and client counts in tethered mode
+ repeated SoftApConnectedClientsEvent soft_ap_connected_clients_events_tethered = 89;
+
+ // SoftAP event list tracking sessions and client counts in local only mode
+ repeated SoftApConnectedClientsEvent soft_ap_connected_clients_events_local_only = 90;
+
+ // Wps connection metrics
+ optional WpsMetrics wps_metrics = 91;
}
// Information that gets logged for every WiFi connection.
@@ -1071,3 +1080,57 @@
// Occurrences of this action.
optional int32 count = 4;
}
+
+// SoftAP event tracking sessions and client counts
+message SoftApConnectedClientsEvent {
+
+ // Soft AP event Types
+ enum SoftApEventType {
+
+ // Soft AP is Up and ready for use
+ SOFT_AP_UP = 0;
+
+ // Soft AP is Down
+ SOFT_AP_DOWN = 1;
+
+ // Number of connected soft AP clients has changed
+ NUM_CLIENTS_CHANGED = 2;
+ }
+
+ // Type of event being recorded
+ optional SoftApEventType event_type = 1;
+
+ // Absolute time when event happened
+ optional int64 time_stamp_millis = 2;
+
+ // Number of connected clients if event_type is NUM_CLIENTS_CHANGED, otherwise zero.
+ optional int32 num_connected_clients = 3;
+}
+
+// Wps connection metrics
+// Keeps track of Wi-Fi Protected Setup usage
+message WpsMetrics {
+ // Total number of wps connection attempts
+ optional int32 num_wps_attempts = 1;
+
+ // Total number of wps connection successes
+ optional int32 num_wps_success = 2;
+
+ // Total number of wps failures on start
+ optional int32 num_wps_start_failure = 3;
+
+ // Total number of wps overlap failure
+ optional int32 num_wps_overlap_failure = 4;
+
+ // Total number of wps timeout failure
+ optional int32 num_wps_timeout_failure = 5;
+
+ // Total number of other wps failure during connection
+ optional int32 num_wps_other_connection_failure = 6;
+
+ // Total number of supplicant failure after wps
+ optional int32 num_wps_supplicant_failure = 7;
+
+ // Total number of wps cancellation
+ optional int32 num_wps_cancellation = 8;
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index caff0ba..23e4f50 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -512,6 +512,16 @@
}
@Override
+ public void removeClient(IAutoFillManagerClient client, int userId) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.removeClientLocked(client);
+ }
+ }
+ }
+
+ @Override
public void setAuthenticationResult(Bundle data, int sessionId, int authenticationId,
int userId) {
synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a3def14..21e2722 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -273,6 +273,12 @@
return isEnabled();
}
+ void removeClientLocked(IAutoFillManagerClient client) {
+ if (mClients != null) {
+ mClients.unregister(client);
+ }
+ }
+
void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {
if (!isEnabled()) {
return;
@@ -548,6 +554,10 @@
}
sendStateToClients(true);
+ if (mClients != null) {
+ mClients.kill();
+ mClients = null;
+ }
}
@NonNull
@@ -752,6 +762,9 @@
}
}
+ pw.print(prefix); pw.println("Clients");
+ mClients.dump(pw, prefix2);
+
if (mEventHistory == null || mEventHistory.getEvents() == null
|| mEventHistory.getEvents().size() == 0) {
pw.print(prefix); pw.println("No event on last fill response");
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 5823ab1..9741486 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -36,6 +36,7 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.IAssistDataReceiver;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.AutofillOverlay;
import android.app.assist.AssistStructure.ViewNode;
@@ -76,7 +77,6 @@
import android.view.autofill.IAutofillWindowPresenter;
import com.android.internal.R;
-import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -1167,7 +1167,16 @@
break;
}
}
+
value = getSanitizedValue(sanitizers, id, value);
+ if (value == null) {
+ if (sDebug) {
+ Slog.d(TAG, "value of required field " + id + " failed sanitization");
+ }
+ allRequiredAreNotEmpty = false;
+ break;
+ }
+ viewState.setSanitizedValue(value);
currentValues.put(id, value);
final AutofillValue filledValue = viewState.getAutofilledValue();
@@ -1337,7 +1346,7 @@
return sanitizers;
}
- @NonNull
+ @Nullable
private AutofillValue getSanitizedValue(
@Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
@NonNull AutofillId id,
@@ -1431,10 +1440,10 @@
if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + context);
for (int viewStateNum = 0; viewStateNum < mViewStates.size(); viewStateNum++) {
- final ViewState state = mViewStates.valueAt(viewStateNum);
+ final ViewState viewState = mViewStates.valueAt(viewStateNum);
- final AutofillId id = state.id;
- final AutofillValue value = state.getCurrentValue();
+ final AutofillId id = viewState.id;
+ final AutofillValue value = viewState.getCurrentValue();
if (value == null) {
if (sVerbose) Slog.v(TAG, "callSaveLocked(): skipping " + id);
continue;
@@ -1446,9 +1455,17 @@
}
if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
- final AutofillValue sanitizedValue = getSanitizedValue(sanitizers, id, value);
+ AutofillValue sanitizedValue = viewState.getSanitizedValue();
- node.updateAutofillValue(sanitizedValue);
+ if (sanitizedValue == null) {
+ // Field is optional and haven't been sanitized yet.
+ sanitizedValue = getSanitizedValue(sanitizers, id, value);
+ }
+ if (sanitizedValue != null) {
+ node.updateAutofillValue(sanitizedValue);
+ } else if (sDebug) {
+ Slog.d(TAG, "Not updating field " + id + " because it failed sanitization");
+ }
}
// Sanitize structure before it's sent to service.
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 832a66b..0dbdc13 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -76,6 +76,7 @@
private FillResponse mResponse;
private AutofillValue mCurrentValue;
private AutofillValue mAutofilledValue;
+ private AutofillValue mSanitizedValue;
private Rect mVirtualBounds;
private int mState;
private String mDatasetId;
@@ -117,6 +118,15 @@
}
@Nullable
+ AutofillValue getSanitizedValue() {
+ return mSanitizedValue;
+ }
+
+ void setSanitizedValue(@Nullable AutofillValue value) {
+ mSanitizedValue = value;
+ }
+
+ @Nullable
FillResponse getResponse() {
return mResponse;
}
@@ -218,6 +228,7 @@
}
pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
+ pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
}
}
\ No newline at end of file
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 633bb3e..3d81baf 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -43,7 +43,8 @@
android.hardware.oemlock-V1.0-java \
android.hardware.tetheroffload.control-V1.0-java \
android.hardware.vibrator-V1.0-java \
- android.hardware.configstore-V1.0-java
+ android.hardware.configstore-V1.0-java \
+ android.hardware.contexthub-V1.0-java
ifneq ($(INCREMENTAL_BUILDS),)
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 75206e4..c34c30c 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -195,6 +195,7 @@
private LinkedList<ActiveLog> mActiveLogs;
private LinkedList<Long> mCrashTimestamps;
private int mCrashes;
+ private long mLastEnabledTime;
// configuration from external IBinder call which is used to
// synchronize with broadcast receiver.
@@ -2021,6 +2022,7 @@
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
quietMode ? 1 : 0, 0));
addActiveLog(packageName, true);
+ mLastEnabledTime = SystemClock.elapsedRealtime();
}
private void addActiveLog(String packageName, boolean enable) {
@@ -2142,7 +2144,7 @@
writer.println(" address: " + mAddress);
writer.println(" name: " + mName);
if (mEnable) {
- long onDuration = System.currentTimeMillis() - mActiveLogs.getLast().getTime();
+ long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
String onDurationString = String.format("%02d:%02d:%02d.%03d",
(int)(onDuration / (1000 * 60 * 60)),
(int)((onDuration / (1000 * 60)) % 60),
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 3e40dfd..bccae06 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5616,7 +5616,8 @@
}
private void logNetworkEvent(NetworkAgentInfo nai, int evtype) {
- mMetricsLog.log(new NetworkEvent(nai.network.netId, evtype));
+ int[] transports = nai.networkCapabilities.getTransportTypes();
+ mMetricsLog.log(nai.network.netId, transports, new NetworkEvent(evtype));
}
private static boolean toBool(int encodedBoolean) {
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
new file mode 100644
index 0000000..5dd3ee0
--- /dev/null
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.PackageOps;
+import android.app.IUidObserver;
+import android.content.Context;
+import android.os.Handler;
+import android.os.PowerManager.ServiceType;
+import android.os.PowerManagerInternal;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Class to track OP_RUN_ANY_IN_BACKGROUND, UID foreground state and "force all app standby".
+ *
+ * TODO Clean up cache when a user is deleted.
+ * TODO Add unit tests. b/68769804.
+ */
+public class ForceAppStandbyTracker {
+ private static final String TAG = "ForceAppStandbyTracker";
+
+ @GuardedBy("ForceAppStandbyTracker.class")
+ private static ForceAppStandbyTracker sInstance;
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+
+ AppOpsManager mAppOpsManager;
+ IAppOpsService mAppOpsService;
+ PowerManagerInternal mPowerManagerInternal;
+
+ private final Handler mCallbackHandler;
+
+ /**
+ * Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
+ */
+ @GuardedBy("mLock")
+ final ArraySet<Pair<Integer, String>> mForcedAppStandbyUidPackages = new ArraySet<>();
+
+ @GuardedBy("mLock")
+ final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
+
+ @GuardedBy("mLock")
+ final ArraySet<Listener> mListeners = new ArraySet<>();
+
+ @GuardedBy("mLock")
+ boolean mStarted;
+
+ @GuardedBy("mLock")
+ boolean mForceAllAppsStandby;
+
+ public static abstract class Listener {
+ public void onRestrictionChanged(int uid, @Nullable String packageName) {
+ }
+
+ public void onGlobalRestrictionChanged() {
+ }
+ }
+
+ private ForceAppStandbyTracker(Context context) {
+ mContext = context;
+ mCallbackHandler = FgThread.getHandler();
+ }
+
+ /**
+ * Get the singleton instance.
+ */
+ public static synchronized ForceAppStandbyTracker getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new ForceAppStandbyTracker(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Call it when the system is ready.
+ */
+ public void start() {
+ synchronized (mLock) {
+ if (mStarted) {
+ return;
+ }
+ mStarted = true;
+
+ mAppOpsManager = Preconditions.checkNotNull(
+ mContext.getSystemService(AppOpsManager.class));
+ mAppOpsService = Preconditions.checkNotNull(
+ IAppOpsService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_OPS_SERVICE)));
+ mPowerManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(PowerManagerInternal.class));
+
+ try {
+ ActivityManager.getService().registerUidObserver(new UidObserver(),
+ ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
+ | ActivityManager.UID_OBSERVER_ACTIVE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
+ new AppOpsWatcher());
+ } catch (RemoteException e) {
+ // shouldn't happen.
+ }
+
+ mPowerManagerInternal.registerLowPowerModeObserver(
+ ServiceType.FORCE_ALL_APPS_STANDBY,
+ state -> updateForceAllAppsStandby(state.batterySaverEnabled));
+
+ updateForceAllAppsStandby(
+ mPowerManagerInternal.getLowPowerState(ServiceType.FORCE_ALL_APPS_STANDBY)
+ .batterySaverEnabled);
+
+ refreshForcedAppStandbyUidPackagesLocked();
+ }
+ }
+
+ /**
+ * Update {@link #mForcedAppStandbyUidPackages} with the current app ops state.
+ */
+ private void refreshForcedAppStandbyUidPackagesLocked() {
+ final int op = AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;
+
+ mForcedAppStandbyUidPackages.clear();
+ final List<PackageOps> ops = mAppOpsManager.getPackagesForOps(new int[] {op});
+
+ if (ops == null) {
+ return;
+ }
+ final int size = ops.size();
+ for (int i = 0; i < size; i++) {
+ final AppOpsManager.PackageOps pkg = ops.get(i);
+ final List<AppOpsManager.OpEntry> entries = ops.get(i).getOps();
+
+ for (int j = 0; j < entries.size(); j++) {
+ AppOpsManager.OpEntry ent = entries.get(j);
+ if (ent.getOp() != op) {
+ continue;
+ }
+ if (ent.getMode() != AppOpsManager.MODE_ALLOWED) {
+ mForcedAppStandbyUidPackages.add(Pair.create(
+ pkg.getUid(), pkg.getPackageName()));
+ }
+ }
+ }
+ }
+
+ boolean isRunAnyInBackgroundAppOpRestricted(int uid, @NonNull String packageName) {
+ try {
+ return mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+ uid, packageName) != AppOpsManager.MODE_ALLOWED;
+ } catch (RemoteException e) {
+ return false; // shouldn't happen.
+ }
+ }
+
+ private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
+ // TODO Maybe we should switch to indexOf(Pair.create()) if the array size is too big.
+ final int size = mForcedAppStandbyUidPackages.size();
+ for (int i = 0; i < size; i++) {
+ final Pair<Integer, String> pair = mForcedAppStandbyUidPackages.valueAt(i);
+
+ if ((pair.first == uid) && packageName.equals(pair.second)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * @return whether a uid package-name pair is in mForcedAppStandbyUidPackages.
+ */
+ boolean isUidPackageRestrictedLocked(int uid, @NonNull String packageName) {
+ return findForcedAppStandbyUidPackageIndexLocked(uid, packageName) >= 0;
+ }
+
+ boolean updateRestrictedUidPackageLocked(int uid, @NonNull String packageName,
+ boolean restricted) {
+ final int index = findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
+ final boolean wasRestricted = index >= 0;
+ if (wasRestricted == restricted) {
+ return false;
+ }
+ if (restricted) {
+ mForcedAppStandbyUidPackages.add(Pair.create(uid, packageName));
+ } else {
+ mForcedAppStandbyUidPackages.removeAt(index);
+ }
+ return true;
+ }
+
+ void uidToForeground(int uid) {
+ synchronized (mLock) {
+ if (!UserHandle.isApp(uid)) {
+ return;
+ }
+ // TODO This can be optimized by calling indexOfKey and sharing the index for get and
+ // put.
+ if (mForegroundUids.get(uid)) {
+ return;
+ }
+ mForegroundUids.put(uid, true);
+ notifyForUidPackage(uid, null);
+ }
+ }
+
+ void uidToBackground(int uid, boolean remove) {
+ synchronized (mLock) {
+ if (!UserHandle.isApp(uid)) {
+ return;
+ }
+ // TODO This can be optimized by calling indexOfKey and sharing the index for get and
+ // put.
+ if (!mForegroundUids.get(uid)) {
+ return;
+ }
+ if (remove) {
+ mForegroundUids.delete(uid);
+ } else {
+ mForegroundUids.put(uid, false);
+ }
+ notifyForUidPackage(uid, null);
+ }
+ }
+
+ // Event handlers
+
+ final class UidObserver extends IUidObserver.Stub {
+ @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
+ }
+
+ @Override public void onUidGone(int uid, boolean disabled) {
+ uidToBackground(uid, /*remove=*/ true);
+ }
+
+ @Override public void onUidActive(int uid) {
+ uidToForeground(uid);
+ }
+
+ @Override public void onUidIdle(int uid, boolean disabled) {
+ // Just to avoid excessive memcpy, don't remove from the array in this case.
+ uidToBackground(uid, /*remove=*/ false);
+ }
+
+ @Override public void onUidCachedChanged(int uid, boolean cached) {
+ }
+ };
+
+ private final class AppOpsWatcher extends IAppOpsCallback.Stub {
+ @Override
+ public void opChanged(int op, int uid, String packageName) throws RemoteException {
+ synchronized (mLock) {
+ final boolean restricted = isRunAnyInBackgroundAppOpRestricted(uid, packageName);
+
+ if (updateRestrictedUidPackageLocked(uid, packageName, restricted)) {
+ notifyForUidPackage(uid, packageName);
+ }
+ }
+ }
+ }
+
+ private Listener[] cloneListeners() {
+ synchronized (mLock) {
+ return mListeners.toArray(new Listener[mListeners.size()]);
+ }
+ }
+
+ void notifyForUidPackage(int uid, String packageName) {
+ mCallbackHandler.post(() -> {
+ for (Listener l : cloneListeners()) {
+ l.onRestrictionChanged(uid, packageName);
+ }
+ });
+ }
+
+ void notifyGlobal() {
+ mCallbackHandler.post(() -> {
+ for (Listener l : cloneListeners()) {
+ l.onGlobalRestrictionChanged();
+ }
+ });
+ }
+
+ void updateForceAllAppsStandby(boolean forceAllAppsStandby) {
+ synchronized (mLock) {
+ if (mForceAllAppsStandby == forceAllAppsStandby) {
+ return;
+ }
+ mForceAllAppsStandby = forceAllAppsStandby;
+ Slog.i(TAG, "Force all app standby: " + mForceAllAppsStandby);
+ notifyGlobal();
+ }
+ }
+
+ // Public interface.
+
+ /**
+ * Register a new listener.
+ */
+ public void addListener(@NonNull Listener listener) {
+ synchronized (mLock) {
+ mListeners.add(listener);
+ }
+ }
+
+ /**
+ * Whether force-app-standby is effective for a UID package-name.
+ */
+ public boolean isRestricted(int uid, @NonNull String packageName) {
+ if (isInForeground(uid)) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (mForceAllAppsStandby) {
+ return true;
+ }
+ return isUidPackageRestrictedLocked(uid, packageName);
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public boolean isInForeground(int uid) {
+ if (!UserHandle.isApp(uid)) {
+ return true;
+ }
+ synchronized (mLock) {
+ return mForegroundUids.get(uid);
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public boolean isForceAllAppsStandbyEnabled() {
+ synchronized (mLock) {
+ return mForceAllAppsStandby;
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public boolean isRunAnyInBackgroundAppOpsAllowed(int uid, @NonNull String packageName) {
+ synchronized (mLock) {
+ return !isUidPackageRestrictedLocked(uid, packageName);
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public SparseBooleanArray getForegroudUids() {
+ synchronized (mLock) {
+ return mForegroundUids.clone();
+ }
+ }
+
+ /** For dumpsys -- otherwise the callers don't need to know it. */
+ public ArraySet<Pair<Integer, String>> getRestrictedUidPackages() {
+ synchronized (mLock) {
+ return new ArraySet(mForcedAppStandbyUidPackages);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
index d8bd0bb..1891ba9 100644
--- a/services/core/java/com/android/server/MountServiceIdler.java
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -72,7 +72,7 @@
synchronized (mFinishCallback) {
mStarted = true;
}
- ms.runIdleMaintenance(mFinishCallback);
+ ms.runIdleMaint(mFinishCallback);
}
return ms != null;
}
@@ -82,8 +82,12 @@
// Once we kick off the fstrim we aren't actually interruptible; just note
// that we don't need to call jobFinished(), and let everything happen in
// the callback from the mount service.
- synchronized (mFinishCallback) {
- mStarted = false;
+ StorageManagerService ms = StorageManagerService.sSelf;
+ if (ms != null) {
+ ms.abortIdleMaint(mFinishCallback);
+ synchronized (mFinishCallback) {
+ mStarted = false;
+ }
}
return false;
}
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 2ff036b..f20ca43 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -397,9 +398,29 @@
}
}
- public @Nullable IBinder getBinder() {
+ /**
+ * The runner that runs on the binder retrieved from {@link ServiceWatcher}.
+ */
+ public interface BinderRunner {
+ /**
+ * Runs on the retrieved binder.
+ * @param binder the binder retrieved from the {@link ServiceWatcher}.
+ */
+ public void run(@NonNull IBinder binder);
+ }
+
+ /**
+ * Retrieves the binder from {@link ServiceWatcher} and runs it.
+ * @return whether a valid service exists.
+ */
+ public boolean runOnBinder(@NonNull BinderRunner runner) {
synchronized (mLock) {
- return mBoundService;
+ if (mBoundService == null) {
+ return false;
+ } else {
+ runner.run(mBoundService);
+ return true;
+ }
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 75e8000..3c955eb 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -542,6 +542,8 @@
private static final int H_VOLUME_UNMOUNT = 8;
private static final int H_PARTITION_FORGET = 9;
private static final int H_RESET = 10;
+ private static final int H_RUN_IDLE_MAINT = 11;
+ private static final int H_ABORT_IDLE_MAINT = 12;
class StorageManagerServiceHandler extends Handler {
public StorageManagerServiceHandler(Looper looper) {
@@ -650,6 +652,17 @@
resetIfReadyAndConnected();
break;
}
+ case H_RUN_IDLE_MAINT: {
+ Slog.i(TAG, "Running idle maintenance");
+ runIdleMaint((Runnable)msg.obj);
+ break;
+ }
+ case H_ABORT_IDLE_MAINT: {
+ Slog.i(TAG, "Aborting idle maintenance");
+ abortIdleMaint((Runnable)msg.obj);
+ break;
+ }
+
}
}
}
@@ -1779,6 +1792,58 @@
}
}
+ void runIdleMaint(Runnable callback) {
+ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+
+ try {
+ mVold.runIdleMaint(new IVoldTaskListener.Stub() {
+ @Override
+ public void onStatus(int status, PersistableBundle extras) {
+ // Not currently used
+ }
+ @Override
+ public void onFinished(int status, PersistableBundle extras) {
+ if (callback != null) {
+ BackgroundThread.getHandler().post(callback);
+ }
+ }
+ });
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ }
+
+ @Override
+ public void runIdleMaintenance() {
+ runIdleMaint(null);
+ }
+
+ void abortIdleMaint(Runnable callback) {
+ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+
+ try {
+ mVold.abortIdleMaint(new IVoldTaskListener.Stub() {
+ @Override
+ public void onStatus(int status, PersistableBundle extras) {
+ // Not currently used
+ }
+ @Override
+ public void onFinished(int status, PersistableBundle extras) {
+ if (callback != null) {
+ BackgroundThread.getHandler().post(callback);
+ }
+ }
+ });
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ }
+
+ @Override
+ public void abortIdleMaintenance() {
+ abortIdleMaint(null);
+ }
+
private void remountUidExternalStorage(int uid, int mode) {
try {
mVold.remountUid(uid, mode);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2131731..3cd2f6a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -58,6 +58,7 @@
import com.android.internal.util.FastPrintWriter;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.am.ActivityManagerService.NeededUriGrants;
+import com.android.server.am.proto.ActiveServicesProto;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -85,6 +86,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import android.webkit.WebViewZygote;
public final class ActiveServices {
@@ -633,7 +635,7 @@
sb.append("Stopping service due to app idle: ");
UserHandle.formatUid(sb, service.appInfo.uid);
sb.append(" ");
- TimeUtils.formatDuration(service.createTime
+ TimeUtils.formatDuration(service.createRealTime
- SystemClock.elapsedRealtime(), sb);
sb.append(" ");
sb.append(compName);
@@ -1043,8 +1045,8 @@
try {
if (AppGlobals.getPackageManager().checkPermission(
android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
- r.appInfo.packageName,
- r.appInfo.uid) != PackageManager.PERMISSION_GRANTED) {
+ r.appInfo.packageName, UserHandle.getUserId(r.appInfo.uid))
+ != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Instant app " + r.appInfo.packageName
+ " does not have permission to create foreground"
+ "services");
@@ -3220,7 +3222,7 @@
info.uid = r.appInfo.uid;
info.process = r.processName;
info.foreground = r.isForeground;
- info.activeSince = r.createTime;
+ info.activeSince = r.createRealTime;
info.started = r.startRequested;
info.clientCount = r.connections.size();
info.crashCount = r.crashCount;
@@ -3574,7 +3576,7 @@
pw.print(" app=");
pw.println(r.app);
pw.print(" created=");
- TimeUtils.formatDuration(r.createTime, nowReal, pw);
+ TimeUtils.formatDuration(r.createRealTime, nowReal, pw);
pw.print(" started=");
pw.print(r.startRequested);
pw.print(" connections=");
@@ -3840,6 +3842,26 @@
return new ServiceDumper(fd, pw, args, opti, dumpAll, dumpPackage);
}
+ protected void writeToProto(ProtoOutputStream proto) {
+ synchronized (mAm) {
+ int[] users = mAm.mUserController.getUsers();
+ for (int user : users) {
+ ServiceMap smap = mServiceMap.get(user);
+ if (smap == null) {
+ continue;
+ }
+ long token = proto.start(ActiveServicesProto.SERVICES_BY_USERS);
+ proto.write(ActiveServicesProto.ServicesByUser.USER_ID, user);
+ ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
+ for (int i=0; i<alls.size(); i++) {
+ alls.valueAt(i).writeToProto(proto,
+ ActiveServicesProto.ServicesByUser.SERVICE_RECORDS);
+ }
+ proto.end(token);
+ }
+ }
+ }
+
/**
* There are three ways to call this:
* - no service specified: dump all the services
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index ea7b443..2289f85 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -276,17 +276,8 @@
if (windowingMode == WINDOWING_MODE_PINNED) {
return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
}
- final T stack = (T) new ActivityStack(
+ return (T) new ActivityStack(
this, stackId, mSupervisor, windowingMode, activityType, onTop);
-
- if (mDisplayId == DEFAULT_DISPLAY && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // Make sure recents stack exist when creating a dock stack as it normally needs to be
- // on the other side of the docked stack and we make visibility decisions based on that.
- // TODO: Not sure if this is needed after we change to calculate visibility based on
- // stack z-order vs. id.
- getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS, onTop);
- }
- return stack;
}
/**
@@ -365,6 +356,7 @@
+ " already exist on display=" + this + " stack=" + stack);
}
mSplitScreenPrimaryStack = stack;
+ onSplitScreenModeActivated();
}
}
@@ -377,6 +369,42 @@
mPinnedStack = null;
} else if (stack == mSplitScreenPrimaryStack) {
mSplitScreenPrimaryStack = null;
+ // Inform the reset of the system that split-screen mode was dismissed so things like
+ // resizing all the other stacks can take place.
+ onSplitScreenModeDismissed();
+ }
+ }
+
+ private void onSplitScreenModeDismissed() {
+ mSupervisor.mWindowManager.deferSurfaceLayout();
+ try {
+ // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack otherStack = mStacks.get(i);
+ if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
+ continue;
+ }
+ otherStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+ } finally {
+ mSupervisor.mWindowManager.continueSurfaceLayout();
+ }
+ }
+
+ private void onSplitScreenModeActivated() {
+ mSupervisor.mWindowManager.deferSurfaceLayout();
+ try {
+ // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack otherStack = mStacks.get(i);
+ if (otherStack == mSplitScreenPrimaryStack
+ || !otherStack.affectedBySplitScreenResize()) {
+ continue;
+ }
+ otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ }
+ } finally {
+ mSupervisor.mWindowManager.continueSurfaceLayout();
}
}
@@ -475,22 +503,10 @@
supportsFreeform, supportsPip, activityType)) {
return windowingMode;
}
- // Return the display's windowing mode
- return getWindowingMode();
- }
-
- /** Returns the top visible stack activity type that isn't in the exclude windowing mode. */
- int getTopVisibleStackActivityType(int excludeWindowingMode) {
- for (int i = mStacks.size() - 1; i >= 0; --i) {
- final ActivityStack stack = mStacks.get(i);
- if (stack.getWindowingMode() == excludeWindowingMode) {
- continue;
- }
- if (stack.shouldBeVisible(null /* starting */)) {
- return stack.getActivityType();
- }
- }
- return ACTIVITY_TYPE_UNDEFINED;
+ // Try to use the display's windowing mode otherwise fallback to fullscreen.
+ windowingMode = getWindowingMode();
+ return windowingMode != WINDOWING_MODE_UNDEFINED
+ ? windowingMode : WINDOWING_MODE_FULLSCREEN;
}
/**
@@ -599,7 +615,20 @@
}
public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "displayId=" + mDisplayId + " mStacks=" + mStacks);
+ pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size());
+ final String myPrefix = prefix + " ";
+ if (mHomeStack != null) {
+ pw.println(myPrefix + "mHomeStack=" + mHomeStack);
+ }
+ if (mRecentsStack != null) {
+ pw.println(myPrefix + "mRecentsStack=" + mRecentsStack);
+ }
+ if (mPinnedStack != null) {
+ pw.println(myPrefix + "mPinnedStack=" + mPinnedStack);
+ }
+ if (mSplitScreenPrimaryStack != null) {
+ pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack);
+ }
}
public void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 842a5b8..b3b831e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -222,6 +222,7 @@
import android.app.IActivityController;
import android.app.IActivityManager;
import android.app.IApplicationThread;
+import android.app.IAssistDataReceiver;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IProcessObserver;
@@ -373,7 +374,6 @@
import com.android.internal.app.DumpHeapActivity;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
-import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.SystemUserHomeActivity;
@@ -415,6 +415,8 @@
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.am.proto.ActivityManagerServiceProto;
import com.android.server.am.proto.BroadcastProto;
+import com.android.server.am.proto.GrantUriProto;
+import com.android.server.am.proto.NeededUriGrantsProto;
import com.android.server.am.proto.StickyBroadcastProto;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
@@ -1191,6 +1193,13 @@
return result;
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(GrantUriProto.URI, uri.toString());
+ proto.write(GrantUriProto.SOURCE_USER_ID, sourceUserId);
+ proto.end(token);
+ }
+
public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
ContentProvider.getUriWithoutUserId(uri), false);
@@ -2779,12 +2788,12 @@
mConfigurationSeq = mTempConfig.seq = 1;
mStackSupervisor = createStackSupervisor();
mStackSupervisor.onConfigurationChanged(mTempConfig);
- mKeyguardController = mStackSupervisor.mKeyguardController;
+ mKeyguardController = mStackSupervisor.getKeyguardController();
mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mTaskChangeNotificationController =
new TaskChangeNotificationController(this, mStackSupervisor, mHandler);
- mActivityStarter = new ActivityStarter(this);
+ mActivityStarter = new ActivityStarter(this, AppGlobals.getPackageManager());
mRecentTasks = createRecentTasks();
mStackSupervisor.setRecentTasks(mRecentTasks);
mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
@@ -2828,7 +2837,9 @@
}
protected ActivityStackSupervisor createStackSupervisor() {
- return new ActivityStackSupervisor(this, mHandler.getLooper());
+ final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mHandler.getLooper());
+ supervisor.initialize();
+ return supervisor;
}
protected RecentTasks createRecentTasks() {
@@ -3840,6 +3851,10 @@
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+
+ // Replace any invalid GIDs
+ if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
+ if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
}
checkTime(startTime, "startProcess: building args");
if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) {
@@ -3889,7 +3904,7 @@
}
if (app.info.isPrivilegedApp() &&
- !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false)) {
runtimeFlags |= Zygote.DISABLE_VERIFIER;
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
@@ -4706,8 +4721,8 @@
}
@Override
- public int startRecentsActivity(IAssistDataReceiver assistDataReceiver, Bundle bOptions,
- int userId) {
+ public int startRecentsActivity(IAssistDataReceiver assistDataReceiver, Bundle options,
+ Bundle activityOptions, int userId) {
if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
String msg = "Permission Denial: startRecentsActivity() from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
@@ -4741,8 +4756,9 @@
final Intent intent = new Intent();
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(recentsComponent);
+ intent.putExtras(options);
return mActivityStarter.startActivityMayWait(null, recentsUid, recentsPackage,
- intent, null, null, null, null, null, 0, 0, null, null, null, bOptions,
+ intent, null, null, null, null, null, 0, 0, null, null, null, activityOptions,
false, userId, null, "startRecentsActivity");
}
} finally {
@@ -4755,7 +4771,7 @@
throws RemoteException {
Slog.i(TAG, "Activity tried to startVoiceInteraction");
synchronized (this) {
- ActivityRecord activity = getFocusedStack().topActivity();
+ ActivityRecord activity = getFocusedStack().getTopActivity();
if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
throw new SecurityException("Only focused activity can call startVoiceInteraction");
}
@@ -5350,8 +5366,7 @@
return -1;
}
- final ProcessRecord getRecordForAppLocked(
- IApplicationThread thread) {
+ ProcessRecord getRecordForAppLocked(IApplicationThread thread) {
if (thread == null) {
return null;
}
@@ -7075,10 +7090,12 @@
}
// We deprecated Build.SERIAL and it is not accessible to
- // apps that target the v2 security sandbox. Since access to
- // the serial is now behind a permission we push down the value.
- String buildSerial = appInfo.targetSandboxVersion < 2
- ? sTheRealBuildSerial : Build.UNKNOWN;
+ // apps that target the v2 security sandbox and to apps that
+ // target APIs higher than O MR1. Since access to the serial
+ // is now behind a permission we push down the value.
+ final String buildSerial = (appInfo.targetSandboxVersion < 2
+ && appInfo.targetSdkVersion <= Build.VERSION_CODES.O_MR1)
+ ? sTheRealBuildSerial : Build.UNKNOWN;
// Check if this is a secondary process that should be incorporated into some
// currently active instrumentation. (Note we do this AFTER all of the profiling
@@ -7115,7 +7132,7 @@
}
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
- mStackSupervisor.mActivityMetricsLogger.notifyBindApplication(app);
+ mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
@@ -9036,6 +9053,19 @@
this.targetUid = targetUid;
this.flags = flags;
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
+ proto.write(NeededUriGrantsProto.TARGET_UID, targetUid);
+ proto.write(NeededUriGrantsProto.FLAGS, flags);
+
+ final int N = this.size();
+ for (int i=0; i<N; i++) {
+ this.get(i).writeToProto(proto, NeededUriGrantsProto.GRANTS);
+ }
+ proto.end(token);
+ }
}
/**
@@ -10429,13 +10459,7 @@
"exitFreeformMode: You can only go fullscreen from freeform.");
}
- final ActivityStack fullscreenStack = stack.getDisplay().getOrCreateStack(
- WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
-
- if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r);
- // TODO: Should just change windowing mode vs. re-parenting...
- r.getTask().reparent(fullscreenStack, ON_TOP,
- REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "exitFreeformMode");
+ stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10444,6 +10468,11 @@
@Override
public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
+ toTop, ANIMATE, null /* initialBounds */);
+ return;
+ }
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
@@ -10456,20 +10485,16 @@
if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
+ " to windowingMode=" + windowingMode + " toTop=" + toTop);
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mWindowManager.setDockedStackCreateState(SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
- null /* initialBounds */);
- }
if (!task.isActivityTypeStandardOrUndefined()) {
- throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move task "
- + taskId + " to non-standard windowin mode=" + windowingMode);
+ throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ + " non-standard task " + taskId + " to windowing mode="
+ + windowingMode);
}
final ActivityDisplay display = task.getStack().getDisplay();
final ActivityStack stack = display.getOrCreateStack(windowingMode,
task.getStack().getActivityType(), toTop);
- // TODO: We should just change the windowing mode for the task vs. creating and
- // moving it to a stack.
+ // TODO: Use ActivityStack.setWindowingMode instead of re-parenting.
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
"moveTaskToStack");
} finally {
@@ -10478,6 +10503,55 @@
}
}
+ /**
+ * Moves the specified task to the primary-split-screen stack.
+ *
+ * @param taskId Id of task to move.
+ * @param createMode The mode the primary split screen stack should be created in if it doesn't
+ * exist already. See
+ * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
+ * and
+ * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
+ * @param toTop If the task and stack should be moved to the top.
+ * @param animate Whether we should play an animation for the moving the task.
+ * @param initialBounds If the primary stack gets created, it will use these bounds for the
+ * stack. Pass {@code null} to use default bounds.
+ */
+ @Override
+ public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
+ boolean animate, Rect initialBounds) {
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ "setTaskWindowingModeSplitScreenPrimary()");
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
+ Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
+ return false;
+ }
+ if (DEBUG_STACK) Slog.d(TAG_STACK,
+ "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
+ + " to createMode=" + createMode + " toTop=" + toTop);
+ if (!task.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ + " non-standard task " + taskId + " to split-screen windowing mode");
+ }
+
+ mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ final int windowingMode = task.getWindowingMode();
+ final ActivityStack stack = task.getStack();
+ if (toTop) {
+ stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
+ }
+ stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate);
+ return windowingMode != task.getWindowingMode();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
@Override
public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
@@ -10515,58 +10589,6 @@
}
/**
- * Moves the input task to the primary-split-screen stack.
- *
- * @param taskId Id of task to move.
- * @param createMode The mode the primary split screen stack should be created in if it doesn't
- * exist already. See
- * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
- * and
- * {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
- * @param toTop If the task and stack should be moved to the top.
- * @param animate Whether we should play an animation for the moving the task
- * @param initialBounds If the primary stack gets created, it will use these bounds for the
- * stack. Pass {@code null} to use default bounds.
- */
- @Override
- public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
- boolean animate, Rect initialBounds) {
- enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
- "setTaskWindowingModeSplitScreenPrimary()");
- synchronized (this) {
- long ident = Binder.clearCallingIdentity();
- try {
- final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
- if (task == null) {
- Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
- return false;
- }
- if (DEBUG_STACK) Slog.d(TAG_STACK,
- "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
- + " to createMode=" + createMode + " toTop=" + toTop);
- mWindowManager.setDockedStackCreateState(createMode, initialBounds);
-
- final ActivityDisplay display = task.getStack().getDisplay();
- final ActivityStack stack = display.getOrCreateStack(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, task.getStack().getActivityType(),
- toTop);
-
- // Defer resuming until we move the home stack to the front below
- // TODO: Should just change windowing mode vs. re-parenting...
- final boolean moved = task.reparent(stack, toTop,
- REPARENT_KEEP_STACK_AT_FRONT, animate, !DEFER_RESUME,
- "setTaskWindowingModeSplitScreenPrimary");
- if (moved) {
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- }
- return moved;
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- /**
* Dismisses split-screen multi-window mode.
* @param toTop If true the current primary split-screen stack will be placed or left on top.
*/
@@ -10582,14 +10604,11 @@
Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
return;
}
+
if (toTop) {
- mStackSupervisor.resizeStackLocked(stack, null /* destBounds */,
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- true /* preserveWindows */, true /* allowResizeInDockedMode */,
- !DEFER_RESUME);
- } else {
- mStackSupervisor.moveTasksToFullscreenStackLocked(stack, false /* onTop */);
+ stack.moveToFront("dismissSplitScreenMode");
}
+ stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -12945,7 +12964,7 @@
return false;
}
- final ActivityRecord activity = focusedStack.topActivity();
+ final ActivityRecord activity = focusedStack.getTopActivity();
if (activity == null) {
return false;
}
@@ -12962,7 +12981,7 @@
try {
synchronized (this) {
ActivityRecord caller = ActivityRecord.forTokenLocked(token);
- ActivityRecord top = getFocusedStack().topActivity();
+ ActivityRecord top = getFocusedStack().getTopActivity();
if (top != caller) {
Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+ " is not current top " + top);
@@ -13005,7 +13024,7 @@
"enqueueAssistContext()");
synchronized (this) {
- ActivityRecord activity = getFocusedStack().topActivity();
+ ActivityRecord activity = getFocusedStack().getTopActivity();
if (activity == null) {
Slog.w(TAG, "getAssistContextExtras failed: no top activity");
return null;
@@ -14925,6 +14944,25 @@
synchronized (this) {
writeBroadcastsToProtoLocked(proto);
}
+ } else if ("provider".equals(cmd)) {
+ String[] newArgs;
+ String name;
+ if (opti >= args.length) {
+ name = null;
+ newArgs = EMPTY_STRING_ARRAY;
+ } else {
+ name = args[opti];
+ opti++;
+ newArgs = new String[args.length - opti];
+ if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
+ args.length - opti);
+ }
+ if (!dumpProviderProto(fd, pw, name, newArgs)) {
+ pw.println("No providers match: " + name);
+ pw.println("Use -h for help.");
+ }
+ } else if ("service".equals(cmd)) {
+ mServices.writeToProto(proto);
} else {
// default option, dump everything, output is ActivityManagerServiceProto
synchronized (this) {
@@ -14935,6 +14973,10 @@
long broadcastToken = proto.start(ActivityManagerServiceProto.BROADCASTS);
writeBroadcastsToProtoLocked(proto);
proto.end(broadcastToken);
+
+ long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
+ mServices.writeToProto(proto);
+ proto.end(serviceToken);
}
}
proto.flush();
@@ -16066,6 +16108,15 @@
return mProviderMap.dumpProvider(fd, pw, name, args, opti, dumpAll);
}
+ /**
+ * Similar to the dumpProvider, but only dumps the first matching provider.
+ * The provider is responsible for dumping as proto.
+ */
+ protected boolean dumpProviderProto(FileDescriptor fd, PrintWriter pw, String name,
+ String[] args) {
+ return mProviderMap.dumpProviderProto(fd, pw, name, args);
+ }
+
static class ItemMatcher {
ArrayList<ComponentName> components;
ArrayList<String> strings;
@@ -23884,7 +23935,7 @@
@Override
public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
synchronized (ActivityManagerService.this) {
- mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(
+ mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
reasons, timestamp);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b2308d5..9a16745 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1529,7 +1529,7 @@
void setVisibility(boolean visible) {
mWindowContainerController.setVisibility(visible, mDeferHidingClient);
- mStackSupervisor.mActivityMetricsLogger.notifyVisibilityChanged(this);
+ mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
}
// TODO: Look into merging with #setVisibility()
@@ -1810,7 +1810,7 @@
}
stack.mFullyDrawnStartTime = 0;
}
- mStackSupervisor.mActivityMetricsLogger.logAppTransitionReportedDrawn(this,
+ mStackSupervisor.getActivityMetricsLogger().logAppTransitionReportedDrawn(this,
restoredFromBundle);
fullyDrawnStartTime = 0;
}
@@ -1852,7 +1852,7 @@
@Override
public void onStartingWindowDrawn(long timestamp) {
synchronized (service) {
- mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn(
+ mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
getStackId(), timestamp);
}
}
@@ -1860,7 +1860,7 @@
@Override
public void onWindowsDrawn(long timestamp) {
synchronized (service) {
- mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(getStackId(), timestamp);
+ mStackSupervisor.getActivityMetricsLogger().notifyWindowsDrawn(getStackId(), timestamp);
if (displayStartTime != 0) {
reportLaunchTimeLocked(timestamp);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8cc584e..c086c52 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
+import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -95,7 +98,6 @@
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IActivityController;
@@ -345,6 +347,7 @@
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
private final Rect mTmpRect2 = new Rect();
+ private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
/** Run all ActivityStacks through this */
protected final ActivityStackSupervisor mStackSupervisor;
@@ -452,8 +455,8 @@
mStackId = stackId;
mCurrentUser = mService.mUserController.getCurrentUserId();
mTmpRect2.setEmpty();
- setWindowingMode(windowingMode);
setActivityType(activityType);
+ setWindowingMode(windowingMode);
mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
mTmpRect2);
postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
@@ -479,6 +482,125 @@
}
@Override
+ public void setWindowingMode(int windowingMode) {
+ setWindowingMode(windowingMode, false /* animate */);
+ }
+
+ void setWindowingMode(int preferredWindowingMode, boolean animate) {
+ final int currentMode = getWindowingMode();
+ final ActivityDisplay display = getDisplay();
+ final TaskRecord topTask = topTask();
+ final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
+ mTmpOptions.setLaunchWindowingMode(preferredWindowingMode);
+
+ // Need to make sure windowing mode is supported.
+ int windowingMode = display.resolveWindowingMode(
+ null /* ActivityRecord */, mTmpOptions, topTask, getActivityType());;
+ if (splitScreenStack == this && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ // Resolution to split-screen secondary for the primary split-screen stack means we want
+ // to go fullscreen.
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ }
+
+ // Take any required action due to us not supporting the preferred windowing mode.
+ if (windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) {
+ if (display.hasSplitScreenPrimaryStack()
+ && (preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
+ // Looks like we can't launch in split screen mode, go ahead an dismiss split-screen
+ // and display a warning toast about it.
+ mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
+ display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+ }
+
+ if (currentMode == windowingMode) {
+ // You are already in the window mode silly...
+ return;
+ }
+
+ final WindowManagerService wm = mService.mWindowManager;
+ final ActivityRecord topActivity = getTopActivity();
+
+ if (windowingMode != WINDOWING_MODE_FULLSCREEN && topActivity != null
+ && topActivity.isNonResizableOrForcedResizable() && !topActivity.noDisplay) {
+ // Inform the user that they are starting an app that may not work correctly in
+ // multi-window mode.
+ final String packageName = topActivity.appInfo.packageName;
+ mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
+ topTask.taskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
+ }
+
+ wm.deferSurfaceLayout();
+ try {
+ if (!animate && topActivity != null) {
+ mNoAnimActivities.add(topActivity);
+ }
+ super.setWindowingMode(windowingMode);
+
+ if (mWindowContainerController == null) {
+ // Nothing else to do if we don't have a window container yet. E.g. call from ctor.
+ return;
+ }
+
+ if (windowingMode == WINDOWING_MODE_PINNED || currentMode == WINDOWING_MODE_PINNED) {
+ // TODO: Need to remove use of PinnedActivityStack for this to be supported.
+ // NOTE: Need to ASS.scheduleUpdatePictureInPictureModeIfNeeded() in
+ // setWindowModeUnchecked() when this support is added. See TaskRecord.reparent()
+ throw new IllegalArgumentException(
+ "Changing pinned windowing mode not currently supported");
+ }
+
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) {
+ // We already have a split-screen stack in this display, so just move the tasks over.
+ // TODO: Figure-out how to do all the stuff in
+ // AMS.setTaskWindowingModeSplitScreenPrimary
+ throw new IllegalArgumentException("Setting primary split-screen windowing mode"
+ + " while there is already one isn't currently supported");
+ //return;
+ }
+
+ mTmpRect2.setEmpty();
+ if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
+ mWindowContainerController.getRawBounds(mTmpRect2);
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ if (topTask != null) {
+ // TODO: Can we consolidate this and other sites that call this methods?
+ Rect bounds = topTask().getLaunchBounds();
+ if (bounds != null) {
+ mTmpRect2.set(bounds);
+ }
+ }
+ }
+ }
+
+ if (!Objects.equals(mBounds, mTmpRect2)) {
+ resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
+ }
+ } finally {
+ if (mDisplayId == DEFAULT_DISPLAY
+ && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // Make sure recents stack exist when creating a dock stack as it normally needs to
+ // be on the other side of the docked stack and we make visibility decisions based
+ // on that.
+ // TODO: This is only here to help out with the case where recents stack doesn't
+ // exist yet. For that case the initial size of the split-screen stack will be the
+ // the one where the home stack is visible since recents isn't visible yet, but the
+ // divider will be off. I think we should just make the initial bounds that of home
+ // so that the divider matches and remove this logic.
+ display.getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ // If task moved to docked stack - show recents if needed.
+ mService.mWindowManager.showRecentApps(false /* fromHome */);
+ }
+ wm.continueSurfaceLayout();
+ }
+
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ }
+
+ @Override
public boolean isCompatible(int windowingMode, int activityType) {
// TODO: Should we just move this to ConfigurationContainer?
if (activityType == ACTIVITY_TYPE_UNDEFINED) {
@@ -538,12 +660,6 @@
* either destroyed completely or re-parented.
*/
private void removeFromDisplay() {
- if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // If we removed a docked stack we want to resize it so it resizes all other stacks
- // in the system to fullscreen.
- mStackSupervisor.resizeDockedStackLocked(
- null, null, null, null, null, PRESERVE_WINDOWS);
- }
final ActivityDisplay display = getDisplay();
if (display != null) {
display.removeChild(this);
@@ -730,14 +846,11 @@
return null;
}
- final ActivityRecord topActivity() {
+ ActivityRecord getTopActivity() {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (!r.finishing) {
- return r;
- }
+ final ActivityRecord r = mTaskHistory.get(taskNdx).getTopActivity();
+ if (r != null) {
+ return r;
}
}
return null;
@@ -751,7 +864,7 @@
return null;
}
- final TaskRecord bottomTask() {
+ private TaskRecord bottomTask() {
if (mTaskHistory.isEmpty()) {
return null;
}
@@ -1607,8 +1720,6 @@
}
final int otherWindowingMode = other.getWindowingMode();
- // TODO: Can be removed once we are no longer using returnToType for back functionality
- final ActivityStack stackBehind = i > 0 ? display.getChildAt(i - 1) : null;
if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
if (other.isStackTranslucent(starting)) {
@@ -1669,7 +1780,7 @@
boolean preserveWindows) {
mTopActivityOccludesKeyguard = false;
mTopDismissingKeyguardActivity = null;
- mStackSupervisor.mKeyguardController.beginActivityVisibilityUpdate();
+ mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
try {
ActivityRecord top = topRunningActivityLocked();
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + top
@@ -1777,7 +1888,7 @@
notifyActivityDrawnLocked(null);
}
} finally {
- mStackSupervisor.mKeyguardController.endActivityVisibilityUpdate();
+ mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate();
}
}
@@ -1802,6 +1913,13 @@
return inPinnedWindowingMode();
}
+ @Override
+ public boolean supportsSplitScreenWindowingMode() {
+ final TaskRecord topTask = topTask();
+ return super.supportsSplitScreenWindowingMode()
+ && (topTask == null || topTask.supportsSplitScreenWindowingMode());
+ }
+
/** @return True if the resizing of the primary-split-screen stack affects this stack size. */
boolean affectedBySplitScreenResize() {
if (!supportsSplitScreenWindowingMode()) {
@@ -1828,9 +1946,9 @@
boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
boolean isTop) {
final boolean isInPinnedStack = r.inPinnedWindowingMode();
- final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing(
+ final boolean keyguardShowing = mStackSupervisor.getKeyguardController().isKeyguardShowing(
mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
- final boolean keyguardLocked = mStackSupervisor.mKeyguardController.isKeyguardLocked();
+ final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
final boolean showWhenLocked = r.canShowWhenLocked() && !isInPinnedStack;
final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
if (shouldBeVisible) {
@@ -1845,7 +1963,7 @@
}
final boolean canShowWithKeyguard = canShowWithInsecureKeyguard()
- && mStackSupervisor.mKeyguardController.canDismissKeyguard();
+ && mStackSupervisor.getKeyguardController().canDismissKeyguard();
if (canShowWithKeyguard) {
return true;
}
@@ -1854,10 +1972,10 @@
// If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
// right away.
- return shouldBeVisible && mStackSupervisor.mKeyguardController
+ return shouldBeVisible && mStackSupervisor.getKeyguardController()
.canShowActivityWhileKeyguardShowing(r, dismissKeyguard);
} else if (keyguardLocked) {
- return shouldBeVisible && mStackSupervisor.mKeyguardController.canShowWhileOccluded(
+ return shouldBeVisible && mStackSupervisor.getKeyguardController().canShowWhileOccluded(
dismissKeyguard, showWhenLocked);
} else {
return shouldBeVisible;
@@ -2421,7 +2539,7 @@
// if needed to get the correct rotation behavior.
// TODO: Remove this once visibilities are set correctly immediately when
// starting an activity.
- if (mStackSupervisor.mKeyguardController.isKeyguardLocked()) {
+ if (mStackSupervisor.getKeyguardController().isKeyguardLocked()) {
mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */,
0 /* configChanges */, false /* preserveWindows */);
}
@@ -4299,7 +4417,7 @@
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
final ActivityStack topStack = getDisplay().getTopStack();
- final ActivityRecord topActivity = topStack != null ? topStack.topActivity() : null;
+ final ActivityRecord topActivity = topStack != null ? topStack.getTopActivity() : null;
final int numTasks = mTaskHistory.size();
final int index = mTaskHistory.indexOf(tr);
if (numTasks == 0 || index < 0) {
@@ -4502,6 +4620,7 @@
final TaskRecord task = mTaskHistory.get(i);
if (task.isResizeable()) {
if (inFreeformWindowingMode()) {
+ // TODO: Can be removed now since each freeform task is in its own stack.
// For freeform stack we don't adjust the size of the tasks to match that
// of the stack, but we do try to make sure the tasks are still contained
// with the bounds of the stack.
@@ -4889,7 +5008,7 @@
voiceInteractor);
// add the task to stack first, mTaskPositioner might need the stack association
addTask(task, toTop, "createTaskRecord");
- final boolean isLockscreenShown = mService.mStackSupervisor.mKeyguardController
+ final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController()
.isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
if (!mStackSupervisor.getLaunchingBoundsController().layoutTask(task, info.windowLayout)
&& mBounds != null && task.isResizeable() && !isLockscreenShown) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 062083c..745e9fb 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -295,7 +295,7 @@
WindowManagerService mWindowManager;
DisplayManager mDisplayManager;
- private final LaunchingBoundsController mLaunchingBoundsController;
+ private LaunchingBoundsController mLaunchingBoundsController;
/** Counter for next free stack ID to use for dynamic activity stacks. */
private int mNextFreeStackId = 0;
@@ -395,7 +395,6 @@
private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
private DisplayManagerInternal mDisplayManagerInternal;
- private InputManagerInternal mInputManagerInternal;
/** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
boolean inResumeTopActivity;
@@ -414,7 +413,7 @@
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
- final ActivityMetricsLogger mActivityMetricsLogger;
+ private ActivityMetricsLogger mActivityMetricsLogger;
private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
@@ -534,11 +533,13 @@
*/
boolean mIsDockMinimized;
- final KeyguardController mKeyguardController;
+ private KeyguardController mKeyguardController;
private PowerManager mPowerManager;
private int mDeferResumeCount;
+ private boolean mInitialized;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
@@ -574,14 +575,32 @@
public ActivityStackSupervisor(ActivityManagerService service, Looper looper) {
mService = service;
mHandler = new ActivityStackSupervisorHandler(looper);
+ }
+
+ public void initialize() {
+ if (mInitialized) {
+ return;
+ }
+
+ mInitialized = true;
mRunningTasks = createRunningTasks();
- mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, looper);
- mKeyguardController = new KeyguardController(service, this);
+ mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
+ mHandler.getLooper());
+ mKeyguardController = new KeyguardController(mService, this);
mLaunchingBoundsController = new LaunchingBoundsController();
mLaunchingBoundsController.registerDefaultPositioners(this);
}
+
+ public ActivityMetricsLogger getActivityMetricsLogger() {
+ return mActivityMetricsLogger;
+ }
+
+ public KeyguardController getKeyguardController() {
+ return mKeyguardController;
+ }
+
void setRecentTasks(RecentTasks recentTasks) {
mRecentTasks = recentTasks;
mRecentTasks.registerCallback(this);
@@ -624,8 +643,6 @@
mHomeStack = mFocusedStack = mLastFocusedStack = getDefaultDisplay().getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-
- mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
}
}
@@ -2945,6 +2962,7 @@
* Returns the reparent target stack, creating the stack if necessary. This call also enforces
* the various checks on tasks that are going to be reparented from one stack to another.
*/
+ // TODO: Look into changing users to this method to ActivityDisplay.resolveWindowingMode()
ActivityStack getReparentTargetStack(TaskRecord task, ActivityStack stack, boolean toTop) {
final ActivityStack prevStack = task.getStack();
final int stackId = stack.mStackId;
@@ -2971,8 +2989,7 @@
+ " reparent task=" + task + " to stackId=" + stackId);
}
- // Ensure that we aren't trying to move into a freeform stack without freeform
- // support
+ // Ensure that we aren't trying to move into a freeform stack without freeform support
if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM
&& !mService.mSupportsFreeformWindowManagement) {
throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
@@ -3376,7 +3393,7 @@
// When launching tasks behind, update the last active time of the top task after the new
// task has been shown briefly
- final ActivityRecord top = stack.topActivity();
+ final ActivityRecord top = stack.getTopActivity();
if (top != null) {
top.getTask().touchActiveTime();
}
@@ -4200,10 +4217,9 @@
}
}
- final ActivityRecord topActivity = task.getTopActivity();
if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
- // Display a warning toast that we tried to put a non-dockable task in the docked
- // stack.
+ // Display a warning toast that we tried to put an app that doesn't support split-screen
+ // in split-screen.
mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
@@ -4217,6 +4233,7 @@
return;
}
+ final ActivityRecord topActivity = task.getTopActivity();
if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
&& !topActivity.noDisplay) {
final String packageName = topActivity.appInfo.packageName;
@@ -4552,7 +4569,7 @@
final ActivityStack stack = display.getChildAt(j);
// Get top activity from a visible stack and add it to the list.
if (stack.shouldBeVisible(null /* starting */)) {
- final ActivityRecord top = stack.topActivity();
+ final ActivityRecord top = stack.getTopActivity();
if (top != null) {
if (stack == mFocusedStack) {
topActivityTokens.add(0, top.appToken);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 1c80282..9b8cbc1 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -90,6 +90,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
@@ -133,6 +134,7 @@
private static final int INVALID_LAUNCH_MODE = -1;
private final ActivityManagerService mService;
+ private final IPackageManager mPackageManager;
private final ActivityStackSupervisor mSupervisor;
private final ActivityStartInterceptor mInterceptor;
@@ -232,8 +234,9 @@
mIntentDelivered = false;
}
- ActivityStarter(ActivityManagerService service) {
+ ActivityStarter(ActivityManagerService service, IPackageManager packageManager) {
mService = service;
+ mPackageManager = packageManager;
mSupervisor = mService.mStackSupervisor;
mInterceptor = new ActivityStartInterceptor(mService, mSupervisor);
}
@@ -264,8 +267,12 @@
outActivity[0] = mLastStartActivityRecord[0];
}
+ return getExternalResult(mLastStartActivityResult);
+ }
+
+ public static int getExternalResult(int result) {
// Aborted results are treated as successes externally, but we must track them internally.
- return mLastStartActivityResult != START_ABORTED ? mLastStartActivityResult : START_SUCCESS;
+ return result != START_ABORTED ? result : START_SUCCESS;
}
/** DO NOT call this method directly. Use {@link #startActivityLocked} instead. */
@@ -295,7 +302,8 @@
}
}
- final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
+ final int userId = aInfo != null && aInfo.applicationInfo != null
+ ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
if (err == ActivityManager.START_SUCCESS) {
Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
@@ -371,7 +379,7 @@
&& sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
try {
intent.addCategory(Intent.CATEGORY_VOICE);
- if (!AppGlobals.getPackageManager().activitySupportsIntent(
+ if (!mPackageManager.activitySupportsIntent(
intent.getComponent(), intent, resolvedType)) {
Slog.w(TAG,
"Activity being started in current voice task does not support voice: "
@@ -389,7 +397,7 @@
// If the caller is starting a new voice session, just make sure the target
// is actually allowing it to run this way.
try {
- if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
+ if (!mPackageManager.activitySupportsIntent(intent.getComponent(),
intent, resolvedType)) {
Slog.w(TAG,
"Activity being started in new voice task does not support: "
@@ -665,7 +673,7 @@
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- mSupervisor.mActivityMetricsLogger.notifyActivityLaunching();
+ mSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
boolean componentSpecified = intent.getComponent() != null;
// Save a copy in case ephemeral needs it
@@ -859,7 +867,7 @@
}
}
- mSupervisor.mActivityMetricsLogger.notifyActivityLaunched(res, outRecord[0]);
+ mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);
return res;
}
}
@@ -1119,7 +1127,7 @@
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
final ActivityStack topStack = mSupervisor.mFocusedStack;
- final ActivityRecord topFocused = topStack.topActivity();
+ final ActivityRecord topFocused = topStack.getTopActivity();
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
final boolean dontStart = top != null && mStartActivity.resultTo == null
&& top.realActivity.equals(mStartActivity.realActivity)
@@ -1565,8 +1573,8 @@
&& (topTask != intentActivity.getTask() || topTask != focusStack.topTask())
&& !mAvoidMoveToFront) {
mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- if (mSourceRecord == null || (mSourceStack.topActivity() != null &&
- mSourceStack.topActivity().getTask() == mSourceRecord.getTask())) {
+ if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
+ mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
// We really do want to push this one into the user's face, right now.
if (mLaunchTaskBehind && mSourceRecord != null) {
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
@@ -1956,7 +1964,7 @@
if (mDoResume) {
mTargetStack.moveToFront("addingToTopTask");
}
- final ActivityRecord prev = mTargetStack.topActivity();
+ final ActivityRecord prev = mTargetStack.getTopActivity();
final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord(
mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info,
mIntent, null, null, true);
diff --git a/services/core/java/com/android/server/am/AppBindRecord.java b/services/core/java/com/android/server/am/AppBindRecord.java
index df833ad..7b38597 100644
--- a/services/core/java/com/android/server/am/AppBindRecord.java
+++ b/services/core/java/com/android/server/am/AppBindRecord.java
@@ -17,6 +17,9 @@
package com.android.server.am;
import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.AppBindRecordProto;
import java.io.PrintWriter;
@@ -60,4 +63,18 @@
+ Integer.toHexString(System.identityHashCode(this))
+ " " + service.shortName + ":" + client.processName + "}";
}
+
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(AppBindRecordProto.HEX_HASH,
+ Integer.toHexString(System.identityHashCode(this)));
+ if (client != null) {
+ client.writeToProto(proto, AppBindRecordProto.CLIENT);
+ }
+ final int N = connections.size();
+ for (int i=0; i<N; i++) {
+ connections.valueAt(i).writeToProto(proto, AppBindRecordProto.CONNECTIONS);
+ }
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/am/AssistDataReceiverProxy.java b/services/core/java/com/android/server/am/AssistDataReceiverProxy.java
index f22fe37..9991ce1 100644
--- a/services/core/java/com/android/server/am/AssistDataReceiverProxy.java
+++ b/services/core/java/com/android/server/am/AssistDataReceiverProxy.java
@@ -19,13 +19,13 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.app.IAssistDataReceiver;
import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
-import com.android.internal.app.IAssistDataReceiver;
import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
/**
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index a8f829f..9f7621f 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -23,6 +23,7 @@
import android.app.AppOpsManager;
import android.app.IActivityManager;
+import android.app.IAssistDataReceiver;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
@@ -31,7 +32,6 @@
import android.view.IWindowManager;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.logging.MetricsLogger;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 9b7a0c4..6df283c 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -19,6 +19,9 @@
import android.app.IServiceConnection;
import android.app.PendingIntent;
import android.content.Context;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.ConnectionRecordProto;
import java.io.PrintWriter;
@@ -119,4 +122,70 @@
sb.append('}');
return stringName = sb.toString();
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ if (binding == null) return; // if binding is null, don't write data, something is wrong.
+ long token = proto.start(fieldId);
+ proto.write(ConnectionRecordProto.HEX_HASH,
+ Integer.toHexString(System.identityHashCode(this)));
+ if (binding.client != null) {
+ proto.write(ConnectionRecordProto.USER_ID, binding.client.userId);
+ }
+ if ((flags&Context.BIND_AUTO_CREATE) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.AUTO_CREATE);
+ }
+ if ((flags&Context.BIND_DEBUG_UNBIND) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEBUG_UNBIND);
+ }
+ if ((flags&Context.BIND_NOT_FOREGROUND) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.NOT_FG);
+ }
+ if ((flags&Context.BIND_IMPORTANT_BACKGROUND) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.IMPORTANT_BG);
+ }
+ if ((flags&Context.BIND_ABOVE_CLIENT) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ABOVE_CLIENT);
+ }
+ if ((flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ALLOW_OOM_MANAGEMENT);
+ }
+ if ((flags&Context.BIND_WAIVE_PRIORITY) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.WAIVE_PRIORITY);
+ }
+ if ((flags&Context.BIND_IMPORTANT) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.IMPORTANT);
+ }
+ if ((flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ADJUST_WITH_ACTIVITY);
+ }
+ if ((flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.FG_SERVICE_WHILE_WAKE);
+ }
+ if ((flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.FG_SERVICE);
+ }
+ if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.TREAT_LIKE_ACTIVITY);
+ }
+ if ((flags&Context.BIND_VISIBLE) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.VISIBLE);
+ }
+ if ((flags&Context.BIND_SHOWING_UI) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.SHOWING_UI);
+ }
+ if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.NOT_VISIBLE);
+ }
+ if (serviceDead) {
+ proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEAD);
+ }
+ if (binding.service != null) {
+ proto.write(ConnectionRecordProto.SERVICE_NAME, binding.service.shortName);
+ }
+ if (conn != null) {
+ proto.write(ConnectionRecordProto.CONN_HEX_HASH,
+ Integer.toHexString(System.identityHashCode(conn.asBinder())));
+ }
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index be290e9..01ce64c 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -21,6 +21,10 @@
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.AppBindRecordProto;
+import com.android.server.am.proto.IntentBindRecordProto;
import java.io.PrintWriter;
@@ -106,4 +110,32 @@
sb.append('}');
return stringName = sb.toString();
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(IntentBindRecordProto.HEX_HASH,
+ Integer.toHexString(System.identityHashCode(this)));
+ proto.write(IntentBindRecordProto.IS_CREATE,
+ (collectFlags()&Context.BIND_AUTO_CREATE) != 0);
+ if (intent != null) {
+ intent.getIntent().writeToProto(proto,
+ IntentBindRecordProto.INTENT, false, true, false, false);
+ }
+ if (binder != null) {
+ proto.write(IntentBindRecordProto.BINDER, binder.toString());
+ }
+ proto.write(IntentBindRecordProto.REQUESTED, requested);
+ proto.write(IntentBindRecordProto.RECEIVED, received);
+ proto.write(IntentBindRecordProto.HAS_BOUND, hasBound);
+ proto.write(IntentBindRecordProto.DO_REBIND, doRebind);
+
+ final int N = apps.size();
+ for (int i=0; i<N; i++) {
+ AppBindRecord a = apps.valueAt(i);
+ if (a != null) {
+ a.writeToProto(proto, IntentBindRecordProto.APPS);
+ }
+ }
+ proto.end(token);
+ }
}
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index c3fed17..76b4679 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -41,10 +41,8 @@
import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.wm.WindowManagerService;
-
import java.io.PrintWriter;
/**
@@ -236,24 +234,33 @@
final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity;
mOccluded = false;
mDismissingKeyguardActivity = null;
- final ActivityDisplay display = mStackSupervisor.getDefaultDisplay();
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- // Only the focused stack top activity may control occluded state
- if (mStackSupervisor.isFocusedStack(stack)) {
+ for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
+ final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
- // A dismissing activity occludes Keyguard in the insecure case for legacy reasons.
- final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
- mOccluded = stack.topActivityOccludesKeyguard()
- || (topDismissing != null
- && stack.topRunningActivityLocked() == topDismissing
- && canShowWhileOccluded(true /* dismissKeyguard */,
- false /* showWhenLocked */));
- }
- if (mDismissingKeyguardActivity == null
- && stack.getTopDismissingKeyguardActivity() != null) {
- mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
+ // Only the top activity of the focused stack on the default display may control
+ // occluded state.
+ if (display.mDisplayId == DEFAULT_DISPLAY
+ && mStackSupervisor.isFocusedStack(stack)) {
+
+ // A dismissing activity occludes Keyguard in the insecure case for legacy
+ // reasons.
+ final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
+ mOccluded =
+ stack.topActivityOccludesKeyguard()
+ || (topDismissing != null
+ && stack.topRunningActivityLocked() == topDismissing
+ && canShowWhileOccluded(
+ true /* dismissKeyguard */,
+ false /* showWhenLocked */));
+ }
+
+ if (mDismissingKeyguardActivity == null
+ && stack.getTopDismissingKeyguardActivity() != null) {
+ mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
+ }
}
}
mOccluded |= mWindowManager.isShowingDream();
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 32d03da..8a905f8 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -28,6 +28,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -322,8 +323,7 @@
return needSep;
}
- protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
- int opti, boolean dumpAll) {
+ private ArrayList<ContentProviderRecord> getProvidersForName(String name) {
ArrayList<ContentProviderRecord> allProviders = new ArrayList<ContentProviderRecord>();
ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
@@ -365,6 +365,12 @@
}
}
}
+ return providers;
+ }
+
+ protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+ int opti, boolean dumpAll) {
+ ArrayList<ContentProviderRecord> providers = getProvidersForName(name);
if (providers.size() <= 0) {
return false;
@@ -417,6 +423,33 @@
}
/**
+ * Similar to the dumpProvider, but only dumps the first matching provider.
+ * The provider is responsible for dumping as proto.
+ */
+ protected boolean dumpProviderProto(FileDescriptor fd, PrintWriter pw, String name,
+ String[] args) {
+ //add back the --proto arg, which was stripped out by PriorityDump
+ String[] newArgs = Arrays.copyOf(args, args.length + 1);
+ newArgs[args.length] = "--proto";
+
+ ArrayList<ContentProviderRecord> providers = getProvidersForName(name);
+
+ if (providers.size() <= 0) {
+ return false;
+ }
+
+ // Only dump the first provider, since we are dumping in proto format
+ for (int i = 0; i < providers.size(); i++) {
+ final ContentProviderRecord r = providers.get(i);
+ if (r.proc != null && r.proc.thread != null) {
+ dumpToTransferPipe(null, fd, pw, r, newArgs);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Invokes IApplicationThread.dumpProvider() on the thread of the specified provider without
* any meta string (e.g., provider info, indentation) written to the file descriptor.
*/
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 16995e5..b6eff00 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -19,6 +19,7 @@
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.LocalServices;
+import com.android.server.am.proto.ServiceRecordProto;
import com.android.server.notification.NotificationManagerInternal;
import android.app.INotificationManager;
@@ -42,6 +43,8 @@
import android.util.ArrayMap;
import android.util.Slog;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -79,7 +82,7 @@
final String permission;// permission needed to access service
final boolean exported; // from ServiceInfo.exported
final Runnable restarter; // used to schedule retries of starting the service
- final long createTime; // when this service was created
+ final long createRealTime; // when this service was created
final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
= new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
// All active bindings to the service.
@@ -103,7 +106,7 @@
boolean startRequested; // someone explicitly called start?
boolean delayedStop; // service has been stopped but is in a delayed start?
boolean stopIfKilled; // last onStart() said to stop if service killed?
- boolean callStart; // last onStart() has asked to alway be called on restart.
+ boolean callStart; // last onStart() has asked to always be called on restart.
int executeNesting; // number of outstanding operations keeping foreground.
boolean executeFg; // should we be executing in the foreground?
long executingStart; // start time of last execute request.
@@ -159,6 +162,27 @@
}
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
+ long token = proto.start(fieldId);
+ proto.write(ServiceRecordProto.StartItemProto.ID, id);
+ ProtoUtils.toDuration(proto,
+ ServiceRecordProto.StartItemProto.DURATION, deliveredTime, now);
+ proto.write(ServiceRecordProto.StartItemProto.DELIVERY_COUNT, deliveryCount);
+ proto.write(ServiceRecordProto.StartItemProto.DONE_EXECUTING_COUNT, doneExecutingCount);
+ if (intent != null) {
+ intent.writeToProto(proto, ServiceRecordProto.StartItemProto.INTENT, true, true,
+ true, false);
+ }
+ if (neededGrants != null) {
+ neededGrants.writeToProto(proto, ServiceRecordProto.StartItemProto.NEEDED_GRANTS);
+ }
+ if (uriPermissions != null) {
+ uriPermissions.writeToProto(proto,
+ ServiceRecordProto.StartItemProto.URI_PERMISSIONS);
+ }
+ proto.end(token);
+ }
+
public String toString() {
if (stringName != null) {
return stringName;
@@ -209,6 +233,117 @@
}
}
+ void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(ServiceRecordProto.SHORT_NAME, this.shortName);
+ proto.write(ServiceRecordProto.HEX_HASH,
+ Integer.toHexString(System.identityHashCode(this)));
+ proto.write(ServiceRecordProto.IS_RUNNING, app != null);
+ if (app != null) {
+ proto.write(ServiceRecordProto.PID, app.pid);
+ }
+ if (intent != null) {
+ intent.getIntent().writeToProto(proto, ServiceRecordProto.INTENT, false, true, false,
+ true);
+ }
+ proto.write(ServiceRecordProto.PACKAGE_NAME, packageName);
+ proto.write(ServiceRecordProto.PROCESS_NAME, processName);
+ proto.write(ServiceRecordProto.PERMISSION, permission);
+
+ long now = SystemClock.uptimeMillis();
+ long nowReal = SystemClock.elapsedRealtime();
+ if (appInfo != null) {
+ long appInfoToken = proto.start(ServiceRecordProto.APPINFO);
+ proto.write(ServiceRecordProto.AppInfo.BASE_DIR, appInfo.sourceDir);
+ if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+ proto.write(ServiceRecordProto.AppInfo.RES_DIR, appInfo.publicSourceDir);
+ }
+ proto.write(ServiceRecordProto.AppInfo.DATA_DIR, appInfo.dataDir);
+ proto.end(appInfoToken);
+ }
+ if (app != null) {
+ app.writeToProto(proto, ServiceRecordProto.APP);
+ }
+ if (isolatedProc != null) {
+ isolatedProc.writeToProto(proto, ServiceRecordProto.ISOLATED_PROC);
+ }
+ proto.write(ServiceRecordProto.WHITELIST_MANAGER, whitelistManager);
+ proto.write(ServiceRecordProto.DELAYED, delayed);
+ if (isForeground || foregroundId != 0) {
+ long fgToken = proto.start(ServiceRecordProto.FOREGROUND);
+ proto.write(ServiceRecordProto.Foreground.ID, foregroundId);
+ foregroundNoti.writeToProto(proto, ServiceRecordProto.Foreground.NOTIFICATION);
+ proto.end(fgToken);
+ }
+ ProtoUtils.toDuration(proto, ServiceRecordProto.CREATE_REAL_TIME, createRealTime, nowReal);
+ ProtoUtils.toDuration(proto,
+ ServiceRecordProto.STARTING_BG_TIMEOUT, startingBgTimeout, now);
+ ProtoUtils.toDuration(proto, ServiceRecordProto.LAST_ACTIVITY_TIME, lastActivity, now);
+ ProtoUtils.toDuration(proto, ServiceRecordProto.RESTART_TIME, restartTime, now);
+ proto.write(ServiceRecordProto.CREATED_FROM_FG, createdFromFg);
+
+ if (startRequested || delayedStop || lastStartId != 0) {
+ long startToken = proto.start(ServiceRecordProto.START);
+ proto.write(ServiceRecordProto.Start.START_REQUESTED, startRequested);
+ proto.write(ServiceRecordProto.Start.DELAYED_STOP, delayedStop);
+ proto.write(ServiceRecordProto.Start.STOP_IF_KILLED, stopIfKilled);
+ proto.write(ServiceRecordProto.Start.LAST_START_ID, lastStartId);
+ proto.end(startToken);
+ }
+
+ if (executeNesting != 0) {
+ long executNestingToken = proto.start(ServiceRecordProto.EXECUTE);
+ proto.write(ServiceRecordProto.ExecuteNesting.EXECUTE_NESTING, executeNesting);
+ proto.write(ServiceRecordProto.ExecuteNesting.EXECUTE_FG, executeFg);
+ ProtoUtils.toDuration(proto,
+ ServiceRecordProto.ExecuteNesting.EXECUTING_START, executingStart, now);
+ proto.end(executNestingToken);
+ }
+ if (destroying || destroyTime != 0) {
+ ProtoUtils.toDuration(proto, ServiceRecordProto.DESTORY_TIME, destroyTime, now);
+ }
+ if (crashCount != 0 || restartCount != 0 || restartDelay != 0 || nextRestartTime != 0) {
+ long crashToken = proto.start(ServiceRecordProto.CRASH);
+ proto.write(ServiceRecordProto.Crash.RESTART_COUNT, restartCount);
+ ProtoUtils.toDuration(proto, ServiceRecordProto.Crash.RESTART_DELAY, restartDelay, now);
+ ProtoUtils.toDuration(proto,
+ ServiceRecordProto.Crash.NEXT_RESTART_TIME, nextRestartTime, now);
+ proto.write(ServiceRecordProto.Crash.CRASH_COUNT, crashCount);
+ proto.end(crashToken);
+ }
+
+ if (deliveredStarts.size() > 0) {
+ final int N = deliveredStarts.size();
+ for (int i = 0; i < N; i++) {
+ deliveredStarts.get(i).writeToProto(proto,
+ ServiceRecordProto.DELIVERED_STARTS, now);
+ }
+ }
+ if (pendingStarts.size() > 0) {
+ final int N = pendingStarts.size();
+ for (int i = 0; i < N; i++) {
+ pendingStarts.get(i).writeToProto(proto, ServiceRecordProto.PENDING_STARTS, now);
+ }
+ }
+ if (bindings.size() > 0) {
+ final int N = bindings.size();
+ for (int i=0; i<N; i++) {
+ IntentBindRecord b = bindings.valueAt(i);
+ b.writeToProto(proto, ServiceRecordProto.BINDINGS);
+ }
+ }
+ if (connections.size() > 0) {
+ final int N = connections.size();
+ for (int conni=0; conni<N; conni++) {
+ ArrayList<ConnectionRecord> c = connections.valueAt(conni);
+ for (int i=0; i<c.size(); i++) {
+ c.get(i).writeToProto(proto, ServiceRecordProto.CONNECTIONS);
+ }
+ }
+ }
+ proto.end(token);
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("intent={");
pw.print(intent.getIntent().toShortString(false, true, false, true));
@@ -243,7 +378,7 @@
pw.print(" foregroundNoti="); pw.println(foregroundNoti);
}
pw.print(prefix); pw.print("createTime=");
- TimeUtils.formatDuration(createTime, nowReal, pw);
+ TimeUtils.formatDuration(createRealTime, nowReal, pw);
pw.print(" startingBgTimeout=");
TimeUtils.formatDuration(startingBgTimeout, now, pw);
pw.println();
@@ -329,7 +464,7 @@
permission = sInfo.permission;
exported = sInfo.exported;
this.restarter = restarter;
- createTime = SystemClock.elapsedRealtime();
+ createRealTime = SystemClock.elapsedRealtime();
lastActivity = SystemClock.uptimeMillis();
userId = UserHandle.getUserId(appInfo.uid);
createdFromFg = callerIsFg;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c5514fb..949f51f 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -606,9 +606,8 @@
final int toStackWindowingMode = toStack.getWindowingMode();
final ActivityRecord topActivity = getTopActivity();
- final boolean mightReplaceWindow =
- replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode)
- && topActivity != null;
+ final boolean mightReplaceWindow = topActivity != null
+ && replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode);
if (mightReplaceWindow) {
// We are about to relaunch the activity because its configuration changed due to
// being maximized, i.e. size change. The activity will first remove the old window
@@ -722,7 +721,6 @@
}
// TODO: Handle incorrect request to move before the actual move, not after.
- final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenPrimaryStack();
supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
DEFAULT_DISPLAY, toStack);
@@ -735,10 +733,9 @@
}
/**
- * Returns true if the windows of tasks being moved to the target stack from the source
- * stack should be replaced, meaning that window manager will keep the old window around
- * until the new is ready.
- * @hide
+ * @return True if the windows of tasks being moved to the target stack from the source stack
+ * should be replaced, meaning that window manager will keep the old window around until the new
+ * is ready.
*/
private static boolean replaceWindowsOnTaskMove(
int sourceWindowingMode, int targetWindowingMode) {
@@ -1139,6 +1136,9 @@
mActivities.remove(newTop);
mActivities.add(newTop);
+
+ // Make sure window manager is aware of the position change.
+ mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
updateEffectiveIntent();
setFrontOfTask();
@@ -2059,11 +2059,8 @@
}
static Rect validateBounds(Rect bounds) {
- if (bounds != null && bounds.isEmpty()) {
- Slog.wtf(TAG, "Received strange task bounds: " + bounds, new Throwable());
- return null;
- }
- return bounds;
+ // TODO: Not needed once we have bounds in WindowConfiguration.
+ return (bounds != null && bounds.isEmpty()) ? null : bounds;
}
/** Updates the task's bounds and override configuration to match what is expected for the
@@ -2092,7 +2089,7 @@
}
/** Returns the bounds that should be used to launch this task. */
- private Rect getLaunchBounds() {
+ Rect getLaunchBounds() {
if (mStack == null) {
return null;
}
diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/am/UriPermissionOwner.java
index 28344df..fc07c1a 100644
--- a/services/core/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/core/java/com/android/server/am/UriPermissionOwner.java
@@ -20,6 +20,9 @@
import android.os.Binder;
import android.os.IBinder;
import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.UriPermissionOwnerProto;
import com.google.android.collect.Sets;
@@ -139,6 +142,26 @@
}
}
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(UriPermissionOwnerProto.OWNER, owner.toString());
+ if (mReadPerms != null) {
+ synchronized (mReadPerms) {
+ for (UriPermission p : mReadPerms) {
+ p.uri.writeToProto(proto, UriPermissionOwnerProto.READ_PERMS);
+ }
+ }
+ }
+ if (mWritePerms != null) {
+ synchronized (mWritePerms) {
+ for (UriPermission p : mWritePerms) {
+ p.uri.writeToProto(proto, UriPermissionOwnerProto.WRITE_PERMS);
+ }
+ }
+ }
+ proto.end(token);
+ }
+
@Override
public String toString() {
return owner.toString();
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 2df5dc9..4e3d8d2 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -89,6 +89,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
import com.android.server.pm.UserManagerService;
@@ -355,27 +356,35 @@
// Only keep marching forward if user is actually unlocked
if (!StorageManager.isUserKeyUnlocked(userId)) return;
synchronized (mLock) {
- // Bail if we ended up with a stale user
- if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
-
- // Do not proceed if unexpected state
- if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
+ // Do not proceed if unexpected state or a stale user
+ if (mStartedUsers.get(userId) != uss || uss.state != STATE_RUNNING_LOCKED) {
return;
}
}
- mInjector.getUserManagerInternal().setUserState(userId, uss.state);
uss.mUnlockProgress.start();
// Prepare app storage before we go any further
uss.mUnlockProgress.setProgress(5,
mInjector.getContext().getString(R.string.android_start_title));
- mInjector.getUserManager().onBeforeUnlockUser(userId);
- uss.mUnlockProgress.setProgress(20);
- // Dispatch unlocked to system services; when fully dispatched,
- // that calls through to the next "unlocked" phase
- mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
- .sendToTarget();
+ // Call onBeforeUnlockUser on a worker thread that allows disk I/O
+ FgThread.getHandler().post(() -> {
+ mInjector.getUserManager().onBeforeUnlockUser(userId);
+ synchronized (mLock) {
+ // Do not proceed if unexpected state
+ if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
+ return;
+ }
+ }
+ mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+
+ uss.mUnlockProgress.setProgress(20);
+
+ // Dispatch unlocked to system services; when fully dispatched,
+ // that calls through to the next "unlocked" phase
+ mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
+ .sendToTarget();
+ });
}
/**
@@ -1819,7 +1828,10 @@
case SYSTEM_USER_UNLOCK_MSG:
final int userId = msg.arg1;
mInjector.getSystemServiceManager().unlockUser(userId);
- mInjector.loadUserRecents(userId);
+ // Loads recents on a worker thread that allows disk I/O
+ FgThread.getHandler().post(() -> {
+ mInjector.loadUserRecents(userId);
+ });
if (userId == UserHandle.USER_SYSTEM) {
mInjector.startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3f23737..15a418dc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1345,8 +1345,9 @@
} else {
final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
final boolean activeForReal;
- if (maybeActiveStreamType == AudioSystem.STREAM_MUSIC) {
- activeForReal = isAfMusicActiveRecently(0);
+ if (maybeActiveStreamType == AudioSystem.STREAM_RING
+ || maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
+ activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
} else {
activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
}
@@ -3883,13 +3884,13 @@
/**
* For code clarity for getActiveStreamType(int)
- * @param delay_ms max time since last STREAM_MUSIC activity to consider
- * @return true if STREAM_MUSIC is active in streams handled by AudioFlinger now or
+ * @param delay_ms max time since last stream activity to consider
+ * @return true if stream is active in streams handled by AudioFlinger now or
* in the last "delay_ms" ms.
*/
- private boolean isAfMusicActiveRecently(int delay_ms) {
- return AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, delay_ms)
- || AudioSystem.isStreamActiveRemotely(AudioSystem.STREAM_MUSIC, delay_ms);
+ private boolean wasStreamActiveRecently(int stream, int delay_ms) {
+ return AudioSystem.isStreamActive(stream, delay_ms)
+ || AudioSystem.isStreamActiveRemotely(stream, delay_ms);
}
private int getActiveStreamType(int suggestedStreamType) {
@@ -3910,21 +3911,30 @@
return AudioSystem.STREAM_VOICE_CALL;
}
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- if (isAfMusicActiveRecently(sStreamOverrideDelayMs)) {
+ if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
if (DEBUG_VOL)
- Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
+ return AudioSystem.STREAM_RING;
+ } else if (wasStreamActiveRecently(
+ AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL)
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION stream active");
+ return AudioSystem.STREAM_NOTIFICATION;
+ } else {
+ if (DEBUG_VOL)
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC b/c default");
return AudioSystem.STREAM_MUSIC;
- } else {
- if (DEBUG_VOL)
- Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
- return AudioSystem.STREAM_RING;
}
- } else if (isAfMusicActiveRecently(0)) {
+ } else if (
+ wasStreamActiveRecently(AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL)
- Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
- return AudioSystem.STREAM_MUSIC;
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION stream active");
+ return AudioSystem.STREAM_NOTIFICATION;
+ } else if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL)
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
+ return AudioSystem.STREAM_RING;
}
- break;
default:
if (isInCommunication()) {
if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
@@ -3935,20 +3945,26 @@
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
return AudioSystem.STREAM_VOICE_CALL;
}
- } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
- sStreamOverrideDelayMs) ||
- AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
- sStreamOverrideDelayMs)) {
+ } else if (AudioSystem.isStreamActive(
+ AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
+ } else if (AudioSystem.isStreamActive(
+ AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
+ return AudioSystem.STREAM_RING;
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- if (isAfMusicActiveRecently(sStreamOverrideDelayMs)) {
- if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: forcing STREAM_MUSIC");
- return AudioSystem.STREAM_MUSIC;
- } else {
- if (DEBUG_VOL) Log.v(TAG,
- "getActiveStreamType: using STREAM_NOTIFICATION as default");
+ if (AudioSystem.isStreamActive(
+ AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
+ } else if (AudioSystem.isStreamActive(
+ AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
+ return AudioSystem.STREAM_RING;
+ } else {
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
+ return AudioSystem.STREAM_MUSIC;
}
}
break;
@@ -6349,10 +6365,10 @@
// stream override timeout when adjusting volume
//---------------------------------------------------------------------------------
- // AudioService.getActiveStreamType() will return:
// - STREAM_NOTIFICATION on tablets during this period after a notification stopped
- // - STREAM_MUSIC on phones during this period after music or talkback/voice search prompt
- // stopped
+ // - STREAM_RING on phones during this period after a notification stopped
+ // - STREAM_MUSIC otherwise
+
private static final int DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS = 0;
private static final int TOUCH_EXPLORE_STREAM_TYPE_OVERRIDE_DELAY_MS = 1000;
diff --git a/services/core/java/com/android/server/broadcastradio/Tuner.java b/services/core/java/com/android/server/broadcastradio/Tuner.java
index e6ae320..2ea4271 100644
--- a/services/core/java/com/android/server/broadcastradio/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/Tuner.java
@@ -27,8 +27,10 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
class Tuner extends ITuner.Stub {
private static final String TAG = "BroadcastRadioService.Tuner";
@@ -96,6 +98,10 @@
private native boolean nativeIsAnalogForced(long nativeContext);
private native void nativeSetAnalogForced(long nativeContext, boolean isForced);
+ private native Map<String, String> nativeSetParameters(long nativeContext,
+ Map<String, String> parameters);
+ private native Map<String, String> nativeGetParameters(long nativeContext, List<String> keys);
+
private native boolean nativeIsAntennaConnected(long nativeContext);
@Override
@@ -273,6 +279,31 @@
}
@Override
+ public Map setParameters(Map parameters) {
+ Map<String, String> results;
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ results = nativeSetParameters(mNativeContext, Objects.requireNonNull(parameters));
+ }
+ if (results == null) return Collections.emptyMap();
+ return results;
+ }
+
+ @Override
+ public Map getParameters(List<String> keys) {
+ if (keys == null) {
+ throw new IllegalArgumentException("The argument must not be a null pointer");
+ }
+ Map<String, String> results;
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ results = nativeGetParameters(mNativeContext, keys);
+ }
+ if (results == null) return Collections.emptyMap();
+ return results;
+ }
+
+ @Override
public boolean isAntennaConnected() {
synchronized (mLock) {
checkNotClosedLocked();
diff --git a/services/core/java/com/android/server/broadcastradio/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/TunerCallback.java
index a87ae8d..2460c67 100644
--- a/services/core/java/com/android/server/broadcastradio/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/TunerCallback.java
@@ -26,6 +26,9 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.List;
+import java.util.Map;
+
class TunerCallback implements ITunerCallback {
private static final String TAG = "BroadcastRadioService.TunerCallback";
@@ -121,6 +124,11 @@
}
@Override
+ public void onParametersUpdated(Map parameters) {
+ dispatch(() -> mClientCallback.onParametersUpdated(parameters));
+ }
+
+ @Override
public IBinder asBinder() {
throw new RuntimeException("Not a binder");
}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index a011692b..397af7b 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -46,7 +46,6 @@
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
-import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair;
import java.io.IOException;
import java.util.ArrayList;
@@ -128,6 +127,11 @@
wakeupStats.nonApplicationWakeups = in.nonApplicationWakeups;
wakeupStats.applicationWakeups = in.applicationWakeups;
wakeupStats.noUidWakeups = in.noUidWakeups;
+ wakeupStats.l2UnicastCount = in.l2UnicastCount;
+ wakeupStats.l2MulticastCount = in.l2MulticastCount;
+ wakeupStats.l2BroadcastCount = in.l2BroadcastCount;
+ wakeupStats.ethertypeCounts = toPairArray(in.ethertypes);
+ wakeupStats.ipNextHeaderCounts = toPairArray(in.ipNextHeaders);
final IpConnectivityEvent out = buildEvent(0, 0, in.iface);
out.setWakeupStats(wakeupStats);
return out;
@@ -242,7 +246,6 @@
private static void setNetworkEvent(IpConnectivityEvent out, NetworkEvent in) {
IpConnectivityLogClass.NetworkEvent networkEvent =
new IpConnectivityLogClass.NetworkEvent();
- networkEvent.networkId = netIdOf(in.netId);
networkEvent.eventType = in.eventType;
networkEvent.latencyMs = (int) in.durationMs;
out.setNetworkEvent(networkEvent);
@@ -321,12 +324,6 @@
return pairs;
}
- private static NetworkId netIdOf(int netid) {
- final NetworkId ni = new NetworkId();
- ni.networkId = netid;
- return ni;
- }
-
private static int ipSupportOf(DefaultNetworkEvent in) {
if (in.ipv4 && in.ipv6) {
return IpConnectivityLogClass.DefaultNetworkEvent.DUAL;
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index af138b93..e243e56 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetdEventCallback;
+import android.net.MacAddress;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.metrics.ConnectStats;
@@ -35,6 +36,7 @@
import android.util.Log;
import android.util.ArrayMap;
import android.util.SparseArray;
+import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -58,7 +60,6 @@
private static final String TAG = NetdEventListenerService.class.getSimpleName();
private static final boolean DBG = false;
- private static final boolean VDBG = false;
// Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum
// bursts of 5000 measurements.
@@ -198,8 +199,6 @@
public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
String hostname, String[] ipAddresses, int ipAddressesCount, int uid)
throws RemoteException {
- maybeVerboseLog("onDnsEvent(%d, %d, %d, %dms)", netId, eventType, returnCode, latencyMs);
-
long timestamp = System.currentTimeMillis();
getMetricsForNetwork(timestamp, netId).addDnsResult(eventType, returnCode, latencyMs);
@@ -215,8 +214,6 @@
// This method must not block or perform long-running operations.
public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr,
int port, int uid) throws RemoteException {
- maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs);
-
long timestamp = System.currentTimeMillis();
getMetricsForNetwork(timestamp, netId).addConnectResult(error, latencyMs, ipAddr);
@@ -232,11 +229,8 @@
}
@Override
- public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
- maybeVerboseLog("onWakeupEvent(%s, %d, %d, %sns)", prefix, uid, gid, timestampNs);
-
- // TODO: add ip protocol and port
-
+ public synchronized void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader,
+ byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort, long timestampNs) {
String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
final long timestampMs;
if (timestampNs > 0) {
@@ -245,15 +239,26 @@
timestampMs = System.currentTimeMillis();
}
- addWakeupEvent(iface, timestampMs, uid);
- }
-
- @GuardedBy("this")
- private void addWakeupEvent(String iface, long timestampMs, int uid) {
WakeupEvent event = new WakeupEvent();
event.iface = iface;
event.timestampMs = timestampMs;
event.uid = uid;
+ event.ethertype = ethertype;
+ event.dstHwAddr = new MacAddress(dstHw);
+ event.srcIp = srcIp;
+ event.dstIp = dstIp;
+ event.ipNextHeader = ipNextHeader;
+ event.srcPort = srcPort;
+ event.dstPort = dstPort;
+ addWakeupEvent(event);
+
+ String dstMac = event.dstHwAddr.toString();
+ StatsLog.write(StatsLog.PACKET_WAKEUP_OCCURRED,
+ uid, iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort);
+ }
+
+ private void addWakeupEvent(WakeupEvent event) {
+ String iface = event.iface;
mWakeupEvents.append(event);
WakeupStats stats = mWakeupStats.get(iface);
if (stats == null) {
@@ -333,10 +338,6 @@
if (DBG) Log.d(TAG, String.format(s, args));
}
- private static void maybeVerboseLog(String s, Object... args) {
- if (VDBG) Log.d(TAG, String.format(s, args));
- }
-
/** Helper class for buffering summaries of NetworkMetrics at regular time intervals */
static class NetworkMetricsSnapshot {
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 8b886d6..7684030 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -1129,7 +1129,8 @@
}
private void logNetworkEvent(int evtype) {
- mMetricsLog.log(new NetworkEvent(mNetId, evtype));
+ int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+ mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype));
}
private int networkEventType(ValidationStage s, EvaluationResult r) {
@@ -1150,7 +1151,8 @@
private void maybeLogEvaluationResult(int evtype) {
if (mEvaluationTimer.isRunning()) {
- mMetricsLog.log(new NetworkEvent(mNetId, evtype, mEvaluationTimer.stop()));
+ int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+ mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype, mEvaluationTimer.stop()));
mEvaluationTimer.reset();
}
}
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
new file mode 100644
index 0000000..361d928
--- /dev/null
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -0,0 +1,639 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ParceledListSlice;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.BrightnessChangeEvent;
+import android.net.Uri;
+import android.os.BatteryManager;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.RingBuffer;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+
+import java.util.Deque;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Class that tracks recent brightness settings changes and stores
+ * associated information such as light sensor readings.
+ */
+public class BrightnessTracker {
+
+ private static final String TAG = "BrightnessTracker";
+ private static final boolean DEBUG = false;
+
+ private static final String EVENTS_FILE = "brightness_events.xml";
+ private static final int MAX_EVENTS = 100;
+ // Discard events when reading or writing that are older than this.
+ private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30);
+ // Time over which we keep lux sensor readings.
+ private static final long LUX_EVENT_HORIZON = TimeUnit.SECONDS.toNanos(10);
+
+ private static final String TAG_EVENTS = "events";
+ private static final String TAG_EVENT = "event";
+ private static final String ATTR_BRIGHTNESS = "brightness";
+ private static final String ATTR_TIMESTAMP = "timestamp";
+ private static final String ATTR_PACKAGE_NAME = "packageName";
+ private static final String ATTR_USER = "user";
+ private static final String ATTR_LUX = "lux";
+ private static final String ATTR_LUX_TIMESTAMPS = "luxTimestamps";
+ private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
+ private static final String ATTR_NIGHT_MODE = "nightMode";
+ private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
+ private static final String ATTR_LAST_BRIGHTNESS = "lastBrightness";
+
+ // Lock held while accessing mEvents, is held while writing events to flash.
+ private final Object mEventsLock = new Object();
+ @GuardedBy("mEventsLock")
+ private RingBuffer<BrightnessChangeEvent> mEvents
+ = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
+ private final Runnable mEventsWriter = () -> writeEvents();
+ private volatile boolean mWriteEventsScheduled;
+
+ private UserManager mUserManager;
+ private final Context mContext;
+ private final ContentResolver mContentResolver;
+ private Handler mBgHandler;
+ // mSettingsObserver, mBroadcastReceiver and mSensorListener should only be used on
+ // the mBgHandler thread.
+ private SettingsObserver mSettingsObserver;
+ private BroadcastReceiver mBroadcastReceiver;
+ private SensorListener mSensorListener;
+
+ // Lock held while collecting data related to brightness changes.
+ private final Object mDataCollectionLock = new Object();
+ @GuardedBy("mDataCollectionLock")
+ private Deque<LightData> mLastSensorReadings = new ArrayDeque<>();
+ @GuardedBy("mDataCollectionLock")
+ private float mLastBatteryLevel = Float.NaN;
+ @GuardedBy("mDataCollectionLock")
+ private int mIgnoreBrightness = -1;
+ @GuardedBy("mDataCollectionLock")
+ private int mLastBrightness = -1;
+
+ private final Injector mInjector;
+
+ public BrightnessTracker(Context context, @Nullable Injector injector) {
+ // Note this will be called very early in boot, other system
+ // services may not be present.
+ mContext = context;
+ mContentResolver = context.getContentResolver();
+ if (injector != null) {
+ mInjector = injector;
+ } else {
+ mInjector = new Injector();
+ }
+ }
+
+ /** Start listening for brightness slider events */
+ public void start() {
+ if (DEBUG) {
+ Slog.d(TAG, "Start");
+ }
+ mBgHandler = mInjector.getBackgroundHandler();
+ mUserManager = mContext.getSystemService(UserManager.class);
+
+ mBgHandler.post(() -> backgroundStart());
+ }
+
+ private void backgroundStart() {
+ readEvents();
+
+ mLastBrightness = mInjector.getSystemIntForUser(mContentResolver,
+ Settings.System.SCREEN_BRIGHTNESS, -1,
+ UserHandle.USER_CURRENT);
+
+ mSensorListener = new SensorListener();
+ mInjector.registerSensorListener(mContext, mSensorListener);
+
+ mSettingsObserver = new SettingsObserver(mBgHandler);
+ mInjector.registerBrightnessObserver(mContentResolver, mSettingsObserver);
+
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_SHUTDOWN);
+ intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ mBroadcastReceiver = new Receiver();
+ mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter);
+ }
+
+ /** Stop listening for events */
+ @VisibleForTesting
+ void stop() {
+ if (DEBUG) {
+ Slog.d(TAG, "Stop");
+ }
+ mInjector.unregisterSensorListener(mContext, mSensorListener);
+ mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
+ mInjector.unregisterBrightnessObserver(mContext, mSettingsObserver);
+ }
+
+ /**
+ * @param userId userId to fetch data for.
+ * @return List of recent {@link BrightnessChangeEvent}s
+ */
+ public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId) {
+ // TODO include apps from any managed profiles in the brightness information.
+ BrightnessChangeEvent[] events;
+ synchronized (mEventsLock) {
+ events = mEvents.toArray();
+ }
+ ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length);
+ for (int i = 0; i < events.length; ++i) {
+ if (events[i].userId == userId) {
+ out.add(events[i]);
+ }
+ }
+ return new ParceledListSlice<>(out);
+ }
+
+ /** Sets brightness without logging the brightness change event */
+ public void setBrightness(int brightness, int userId) {
+ synchronized (mDataCollectionLock) {
+ mIgnoreBrightness = brightness;
+ }
+ mInjector.putSystemIntForUser(mContentResolver, Settings.System.SCREEN_BRIGHTNESS,
+ brightness, userId);
+ }
+
+ private void handleBrightnessChanged() {
+ if (DEBUG) {
+ Slog.d(TAG, "Brightness change");
+ }
+ final BrightnessChangeEvent event = new BrightnessChangeEvent();
+ event.timeStamp = mInjector.currentTimeMillis();
+
+ int brightness = mInjector.getSystemIntForUser(mContentResolver,
+ Settings.System.SCREEN_BRIGHTNESS, -1,
+ UserHandle.USER_CURRENT);
+
+ synchronized (mDataCollectionLock) {
+ int previousBrightness = mLastBrightness;
+ mLastBrightness = brightness;
+
+ if (brightness == -1 || brightness == mIgnoreBrightness) {
+ // Notified of brightness change but no setting or self change so ignore.
+ mIgnoreBrightness = -1;
+ return;
+ }
+
+ final int readingCount = mLastSensorReadings.size();
+ if (readingCount == 0) {
+ // No sensor data so ignore this.
+ return;
+ }
+
+ event.luxValues = new float[readingCount];
+ event.luxTimestamps = new long[readingCount];
+
+ int pos = 0;
+
+ // Convert sensor timestamp in elapsed time nanos to current time millis.
+ long currentTimeMillis = mInjector.currentTimeMillis();
+ long elapsedTimeNanos = mInjector.elapsedRealtimeNanos();
+ for (LightData reading : mLastSensorReadings) {
+ event.luxValues[pos] = reading.lux;
+ event.luxTimestamps[pos] = currentTimeMillis -
+ TimeUnit.NANOSECONDS.toMillis(elapsedTimeNanos - reading.timestamp);
+ ++pos;
+ }
+
+ event.batteryLevel = mLastBatteryLevel;
+ event.lastBrightness = previousBrightness;
+ }
+
+ event.brightness = brightness;
+
+ try {
+ final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
+ event.userId = focusedStack.userId;
+ event.packageName = focusedStack.topActivity.getPackageName();
+ } catch (RemoteException e) {
+ // Really shouldn't be possible.
+ }
+
+ event.nightMode = mInjector.getSecureIntForUser(mContentResolver,
+ Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 0, UserHandle.USER_CURRENT)
+ == 1;
+ event.colorTemperature = mInjector.getSecureIntForUser(mContentResolver,
+ Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
+ 0, UserHandle.USER_CURRENT);
+
+ if (DEBUG) {
+ Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
+ }
+ synchronized (mEventsLock) {
+ mEvents.append(event);
+ }
+ }
+
+ private void scheduleWriteEvents() {
+ if (!mWriteEventsScheduled) {
+ mBgHandler.post(mEventsWriter);
+ mWriteEventsScheduled = true;
+ }
+ }
+
+ private void writeEvents() {
+ mWriteEventsScheduled = false;
+ // TODO kick off write on handler thread e.g. every 24 hours.
+ synchronized (mEventsLock) {
+ final AtomicFile writeTo = mInjector.getFile();
+ if (writeTo == null) {
+ return;
+ }
+ if (mEvents.isEmpty()) {
+ if (writeTo.exists()) {
+ writeTo.delete();
+ }
+ } else {
+ FileOutputStream output = null;
+ try {
+ output = writeTo.startWrite();
+ writeEventsLocked(output);
+ writeTo.finishWrite(output);
+ } catch (IOException e) {
+ writeTo.failWrite(output);
+ Slog.e(TAG, "Failed to write change mEvents.", e);
+ }
+ }
+ }
+ }
+
+ private void readEvents() {
+ synchronized (mEventsLock) {
+ mEvents.clear();
+ final AtomicFile readFrom = mInjector.getFile();
+ if (readFrom != null && readFrom.exists()) {
+ FileInputStream input = null;
+ try {
+ input = readFrom.openRead();
+ readEventsLocked(input);
+ } catch (IOException e) {
+ readFrom.delete();
+ Slog.e(TAG, "Failed to read change mEvents.", e);
+ } finally {
+ IoUtils.closeQuietly(input);
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mEventsLock")
+ void writeEventsLocked(OutputStream stream) throws IOException {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ out.startTag(null, TAG_EVENTS);
+ BrightnessChangeEvent[] toWrite = mEvents.toArray();
+ if (DEBUG) {
+ Slog.d(TAG, "Writing events " + toWrite.length);
+ }
+ final long timeCutOff = System.currentTimeMillis() - MAX_EVENT_AGE;
+ for (int i = 0; i < toWrite.length; ++i) {
+ int userSerialNo = mInjector.getUserSerialNumber(mUserManager, toWrite[i].userId);
+ if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) {
+ out.startTag(null, TAG_EVENT);
+ out.attribute(null, ATTR_BRIGHTNESS, Integer.toString(toWrite[i].brightness));
+ out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp));
+ out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
+ out.attribute(null, ATTR_USER, Integer.toString(userSerialNo));
+ out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel));
+ out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode));
+ out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString(
+ toWrite[i].colorTemperature));
+ out.attribute(null, ATTR_LAST_BRIGHTNESS,
+ Integer.toString(toWrite[i].lastBrightness));
+ StringBuilder luxValues = new StringBuilder();
+ StringBuilder luxTimestamps = new StringBuilder();
+ for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
+ if (j > 0) {
+ luxValues.append(',');
+ luxTimestamps.append(',');
+ }
+ luxValues.append(Float.toString(toWrite[i].luxValues[j]));
+ luxTimestamps.append(Long.toString(toWrite[i].luxTimestamps[j]));
+ }
+ out.attribute(null, ATTR_LUX, luxValues.toString());
+ out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString());
+ out.endTag(null, TAG_EVENT);
+ }
+ }
+ out.endTag(null, TAG_EVENTS);
+ out.endDocument();
+ stream.flush();
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mEventsLock")
+ void readEventsLocked(InputStream stream) throws IOException {
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+ String tag = parser.getName();
+ if (!TAG_EVENTS.equals(tag)) {
+ throw new XmlPullParserException(
+ "Events not found in brightness tracker file " + tag);
+ }
+
+ final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
+
+ parser.next();
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ tag = parser.getName();
+ if (TAG_EVENT.equals(tag)) {
+ BrightnessChangeEvent event = new BrightnessChangeEvent();
+
+ String brightness = parser.getAttributeValue(null, ATTR_BRIGHTNESS);
+ event.brightness = Integer.parseInt(brightness);
+ String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP);
+ event.timeStamp = Long.parseLong(timestamp);
+ event.packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ String user = parser.getAttributeValue(null, ATTR_USER);
+ event.userId = mInjector.getUserId(mUserManager, Integer.parseInt(user));
+ String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL);
+ event.batteryLevel = Float.parseFloat(batteryLevel);
+ String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE);
+ event.nightMode = Boolean.parseBoolean(nightMode);
+ String colorTemperature =
+ parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE);
+ event.colorTemperature = Integer.parseInt(colorTemperature);
+ String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_BRIGHTNESS);
+ event.lastBrightness = Integer.parseInt(lastBrightness);
+
+ String luxValue = parser.getAttributeValue(null, ATTR_LUX);
+ String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS);
+
+ String[] luxValues = luxValue.split(",");
+ String[] luxTimestamps = luxTimestamp.split(",");
+ if (luxValues.length != luxTimestamps.length) {
+ continue;
+ }
+ event.luxValues = new float[luxValues.length];
+ event.luxTimestamps = new long[luxValues.length];
+ for (int i = 0; i < luxValues.length; ++i) {
+ event.luxValues[i] = Float.parseFloat(luxValues[i]);
+ event.luxTimestamps[i] = Long.parseLong(luxTimestamps[i]);
+ }
+
+ if (DEBUG) {
+ Slog.i(TAG, "Read event " + event.brightness
+ + " " + event.packageName);
+ }
+
+ if (event.userId != -1 && event.timeStamp > timeCutOff
+ && event.luxValues.length > 0) {
+ mEvents.append(event);
+ }
+ }
+ }
+ } catch (NullPointerException | NumberFormatException | XmlPullParserException
+ | IOException e) {
+ // Failed to parse something, just start with an empty event log.
+ mEvents = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
+ Slog.e(TAG, "Failed to parse brightness event", e);
+ // Re-throw so we will delete the bad file.
+ throw new IOException("failed to parse file", e);
+ }
+ }
+
+ // Not allowed to keep the SensorEvent so used to copy the data we care about.
+ private static class LightData {
+ public float lux;
+ // Time in elapsedRealtimeNanos
+ public long timestamp;
+ }
+
+ private void recordSensorEvent(SensorEvent event) {
+ long horizon = mInjector.elapsedRealtimeNanos() - LUX_EVENT_HORIZON;
+ synchronized (mDataCollectionLock) {
+ if (DEBUG) {
+ Slog.v(TAG, "Sensor event " + event);
+ }
+ if (!mLastSensorReadings.isEmpty()
+ && event.timestamp < mLastSensorReadings.getLast().timestamp) {
+ // Ignore event that came out of order.
+ return;
+ }
+ LightData data = null;
+ while (!mLastSensorReadings.isEmpty()
+ && mLastSensorReadings.getFirst().timestamp < horizon) {
+ // Remove data that has fallen out of the window.
+ data = mLastSensorReadings.removeFirst();
+ }
+ // We put back the last one we removed so we know how long
+ // the first sensor reading was valid for.
+ if (data != null) {
+ mLastSensorReadings.addFirst(data);
+ }
+
+ data = new LightData();
+ data.timestamp = event.timestamp;
+ data.lux = event.values[0];
+ mLastSensorReadings.addLast(data);
+ }
+ }
+
+ private void batteryLevelChanged(int level, int scale) {
+ synchronized (mDataCollectionLock) {
+ mLastBatteryLevel = (float) level / (float) scale;
+ }
+ }
+
+ private final class SensorListener implements SensorEventListener {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ recordSensorEvent(event);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+
+ }
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (DEBUG) {
+ Slog.v(TAG, "settings change " + uri);
+ }
+ // Self change is based on observer passed to notifyObserver, SettingsProvider
+ // passes null so no changes are self changes.
+ handleBrightnessChanged();
+ }
+ }
+
+ private final class Receiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received " + intent.getAction());
+ }
+ String action = intent.getAction();
+ if (Intent.ACTION_SHUTDOWN.equals(action)) {
+ stop();
+ scheduleWriteEvents();
+ } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+ int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
+ if (level != -1 && scale != 0) {
+ batteryLevelChanged(level, scale);
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ public void registerSensorListener(Context context,
+ SensorEventListener sensorListener) {
+ SensorManager sensorManager = context.getSystemService(SensorManager.class);
+ Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ sensorManager.registerListener(sensorListener,
+ lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+
+ public void unregisterSensorListener(Context context, SensorEventListener sensorListener) {
+ SensorManager sensorManager = context.getSystemService(SensorManager.class);
+ sensorManager.unregisterListener(sensorListener);
+ }
+
+ public void registerBrightnessObserver(ContentResolver resolver,
+ ContentObserver settingsObserver) {
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS),
+ false, settingsObserver, UserHandle.USER_ALL);
+ }
+
+ public void unregisterBrightnessObserver(Context context,
+ ContentObserver settingsObserver) {
+ context.getContentResolver().unregisterContentObserver(settingsObserver);
+ }
+
+ public void registerReceiver(Context context,
+ BroadcastReceiver receiver, IntentFilter filter) {
+ context.registerReceiver(receiver, filter);
+ }
+
+ public void unregisterReceiver(Context context,
+ BroadcastReceiver receiver) {
+ context.unregisterReceiver(receiver);
+ }
+
+ public Handler getBackgroundHandler() {
+ return BackgroundThread.getHandler();
+ }
+
+ public int getSystemIntForUser(ContentResolver resolver, String setting, int defaultValue,
+ int userId) {
+ return Settings.System.getIntForUser(resolver, setting, defaultValue, userId);
+ }
+
+ public void putSystemIntForUser(ContentResolver resolver, String setting, int value,
+ int userId) {
+ Settings.System.putIntForUser(resolver, setting, value, userId);
+ }
+
+ public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue,
+ int userId) {
+ return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
+ }
+
+ public AtomicFile getFile() {
+ return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), EVENTS_FILE));
+ }
+
+ public long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ public long elapsedRealtimeNanos() {
+ return SystemClock.elapsedRealtimeNanos();
+ }
+
+ public int getUserSerialNumber(UserManager userManager, int userId) {
+ return userManager.getUserSerialNumber(userId);
+ }
+
+ public int getUserId(UserManager userManager, int userSerialNumber) {
+ return userManager.getUserHandle(userSerialNumber);
+ }
+
+ public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
+ return ActivityManager.getService().getFocusedStackInfo();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d0a1d9e..f1e2011 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -31,9 +31,11 @@
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Point;
import android.hardware.SensorManager;
+import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayViewport;
@@ -58,6 +60,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.IntArray;
import android.util.Slog;
@@ -139,6 +142,7 @@
private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
private static final int MSG_REQUEST_TRAVERSAL = 4;
private static final int MSG_UPDATE_VIEWPORT = 5;
+ private static final int MSG_REGISTER_BRIGHTNESS_TRACKER = 6;
private final Context mContext;
private final DisplayManagerHandler mHandler;
@@ -256,6 +260,8 @@
private final Injector mInjector;
+ private final BrightnessTracker mBrightnessTracker;
+
public DisplayManagerService(Context context) {
this(context, new Injector());
}
@@ -274,6 +280,7 @@
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
+ mBrightnessTracker = new BrightnessTracker(context, null);
}
public void setupSchedulerPolicies() {
@@ -350,6 +357,7 @@
}
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
+ mHandler.sendEmptyMessage(MSG_REGISTER_BRIGHTNESS_TRACKER);
}
@VisibleForTesting
@@ -1352,6 +1360,10 @@
mTempExternalTouchViewport, mTempVirtualTouchViewports);
break;
}
+
+ case MSG_REGISTER_BRIGHTNESS_TRACKER:
+ mBrightnessTracker.start();
+ break;
}
}
}
@@ -1736,6 +1748,35 @@
}
}
+ @Override // Binder call
+ public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents() {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.BRIGHTNESS_SLIDER_USAGE,
+ "Permission to read brightness events.");
+ int userId = UserHandle.getUserId(Binder.getCallingUid());
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mBrightnessTracker.getEvents(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void setBrightness(int brightness) {
+ // STOPSHIP - remove when adaptive brightness controller accepts curves.
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.BRIGHTNESS_SLIDER_USAGE,
+ "Permission to set brightness.");
+ int userId = UserHandle.getUserId(Binder.getCallingUid());
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mBrightnessTracker.setBrightness(brightness, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b549768..b9777ec 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -622,24 +622,15 @@
if (disabled) {
cancelJobsForUid(uid, "uid gone");
}
- synchronized (mLock) {
- mBackgroundJobsController.setUidActiveLocked(uid, false);
- }
}
@Override public void onUidActive(int uid) throws RemoteException {
- synchronized (mLock) {
- mBackgroundJobsController.setUidActiveLocked(uid, true);
- }
}
@Override public void onUidIdle(int uid, boolean disabled) {
if (disabled) {
cancelJobsForUid(uid, "app uid idle");
}
- synchronized (mLock) {
- mBackgroundJobsController.setUidActiveLocked(uid, false);
- }
}
@Override public void onUidCachedChanged(int uid, boolean cached) {
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index 78b4160..f67bd04 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -16,7 +16,6 @@
package com.android.server.job.controllers;
-import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -25,19 +24,20 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.UserHandle;
-import android.util.ArraySet;
+import android.util.Pair;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import com.android.internal.app.IAppOpsCallback;
-import com.android.internal.app.IAppOpsService;
import com.android.internal.util.ArrayUtils;
+import com.android.server.ForceAppStandbyTracker;
+import com.android.server.ForceAppStandbyTracker.Listener;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
import java.io.PrintWriter;
+import java.util.function.Predicate;
public final class BackgroundJobsController extends StateController {
@@ -49,18 +49,13 @@
private static volatile BackgroundJobsController sController;
private final JobSchedulerService mJobSchedulerService;
- private final IAppOpsService mAppOpsService;
private final IDeviceIdleController mDeviceIdleController;
- private final SparseBooleanArray mForegroundUids;
private int[] mPowerWhitelistedUserAppIds;
private int[] mTempWhitelistedAppIds;
- /**
- * Only tracks jobs for which source package app op RUN_ANY_IN_BACKGROUND is not ALLOWED.
- * Maps jobs to the sourceUid unlike the global {@link JobSchedulerService#mJobs JobStore}
- * which uses callingUid.
- */
- private SparseArray<ArraySet<JobStatus>> mTrackedJobs;
+
+ private final ForceAppStandbyTracker mForceAppStandbyTracker;
+
public static BackgroundJobsController get(JobSchedulerService service) {
synchronized (sCreationLock) {
@@ -89,9 +84,7 @@
} catch (RemoteException rexc) {
Slog.e(LOG_TAG, "Device idle controller not reachable");
}
- if (checkAllTrackedJobsLocked()) {
- mStateChangedListener.onControllerStateChanged();
- }
+ updateAllJobRestrictionsLocked();
}
}
};
@@ -99,16 +92,12 @@
private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) {
super(service, context, lock);
mJobSchedulerService = service;
- mAppOpsService = IAppOpsService.Stub.asInterface(
- ServiceManager.getService(Context.APP_OPS_SERVICE));
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- mForegroundUids = new SparseBooleanArray();
- mTrackedJobs = new SparseArray<>();
+ mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
+
try {
- mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
- new AppOpsWatcher());
mPowerWhitelistedUserAppIds = mDeviceIdleController.getAppIdUserWhitelist();
mTempWhitelistedAppIds = mDeviceIdleController.getAppIdTempWhitelist();
} catch (RemoteException rexc) {
@@ -120,184 +109,165 @@
powerWhitelistFilter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
context.registerReceiverAsUser(mDozeWhitelistReceiver, UserHandle.ALL, powerWhitelistFilter,
null, null);
+
+ mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
+ mForceAppStandbyTracker.start();
}
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
- final int uid = jobStatus.getSourceUid();
- final String packageName = jobStatus.getSourcePackageName();
- try {
- final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
- uid, packageName);
- if (mode == AppOpsManager.MODE_ALLOWED) {
- jobStatus.setBackgroundNotRestrictedConstraintSatisfied(true);
- return;
- }
- } catch (RemoteException rexc) {
- Slog.e(LOG_TAG, "Cannot reach app ops service", rexc);
- }
- jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRunJobLocked(uid));
- startTrackingJobLocked(jobStatus);
+ updateSingleJobRestrictionLocked(jobStatus);
}
@Override
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean forUpdate) {
- stopTrackingJobLocked(jobStatus);
- }
-
- /* Called by JobSchedulerService to report uid state changes between active and idle */
- public void setUidActiveLocked(int uid, boolean active) {
- final boolean changed = (active != mForegroundUids.get(uid));
- if (!changed) {
- return;
- }
- if (DEBUG) {
- Slog.d(LOG_TAG, "uid " + uid + " going to " + (active ? "fg" : "bg"));
- }
- if (active) {
- mForegroundUids.put(uid, true);
- } else {
- mForegroundUids.delete(uid);
- }
- if (checkTrackedJobsForUidLocked(uid)) {
- mStateChangedListener.onControllerStateChanged();
- }
}
@Override
public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
pw.println("BackgroundJobsController");
+
+ pw.print("Force all apps standby: ");
+ pw.println(mForceAppStandbyTracker.isForceAllAppsStandbyEnabled());
+
pw.print("Foreground uids: [");
- for (int i = 0; i < mForegroundUids.size(); i++) {
- if (mForegroundUids.valueAt(i)) pw.print(mForegroundUids.keyAt(i) + " ");
+ final SparseBooleanArray foregroundUids = mForceAppStandbyTracker.getForegroudUids();
+
+ String sep = "";
+ for (int i = 0; i < foregroundUids.size(); i++) {
+ if (foregroundUids.valueAt(i)) {
+ pw.print(sep);
+ pw.print(UserHandle.formatUid(foregroundUids.keyAt(i)));
+ sep = " ";
+ }
}
pw.println("]");
- mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
- @Override
- public void process(JobStatus jobStatus) {
- if (!jobStatus.shouldDump(filterUid)) {
- return;
- }
- final int uid = jobStatus.getSourceUid();
- pw.print(" #");
- jobStatus.printUniqueId(pw);
- pw.print(" from ");
- UserHandle.formatUid(pw, uid);
- pw.print(mForegroundUids.get(uid) ? " foreground" : " background");
- if (isWhitelistedLocked(uid)) {
- pw.print(", whitelisted");
- }
- pw.print(": ");
- pw.print(jobStatus.getSourcePackageName());
- pw.print(" [background restrictions");
- final ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- pw.print(jobsForUid != null && jobsForUid.contains(jobStatus) ? " on]" : " off]");
- if ((jobStatus.satisfiedConstraints
- & JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
- pw.println(" RUNNABLE");
- } else {
- pw.println(" WAITING");
- }
+
+ pw.println("Restricted packages:");
+ for (Pair<Integer, String> uidAndPackage
+ : mForceAppStandbyTracker.getRestrictedUidPackages()) {
+ pw.print(" ");
+ pw.print(UserHandle.formatUid(uidAndPackage.first));
+ pw.print(" ");
+ pw.print(uidAndPackage.second);
+ pw.println();
+ }
+
+ pw.println("Job state:");
+ mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> {
+ if (!jobStatus.shouldDump(filterUid)) {
+ return;
+ }
+ final int uid = jobStatus.getSourceUid();
+ pw.print(" #");
+ jobStatus.printUniqueId(pw);
+ pw.print(" from ");
+ UserHandle.formatUid(pw, uid);
+ pw.print(mForceAppStandbyTracker.isInForeground(uid) ? " foreground" : " background");
+ if (isWhitelistedLocked(uid)) {
+ pw.print(", whitelisted");
+ }
+ pw.print(": ");
+ pw.print(jobStatus.getSourcePackageName());
+
+ pw.print(" [background restrictions ");
+ pw.print(mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(
+ jobStatus.getSourceUid(), jobStatus.getSourcePackageName()) ? "off]" : "on]");
+
+ if ((jobStatus.satisfiedConstraints
+ & JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
+ pw.println(" RUNNABLE");
+ } else {
+ pw.println(" WAITING");
}
});
}
- void startTrackingJobLocked(JobStatus jobStatus) {
+ private void updateAllJobRestrictionsLocked() {
+ updateJobRestrictionsLocked(/*filterUid=*/ -1);
+ }
+
+ private void updateJobRestrictionsForUidLocked(int uid) {
+
+ // TODO Use forEachJobForSourceUid() once we have it.
+
+ updateJobRestrictionsLocked(/*filterUid=*/ uid);
+ }
+
+ private void updateJobRestrictionsLocked(int filterUid) {
+ final UpdateJobFunctor updateTrackedJobs =
+ new UpdateJobFunctor(filterUid);
+
+ final long start = DEBUG ? SystemClock.elapsedRealtimeNanos() : 0;
+
+ mJobSchedulerService.getJobStore().forEachJob(updateTrackedJobs);
+
+ final long time = DEBUG ? (SystemClock.elapsedRealtimeNanos() - start) : 0;
+ if (DEBUG) {
+ Slog.d(LOG_TAG, String.format(
+ "Job status updated: %d/%d checked/total jobs, %d us",
+ updateTrackedJobs.mCheckedCount,
+ updateTrackedJobs.mTotalCount,
+ (time / 1000)
+ ));
+ }
+
+ if (updateTrackedJobs.mChanged) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+
+ private boolean isWhitelistedLocked(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ return ArrayUtils.contains(mTempWhitelistedAppIds, appId)
+ || ArrayUtils.contains(mPowerWhitelistedUserAppIds, appId);
+ }
+
+ boolean updateSingleJobRestrictionLocked(JobStatus jobStatus) {
+
final int uid = jobStatus.getSourceUid();
- ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- if (jobsForUid == null) {
- jobsForUid = new ArraySet<>();
- mTrackedJobs.put(uid, jobsForUid);
- }
- jobsForUid.add(jobStatus);
+ final String packageName = jobStatus.getSourcePackageName();
+
+ final boolean canRun = isWhitelistedLocked(uid)
+ || !mForceAppStandbyTracker.isRestricted(uid, packageName);
+
+ return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
}
- void stopTrackingJobLocked(JobStatus jobStatus) {
- final int uid = jobStatus.getSourceUid();
- ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- if (jobsForUid != null) {
- jobsForUid.remove(jobStatus);
- }
- }
+ private final class UpdateJobFunctor implements JobStore.JobStatusFunctor {
+ private final int mFilterUid;
- boolean checkAllTrackedJobsLocked() {
- boolean changed = false;
- for (int i = 0; i < mTrackedJobs.size(); i++) {
- changed |= checkTrackedJobsForUidLocked(mTrackedJobs.keyAt(i));
- }
- return changed;
- }
+ boolean mChanged = false;
+ int mTotalCount = 0;
+ int mCheckedCount = 0;
- private boolean checkTrackedJobsForUidLocked(int uid) {
- final ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
- boolean changed = false;
- if (jobsForUid != null) {
- for (int i = 0; i < jobsForUid.size(); i++) {
- JobStatus jobStatus = jobsForUid.valueAt(i);
- changed |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(
- canRunJobLocked(uid));
- }
- }
- return changed;
- }
-
- boolean isWhitelistedLocked(int uid) {
- return ArrayUtils.contains(mTempWhitelistedAppIds, UserHandle.getAppId(uid))
- || ArrayUtils.contains(mPowerWhitelistedUserAppIds, UserHandle.getAppId(uid));
- }
-
- boolean canRunJobLocked(int uid) {
- return mForegroundUids.get(uid) || isWhitelistedLocked(uid);
- }
-
- private final class AppOpsWatcher extends IAppOpsCallback.Stub {
- @Override
- public void opChanged(int op, int uid, String packageName) throws RemoteException {
- synchronized (mLock) {
- final int mode = mAppOpsService.checkOperation(op, uid, packageName);
- if (DEBUG) {
- Slog.d(LOG_TAG,
- "Appop changed for " + uid + ", " + packageName + " to " + mode);
- }
- final boolean shouldTrack = (mode != AppOpsManager.MODE_ALLOWED);
- UpdateTrackedJobsFunc updateTrackedJobs = new UpdateTrackedJobsFunc(uid,
- packageName, shouldTrack);
- mJobSchedulerService.getJobStore().forEachJob(updateTrackedJobs);
- if (updateTrackedJobs.mChanged) {
- mStateChangedListener.onControllerStateChanged();
- }
- }
- }
- }
-
- private final class UpdateTrackedJobsFunc implements JobStore.JobStatusFunctor {
- private final String mPackageName;
- private final int mUid;
- private final boolean mShouldTrack;
- private boolean mChanged = false;
-
- UpdateTrackedJobsFunc(int uid, String packageName, boolean shouldTrack) {
- mUid = uid;
- mPackageName = packageName;
- mShouldTrack = shouldTrack;
+ UpdateJobFunctor(int filterUid) {
+ mFilterUid = filterUid;
}
@Override
public void process(JobStatus jobStatus) {
- final String packageName = jobStatus.getSourcePackageName();
- final int uid = jobStatus.getSourceUid();
- if (mUid != uid || !mPackageName.equals(packageName)) {
+ mTotalCount++;
+ if ((mFilterUid > 0) && (mFilterUid != jobStatus.getSourceUid())) {
return;
}
- if (mShouldTrack) {
- mChanged |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(
- canRunJobLocked(uid));
- startTrackingJobLocked(jobStatus);
- } else {
- mChanged |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(true);
- stopTrackingJobLocked(jobStatus);
+ mCheckedCount++;
+ if (updateSingleJobRestrictionLocked(jobStatus)) {
+ mChanged = true;
}
}
}
+
+ private final Listener mForceAppStandbyListener = new Listener() {
+ @Override
+ public void onRestrictionChanged(int uid, String packageName) {
+ updateJobRestrictionsForUidLocked(uid);
+ }
+
+ @Override
+ public void onGlobalRestrictionChanged() {
+ updateAllJobRestrictionsLocked();
+ }
+ };
}
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
index 55222dc..f82b976 100644
--- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
@@ -103,51 +103,55 @@
* Helper function to bind the FusedLocationHardware to the appropriate FusedProvider instance.
*/
private void bindProvider() {
- IBinder binder = mServiceWatcher.getBinder();
- if (binder == null) {
- Log.e(TAG, "Null binder found on connection.");
- return;
- }
- String descriptor;
- try {
- descriptor = binder.getInterfaceDescriptor();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to get interface descriptor.", e);
- return;
- }
+ if (!mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ String descriptor;
+ try {
+ descriptor = binder.getInterfaceDescriptor();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get interface descriptor.", e);
+ return;
+ }
- if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) {
- IActivityRecognitionHardwareWatcher watcher =
- IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
- if (watcher == null) {
- Log.e(TAG, "No watcher found on connection.");
- return;
+ if (IActivityRecognitionHardwareWatcher.class.getCanonicalName()
+ .equals(descriptor)) {
+ IActivityRecognitionHardwareWatcher watcher =
+ IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
+ if (watcher == null) {
+ Log.e(TAG, "No watcher found on connection.");
+ return;
+ }
+ if (mInstance == null) {
+ // to keep backwards compatibility do not update the watcher when there is
+ // no instance available, or it will cause an NPE
+ Log.d(TAG, "AR HW instance not available, binding will be a no-op.");
+ return;
+ }
+ try {
+ watcher.onInstanceChanged(mInstance);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error delivering hardware interface to watcher.", e);
+ }
+ } else if (IActivityRecognitionHardwareClient.class.getCanonicalName()
+ .equals(descriptor)) {
+ IActivityRecognitionHardwareClient client =
+ IActivityRecognitionHardwareClient.Stub.asInterface(binder);
+ if (client == null) {
+ Log.e(TAG, "No client found on connection.");
+ return;
+ }
+ try {
+ client.onAvailabilityChanged(mIsSupported, mInstance);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error delivering hardware interface to client.", e);
+ }
+ } else {
+ Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
+ }
}
- if (mInstance == null) {
- // to keep backwards compatibility do not update the watcher when there is no
- // instance available, or it will cause an NPE
- Log.d(TAG, "AR HW instance not available, binding will be a no-op.");
- return;
- }
- try {
- watcher.onInstanceChanged(mInstance);
- } catch (RemoteException e) {
- Log.e(TAG, "Error delivering hardware interface to watcher.", e);
- }
- } else if (IActivityRecognitionHardwareClient.class.getCanonicalName().equals(descriptor)) {
- IActivityRecognitionHardwareClient client =
- IActivityRecognitionHardwareClient.Stub.asInterface(binder);
- if (client == null) {
- Log.e(TAG, "No client found on connection.");
- return;
- }
- try {
- client.onAvailabilityChanged(mIsSupported, mInstance);
- } catch (RemoteException e) {
- Log.e(TAG, "Error delivering hardware interface to client.", e);
- }
- } else {
- Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
+ })) {
+ Log.e(TAG, "Null binder found on connection.");
}
}
}
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 5e9f355..da481a8 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -18,15 +18,25 @@
import android.Manifest;
import android.content.Context;
-import android.content.pm.PackageManager;
+import android.hardware.contexthub.V1_0.AsyncEventType;
+import android.hardware.contexthub.V1_0.ContextHub;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.HostEndPoint;
+import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.IContexthubCallback;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.contexthub.V1_0.TransactionResult;
import android.hardware.location.ContextHubInfo;
-import android.hardware.location.ContextHubManager;
import android.hardware.location.ContextHubMessage;
-import android.hardware.location.IContextHubService;
import android.hardware.location.IContextHubCallback;
-import android.hardware.location.NanoAppFilter;
+import android.hardware.location.IContextHubService;
+import android.hardware.location.IContextHubTransactionCallback;
import android.hardware.location.NanoApp;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppFilter;
import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.NanoAppState;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
@@ -38,8 +48,10 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.HashMap;
/**
* @hide
@@ -48,55 +60,152 @@
private static final String TAG = "ContextHubService";
private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
- + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
+ + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
- public static final int ANY_HUB = -1;
- public static final int MSG_LOAD_NANO_APP = 3;
+ /*
+ * Constants for the type of transaction that is defined by ContextHubService.
+ * This is used to report the transaction callback to clients, and is different from
+ * ContextHubTransaction.Type.
+ */
+ public static final int MSG_ENABLE_NANO_APP = 1;
+ public static final int MSG_DISABLE_NANO_APP = 2;
+ public static final int MSG_LOAD_NANO_APP = 3;
public static final int MSG_UNLOAD_NANO_APP = 4;
+ public static final int MSG_QUERY_NANO_APPS = 5;
+ public static final int MSG_QUERY_MEMORY = 6;
+ public static final int MSG_HUB_RESET = 7;
private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown";
private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN;
private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN;
private static final int PRE_LOADED_APP_MEM_REQ = 0;
- private static final int MSG_HEADER_SIZE = 4;
- private static final int HEADER_FIELD_MSG_TYPE = 0;
- private static final int HEADER_FIELD_MSG_VERSION = 1;
- private static final int HEADER_FIELD_HUB_HANDLE = 2;
- private static final int HEADER_FIELD_APP_INSTANCE = 3;
-
- private static final int HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
- private static final int HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
- private static final int MSG_LOAD_APP_HEADER_SIZE = MSG_HEADER_SIZE + 2;
-
private static final int OS_APP_INSTANCE = -1;
private final Context mContext;
+
+ // TODO(b/69270990): Remove once old ContextHubManager API is deprecated
+ // Service cache maintaining of instance ID to nanoapp infos
private final ConcurrentHashMap<Integer, NanoAppInstanceInfo> mNanoAppHash =
new ConcurrentHashMap<>();
+ // The next available instance ID (managed by the service) to assign to a nanoapp
+ private int mNextAvailableInstanceId = 0;
+ // A map of the long nanoapp ID to instance ID managed by the service
+ private final ConcurrentHashMap<Long, Integer> mNanoAppIdToInstanceMap =
+ new ConcurrentHashMap<>();
+
private final ContextHubInfo[] mContextHubInfo;
private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
new RemoteCallbackList<>();
- private native int nativeSendMessage(int[] header, byte[] data);
- private native ContextHubInfo[] nativeInitialize();
+ // Proxy object to communicate with the Context Hub HAL
+ private final IContexthub mContextHubProxy;
+
+ // The manager for transaction queue
+ private final ContextHubTransactionManager mTransactionManager;
+
+ /**
+ * Class extending the callback to register with a Context Hub.
+ */
+ private class ContextHubServiceCallback extends IContexthubCallback.Stub {
+ private final int mContextHubId;
+
+ ContextHubServiceCallback(int contextHubId) {
+ mContextHubId = contextHubId;
+ }
+
+ @Override
+ public void handleClientMsg(ContextHubMsg message) {
+ handleClientMessageCallback(mContextHubId, message);
+ }
+
+ @Override
+ public void handleTxnResult(int transactionId, int result) {
+ handleTransactionResultCallback(mContextHubId, transactionId, result);
+ }
+
+ @Override
+ public void handleHubEvent(int eventType) {
+ handleHubEventCallback(mContextHubId, eventType);
+ }
+
+ @Override
+ public void handleAppAbort(long nanoAppId, int abortCode) {
+ handleAppAbortCallback(mContextHubId, nanoAppId, abortCode);
+ }
+
+ @Override
+ public void handleAppsInfo(ArrayList<HubAppInfo> nanoAppInfoList) {
+ handleQueryAppsCallback(mContextHubId, nanoAppInfoList);
+ }
+ }
public ContextHubService(Context context) {
mContext = context;
- mContextHubInfo = nativeInitialize();
+
+ mContextHubProxy = getContextHubProxy();
+ if (mContextHubProxy == null) {
+ mTransactionManager = null;
+ mContextHubInfo = new ContextHubInfo[0];
+ return;
+ }
+
+ mTransactionManager = new ContextHubTransactionManager(mContextHubProxy);
+
+ List<ContextHub> hubList;
+ try {
+ hubList = mContextHubProxy.getHubs();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while getting Context Hub info");
+ hubList = Collections.emptyList();
+ }
+ mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList);
+
+ for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+ int contextHubId = contextHubInfo.getId();
+ try {
+ mContextHubProxy.registerCallback(
+ contextHubId, new ContextHubServiceCallback(contextHubId));
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
+ + contextHubId + ")");
+ }
+ }
+
+ // Do a query to initialize the service cache list of nanoapps
+ // TODO(b/69270990): Remove this when old API is deprecated
+ for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+ queryNanoAppsInternal(contextHubInfo.getId());
+ }
for (int i = 0; i < mContextHubInfo.length; i++) {
Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
- + ", name: " + mContextHubInfo[i].getName());
+ + ", name: " + mContextHubInfo[i].getName());
}
}
+ /**
+ * @return the IContexthub proxy interface
+ */
+ private IContexthub getContextHubProxy() {
+ IContexthub proxy = null;
+ try {
+ proxy = IContexthub.getService(true /* retry */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy");
+ } catch (NoSuchElementException e) {
+ Log.i(TAG, "Context Hub HAL service not found");
+ }
+
+ return proxy;
+ }
+
@Override
public int registerCallback(IContextHubCallback callback) throws RemoteException {
checkPermissions();
mCallbacksList.register(callback);
Log.d(TAG, "Added callback, total callbacks " +
- mCallbacksList.getRegisteredCallbackCount());
+ mCallbacksList.getRegisteredCallbackCount());
return 0;
}
@@ -109,29 +218,112 @@
for (int i = 0; i < returnArray.length; ++i) {
returnArray[i] = i;
Log.d(TAG, String.format("Hub %s is mapped to %d",
- mContextHubInfo[i].getName(), returnArray[i]));
+ mContextHubInfo[i].getName(), returnArray[i]));
}
return returnArray;
}
@Override
- public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
+ public ContextHubInfo getContextHubInfo(int contextHubId) throws RemoteException {
checkPermissions();
- if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
- Log.e(TAG, "Invalid context hub handle " + contextHubHandle);
+ if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) {
+ Log.e(TAG, "Invalid context hub handle " + contextHubId);
return null; // null means fail
}
- return mContextHubInfo[contextHubHandle];
+ return mContextHubInfo[contextHubId];
+ }
+
+ /**
+ * Creates an internal load transaction callback to be used for old API clients
+ *
+ * @param contextHubId the ID of the hub to load the binary
+ * @param nanoAppBinary the binary to load
+ * @return the callback interface
+ */
+ private IContextHubTransactionCallback createLoadTransactionCallback(
+ int contextHubId, NanoAppBinary nanoAppBinary) {
+ return new IContextHubTransactionCallback.Stub() {
+ @Override
+ public void onTransactionComplete(int result) {
+ handleLoadResponseOldApi(contextHubId, result, nanoAppBinary);
+ }
+
+ @Override
+ public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ }
+ };
+ }
+
+ /**
+ * Creates an internal unload transaction callback to be used for old API clients
+ *
+ * @param contextHubId the ID of the hub to unload the nanoapp
+ * @param nanoAppId the ID of the nanoapp to unload
+ * @return the callback interface
+ */
+ private IContextHubTransactionCallback createUnloadTransactionCallback(
+ int contextHubId, long nanoAppId) {
+ return new IContextHubTransactionCallback.Stub() {
+ @Override
+ public void onTransactionComplete(int result) {
+ handleUnloadResponseOldApi(contextHubId, result, nanoAppId);
+ }
+
+ @Override
+ public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ }
+ };
+ }
+
+ /**
+ * Creates an internal query transaction callback to be used for old API clients
+ *
+ * @param contextHubId the ID of the hub to query
+ * @return the callback interface
+ */
+ private IContextHubTransactionCallback createQueryTransactionCallback(int contextHubId) {
+ return new IContextHubTransactionCallback.Stub() {
+ @Override
+ public void onTransactionComplete(int result) {
+ }
+
+ @Override
+ public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ byte[] data = {(byte) result};
+ onMessageReceipt(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
+ }
+ };
+ }
+
+ /**
+ * Adds a new transaction to the transaction manager queue
+ *
+ * @param transaction the transaction to add
+ * @return the result of adding the transaction
+ */
+ private int addTransaction(ContextHubServiceTransaction transaction) {
+ int result = Result.OK;
+ try {
+ mTransactionManager.addTransaction(transaction);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, e.getMessage());
+ result = Result.TRANSACTION_PENDING; /* failed */
+ }
+
+ return result;
}
@Override
- public int loadNanoApp(int contextHubHandle, NanoApp app) throws RemoteException {
+ public int loadNanoApp(int contextHubId, NanoApp app) throws RemoteException {
checkPermissions();
+ if (mContextHubProxy == null) {
+ return -1;
+ }
- if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
- Log.e(TAG, "Invalid contextHubhandle " + contextHubHandle);
+ if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) {
+ Log.e(TAG, "Invalid contextHubhandle " + contextHubId);
return -1;
}
if (app == null) {
@@ -139,20 +331,17 @@
return -1;
}
- int[] msgHeader = new int[MSG_LOAD_APP_HEADER_SIZE];
- msgHeader[HEADER_FIELD_HUB_HANDLE] = contextHubHandle;
- msgHeader[HEADER_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
- msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
- msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_LOAD_NANO_APP;
+ // Create an internal IContextHubTransactionCallback for the old API clients
+ NanoAppBinary nanoAppBinary = new NanoAppBinary(app.getAppBinary());
+ IContextHubTransactionCallback onCompleteCallback =
+ createLoadTransactionCallback(contextHubId, nanoAppBinary);
- long appId = app.getAppId();
+ ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
+ contextHubId, nanoAppBinary, onCompleteCallback);
- msgHeader[HEADER_FIELD_LOAD_APP_ID_LO] = (int)(appId & 0xFFFFFFFF);
- msgHeader[HEADER_FIELD_LOAD_APP_ID_HI] = (int)((appId >> 32) & 0xFFFFFFFF);
-
- int errVal = nativeSendMessage(msgHeader, app.getAppBinary());
- if (errVal != 0) {
- Log.e(TAG, "Send Message returns error" + contextHubHandle);
+ int result = addTransaction(transaction);
+ if (result != Result.OK) {
+ Log.e(TAG, "Failed to load nanoapp with error code " + result);
return -1;
}
@@ -163,23 +352,26 @@
@Override
public int unloadNanoApp(int nanoAppInstanceHandle) throws RemoteException {
checkPermissions();
+ if (mContextHubProxy == null) {
+ return -1;
+ }
+
NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstanceHandle);
if (info == null) {
Log.e(TAG, "Cannot find app with handle " + nanoAppInstanceHandle);
return -1; //means failed
}
- // Call Native interface here
- int[] msgHeader = new int[MSG_HEADER_SIZE];
- msgHeader[HEADER_FIELD_HUB_HANDLE] = ANY_HUB;
- msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppInstanceHandle;
- msgHeader[HEADER_FIELD_MSG_VERSION] = 0;
- msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_UNLOAD_NANO_APP;
+ int contextHubId = info.getContexthubId();
+ long nanoAppId = info.getAppId();
+ IContextHubTransactionCallback onCompleteCallback =
+ createUnloadTransactionCallback(contextHubId, nanoAppId);
+ ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
+ contextHubId, nanoAppId, onCompleteCallback);
- byte msg[] = new byte[0];
-
- if (nativeSendMessage(msgHeader, msg) != 0) {
- Log.e(TAG, "native send message fails");
+ int result = addTransaction(transaction);
+ if (result != Result.OK) {
+ Log.e(TAG, "Failed to unload nanoapp with error code " + result);
return -1;
}
@@ -189,7 +381,7 @@
@Override
public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle)
- throws RemoteException {
+ throws RemoteException {
checkPermissions();
// This assumes that all the nanoAppInfo is current. This is reasonable
// for the use cases for tightly controlled nanoApps.
@@ -206,7 +398,7 @@
checkPermissions();
ArrayList<Integer> foundInstances = new ArrayList<Integer>();
- for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
+ for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);
if (filter.testMatch(info)) {
@@ -223,23 +415,230 @@
return retArray;
}
- @Override
- public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg)
- throws RemoteException {
- checkPermissions();
+ /**
+ * Performs a query at the specified hub.
+ *
+ * This method should only be invoked internally by the service, either to update the service
+ * cache or as a result of an explicit query requested by a client through the sendMessage API.
+ *
+ * @param contextHubId the ID of the hub to do the query
+ * @return the result of the query
+ */
+ private int queryNanoAppsInternal(int contextHubId) {
+ if (mContextHubProxy == null) {
+ return Result.UNKNOWN_FAILURE;
+ }
- if (msg == null || msg.getData() == null) {
- Log.w(TAG, "null ptr");
+ IContextHubTransactionCallback onCompleteCallback =
+ createQueryTransactionCallback(contextHubId);
+ ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
+ contextHubId, onCompleteCallback);
+
+ return addTransaction(transaction);
+ }
+
+ @Override
+ public int sendMessage(
+ int hubHandle, int nanoAppHandle, ContextHubMessage msg) throws RemoteException {
+ checkPermissions();
+ if (mContextHubProxy == null) {
+ return -1;
+ }
+ if (msg == null) {
+ Log.e(TAG, "ContextHubMessage cannot be null");
+ return -1;
+ }
+ if (msg.getData() == null) {
+ Log.w(TAG, "ContextHubMessage message body cannot be null");
return -1;
}
- int[] msgHeader = new int[MSG_HEADER_SIZE];
- msgHeader[HEADER_FIELD_HUB_HANDLE] = hubHandle;
- msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppHandle;
- msgHeader[HEADER_FIELD_MSG_VERSION] = msg.getVersion();
- msgHeader[HEADER_FIELD_MSG_TYPE] = msg.getMsgType();
+ int result;
+ if (nanoAppHandle == OS_APP_INSTANCE) {
+ if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
+ result = queryNanoAppsInternal(hubHandle);
+ } else {
+ Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
+ result = Result.BAD_PARAMS;
+ }
+ } else {
+ NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle);
+ if (info != null) {
+ ContextHubMsg hubMessage = new ContextHubMsg();
+ hubMessage.appName = info.getAppId();
+ hubMessage.msgType = msg.getMsgType();
+ hubMessage.hostEndPoint = HostEndPoint.UNSPECIFIED;
+ ContextHubServiceUtil.copyToByteArrayList(msg.getData(), hubMessage.msg);
- return nativeSendMessage(msgHeader, msg.getData());
+ try {
+ result = mContextHubProxy.sendMessageToHub(hubHandle, hubMessage);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send nanoapp message - RemoteException");
+ result = Result.UNKNOWN_FAILURE;
+ }
+ } else {
+ Log.e(TAG, "Failed to send nanoapp message - nanoapp with instance ID "
+ + nanoAppHandle + " does not exist.");
+ result = Result.BAD_PARAMS;
+ }
+ }
+
+ return (result == Result.OK ? 0 : -1);
+ }
+
+ /**
+ * Handles a unicast or broadcast message from a nanoapp.
+ *
+ * @param contextHubId the ID of the hub the message came from
+ * @param message the message contents
+ */
+ private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) {
+ // TODO(b/67734082): Send to new API clients
+ byte[] data = ContextHubServiceUtil.createPrimitiveByteArray(message.msg);
+
+ int nanoAppInstanceId = mNanoAppIdToInstanceMap.containsKey(message.appName) ?
+ mNanoAppIdToInstanceMap.get(message.appName) : -1;
+ onMessageReceipt(message.msgType, contextHubId, nanoAppInstanceId, data);
+ }
+
+ /**
+ * A helper function to handle a load response from the Context Hub for the old API.
+ *
+ * TODO(b/69270990): Remove this once the old APIs are obsolete.
+ */
+ private void handleLoadResponseOldApi(
+ int contextHubId, int result, NanoAppBinary nanoAppBinary) {
+ if (nanoAppBinary == null) {
+ Log.e(TAG, "Nanoapp binary field was null for a load transaction");
+ return;
+ }
+
+ // NOTE: The legacy JNI code used to do a query right after a load success
+ // to synchronize the service cache. Instead store the binary that was requested to
+ // load to update the cache later without doing a query.
+ int instanceId = 0;
+ long nanoAppId = nanoAppBinary.getNanoAppId();
+ int nanoAppVersion = nanoAppBinary.getNanoAppVersion();
+ if (result == TransactionResult.SUCCESS) {
+ if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
+ instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+ } else {
+ instanceId = mNextAvailableInstanceId++;
+ mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
+ }
+
+ addAppInstance(contextHubId, instanceId, nanoAppId, nanoAppVersion);
+ }
+
+ byte[] data = new byte[5];
+ data[0] = (byte) result;
+ ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId);
+
+ onMessageReceipt(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+ }
+
+ /**
+ * A helper function to handle an unload response from the Context Hub for the old API.
+ *
+ * TODO(b/69270990): Remove this once the old APIs are obsolete.
+ */
+ private void handleUnloadResponseOldApi(
+ int contextHubId, int result, long nanoAppId) {
+ if (result == TransactionResult.SUCCESS) {
+ int instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+ deleteAppInstance(instanceId);
+ mNanoAppIdToInstanceMap.remove(nanoAppId);
+ }
+
+ byte[] data = new byte[1];
+ data[0] = (byte) result;
+ onMessageReceipt(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+ }
+
+ /**
+ * Handles a transaction response from a Context Hub.
+ *
+ * @param contextHubId the ID of the hub the response came from
+ * @param transactionId the ID of the transaction
+ * @param result the result of the transaction reported by the hub
+ */
+ private void handleTransactionResultCallback(int contextHubId, int transactionId, int result) {
+ mTransactionManager.onTransactionResponse(transactionId, result);
+ }
+
+ /**
+ * Handles an asynchronous event from a Context Hub.
+ *
+ * @param contextHubId the ID of the hub the response came from
+ * @param eventType the type of the event as defined in Context Hub HAL AsyncEventType
+ */
+ private void handleHubEventCallback(int contextHubId, int eventType) {
+ if (eventType == AsyncEventType.RESTARTED) {
+ mTransactionManager.onHubReset();
+ queryNanoAppsInternal(contextHubId);
+
+ byte[] data = {TransactionResult.SUCCESS};
+ onMessageReceipt(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
+ } else {
+ Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = "
+ + eventType + ")");
+ }
+ }
+
+ /**
+ * Handles an asynchronous abort event of a nanoapp.
+ *
+ * @param contextHubId the ID of the hub that the nanoapp aborted in
+ * @param nanoAppId the ID of the aborted nanoapp
+ * @param abortCode the nanoapp-specific abort code
+ */
+ private void handleAppAbortCallback(int contextHubId, long nanoAppId, int abortCode) {
+ // TODO(b/31049861): Implement this
+ }
+
+ /**
+ * Handles a query response from a Context Hub.
+ *
+ * @param contextHubId the ID of the hub of the response
+ * @param nanoAppInfoList the list of loaded nanoapps
+ */
+ private void handleQueryAppsCallback(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+ List<NanoAppState> nanoAppStateList =
+ ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);
+
+ updateServiceCache(contextHubId, nanoAppInfoList);
+ mTransactionManager.onQueryResponse(nanoAppStateList);
+ }
+
+ /**
+ * Updates the service's cache of the list of loaded nanoapps using a nanoapp list response.
+ *
+ * TODO(b/69270990): Remove this when the old API functionality is removed.
+ *
+ * @param contextHubId the ID of the hub the response came from
+ * @param nanoAppInfoList the list of loaded nanoapps
+ */
+ private void updateServiceCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+ synchronized (mNanoAppHash) {
+ for (int instanceId : mNanoAppHash.keySet()) {
+ if (mNanoAppHash.get(instanceId).getContexthubId() == contextHubId) {
+ deleteAppInstance(instanceId);
+ }
+ }
+
+ for (HubAppInfo appInfo : nanoAppInfoList) {
+ int instanceId;
+ long nanoAppId = appInfo.appId;
+ if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
+ instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
+ } else {
+ instanceId = mNextAvailableInstanceId++;
+ mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
+ }
+
+ addAppInstance(contextHubId, instanceId, nanoAppId, appInfo.version);
+ }
+ }
}
@Override
@@ -257,7 +656,7 @@
pw.println("");
pw.println("=================== NANOAPPS ====================");
// Dump nanoAppHash
- for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
+ for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
pw.println(nanoAppInstance + " : " + mNanoAppHash.get(nanoAppInstance).toString());
}
@@ -268,19 +667,15 @@
mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
}
- private int onMessageReceipt(int[] header, byte[] data) {
- if (header == null || data == null || header.length < MSG_HEADER_SIZE) {
- return -1;
+ private int onMessageReceipt(int msgType, int hubHandle, int appInstance, byte[] data) {
+ if (data == null) {
+ return -1;
}
+ int msgVersion = 0;
int callbacksCount = mCallbacksList.beginBroadcast();
- int msgType = header[HEADER_FIELD_MSG_TYPE];
- int msgVersion = header[HEADER_FIELD_MSG_VERSION];
- int hubHandle = header[HEADER_FIELD_HUB_HANDLE];
- int appInstance = header[HEADER_FIELD_APP_INSTANCE];
-
Log.d(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle " +
- hubHandle + ", appInstance " + appInstance + ", callBackCount " + callbacksCount);
+ hubHandle + ", appInstance " + appInstance + ", callBackCount " + callbacksCount);
if (callbacksCount < 1) {
Log.v(TAG, "No message callbacks registered.");
@@ -323,8 +718,8 @@
}
mNanoAppHash.put(appInstanceHandle, appInfo);
- Log.d(TAG, action + " app instance " + appInstanceHandle + " with id "
- + appId + " version " + appVersion);
+ Log.d(TAG, action + " app instance " + appInstanceHandle + " with id 0x"
+ + Long.toHexString(appId) + " version 0x" + Integer.toHexString(appVersion));
return 0;
}
diff --git a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
new file mode 100644
index 0000000..66145bb
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.NanoAppState;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An abstract class representing transactions requested to the Context Hub Service.
+ *
+ * @hide
+ */
+/* package */ abstract class ContextHubServiceTransaction {
+ private final int mTransactionId;
+ @ContextHubTransaction.Type
+ private final int mTransactionType;
+
+ /*
+ * true if the transaction has already completed, false otherwise
+ */
+ private boolean mIsComplete = false;
+
+ /* package */ ContextHubServiceTransaction(int id, int type) {
+ mTransactionId = id;
+ mTransactionType = type;
+ }
+
+ /**
+ * Starts this transaction with a Context Hub.
+ *
+ * All instances of this class must implement this method by making an asynchronous request to
+ * a hub.
+ *
+ * @return the synchronous error code of the transaction start
+ */
+ /* package */
+ abstract int onTransact();
+
+ /**
+ * A function to invoke when a transaction times out.
+ *
+ * All instances of this class must implement this method by reporting the timeout to the
+ * client.
+ */
+ /* package */
+ abstract void onTimeout();
+
+ /**
+ * A function to invoke when the transaction completes.
+ *
+ * Only relevant for load, unload, enable, or disable transactions.
+ *
+ * @param result the result of the transaction
+ */
+ /* package */ void onTransactionComplete(int result) {
+ }
+
+ /**
+ * A function to invoke when a query transaction completes.
+ *
+ * Only relevant for query transactions.
+ *
+ * @param result the result of the query
+ * @param nanoAppStateList the list of nanoapps given by the query response
+ */
+ /* package */ void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ }
+
+ /**
+ * @return the ID of this transaction
+ */
+ /* package */ int getTransactionId() {
+ return mTransactionId;
+ }
+
+ /**
+ * @return the type of this transaction
+ * @see ContextHubTransaction.Type
+ */
+ @ContextHubTransaction.Type
+ /* package */ int getTransactionType() {
+ return mTransactionType;
+ }
+
+ /**
+ * Gets the timeout period as defined in IContexthub.hal
+ *
+ * @return the timeout of this transaction in the specified time unit
+ */
+ /* package */ long getTimeout(TimeUnit unit) {
+ switch (mTransactionType) {
+ case ContextHubTransaction.TYPE_LOAD_NANOAPP:
+ return unit.convert(30L, TimeUnit.SECONDS);
+ case ContextHubTransaction.TYPE_UNLOAD_NANOAPP:
+ case ContextHubTransaction.TYPE_ENABLE_NANOAPP:
+ case ContextHubTransaction.TYPE_DISABLE_NANOAPP:
+ case ContextHubTransaction.TYPE_QUERY_NANOAPPS:
+ // Note: query timeout is not specified at the HAL
+ default: /* fall through */
+ return unit.convert(5L, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Marks the transaction as complete.
+ *
+ * Should only be called as a result of a response from a Context Hub callback
+ */
+ /* package */ void setComplete() {
+ mIsComplete = true;
+ }
+
+ /**
+ * @return true if the transaction has already completed, false otherwise
+ */
+ /* package */ boolean isComplete() {
+ return mIsComplete;
+ }
+
+ /**
+ * @return the human-readable string of this transaction's type
+ */
+ private String getTransactionTypeString() {
+ switch (mTransactionType) {
+ case ContextHubTransaction.TYPE_LOAD_NANOAPP:
+ return "Load";
+ case ContextHubTransaction.TYPE_UNLOAD_NANOAPP:
+ return "Unload";
+ case ContextHubTransaction.TYPE_ENABLE_NANOAPP:
+ return "Enable";
+ case ContextHubTransaction.TYPE_DISABLE_NANOAPP:
+ return "Disable";
+ case ContextHubTransaction.TYPE_QUERY_NANOAPPS:
+ return "Query";
+ default:
+ return "Unknown";
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getTransactionTypeString() + " transaction (ID = " + mTransactionId + ")";
+ }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
new file mode 100644
index 0000000..ddbaf86
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.hardware.contexthub.V1_0.ContextHub;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.HostEndPoint;
+import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppState;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A class encapsulating helper functions used by the ContextHubService class
+ */
+/* package */ class ContextHubServiceUtil {
+ private static final String TAG = "ContextHubServiceUtil";
+
+ /**
+ * Creates a ContextHubInfo array from an ArrayList of HIDL ContextHub objects.
+ *
+ * @param hubList the ContextHub ArrayList
+ * @return the ContextHubInfo array
+ */
+ /* package */
+ static ContextHubInfo[] createContextHubInfoArray(List<ContextHub> hubList) {
+ ContextHubInfo[] contextHubInfoList = new ContextHubInfo[hubList.size()];
+ for (int i = 0; i < hubList.size(); i++) {
+ contextHubInfoList[i] = new ContextHubInfo(hubList.get(i));
+ }
+
+ return contextHubInfoList;
+ }
+
+ /**
+ * Copies a primitive byte array to a ArrayList<Byte>.
+ *
+ * @param inputArray the primitive byte array
+ * @param outputArray the ArrayList<Byte> array to append
+ */
+ /* package */
+ static void copyToByteArrayList(byte[] inputArray, ArrayList<Byte> outputArray) {
+ outputArray.clear();
+ outputArray.ensureCapacity(inputArray.length);
+ for (byte element : inputArray) {
+ outputArray.add(element);
+ }
+ }
+
+ /**
+ * Creates a byte array given a ArrayList<Byte> and copies its contents.
+ *
+ * @param array the ArrayList<Byte> object
+ * @return the byte array
+ */
+ /* package */
+ static byte[] createPrimitiveByteArray(ArrayList<Byte> array) {
+ byte[] primitiveArray = new byte[array.size()];
+ for (int i = 0; i < array.size(); i++) {
+ primitiveArray[i] = array.get(i);
+ }
+
+ return primitiveArray;
+ }
+
+ /**
+ * Generates the Context Hub HAL's NanoAppBinary object from the client-facing
+ * android.hardware.location.NanoAppBinary object.
+ *
+ * @param nanoAppBinary the client-facing NanoAppBinary object
+ * @return the Context Hub HAL's NanoAppBinary object
+ */
+ /* package */
+ static android.hardware.contexthub.V1_0.NanoAppBinary createHidlNanoAppBinary(
+ NanoAppBinary nanoAppBinary) {
+ android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
+ new android.hardware.contexthub.V1_0.NanoAppBinary();
+
+ hidlNanoAppBinary.appId = nanoAppBinary.getNanoAppId();
+ hidlNanoAppBinary.appVersion = nanoAppBinary.getNanoAppVersion();
+ hidlNanoAppBinary.flags = nanoAppBinary.getFlags();
+ hidlNanoAppBinary.targetChreApiMajorVersion = nanoAppBinary.getTargetChreApiMajorVersion();
+ hidlNanoAppBinary.targetChreApiMinorVersion = nanoAppBinary.getTargetChreApiMinorVersion();
+
+ // Log exceptions while processing the binary, but continue to pass down the binary
+ // since the error checking is deferred to the Context Hub.
+ try {
+ copyToByteArrayList(nanoAppBinary.getBinaryNoHeader(), hidlNanoAppBinary.customBinary);
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, e.getMessage());
+ } catch (NullPointerException e) {
+ Log.w(TAG, "NanoApp binary was null");
+ }
+
+ return hidlNanoAppBinary;
+ }
+
+ /**
+ * Generates a client-facing NanoAppState array from a HAL HubAppInfo array.
+ *
+ * @param nanoAppInfoList the array of HubAppInfo objects
+ * @return the corresponding array of NanoAppState objects
+ */
+ /* package */
+ static List<NanoAppState> createNanoAppStateList(
+ List<HubAppInfo> nanoAppInfoList) {
+ ArrayList<NanoAppState> nanoAppStateList = new ArrayList<>();
+ for (HubAppInfo appInfo : nanoAppInfoList) {
+ nanoAppStateList.add(
+ new NanoAppState(appInfo.appId, appInfo.version, appInfo.enabled));
+ }
+
+ return nanoAppStateList;
+ }
+
+ /**
+ * Creates a HIDL ContextHubMsg object to send to a nanoapp.
+ *
+ * @param hostEndPoint the ID of the client sending the message
+ * @param message the client-facing NanoAppMessage object describing the message
+ * @return the HIDL ContextHubMsg object
+ */
+ /* package */
+ static ContextHubMsg createHidlContextHubMessage(short hostEndPoint, NanoAppMessage message) {
+ ContextHubMsg hidlMessage = new ContextHubMsg();
+
+ hidlMessage.appName = message.getNanoAppId();
+ hidlMessage.hostEndPoint = hostEndPoint;
+ hidlMessage.msgType = message.getMessageType();
+ copyToByteArrayList(message.getMessageBody(), hidlMessage.msg);
+
+ return hidlMessage;
+ }
+
+ /**
+ * Creates a client-facing NanoAppMessage object to send to a client.
+ *
+ * @param message the HIDL ContextHubMsg object from a nanoapp
+ * @return the NanoAppMessage object
+ */
+ /* package */
+ static NanoAppMessage createNanoAppMessage(ContextHubMsg message) {
+ byte[] messageArray = createPrimitiveByteArray(message.msg);
+
+ return NanoAppMessage.createMessageFromNanoApp(
+ message.appName, message.msgType, messageArray,
+ message.hostEndPoint == HostEndPoint.BROADCAST);
+ }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
new file mode 100644
index 0000000..898b76c
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.contexthub.V1_0.TransactionResult;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.IContextHubTransactionCallback;
+import android.hardware.location.NanoAppBinary;
+import android.hardware.location.NanoAppState;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Manages transactions at the Context Hub Service.
+ *
+ * This class maintains a queue of transaction requests made to the ContextHubService by clients,
+ * and executes them through the Context Hub. At any point in time, either the transaction queue is
+ * empty, or there is a pending transaction that is waiting for an asynchronous response from the
+ * hub. This class also handles synchronous errors and timeouts of each transaction.
+ *
+ * @hide
+ */
+/* package */ class ContextHubTransactionManager {
+ private static final String TAG = "ContextHubTransactionManager";
+
+ /*
+ * Maximum number of transaction requests that can be pending at a time
+ */
+ private static final int MAX_PENDING_REQUESTS = 10;
+
+ /*
+ * The proxy to talk to the Context Hub
+ */
+ private final IContexthub mContextHubProxy;
+
+ /*
+ * A queue containing the current transactions
+ */
+ private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
+
+ /*
+ * The next available transaction ID
+ */
+ private final AtomicInteger mNextAvailableId = new AtomicInteger();
+
+ /*
+ * An executor and the future object for scheduling timeout timers
+ */
+ private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1);
+ private ScheduledFuture<?> mTimeoutFuture = null;
+
+ /* package */ ContextHubTransactionManager(IContexthub contextHubProxy) {
+ mContextHubProxy = contextHubProxy;
+ }
+
+ /**
+ * Creates a transaction for loading a nanoapp.
+ *
+ * @param contextHubId the ID of the hub to load the nanoapp to
+ * @param nanoAppBinary the binary of the nanoapp to load
+ * @param onCompleteCallback the client on complete callback
+ * @return the generated transaction
+ */
+ /* package */ ContextHubServiceTransaction createLoadTransaction(
+ int contextHubId, NanoAppBinary nanoAppBinary,
+ IContextHubTransactionCallback onCompleteCallback) {
+ return new ContextHubServiceTransaction(
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_LOAD_NANOAPP) {
+ @Override
+ /* package */ int onTransact() {
+ android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary =
+ ContextHubServiceUtil.createHidlNanoAppBinary(nanoAppBinary);
+ try {
+ return mContextHubProxy.loadNanoApp(
+ contextHubId, hidlNanoAppBinary, this.getTransactionId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while trying to load nanoapp with ID 0x" +
+ Long.toHexString(nanoAppBinary.getNanoAppId()));
+ return Result.UNKNOWN_FAILURE;
+ }
+ }
+
+ @Override
+ /* package */ void onTimeout() {
+ onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
+ }
+
+ @Override
+ /* package */ void onTransactionComplete(int result) {
+ try {
+ onCompleteCallback.onTransactionComplete(result);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling client onTransactionComplete");
+ }
+ }
+ };
+ }
+
+ /**
+ * Creates a transaction for unloading a nanoapp.
+ *
+ * @param contextHubId the ID of the hub to load the nanoapp to
+ * @param nanoAppId the ID of the nanoapp to unload
+ * @param onCompleteCallback the client on complete callback
+ * @return the generated transaction
+ */
+ /* package */ ContextHubServiceTransaction createUnloadTransaction(
+ int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+ return new ContextHubServiceTransaction(
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_UNLOAD_NANOAPP) {
+ @Override
+ /* package */ int onTransact() {
+ try {
+ return mContextHubProxy.unloadNanoApp(
+ contextHubId, nanoAppId, this.getTransactionId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while trying to unload nanoapp with ID 0x" +
+ Long.toHexString(nanoAppId));
+ return Result.UNKNOWN_FAILURE;
+ }
+ }
+
+ @Override
+ /* package */ void onTimeout() {
+ onTransactionComplete(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT);
+ }
+
+ @Override
+ /* package */ void onTransactionComplete(int result) {
+ try {
+ onCompleteCallback.onTransactionComplete(result);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling client onTransactionComplete");
+ }
+ }
+ };
+ }
+
+ /**
+ * Creates a transaction for querying for a list of nanoapps.
+ *
+ * @param contextHubId the ID of the hub to query
+ * @param onCompleteCallback the client on complete callback
+ * @return the generated transaction
+ */
+ /* package */ ContextHubServiceTransaction createQueryTransaction(
+ int contextHubId, IContextHubTransactionCallback onCompleteCallback) {
+ return new ContextHubServiceTransaction(
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+ @Override
+ /* package */ int onTransact() {
+ try {
+ return mContextHubProxy.queryApps(contextHubId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while trying to query for nanoapps");
+ return Result.UNKNOWN_FAILURE;
+ }
+ }
+
+ @Override
+ /* package */ void onTimeout() {
+ onQueryResponse(ContextHubTransaction.TRANSACTION_FAILED_TIMEOUT,
+ Collections.emptyList());
+ }
+
+ @Override
+ /* package */ void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ try {
+ onCompleteCallback.onQueryResponse(result, nanoAppStateList);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling client onQueryComplete");
+ }
+ }
+ };
+ }
+
+ /**
+ * Adds a new transaction to the queue.
+ *
+ * If there was no pending transaction at the time, the transaction that was added will be
+ * started in this method.
+ *
+ * @param transaction the transaction to add
+ * @throws IllegalStateException if the queue is full
+ */
+ /* package */
+ synchronized void addTransaction(
+ ContextHubServiceTransaction transaction) throws IllegalStateException {
+ if (mTransactionQueue.size() == MAX_PENDING_REQUESTS) {
+ throw new IllegalStateException("Transaction transaction queue is full (capacity = "
+ + MAX_PENDING_REQUESTS + ")");
+ }
+ mTransactionQueue.add(transaction);
+
+ if (mTransactionQueue.size() == 1) {
+ startNextTransaction();
+ }
+ }
+
+ /**
+ * Handles a transaction response from a Context Hub.
+ *
+ * @param transactionId the transaction ID of the response
+ * @param result the result of the transaction
+ */
+ /* package */
+ synchronized void onTransactionResponse(int transactionId, int result) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+ return;
+ }
+ if (transaction.getTransactionId() != transactionId) {
+ Log.w(TAG, "Received unexpected transaction response (expected ID = "
+ + transaction.getTransactionId() + ", received ID = " + transactionId + ")");
+ return;
+ }
+
+ transaction.onTransactionComplete(result);
+ removeTransactionAndStartNext();
+ }
+
+ /**
+ * Handles a query response from a Context Hub.
+ *
+ * @param nanoAppStateList the list of nanoapps included in the response
+ */
+ /* package */
+ synchronized void onQueryResponse(List<NanoAppState> nanoAppStateList) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ Log.w(TAG, "Received unexpected query response (no transaction pending)");
+ return;
+ }
+ if (transaction.getTransactionType() != ContextHubTransaction.TYPE_QUERY_NANOAPPS) {
+ Log.w(TAG, "Received unexpected query response (expected " + transaction + ")");
+ return;
+ }
+
+ transaction.onQueryResponse(TransactionResult.SUCCESS, nanoAppStateList);
+ removeTransactionAndStartNext();
+ }
+
+ /**
+ * Handles a hub reset event by stopping a pending transaction and starting the next.
+ */
+ /* package */
+ synchronized void onHubReset() {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ return;
+ }
+
+ removeTransactionAndStartNext();
+ }
+
+ /**
+ * Pops the front transaction from the queue and starts the next pending transaction request.
+ *
+ * Removing elements from the transaction queue must only be done through this method. When a
+ * pending transaction is removed, the timeout timer is cancelled and the transaction is marked
+ * complete.
+ *
+ * It is assumed that the transaction queue is non-empty when this method is invoked, and that
+ * the caller has obtained a lock on this ContextHubTransactionManager object.
+ */
+ private void removeTransactionAndStartNext() {
+ mTimeoutFuture.cancel(false /* mayInterruptIfRunning */);
+
+ ContextHubServiceTransaction transaction = mTransactionQueue.remove();
+ transaction.setComplete();
+
+ if (!mTransactionQueue.isEmpty()) {
+ startNextTransaction();
+ }
+ }
+
+ /**
+ * Starts the next pending transaction request.
+ *
+ * Starting new transactions must only be done through this method. This method continues to
+ * process the transaction queue as long as there are pending requests, and no transaction is
+ * pending.
+ *
+ * It is assumed that the caller has obtained a lock on this ContextHubTransactionManager
+ * object.
+ */
+ private void startNextTransaction() {
+ int result = Result.UNKNOWN_FAILURE;
+ while (result != Result.OK && !mTransactionQueue.isEmpty()) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ result = transaction.onTransact();
+
+ if (result == Result.OK) {
+ Runnable onTimeoutFunc = () -> {
+ synchronized (this) {
+ if (!transaction.isComplete()) {
+ Log.d(TAG, transaction + " timed out");
+ transaction.onTimeout();
+
+ removeTransactionAndStartNext();
+ }
+ }
+ };
+
+ long timeoutSeconds = transaction.getTimeout(TimeUnit.SECONDS);
+ mTimeoutFuture = mTimeoutExecutor.schedule(onTimeoutFunc, timeoutSeconds,
+ TimeUnit.SECONDS);
+ } else {
+ mTransactionQueue.remove();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/FusedProxy.java b/services/core/java/com/android/server/location/FusedProxy.java
index f7fac77..b90f037 100644
--- a/services/core/java/com/android/server/location/FusedProxy.java
+++ b/services/core/java/com/android/server/location/FusedProxy.java
@@ -23,6 +23,7 @@
import android.hardware.location.IFusedLocationHardware;
import android.location.IFusedProvider;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -112,17 +113,18 @@
* @param locationHardware The FusedLocationHardware instance to use for the binding operation.
*/
private void bindProvider(IFusedLocationHardware locationHardware) {
- IFusedProvider provider = IFusedProvider.Stub.asInterface(mServiceWatcher.getBinder());
-
- if (provider == null) {
+ if (!mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ IFusedProvider provider = IFusedProvider.Stub.asInterface(binder);
+ try {
+ provider.onFusedLocationHardwareChange(locationHardware);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ })) {
Log.e(TAG, "No instance of FusedProvider found on FusedLocationHardware connected.");
- return;
- }
-
- try {
- provider.onFusedLocationHardwareChange(locationHardware);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
}
}
}
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index 422b94b..9ad4aa1 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -21,6 +21,7 @@
import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -63,42 +64,47 @@
return mServiceWatcher.start();
}
- private IGeocodeProvider getService() {
- return IGeocodeProvider.Stub.asInterface(mServiceWatcher.getBinder());
- }
-
public String getConnectedPackageName() {
return mServiceWatcher.getBestPackageName();
}
public String getFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
- IGeocodeProvider provider = getService();
- if (provider != null) {
- try {
- return provider.getFromLocation(latitude, longitude, maxResults, params, addrs);
- } catch (RemoteException e) {
- Log.w(TAG, e);
+ final String[] result = new String[] {"Service not Available"};
+ mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder);
+ try {
+ result[0] = provider.getFromLocation(
+ latitude, longitude, maxResults, params, addrs);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ }
}
- }
- return "Service not Available";
+ });
+ return result[0];
}
public String getFromLocationName(String locationName,
double lowerLeftLatitude, double lowerLeftLongitude,
double upperRightLatitude, double upperRightLongitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
- IGeocodeProvider provider = getService();
- if (provider != null) {
- try {
- return provider.getFromLocationName(locationName, lowerLeftLatitude,
- lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
- maxResults, params, addrs);
- } catch (RemoteException e) {
- Log.w(TAG, e);
+ final String[] result = new String[] {"Service not Available"};
+ mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder);
+ try {
+ result[0] = provider.getFromLocationName(locationName, lowerLeftLatitude,
+ lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+ maxResults, params, addrs);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ }
}
- }
- return "Service not Available";
+ });
+ return result[0];
}
}
diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java
index b3a0010..eb47b2f 100644
--- a/services/core/java/com/android/server/location/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/GeofenceProxy.java
@@ -116,15 +116,17 @@
};
private void setGeofenceHardwareInProviderLocked() {
- try {
- IGeofenceProvider provider = IGeofenceProvider.Stub.asInterface(
- mServiceWatcher.getBinder());
- if (provider != null) {
- provider.setGeofenceHardware(mGeofenceHardware);
+ mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ final IGeofenceProvider provider = IGeofenceProvider.Stub.asInterface(binder);
+ try {
+ provider.setGeofenceHardware(mGeofenceHardware);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote Exception: setGeofenceHardwareInProviderLocked: " + e);
+ }
}
- } catch (RemoteException e) {
- Log.e(TAG, "Remote Exception: setGeofenceHardwareInProviderLocked: " + e);
- }
+ });
}
private void setGpsGeofenceLocked() {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index beb7486..0b511f2 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1361,24 +1361,26 @@
public boolean sendExtraCommand(String command, Bundle extras) {
long identity = Binder.clearCallingIdentity();
- boolean result = false;
+ try {
+ boolean result = false;
- if ("delete_aiding_data".equals(command)) {
- result = deleteAidingData(extras);
- } else if ("force_time_injection".equals(command)) {
- requestUtcTime();
- result = true;
- } else if ("force_xtra_injection".equals(command)) {
- if (mSupportsXtra) {
- xtraDownloadRequest();
+ if ("delete_aiding_data".equals(command)) {
+ result = deleteAidingData(extras);
+ } else if ("force_time_injection".equals(command)) {
+ requestUtcTime();
result = true;
+ } else if ("force_xtra_injection".equals(command)) {
+ if (mSupportsXtra) {
+ xtraDownloadRequest();
+ result = true;
+ }
+ } else {
+ Log.w(TAG, "sendExtraCommand: unknown command " + command);
}
- } else {
- Log.w(TAG, "sendExtraCommand: unknown command " + command);
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
-
- Binder.restoreCallingIdentity(identity);
- return result;
}
private IGpsGeofenceHardware mGpsGeofenceBinder = new IGpsGeofenceHardware.Stub() {
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index b44087c..16eae62 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -24,6 +24,7 @@
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
@@ -82,10 +83,6 @@
return mServiceWatcher.start();
}
- private ILocationProvider getService() {
- return ILocationProvider.Stub.asInterface(mServiceWatcher.getBinder());
- }
-
public String getConnectedPackageName() {
return mServiceWatcher.getBestPackageName();
}
@@ -101,43 +98,46 @@
if (D) Log.d(TAG, "applying state to connected service");
boolean enabled;
- ProviderProperties properties = null;
+ final ProviderProperties[] properties = new ProviderProperties[1];
ProviderRequest request;
WorkSource source;
- ILocationProvider service;
synchronized (mLock) {
enabled = mEnabled;
request = mRequest;
source = mWorksource;
- service = getService();
}
- if (service == null) return;
- try {
- // load properties from provider
- properties = service.getProperties();
- if (properties == null) {
- Log.e(TAG, mServiceWatcher.getBestPackageName() +
- " has invalid locatino provider properties");
- }
+ mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+ try {
+ // load properties from provider
+ properties[0] = service.getProperties();
+ if (properties[0] == null) {
+ Log.e(TAG, mServiceWatcher.getBestPackageName() +
+ " has invalid location provider properties");
+ }
- // apply current state to new service
- if (enabled) {
- service.enable();
- if (request != null) {
- service.setRequest(request, source);
+ // apply current state to new service
+ if (enabled) {
+ service.enable();
+ if (request != null) {
+ service.setRequest(request, source);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
- } catch (RemoteException e) {
- Log.w(TAG, e);
- } catch (Exception e) {
- // never let remote service crash system server
- Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
- }
+ });
synchronized (mLock) {
- mProperties = properties;
+ mProperties = properties[0];
}
}
};
@@ -159,17 +159,20 @@
synchronized (mLock) {
mEnabled = true;
}
- ILocationProvider service = getService();
- if (service == null) return;
-
- try {
- service.enable();
- } catch (RemoteException e) {
- Log.w(TAG, e);
- } catch (Exception e) {
- // never let remote service crash system server
- Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
- }
+ mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+ try {
+ service.enable();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ }
+ });
}
@Override
@@ -177,17 +180,20 @@
synchronized (mLock) {
mEnabled = false;
}
- ILocationProvider service = getService();
- if (service == null) return;
-
- try {
- service.disable();
- } catch (RemoteException e) {
- Log.w(TAG, e);
- } catch (Exception e) {
- // never let remote service crash system server
- Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
- }
+ mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+ try {
+ service.disable();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ }
+ });
}
@Override
@@ -203,17 +209,20 @@
mRequest = request;
mWorksource = source;
}
- ILocationProvider service = getService();
- if (service == null) return;
-
- try {
- service.setRequest(request, source);
- } catch (RemoteException e) {
- Log.w(TAG, e);
- } catch (Exception e) {
- // never let remote service crash system server
- Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
- }
+ mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+ try {
+ service.setRequest(request, source);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ }
+ });
}
@Override
@@ -223,66 +232,78 @@
pw.append(" pkg=").append(mServiceWatcher.getBestPackageName());
pw.append(" version=").append("" + mServiceWatcher.getBestVersion());
pw.append('\n');
-
- ILocationProvider service = getService();
- if (service == null) {
+ if (!mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+ try {
+ TransferPipe.dumpAsync(service.asBinder(), fd, args);
+ } catch (IOException | RemoteException e) {
+ pw.println("Failed to dump location provider: " + e);
+ }
+ }
+ })) {
pw.println("service down (null)");
- return;
- }
- pw.flush();
-
- try {
- TransferPipe.dumpAsync(service.asBinder(), fd, args);
- } catch (IOException | RemoteException e) {
- pw.println("Failed to dump location provider: " + e);
}
}
@Override
public int getStatus(Bundle extras) {
- ILocationProvider service = getService();
- if (service == null) return LocationProvider.TEMPORARILY_UNAVAILABLE;
-
- try {
- return service.getStatus(extras);
- } catch (RemoteException e) {
- Log.w(TAG, e);
- } catch (Exception e) {
- // never let remote service crash system server
- Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
- }
- return LocationProvider.TEMPORARILY_UNAVAILABLE;
+ final int[] result = new int[] {LocationProvider.TEMPORARILY_UNAVAILABLE};
+ mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+ try {
+ result[0] = service.getStatus(extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ }
+ });
+ return result[0];
}
@Override
public long getStatusUpdateTime() {
- ILocationProvider service = getService();
- if (service == null) return 0;
-
- try {
- return service.getStatusUpdateTime();
- } catch (RemoteException e) {
- Log.w(TAG, e);
- } catch (Exception e) {
- // never let remote service crash system server
- Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
- }
- return 0;
+ final long[] result = new long[] {0L};
+ mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+ try {
+ result[0] = service.getStatusUpdateTime();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ }
+ });
+ return result[0];
}
@Override
public boolean sendExtraCommand(String command, Bundle extras) {
- ILocationProvider service = getService();
- if (service == null) return false;
-
- try {
- return service.sendExtraCommand(command, extras);
- } catch (RemoteException e) {
- Log.w(TAG, e);
- } catch (Exception e) {
- // never let remote service crash system server
- Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
- }
- return false;
+ final boolean[] result = new boolean[] {false};
+ mServiceWatcher.runOnBinder(new ServiceWatcher.BinderRunner() {
+ @Override
+ public void run(IBinder binder) {
+ ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
+ try {
+ result[0] = service.sendExtraCommand(command, extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ }
+ });
+ return result[0];
}
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index ca6c9e7..1a1aa56 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -390,11 +390,9 @@
}
public void removeUser(int userId) {
- if (isWeaverAvailable()) {
- for (long handle : mStorage.listSyntheticPasswordHandlesForUser(WEAVER_SLOT_NAME,
- userId)) {
- destroyWeaverSlot(handle, userId);
- }
+ for (long handle : mStorage.listSyntheticPasswordHandlesForUser(SP_BLOB_NAME, userId)) {
+ destroyWeaverSlot(handle, userId);
+ destroySPBlobKey(getHandleName(handle));
}
}
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index be223f1..d71c3b0 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -16,7 +16,7 @@
package com.android.server.media;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
import android.content.Context;
import android.media.AudioPlaybackConfiguration;
import android.media.IAudioService;
@@ -27,14 +27,15 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.IntArray;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.HashMap;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -49,47 +50,57 @@
private static AudioPlayerStateMonitor sInstance = new AudioPlayerStateMonitor();
/**
- * Called when the state of audio player is changed.
+ * Listener for handling the active state changes of audio players.
*/
- interface OnAudioPlayerStateChangedListener {
- void onAudioPlayerStateChanged(
- int uid, int prevState, @Nullable AudioPlaybackConfiguration config);
+ interface OnAudioPlayerActiveStateChangedListener {
+ /**
+ * Called when the active state of audio player is changed.
+ *
+ * @param config The audio playback configuration for the audio player of which active state
+ * was changed. If {@param isRemoved} is {@code true}, this hold outdated
+ * information.
+ * @param isRemoved {@code true} if the audio player is removed.
+ */
+ void onAudioPlayerActiveStateChanged(
+ @NonNull AudioPlaybackConfiguration config, boolean isRemoved);
}
private final static class MessageHandler extends Handler {
- private static final int MSG_AUDIO_PLAYER_STATE_CHANGED = 1;
+ private static final int MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED = 1;
- private final OnAudioPlayerStateChangedListener mListsner;
+ private final OnAudioPlayerActiveStateChangedListener mListener;
- public MessageHandler(Looper looper, OnAudioPlayerStateChangedListener listener) {
+ public MessageHandler(Looper looper, OnAudioPlayerActiveStateChangedListener listener) {
super(looper);
- mListsner = listener;
+ mListener = listener;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_AUDIO_PLAYER_STATE_CHANGED:
- mListsner.onAudioPlayerStateChanged(
- msg.arg1, msg.arg2, (AudioPlaybackConfiguration) msg.obj);
+ case MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED:
+ mListener.onAudioPlayerActiveStateChanged((AudioPlaybackConfiguration) msg.obj,
+ msg.arg1 != 0);
break;
}
}
- public void sendAudioPlayerStateChangedMessage(int uid, int prevState,
- AudioPlaybackConfiguration config) {
- obtainMessage(MSG_AUDIO_PLAYER_STATE_CHANGED, uid, prevState, config).sendToTarget();
+ public void sendAudioPlayerActiveStateChangedMessage(
+ final AudioPlaybackConfiguration config, final boolean isRemoved) {
+ obtainMessage(MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED,
+ isRemoved ? 1 : 0, 0 /* unused */, config).sendToTarget();
}
}
private final Object mLock = new Object();
@GuardedBy("mLock")
- private final Map<OnAudioPlayerStateChangedListener, MessageHandler> mListenerMap =
- new HashMap<>();
+ private final Map<OnAudioPlayerActiveStateChangedListener, MessageHandler> mListenerMap =
+ new ArrayMap<>();
@GuardedBy("mLock")
- private final Map<Integer, Integer> mAudioPlayerStates = new HashMap<>();
+ private final Set<Integer> mActiveAudioUids = new ArraySet();
@GuardedBy("mLock")
- private final Map<Integer, HashSet<Integer>> mAudioPlayersForUid = new HashMap<>();
+ private ArrayMap<Integer, AudioPlaybackConfiguration> mPrevActiveAudioPlaybackConfigs =
+ new ArrayMap<>();
// Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video)
// The UID whose audio playback becomes active at the last comes first.
// TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
@@ -122,32 +133,24 @@
}
final long token = Binder.clearCallingIdentity();
try {
- final Map<Integer, Integer> prevAudioPlayerStates = new HashMap<>(mAudioPlayerStates);
- final Map<Integer, HashSet<Integer>> prevAudioPlayersForUid =
- new HashMap<>(mAudioPlayersForUid);
synchronized (mLock) {
- mAudioPlayerStates.clear();
- mAudioPlayersForUid.clear();
+ // Update mActiveAudioUids
+ mActiveAudioUids.clear();
+ ArrayMap<Integer, AudioPlaybackConfiguration> activeAudioPlaybackConfigs =
+ new ArrayMap<>();
for (AudioPlaybackConfiguration config : configs) {
- int pii = config.getPlayerInterfaceId();
- int uid = config.getClientUid();
- mAudioPlayerStates.put(pii, config.getPlayerState());
- HashSet<Integer> players = mAudioPlayersForUid.get(uid);
- if (players == null) {
- players = new HashSet<Integer>();
- players.add(pii);
- mAudioPlayersForUid.put(uid, players);
- } else {
- players.add(pii);
+ if (config.isActive()) {
+ mActiveAudioUids.add(config.getClientUid());
+ activeAudioPlaybackConfigs.put(config.getPlayerInterfaceId(), config);
}
}
- for (AudioPlaybackConfiguration config : configs) {
- if (!config.isActive()) {
- continue;
- }
- int uid = config.getClientUid();
- if (!isActiveState(prevAudioPlayerStates.get(config.getPlayerInterfaceId()))) {
+ // Update mSortedAuioPlaybackClientUids.
+ for (int i = 0; i < activeAudioPlaybackConfigs.size(); ++i) {
+ AudioPlaybackConfiguration config = activeAudioPlaybackConfigs.valueAt(i);
+ final int uid = config.getClientUid();
+ if (!mPrevActiveAudioPlaybackConfigs.containsKey(
+ config.getPlayerInterfaceId())) {
if (DEBUG) {
Log.d(TAG, "Found a new active media playback. " +
AudioPlaybackConfiguration.toLogFriendlyString(config));
@@ -163,40 +166,21 @@
mSortedAudioPlaybackClientUids.add(0, uid);
}
}
- // Notify the change of audio player states.
+ // Notify the active state change of audio players.
for (AudioPlaybackConfiguration config : configs) {
- final Integer prevState = prevAudioPlayerStates.get(config.getPlayerInterfaceId());
- final int prevStateInt =
- (prevState == null) ? AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN :
- prevState.intValue();
- if (prevStateInt != config.getPlayerState()) {
- sendAudioPlayerStateChangedMessageLocked(
- config.getClientUid(), prevStateInt, config);
+ final int pii = config.getPlayerInterfaceId();
+ boolean wasActive = mPrevActiveAudioPlaybackConfigs.remove(pii) != null;
+ if (wasActive != config.isActive()) {
+ sendAudioPlayerActiveStateChangedMessageLocked(
+ config, /* isRemoved */ false);
}
}
- for (Integer prevUid : prevAudioPlayersForUid.keySet()) {
- // If all players for prevUid is removed, notify the prev state was
- // PLAYER_STATE_STARTED only when there were a player whose state was
- // PLAYER_STATE_STARTED, otherwise any inactive state is okay to notify.
- if (!mAudioPlayersForUid.containsKey(prevUid)) {
- Set<Integer> prevPlayers = prevAudioPlayersForUid.get(prevUid);
- int prevState = AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN;
- for (int pii : prevPlayers) {
- Integer state = prevAudioPlayerStates.get(pii);
- if (state == null) {
- continue;
- }
- if (state == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
- prevState = state;
- break;
- } else if (prevState
- == AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN) {
- prevState = state;
- }
- }
- sendAudioPlayerStateChangedMessageLocked(prevUid, prevState, null);
- }
+ for (AudioPlaybackConfiguration config : mPrevActiveAudioPlaybackConfigs.values()) {
+ sendAudioPlayerActiveStateChangedMessageLocked(config, /* isRemoved */ true);
}
+
+ // Update mPrevActiveAudioPlaybackConfigs
+ mPrevActiveAudioPlaybackConfigs = activeAudioPlaybackConfigs;
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -204,9 +188,10 @@
}
/**
- * Registers OnAudioPlayerStateChangedListener.
+ * Registers OnAudioPlayerActiveStateChangedListener.
*/
- public void registerListener(OnAudioPlayerStateChangedListener listener, Handler handler) {
+ public void registerListener(
+ OnAudioPlayerActiveStateChangedListener listener, Handler handler) {
synchronized (mLock) {
mListenerMap.put(listener, new MessageHandler((handler == null) ?
Looper.myLooper() : handler.getLooper(), listener));
@@ -214,9 +199,9 @@
}
/**
- * Unregisters OnAudioPlayerStateChangedListener.
+ * Unregisters OnAudioPlayerActiveStateChangedListener.
*/
- public void unregisterListener(OnAudioPlayerStateChangedListener listener) {
+ public void unregisterListener(OnAudioPlayerActiveStateChangedListener listener) {
synchronized (mLock) {
mListenerMap.remove(listener);
}
@@ -239,16 +224,7 @@
*/
public boolean isPlaybackActive(int uid) {
synchronized (mLock) {
- Set<Integer> players = mAudioPlayersForUid.get(uid);
- if (players == null) {
- return false;
- }
- for (Integer pii : players) {
- if (isActiveState(mAudioPlayerStates.get(pii))) {
- return true;
- }
- }
- return false;
+ return mActiveAudioUids.contains(uid);
}
}
@@ -314,14 +290,10 @@
}
}
- private void sendAudioPlayerStateChangedMessageLocked(
- final int uid, final int prevState, final AudioPlaybackConfiguration config) {
+ private void sendAudioPlayerActiveStateChangedMessageLocked(
+ final AudioPlaybackConfiguration config, final boolean isRemoved) {
for (MessageHandler messageHandler : mListenerMap.values()) {
- messageHandler.sendAudioPlayerStateChangedMessage(uid, prevState, config);
+ messageHandler.sendAudioPlayerActiveStateChangedMessage(config, isRemoved);
}
}
-
- private static boolean isActiveState(Integer state) {
- return state != null && state.equals(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
- }
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3c9e1d4..3e51252 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -19,7 +19,7 @@
import com.android.internal.util.DumpUtils;
import com.android.server.Watchdog;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -101,6 +101,8 @@
private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
private final Handler mHandler = new Handler();
private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
+ private final IntArray mActivePlayerMinPriorityQueue = new IntArray();
+ private final IntArray mActivePlayerUidMinPriorityQueue = new IntArray();
public MediaRouterService(Context context) {
mContext = context;
@@ -111,7 +113,7 @@
mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
mAudioPlayerStateMonitor.registerListener(
- new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
+ new AudioPlayerStateMonitor.OnAudioPlayerActiveStateChangedListener() {
static final long WAIT_MS = 500;
final Runnable mRestoreBluetoothA2dpRunnable = new Runnable() {
@Override
@@ -121,39 +123,41 @@
};
@Override
- public void onAudioPlayerStateChanged(
- int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
+ public void onAudioPlayerActiveStateChanged(
+ @NonNull AudioPlaybackConfiguration config, boolean isRemoved) {
+ final boolean active = !isRemoved && config.isActive();
+ final int pii = config.getPlayerInterfaceId();
+ final int uid = config.getClientUid();
+
+ final int idx = mActivePlayerMinPriorityQueue.indexOf(pii);
+ // Keep the latest active player and its uid at the end of the queue.
+ if (idx >= 0) {
+ mActivePlayerMinPriorityQueue.remove(idx);
+ mActivePlayerUidMinPriorityQueue.remove(idx);
+ }
+
int restoreUid = -1;
- boolean active = config == null ? false : config.isActive();
if (active) {
+ mActivePlayerMinPriorityQueue.add(config.getPlayerInterfaceId());
+ mActivePlayerUidMinPriorityQueue.add(uid);
restoreUid = uid;
- } else if (prevState != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
- // Noting to do if the prev state is not an active state.
- return;
- } else {
- IntArray sortedAudioPlaybackClientUids =
- mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
- for (int i = 0; i < sortedAudioPlaybackClientUids.size(); ++i) {
- if (mAudioPlayerStateMonitor.isPlaybackActive(
- sortedAudioPlaybackClientUids.get(i))) {
- restoreUid = sortedAudioPlaybackClientUids.get(i);
- break;
- }
- }
+ } else if (mActivePlayerUidMinPriorityQueue.size() > 0) {
+ restoreUid = mActivePlayerUidMinPriorityQueue.get(
+ mActivePlayerUidMinPriorityQueue.size() - 1);
}
mHandler.removeCallbacks(mRestoreBluetoothA2dpRunnable);
if (restoreUid >= 0) {
restoreRoute(restoreUid);
if (DEBUG) {
- Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
- + " active " + active + " restoring " + restoreUid);
+ Slog.d(TAG, "onAudioPlayerActiveStateChanged: " + "uid=" + uid
+ + ", active=" + active + ", restoreUid=" + restoreUid);
}
} else {
mHandler.postDelayed(mRestoreBluetoothA2dpRunnable, WAIT_MS);
if (DEBUG) {
- Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid
- + " active " + active + " delaying");
+ Slog.d(TAG, "onAudioPlayerACTIVEStateChanged: " + "uid=" + uid
+ + ", active=" + active + ", delaying");
}
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index f6a81d0..06f4f5e 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -16,7 +16,6 @@
package com.android.server.media;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.KeyguardManager;
@@ -138,23 +137,19 @@
mAudioService = getAudioService();
mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
mAudioPlayerStateMonitor.registerListener(
- new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() {
- @Override
- public void onAudioPlayerStateChanged(
- int uid, int prevState, @Nullable AudioPlaybackConfiguration config) {
- if (config == null || !config.isActive() || config.getPlayerType()
- == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
- return;
- }
- synchronized (mLock) {
- FullUserRecord user =
- getFullUserRecordLocked(UserHandle.getUserId(uid));
- if (user != null) {
- user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+ (config, isRemoved) -> {
+ if (isRemoved || !config.isActive() || config.getPlayerType()
+ == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+ return;
}
- }
- }
- }, null /* handler */);
+ synchronized (mLock) {
+ FullUserRecord user = getFullUserRecordLocked(
+ UserHandle.getUserId(config.getClientUid()));
+ if (user != null) {
+ user.mPriorityStack.updateMediaButtonSessionIfNeeded();
+ }
+ }
+ }, null /* handler */);
mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService);
mContentResolver = getContext().getContentResolver();
mSettingsObserver = new SettingsObserver();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6cebdd6..6ba1d8d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -736,6 +736,11 @@
for (NotificationVisibility nv : newlyVisibleKeys) {
NotificationRecord r = mNotificationsByKey.get(nv.key);
if (r == null) continue;
+ if (!r.isSeen()) {
+ // Report to usage stats that notification was made visible
+ if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
+ reportSeen(r);
+ }
r.setVisibility(true, nv.rank);
nv.recycle();
}
@@ -766,7 +771,7 @@
.setType(expanded ? MetricsEvent.TYPE_DETAIL
: MetricsEvent.TYPE_COLLAPSE));
}
- if (expanded) {
+ if (expanded && userAction) {
r.recordExpanded();
}
EventLogTags.writeNotificationExpansion(key,
@@ -1643,6 +1648,14 @@
return INotificationManager.Stub.asInterface(mService);
}
+ protected void reportSeen(NotificationRecord r) {
+ final int userId = r.sbn.getUserId();
+ mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+ userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
+ : userId,
+ UsageEvents.Event.NOTIFICATION_SEEN);
+ }
+
@VisibleForTesting
NotificationManagerInternal getInternalService() {
return mInternalService;
@@ -2269,10 +2282,7 @@
}
if (!r.isSeen()) {
if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
- mAppUsageStats.reportEvent(r.sbn.getPackageName(),
- userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM
- : userId,
- UsageEvents.Event.USER_INTERACTION);
+ reportSeen(r);
r.setSeen();
}
}
@@ -2720,9 +2730,9 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
final DumpFilter filter = DumpFilter.parseFromArguments(args);
- if (filter != null && filter.stats) {
+ if (filter.stats) {
dumpJson(pw, filter);
- } else if (filter != null && filter.proto) {
+ } else if (filter.proto) {
dumpProto(fd, filter);
} else {
dumpImpl(pw, filter);
@@ -2839,7 +2849,6 @@
@Override
public Policy getNotificationPolicy(String pkg) {
- enforcePolicyAccess(pkg, "getNotificationPolicy");
final long identity = Binder.clearCallingIdentity();
try {
return mZenModeHelper.getNotificationPolicy();
@@ -3236,7 +3245,7 @@
return null;
};
- private void dumpJson(PrintWriter pw, DumpFilter filter) {
+ private void dumpJson(PrintWriter pw, @NonNull DumpFilter filter) {
JSONObject dump = new JSONObject();
try {
dump.put("service", "Notification Manager");
@@ -3250,7 +3259,7 @@
pw.println(dump);
}
- private void dumpProto(FileDescriptor fd, DumpFilter filter) {
+ private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mNotificationLock) {
long records = proto.start(NotificationServiceDumpProto.RECORDS);
@@ -3332,7 +3341,7 @@
proto.flush();
}
- void dumpImpl(PrintWriter pw, DumpFilter filter) {
+ void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter) {
pw.print("Current Notification Manager state");
if (filter.filtered) {
pw.print(" (filtered to "); pw.print(filter); pw.print(")");
@@ -5901,6 +5910,7 @@
public boolean redact = true;
public boolean proto = false;
+ @NonNull
public static DumpFilter parseFromArguments(String[] args) {
final DumpFilter filter = new DumpFilter();
for (int ai = 0; ai < args.length; ai++) {
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index d7e9cf3..d566a45 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -22,6 +22,7 @@
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -915,21 +916,21 @@
}
}
- public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
- if (filter == null) {
- final int N = mSignalExtractors.length;
+ public void dump(PrintWriter pw, String prefix,
+ @NonNull NotificationManagerService.DumpFilter filter) {
+ final int N = mSignalExtractors.length;
+ pw.print(prefix);
+ pw.print("mSignalExtractors.length = ");
+ pw.println(N);
+ for (int i = 0; i < N; i++) {
pw.print(prefix);
- pw.print("mSignalExtractors.length = ");
- pw.println(N);
- for (int i = 0; i < N; i++) {
- pw.print(prefix);
- pw.print(" ");
- pw.println(mSignalExtractors[i]);
- }
-
- pw.print(prefix);
- pw.println("per-package config:");
+ pw.print(" ");
+ pw.println(mSignalExtractors[i].getClass().getSimpleName());
}
+
+ pw.print(prefix);
+ pw.println("per-package config:");
+
pw.println("Records:");
synchronized (mRecords) {
dumpRecords(pw, prefix, filter, mRecords);
@@ -938,7 +939,8 @@
dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
}
- public void dump(ProtoOutputStream proto, NotificationManagerService.DumpFilter filter) {
+ public void dump(ProtoOutputStream proto,
+ @NonNull NotificationManagerService.DumpFilter filter) {
final int N = mSignalExtractors.length;
for (int i = 0; i < N; i++) {
proto.write(RankingHelperProto.NOTIFICATION_SIGNAL_EXTRACTORS,
@@ -952,12 +954,13 @@
}
private static void dumpRecords(ProtoOutputStream proto, long fieldId,
- NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) {
+ @NonNull NotificationManagerService.DumpFilter filter,
+ ArrayMap<String, Record> records) {
final int N = records.size();
long fToken;
for (int i = 0; i < N; i++) {
final Record r = records.valueAt(i);
- if (filter == null || filter.matches(r.pkg)) {
+ if (filter.matches(r.pkg)) {
fToken = proto.start(fieldId);
proto.write(RecordProto.PACKAGE, r.pkg);
@@ -985,11 +988,12 @@
}
private static void dumpRecords(PrintWriter pw, String prefix,
- NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) {
+ @NonNull NotificationManagerService.DumpFilter filter,
+ ArrayMap<String, Record> records) {
final int N = records.size();
for (int i = 0; i < N; i++) {
final Record r = records.valueAt(i);
- if (filter == null || filter.matches(r.pkg)) {
+ if (filter.matches(r.pkg)) {
pw.print(prefix);
pw.print(" AppSettings: ");
pw.print(r.pkg);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f61cec9..1e9fab5 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -835,7 +835,8 @@
// alarm restrictions
final boolean muteMediaAndSystemSounds = zen && !mConfig.allowMediaSystemOther;
// total silence restrictions
- final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+ final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
+ || areAllBehaviorSoundsMuted();
for (int usage : AudioAttributes.SDK_USAGES) {
final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage);
@@ -855,9 +856,11 @@
}
}
+
@VisibleForTesting
protected void applyRestrictions(boolean mute, int usage) {
final String[] exceptionPackages = null; // none (for now)
+
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, usage,
mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
exceptionPackages);
@@ -866,6 +869,12 @@
exceptionPackages);
}
+ private boolean areAllBehaviorSoundsMuted() {
+ return !mConfig.allowAlarms && !mConfig.allowMediaSystemOther && !mConfig.allowReminders
+ && !mConfig.allowCalls && !mConfig.allowMessages && !mConfig.allowEvents
+ && !mConfig.allowRepeatCallers;
+ }
+
private void applyZenToRingerMode() {
if (mAudioManager == null) return;
// force the ringer mode into compliance
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 679250c..8591304 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -18,6 +18,7 @@
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
@@ -40,6 +41,7 @@
import com.android.server.pm.dex.DexoptOptions;
import java.io.File;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.TimeUnit;
@@ -402,14 +404,22 @@
}
/**
- * Execute the idle optimizations immediately.
+ * Execute idle optimizations immediately on packages in packageNames. If packageNames is null,
+ * then execute on all packages.
*/
- public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context) {
+ public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context,
+ @Nullable List<String> packageNames) {
// Create a new object to make sure we don't interfere with the scheduled jobs.
// Note that this may still run at the same time with the job scheduled by the
// JobScheduler but the scheduler will not be able to cancel it.
BackgroundDexOptService bdos = new BackgroundDexOptService();
- int result = bdos.idleOptimization(pm, pm.getOptimizablePackages(), context);
+ ArraySet<String> packagesToOptimize;
+ if (packageNames == null) {
+ packagesToOptimize = pm.getOptimizablePackages();
+ } else {
+ packagesToOptimize = new ArraySet<>(packageNames);
+ }
+ int result = bdos.idleOptimization(pm, packagesToOptimize, context);
return result == OPTIMIZE_PROCESSED;
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 210eb13..6a06be2 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -486,6 +486,16 @@
}
}
+ public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid,
+ @Nullable String volumeUuid, int flags) throws InstallerException {
+ if (!checkBeforeRemote()) return new byte[0];
+ try {
+ return mInstalld.hashSecondaryDexFile(dexPath, packageName, uid, volumeUuid, flags);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
public void invalidateMounts() throws InstallerException {
if (!checkBeforeRemote()) return;
try {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 50ac409..00cfa31 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -110,9 +110,9 @@
return false;
}
- // We do not dexopt a priv-app package when pm.dexopt.priv-apps is false.
+ // We do not dexopt a priv-app package when pm.dexopt.priv-apps-oob is true.
if (pkg.isPrivileged()) {
- return SystemProperties.getBoolean("pm.dexopt.priv-apps", true);
+ return !SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false);
}
return true;
@@ -154,7 +154,13 @@
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
final List<String> paths = pkg.getAllCodePaths();
- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+
+ int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ if (sharedGid == -1) {
+ Slog.wtf(TAG, "Well this is awkward; package " + pkg.applicationInfo.name + " had UID "
+ + pkg.applicationInfo.uid, new Throwable());
+ sharedGid = android.os.Process.NOBODY_UID;
+ }
// Get the class loader context dependencies.
// For each code path in the package, this array contains the class loader context that
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 09f9cb8..be9b2f3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -32,6 +32,7 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
@@ -45,6 +46,7 @@
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -724,6 +726,12 @@
Binder.restoreCallingIdentity(ident);
}
} else {
+ ApplicationInfo appInfo = mPm.getApplicationInfo(callerPackageName, 0, userId);
+ if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.REQUEST_DELETE_PACKAGES,
+ null);
+ }
+
// Take a short detour to confirm with user
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null));
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6a0ea4ee..bbe59eb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -293,6 +293,7 @@
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
+import com.android.server.pm.dex.DexLogger;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
@@ -551,6 +552,8 @@
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
+ private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob";
+
/** Canonical intent used to identify what counts as a "web browser" app */
private static final Intent sBrowserIntent;
static {
@@ -2378,7 +2381,10 @@
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
- mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
+ DexManager.Listener dexManagerListener = DexLogger.getListener(this,
+ installer, mInstallLock);
+ mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
+ dexManagerListener);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -8983,11 +8989,11 @@
* Execute the background dexopt job immediately.
*/
@Override
- public boolean runBackgroundDexoptJob() {
+ public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
}
- return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext);
+ return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames);
}
List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
@@ -9598,7 +9604,7 @@
if (Build.IS_DEBUGGABLE &&
pkg.isPrivileged() &&
- !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) {
PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
}
@@ -19900,6 +19906,23 @@
.getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
co.onChange(true);
+ // This observer provides an one directional mapping from Global.PRIV_APP_OOB_ENABLED to
+ // pm.dexopt.priv-apps-oob property. This is only for experiment and should be removed once
+ // it is done.
+ ContentObserver privAppOobObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ int oobEnabled = Global.getInt(resolver, Global.PRIV_APP_OOB_ENABLED, 0);
+ SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB,
+ oobEnabled == 1 ? "true" : "false");
+ }
+ };
+ mContext.getContentResolver().registerContentObserver(
+ Global.getUriFor(Global.PRIV_APP_OOB_ENABLED), false, privAppOobObserver,
+ UserHandle.USER_SYSTEM);
+ // At boot, restore the value from the setting, which persists across reboot.
+ privAppOobObserver.onChange(true);
+
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
// disabled after already being started.
CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 807eb1a..ee773a5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1219,7 +1219,13 @@
}
private int runDexoptJob() throws RemoteException {
- boolean result = mInterface.runBackgroundDexoptJob();
+ String arg;
+ List<String> packageNames = new ArrayList<>();
+ while ((arg = getNextArg()) != null) {
+ packageNames.add(arg);
+ }
+ boolean result = mInterface.runBackgroundDexoptJob(packageNames.isEmpty() ? null :
+ packageNames);
return result ? 0 : -1;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index a3585bc..ba97c42 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -615,10 +615,16 @@
// Fix up isPinned for the caller. Note we need to do it before the "test" callback,
// since it may check isPinned.
- if (!isPinnedByCaller) {
- clone.clearFlags(ShortcutInfo.FLAG_PINNED);
+ // However, if getPinnedByAnyLauncher is set, we do it after the test.
+ if (!getPinnedByAnyLauncher) {
+ if (!isPinnedByCaller) {
+ clone.clearFlags(ShortcutInfo.FLAG_PINNED);
+ }
}
if (query == null || query.test(clone)) {
+ if (!isPinnedByCaller) {
+ clone.clearFlags(ShortcutInfo.FLAG_PINNED);
+ }
result.add(clone);
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 25e9239..0907dd7 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2234,7 +2234,7 @@
// We override this method in unit tests to do a simpler check.
boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId,
int callingPid, int callingUid) {
- if (injectCheckAccessShortcutsPermission(callingPid, callingUid)) {
+ if (canSeeAnyPinnedShortcut(callingPackage, userId, callingPid, callingUid)) {
return true;
}
final long start = injectElapsedRealtime();
@@ -2245,10 +2245,21 @@
}
}
+ boolean canSeeAnyPinnedShortcut(@NonNull String callingPackage, int userId,
+ int callingPid, int callingUid) {
+ if (injectHasAccessShortcutsPermission(callingPid, callingUid)) {
+ return true;
+ }
+ synchronized (mLock) {
+ return getUserShortcutsLocked(userId).hasHostPackage(callingPackage);
+ }
+ }
+
/**
* Returns true if the caller has the "ACCESS_SHORTCUTS" permission.
*/
- boolean injectCheckAccessShortcutsPermission(int callingPid, int callingUid) {
+ @VisibleForTesting
+ boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) {
return mContext.checkPermission(android.Manifest.permission.ACCESS_SHORTCUTS,
callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
}
@@ -2262,10 +2273,6 @@
final ShortcutUser user = getUserShortcutsLocked(userId);
- if (user.hasHostPackage(packageName)) {
- return true;
- }
-
// Always trust the cached component.
final ComponentName cached = user.getCachedLauncher();
if (cached != null) {
@@ -2491,8 +2498,8 @@
final ArraySet<String> ids = shortcutIds == null ? null
: new ArraySet<>(shortcutIds);
- final ShortcutPackage p = getUserShortcutsLocked(userId)
- .getPackageShortcutsIfExists(packageName);
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ final ShortcutPackage p = user.getPackageShortcutsIfExists(packageName);
if (p == null) {
return; // No need to instantiate ShortcutPackage.
}
@@ -2500,9 +2507,12 @@
final boolean matchPinned = (queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
final boolean matchManifest = (queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
+ final boolean canAccessAllShortcuts =
+ canSeeAnyPinnedShortcut(callingPackage, launcherUserId, callingPid, callingUid);
+
final boolean getPinnedByAnyLauncher =
- ((queryFlags & ShortcutQuery.FLAG_MATCH_ALL_PINNED) != 0)
- && injectCheckAccessShortcutsPermission(callingPid, callingUid);
+ canAccessAllShortcuts &&
+ ((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER) != 0);
p.findAll(ret,
(ShortcutInfo si) -> {
@@ -2521,7 +2531,7 @@
if (matchDynamic && si.isDynamic()) {
return true;
}
- if ((matchPinned && si.isPinned()) || getPinnedByAnyLauncher) {
+ if ((matchPinned || getPinnedByAnyLauncher) && si.isPinned()) {
return true;
}
if (matchManifest && si.isDeclaredInManifest()) {
@@ -2614,14 +2624,15 @@
.attemptToRestoreIfNeededAndSave();
final boolean getPinnedByAnyLauncher =
- injectCheckAccessShortcutsPermission(callingPid, callingUid);
+ canSeeAnyPinnedShortcut(callingPackage, launcherUserId,
+ callingPid, callingUid);
// Make sure the shortcut is actually visible to the launcher.
final ShortcutInfo si = getShortcutInfoLocked(
launcherUserId, callingPackage, packageName, shortcutId, userId,
getPinnedByAnyLauncher);
// "si == null" should suffice here, but check the flags too just to make sure.
- if (si == null || !si.isEnabled() || !si.isAlive()) {
+ if (si == null || !si.isEnabled() || !(si.isAlive() || getPinnedByAnyLauncher)) {
Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled");
return null;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1e5245c..dbf413f 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -717,6 +717,19 @@
}
}
+ @Override
+ public int getProfileParentId(int userHandle) {
+ checkManageUsersPermission("get the profile parent");
+ synchronized (mUsersLock) {
+ UserInfo profileParent = getProfileParentLU(userHandle);
+ if (profileParent == null) {
+ return userHandle;
+ }
+
+ return profileParent.id;
+ }
+ }
+
private UserInfo getProfileParentLU(int userHandle) {
UserInfo profile = getUserInfoLU(userHandle);
if (profile == null) {
@@ -1010,6 +1023,23 @@
}
}
+ @Override
+ public boolean hasRestrictedProfiles() {
+ checkManageUsersPermission("hasRestrictedProfiles");
+ final int callingUserId = UserHandle.getCallingUserId();
+ synchronized (mUsersLock) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ UserInfo profile = mUsers.valueAt(i).info;
+ if (callingUserId != profile.id
+ && profile.restrictedProfileParentId == callingUserId) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
/*
* Should be locked on mUsers before calling this.
*/
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java
new file mode 100644
index 0000000..0913269
--- /dev/null
+++ b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.crossprofile;
+
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+public class CrossProfileAppsService extends SystemService {
+ private CrossProfileAppsServiceImpl mServiceImpl;
+
+ public CrossProfileAppsService(Context context) {
+ super(context);
+ mServiceImpl = new CrossProfileAppsServiceImpl(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.CROSS_PROFILE_APPS_SERVICE, mServiceImpl);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
new file mode 100644
index 0000000..854b704
--- /dev/null
+++ b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.crossprofile;
+
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.content.pm.crossprofile.ICrossProfileApps;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
+ private static final String TAG = "CrossProfileAppsService";
+
+ private Context mContext;
+ private Injector mInjector;
+
+ public CrossProfileAppsServiceImpl(Context context) {
+ this(context, new InjectorImpl(context));
+ }
+
+ @VisibleForTesting
+ CrossProfileAppsServiceImpl(Context context, Injector injector) {
+ mContext = context;
+ mInjector = injector;
+ }
+
+ @Override
+ public List<UserHandle> getTargetUserProfiles(String callingPackage) {
+ Preconditions.checkNotNull(callingPackage);
+
+ verifyCallingPackage(callingPackage);
+
+ return getTargetUserProfilesUnchecked(
+ callingPackage, mInjector.getCallingUserId());
+ }
+
+ @Override
+ public void startActivityAsUser(
+ String callingPackage,
+ ComponentName component,
+ Rect sourceBounds,
+ Bundle startActivityOptions,
+ UserHandle user) throws RemoteException {
+ Preconditions.checkNotNull(callingPackage);
+ Preconditions.checkNotNull(component);
+ Preconditions.checkNotNull(user);
+
+ verifyCallingPackage(callingPackage);
+
+ List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked(
+ callingPackage, mInjector.getCallingUserId());
+ if (!allowedTargetUsers.contains(user)) {
+ throw new SecurityException(
+ callingPackage + " cannot access unrelated user " + user.getIdentifier());
+ }
+
+ // Verify that caller package is starting activity in its own package.
+ if (!callingPackage.equals(component.getPackageName())) {
+ throw new SecurityException(
+ callingPackage + " attempts to start an activity in other package - "
+ + component.getPackageName());
+ }
+
+ final int callingUid = mInjector.getCallingUid();
+
+ // Verify that target activity does handle the intent with ACTION_MAIN and
+ // CATEGORY_LAUNCHER as calling startActivityAsUser ignore them if component is present.
+ final Intent launchIntent = new Intent(Intent.ACTION_MAIN);
+ launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ launchIntent.setSourceBounds(sourceBounds);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ // Only package name is set here, as opposed to component name, because intent action and
+ // category are ignored if component name is present while we are resolving intent.
+ launchIntent.setPackage(component.getPackageName());
+ verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, user);
+
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ launchIntent.setComponent(component);
+ mContext.startActivityAsUser(launchIntent, startActivityOptions, user);
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
+ private List<UserHandle> getTargetUserProfilesUnchecked(
+ String callingPackage, @UserIdInt int callingUserId) {
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ final int[] enabledProfileIds =
+ mInjector.getUserManager().getEnabledProfileIds(callingUserId);
+
+ List<UserHandle> targetProfiles = new ArrayList<>();
+ for (final int userId : enabledProfileIds) {
+ if (userId == callingUserId) {
+ continue;
+ }
+ if (!isPackageEnabled(callingPackage, userId)) {
+ continue;
+ }
+ targetProfiles.add(UserHandle.of(userId));
+ }
+ return targetProfiles;
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
+ private boolean isPackageEnabled(String packageName, @UserIdInt int userId) {
+ final int callingUid = mInjector.getCallingUid();
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ final PackageInfo info = mInjector.getPackageManagerInternal()
+ .getPackageInfo(
+ packageName,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ callingUid,
+ userId);
+ return info != null && info.applicationInfo.enabled;
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Verify that the specified intent does resolved to the specified component and the resolved
+ * activity is exported.
+ */
+ private void verifyActivityCanHandleIntentAndExported(
+ Intent launchIntent, ComponentName component, int callingUid, UserHandle user) {
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ final List<ResolveInfo> apps =
+ mInjector.getPackageManagerInternal().queryIntentActivities(
+ launchIntent,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ callingUid,
+ user.getIdentifier());
+ final int size = apps.size();
+ for (int i = 0; i < size; ++i) {
+ final ActivityInfo activityInfo = apps.get(i).activityInfo;
+ if (TextUtils.equals(activityInfo.packageName, component.getPackageName())
+ && TextUtils.equals(activityInfo.name, component.getClassName())
+ && activityInfo.exported) {
+ return;
+ }
+ }
+ throw new SecurityException("Attempt to launch activity without "
+ + " category Intent.CATEGORY_LAUNCHER or activity is not exported" + component);
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Verify that the given calling package is belong to the calling UID.
+ */
+ private void verifyCallingPackage(String callingPackage) {
+ mInjector.getAppOpsManager().checkPackage(mInjector.getCallingUid(), callingPackage);
+ }
+
+ private static class InjectorImpl implements Injector {
+ private Context mContext;
+
+ public InjectorImpl(Context context) {
+ mContext = context;
+ }
+
+ public int getCallingUid() {
+ return Binder.getCallingUid();
+ }
+
+ public int getCallingUserId() {
+ return UserHandle.getCallingUserId();
+ }
+
+ public UserHandle getCallingUserHandle() {
+ return Binder.getCallingUserHandle();
+ }
+
+ public long clearCallingIdentity() {
+ return Binder.clearCallingIdentity();
+ }
+
+ public void restoreCallingIdentity(long token) {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ public UserManager getUserManager() {
+ return mContext.getSystemService(UserManager.class);
+ }
+
+ public PackageManagerInternal getPackageManagerInternal() {
+ return LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ public PackageManager getPackageManager() {
+ return mContext.getPackageManager();
+ }
+
+ public AppOpsManager getAppOpsManager() {
+ return mContext.getSystemService(AppOpsManager.class);
+ }
+ }
+
+ @VisibleForTesting
+ public interface Injector {
+ int getCallingUid();
+
+ int getCallingUserId();
+
+ UserHandle getCallingUserHandle();
+
+ long clearCallingIdentity();
+
+ void restoreCallingIdentity(long token);
+
+ UserManager getUserManager();
+
+ PackageManagerInternal getPackageManagerInternal();
+
+ PackageManager getPackageManager();
+
+ AppOpsManager getAppOpsManager();
+
+ }
+}
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
new file mode 100644
index 0000000..c7bbf1c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.os.RemoteException;
+
+import android.util.ArraySet;
+import android.util.ByteStringUtils;
+import android.util.EventLog;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
+
+import java.io.File;
+import java.util.Set;
+
+import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+
+/**
+ * This class is responsible for logging data about secondary dex files.
+ * The data logged includes hashes of the name and content of each file.
+ */
+public class DexLogger implements DexManager.Listener {
+ private static final String TAG = "DexLogger";
+
+ // Event log tag & subtag used for SafetyNet logging of dynamic
+ // code loading (DCL) - see b/63927552.
+ private static final int SNET_TAG = 0x534e4554;
+ private static final String DCL_SUBTAG = "dcl";
+
+ private final IPackageManager mPackageManager;
+ private final Object mInstallLock;
+ @GuardedBy("mInstallLock")
+ private final Installer mInstaller;
+
+ public static DexManager.Listener getListener(IPackageManager pms,
+ Installer installer, Object installLock) {
+ return new DexLogger(pms, installer, installLock);
+ }
+
+ private DexLogger(IPackageManager pms, Installer installer, Object installLock) {
+ mPackageManager = pms;
+ mInstaller = installer;
+ mInstallLock = installLock;
+ }
+
+ /**
+ * Compute and log hashes of the name and content of a secondary dex file.
+ */
+ @Override
+ public void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
+ String dexPath, int storageFlags) {
+ int ownerUid = appInfo.uid;
+
+ byte[] hash = null;
+ synchronized(mInstallLock) {
+ try {
+ hash = mInstaller.hashSecondaryDexFile(dexPath, appInfo.packageName,
+ ownerUid, appInfo.volumeUuid, storageFlags);
+ } catch (InstallerException e) {
+ Slog.e(TAG, "Got InstallerException when hashing dex " + dexPath +
+ " : " + e.getMessage());
+ }
+ }
+ if (hash == null) {
+ return;
+ }
+
+ String dexFileName = new File(dexPath).getName();
+ String message = PackageUtils.computeSha256Digest(dexFileName.getBytes());
+ // Valid SHA256 will be 256 bits, 32 bytes.
+ if (hash.length == 32) {
+ message = message + ' ' + ByteStringUtils.toHexString(hash);
+ }
+
+ EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, ownerUid, message);
+
+ if (dexUseInfo.isUsedByOtherApps()) {
+ Set<String> otherPackages = dexUseInfo.getLoadingPackages();
+ Set<Integer> otherUids = new ArraySet<>(otherPackages.size());
+ for (String otherPackageName : otherPackages) {
+ try {
+ int otherUid = mPackageManager.getPackageUid(
+ otherPackageName, /*flags*/0, dexUseInfo.getOwnerUserId());
+ if (otherUid != -1 && otherUid != ownerUid) {
+ otherUids.add(otherUid);
+ }
+ } catch (RemoteException ignore) {
+ // Can't happen, we're local.
+ }
+ }
+ for (int otherUid : otherUids) {
+ EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, otherUid, message);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 6274754..0e2730c 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -76,6 +76,7 @@
private final Object mInstallLock;
@GuardedBy("mInstallLock")
private final Installer mInstaller;
+ private final Listener mListener;
// Possible outcomes of a dex search.
private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
@@ -96,14 +97,24 @@
*/
private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
+ public interface Listener {
+ /**
+ * Invoked just before the secondary dex file {@code dexPath} for the specified application
+ * is reconciled.
+ */
+ void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
+ String dexPath, int storageFlags);
+ }
+
public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
- Installer installer, Object installLock) {
+ Installer installer, Object installLock, Listener listener) {
mPackageCodeLocationsCache = new HashMap<>();
mPackageDexUsage = new PackageDexUsage();
mPackageManager = pms;
mPackageDexOptimizer = pdo;
mInstaller = installer;
mInstallLock = installLock;
+ mListener = listener;
}
/**
@@ -389,7 +400,7 @@
: mPackageDexOptimizer;
String packageName = options.getPackageName();
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
- if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+ if (useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
}
@@ -433,7 +444,7 @@
*/
public void reconcileSecondaryDexFiles(String packageName) {
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
- if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+ if (useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
}
@@ -481,12 +492,16 @@
continue;
}
+ if (mListener != null) {
+ mListener.onReconcileSecondaryDexFile(info, dexUseInfo, dexPath, flags);
+ }
+
boolean dexStillExists = true;
synchronized(mInstallLock) {
try {
String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]);
dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName,
- pkg.applicationInfo.uid, isas, pkg.applicationInfo.volumeUuid, flags);
+ info.uid, isas, info.volumeUuid, flags);
} catch (InstallerException e) {
Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath +
" : " + e.getMessage());
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 7a2e630..3707a5e 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -58,7 +58,7 @@
public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
- if (mStatusBarInternal.isGlobalActionsDisabled()) {
+ if (mStatusBarInternal != null && mStatusBarInternal.isGlobalActionsDisabled()) {
return;
}
mKeyguardShowing = keyguardShowing;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7837940..7748ae4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -59,6 +59,8 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
@@ -66,6 +68,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
@@ -123,11 +126,8 @@
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.STABLE_BOUNDS;
-
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityManager.StackId;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityThread;
@@ -210,6 +210,7 @@
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import android.view.DisplayFrames;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.IApplicationToken;
@@ -596,47 +597,6 @@
PointerLocationView mPointerLocationView;
- // The current size of the screen; really; extends into the overscan area of
- // the screen and doesn't account for any system elements like the status bar.
- int mOverscanScreenLeft, mOverscanScreenTop;
- int mOverscanScreenWidth, mOverscanScreenHeight;
- // The current visible size of the screen; really; (ir)regardless of whether the status
- // bar can be hidden but not extending into the overscan area.
- int mUnrestrictedScreenLeft, mUnrestrictedScreenTop;
- int mUnrestrictedScreenWidth, mUnrestrictedScreenHeight;
- // Like mOverscanScreen*, but allowed to move into the overscan region where appropriate.
- int mRestrictedOverscanScreenLeft, mRestrictedOverscanScreenTop;
- int mRestrictedOverscanScreenWidth, mRestrictedOverscanScreenHeight;
- // The current size of the screen; these may be different than (0,0)-(dw,dh)
- // if the status bar can't be hidden; in that case it effectively carves out
- // that area of the display from all other windows.
- int mRestrictedScreenLeft, mRestrictedScreenTop;
- int mRestrictedScreenWidth, mRestrictedScreenHeight;
- // During layout, the current screen borders accounting for any currently
- // visible system UI elements.
- int mSystemLeft, mSystemTop, mSystemRight, mSystemBottom;
- // For applications requesting stable content insets, these are them.
- int mStableLeft, mStableTop, mStableRight, mStableBottom;
- // For applications requesting stable content insets but have also set the
- // fullscreen window flag, these are the stable dimensions without the status bar.
- int mStableFullscreenLeft, mStableFullscreenTop;
- int mStableFullscreenRight, mStableFullscreenBottom;
- // During layout, the current screen borders with all outer decoration
- // (status bar, input method dock) accounted for.
- int mCurLeft, mCurTop, mCurRight, mCurBottom;
- // During layout, the frame in which content should be displayed
- // to the user, accounting for all screen decoration except for any
- // space they deem as available for other content. This is usually
- // the same as mCur*, but may be larger if the screen decor has supplied
- // content insets.
- int mContentLeft, mContentTop, mContentRight, mContentBottom;
- // During layout, the frame in which voice content should be displayed
- // to the user, accounting for all screen decoration except for any
- // space they deem as available for other content.
- int mVoiceContentLeft, mVoiceContentTop, mVoiceContentRight, mVoiceContentBottom;
- // During layout, the current screen borders along which input method
- // windows are placed.
- int mDockLeft, mDockTop, mDockRight, mDockBottom;
// During layout, the layer at which the doc window is placed.
int mDockLayer;
// During layout, this is the layer of the status bar.
@@ -734,18 +694,11 @@
Display mDisplay;
- private int mDisplayRotation;
-
int mLandscapeRotation = 0; // default landscape rotation
int mSeascapeRotation = 0; // "other" landscape rotation, 180 degrees from mLandscapeRotation
int mPortraitRotation = 0; // default portrait rotation
int mUpsideDownRotation = 0; // "other" portrait rotation
- int mOverscanLeft = 0;
- int mOverscanTop = 0;
- int mOverscanRight = 0;
- int mOverscanBottom = 0;
-
// What we do when the user long presses on home
private int mLongPressOnHomeBehavior;
@@ -1061,7 +1014,7 @@
View.NAVIGATION_BAR_UNHIDE,
View.NAVIGATION_BAR_TRANSLUCENT,
StatusBarManager.WINDOW_NAVIGATION_BAR,
- WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
+ FLAG_TRANSLUCENT_NAVIGATION,
View.NAVIGATION_BAR_TRANSPARENT);
private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
@@ -2068,6 +2021,7 @@
context.registerReceiver(mMultiuserReceiver, filter);
// monitor for system gestures
+ // TODO(multi-display): Needs to be display specific.
mSystemGestures = new SystemGesturesPointerEventListener(context,
new SystemGesturesPointerEventListener.Callbacks() {
@Override
@@ -2314,17 +2268,6 @@
return mForceDefaultOrientation;
}
- @Override
- public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) {
- // TODO(multi-display): Define policy for secondary displays.
- if (display.getDisplayId() == DEFAULT_DISPLAY) {
- mOverscanLeft = left;
- mOverscanTop = top;
- mOverscanRight = right;
- mOverscanBottom = bottom;
- }
- }
-
public void updateSettings() {
ContentResolver resolver = mContext.getContentResolver();
boolean updateRotation = false;
@@ -4321,12 +4264,16 @@
}
@Override
+ // TODO: Should probably be moved into DisplayFrames.
public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
- int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets,
- Rect outStableInsets, Rect outOutsets) {
+ DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
+ Rect outOutsets) {
final int fl = PolicyControl.getWindowFlags(null, attrs);
final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs);
final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility);
+ final int displayRotation = displayFrames.mRotation;
+ final int displayWidth = displayFrames.mDisplayWidth;
+ final int displayHeight = displayFrames.mDisplayHeight;
final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
if (useOutsets) {
@@ -4349,34 +4296,33 @@
int availRight, availBottom;
if (canHideNavigationBar() &&
(systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
- availRight = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- availBottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ availRight = displayFrames.mUnrestricted.right;
+ availBottom = displayFrames.mUnrestricted.bottom;
} else {
- availRight = mRestrictedScreenLeft + mRestrictedScreenWidth;
- availBottom = mRestrictedScreenTop + mRestrictedScreenHeight;
+ availRight = displayFrames.mRestricted.right;
+ availBottom = displayFrames.mRestricted.bottom;
}
+ outStableInsets.set(displayFrames.mStable.left, displayFrames.mStable.top,
+ availRight - displayFrames.mStable.right,
+ availBottom - displayFrames.mStable.bottom);
+
if ((systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
if ((fl & FLAG_FULLSCREEN) != 0) {
- outContentInsets.set(mStableFullscreenLeft, mStableFullscreenTop,
- availRight - mStableFullscreenRight,
- availBottom - mStableFullscreenBottom);
+ outContentInsets.set(displayFrames.mStableFullscreen.left,
+ displayFrames.mStableFullscreen.top,
+ availRight - displayFrames.mStableFullscreen.right,
+ availBottom - displayFrames.mStableFullscreen.bottom);
} else {
- outContentInsets.set(mStableLeft, mStableTop,
- availRight - mStableRight, availBottom - mStableBottom);
+ outContentInsets.set(outStableInsets);
}
} else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
outContentInsets.setEmpty();
- } else if ((systemUiVisibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) == 0) {
- outContentInsets.set(mCurLeft, mCurTop,
- availRight - mCurRight, availBottom - mCurBottom);
} else {
- outContentInsets.set(mCurLeft, mCurTop,
- availRight - mCurRight, availBottom - mCurBottom);
+ outContentInsets.set(displayFrames.mCurrent.left, displayFrames.mCurrent.top,
+ availRight - displayFrames.mCurrent.right,
+ availBottom - displayFrames.mCurrent.bottom);
}
- outStableInsets.set(mStableLeft, mStableTop,
- availRight - mStableRight, availBottom - mStableBottom);
if (taskBounds != null) {
calculateRelevantTaskInsets(taskBounds, outContentInsets,
displayWidth, displayHeight);
@@ -4413,68 +4359,11 @@
/** {@inheritDoc} */
@Override
- public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
- int displayRotation, int uiMode) {
- final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY;
- mDisplayRotation = displayRotation;
- final int overscanLeft, overscanTop, overscanRight, overscanBottom;
- if (isDefaultDisplay) {
- switch (displayRotation) {
- case Surface.ROTATION_90:
- overscanLeft = mOverscanTop;
- overscanTop = mOverscanRight;
- overscanRight = mOverscanBottom;
- overscanBottom = mOverscanLeft;
- break;
- case Surface.ROTATION_180:
- overscanLeft = mOverscanRight;
- overscanTop = mOverscanBottom;
- overscanRight = mOverscanLeft;
- overscanBottom = mOverscanTop;
- break;
- case Surface.ROTATION_270:
- overscanLeft = mOverscanBottom;
- overscanTop = mOverscanLeft;
- overscanRight = mOverscanTop;
- overscanBottom = mOverscanRight;
- break;
- default:
- overscanLeft = mOverscanLeft;
- overscanTop = mOverscanTop;
- overscanRight = mOverscanRight;
- overscanBottom = mOverscanBottom;
- break;
- }
- } else {
- overscanLeft = 0;
- overscanTop = 0;
- overscanRight = 0;
- overscanBottom = 0;
- }
- mOverscanScreenLeft = mRestrictedOverscanScreenLeft = 0;
- mOverscanScreenTop = mRestrictedOverscanScreenTop = 0;
- mOverscanScreenWidth = mRestrictedOverscanScreenWidth = displayWidth;
- mOverscanScreenHeight = mRestrictedOverscanScreenHeight = displayHeight;
- mSystemLeft = 0;
- mSystemTop = 0;
- mSystemRight = displayWidth;
- mSystemBottom = displayHeight;
- mUnrestrictedScreenLeft = overscanLeft;
- mUnrestrictedScreenTop = overscanTop;
- mUnrestrictedScreenWidth = displayWidth - overscanLeft - overscanRight;
- mUnrestrictedScreenHeight = displayHeight - overscanTop - overscanBottom;
- mRestrictedScreenLeft = mUnrestrictedScreenLeft;
- mRestrictedScreenTop = mUnrestrictedScreenTop;
- mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth;
- mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight;
- mDockLeft = mContentLeft = mVoiceContentLeft = mStableLeft = mStableFullscreenLeft
- = mCurLeft = mUnrestrictedScreenLeft;
- mDockTop = mContentTop = mVoiceContentTop = mStableTop = mStableFullscreenTop
- = mCurTop = mUnrestrictedScreenTop;
- mDockRight = mContentRight = mVoiceContentRight = mStableRight = mStableFullscreenRight
- = mCurRight = displayWidth - overscanRight;
- mDockBottom = mContentBottom = mVoiceContentBottom = mStableBottom = mStableFullscreenBottom
- = mCurBottom = displayHeight - overscanBottom;
+ public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
+ displayFrames.onBeginLayout();
+ // TODO(multi-display): This doesn't seem right...Maybe only apply to default display?
+ mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
+ mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
mDockLayer = 0x10000000;
mStatusBarLayer = -1;
@@ -4484,13 +4373,13 @@
final Rect of = mTmpOverscanFrame;
final Rect vf = mTmpVisibleFrame;
final Rect dcf = mTmpDecorFrame;
- pf.left = df.left = of.left = vf.left = mDockLeft;
- pf.top = df.top = of.top = vf.top = mDockTop;
- pf.right = df.right = of.right = vf.right = mDockRight;
- pf.bottom = df.bottom = of.bottom = vf.bottom = mDockBottom;
+ vf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
+ pf.set(displayFrames.mDock);
dcf.setEmpty(); // Decor frame N/A for system bars.
- if (isDefaultDisplay) {
+ if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
// For purposes of putting out fake window up to steal focus, we will
// drive nav being hidden only by whether it is requested.
final int sysui = mLastSystemUiFlags;
@@ -4509,10 +4398,9 @@
&& mStatusBar.getAttrs().height == MATCH_PARENT
&& mStatusBar.getAttrs().width == MATCH_PARENT;
- // When the navigation bar isn't visible, we put up a fake
- // input window to catch all touch events. This way we can
- // detect when the user presses anywhere to bring back the nav
- // bar and ensure the application doesn't see the event.
+ // When the navigation bar isn't visible, we put up a fake input window to catch all
+ // touch events. This way we can detect when the user presses anywhere to bring back the
+ // nav bar and ensure the application doesn't see the event.
if (navVisible || navAllowedHidden) {
if (mInputConsumer != null) {
mHandler.sendMessage(
@@ -4528,30 +4416,32 @@
InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
}
- // For purposes of positioning and showing the nav bar, if we have
- // decided that it can't be hidden (because of the screen aspect ratio),
- // then take that into account.
+ // For purposes of positioning and showing the nav bar, if we have decided that it can't
+ // be hidden (because of the screen aspect ratio), then take that into account.
navVisible |= !canHideNavigationBar();
- boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
- displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
- navAllowedHidden, statusBarExpandedNotKeyguard);
- if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
- mDockLeft, mDockTop, mDockRight, mDockBottom));
- updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing);
+ boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf,
+ navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
+ if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
+ updateSysUiVisibility |= layoutStatusBar(
+ displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing);
if (updateSysUiVisibility) {
updateSystemUiVisibilityLw();
}
}
- layoutScreenDecorWindows(displayId, displayWidth, displayHeight, pf, df, dcf);
+ layoutScreenDecorWindows(displayFrames, pf, df, dcf);
}
- private void layoutScreenDecorWindows(int displayId, int displayWidth, int displayHeight,
- Rect pf, Rect df, Rect dcf) {
+ private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) {
if (mScreenDecorWindows.isEmpty()) {
return;
}
+ final int displayId = displayFrames.mDisplayId;
+ final Rect dockFrame = displayFrames.mDock;
+ final int displayHeight = displayFrames.mDisplayHeight;
+ final int displayWidth = displayFrames.mDisplayWidth;
+
for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) {
final WindowState w = mScreenDecorWindows.valueAt(i);
if (w.getDisplayId() != displayId || !w.isVisibleLw()) {
@@ -4568,10 +4458,10 @@
// Docked at left or top.
if (frame.bottom >= displayHeight) {
// Docked left.
- mDockLeft = Math.max(frame.right, mDockLeft);
+ dockFrame.left = Math.max(frame.right, dockFrame.left);
} else if (frame.right >= displayWidth ) {
// Docked top.
- mDockTop = Math.max(frame.bottom, mDockTop);
+ dockFrame.top = Math.max(frame.bottom, dockFrame.top);
} else {
Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
+ " not docked on left or top of display. frame=" + frame
@@ -4581,10 +4471,10 @@
// Docked at right or bottom.
if (frame.top <= 0) {
// Docked right.
- mDockRight = Math.min(frame.left, mDockRight);
+ dockFrame.right = Math.min(frame.left, dockFrame.right);
} else if (frame.left <= 0) {
// Docked bottom.
- mDockBottom = Math.min(frame.top, mDockBottom);
+ dockFrame.bottom = Math.min(frame.top, dockFrame.bottom);
} else {
Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
+ " not docked on right or bottom" + " of display. frame=" + frame
@@ -4598,194 +4488,165 @@
}
}
- mContentTop = mSystemTop = mVoiceContentTop = mCurTop = mRestrictedScreenTop = mDockTop;
- mContentLeft = mSystemLeft = mVoiceContentLeft = mCurLeft = mRestrictedScreenLeft
- = mRestrictedOverscanScreenLeft = mDockLeft;
- mContentBottom = mSystemBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
- mContentRight = mSystemRight = mVoiceContentRight = mCurRight = mDockRight;
-
- mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
- mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
- mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
- mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
+ displayFrames.mRestricted.set(dockFrame);
+ displayFrames.mCurrent.set(dockFrame);
+ displayFrames.mVoiceContent.set(dockFrame);
+ displayFrames.mSystem.set(dockFrame);
+ displayFrames.mContent.set(dockFrame);
+ displayFrames.mRestrictedOverscan.set(dockFrame);
}
- private boolean layoutStatusBar(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui,
- boolean isKeyguardShowing) {
+ private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf,
+ Rect dcf, int sysui, boolean isKeyguardShowing) {
// decide where the status bar goes ahead of time
- if (mStatusBar != null) {
- // apply any navigation bar insets
- pf.left = df.left = of.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight
- + mUnrestrictedScreenTop;
- vf.left = mStableLeft;
- vf.top = mStableTop;
- vf.right = mStableRight;
- vf.bottom = mStableBottom;
+ if (mStatusBar == null) {
+ return false;
+ }
+ // apply any navigation bar insets
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ vf.set(displayFrames.mStable);
- mStatusBarLayer = mStatusBar.getSurfaceLayer();
+ mStatusBarLayer = mStatusBar.getSurfaceLayer();
- // Let the status bar determine its size.
- mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
- vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
- dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
+ // Let the status bar determine its size.
+ mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
+ vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
+ dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
- // For layout, the status bar is always at the top with our fixed height.
- mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
+ // For layout, the status bar is always at the top with our fixed height.
+ displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeight;
- boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
- boolean statusBarTranslucent = (sysui
- & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
- if (!isKeyguardShowing) {
- statusBarTranslucent &= areTranslucentBarsAllowed();
- }
+ boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
+ boolean statusBarTranslucent = (sysui
+ & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
+ if (!isKeyguardShowing) {
+ statusBarTranslucent &= areTranslucentBarsAllowed();
+ }
- // If the status bar is hidden, we don't want to cause
- // windows behind it to scroll.
- if (mStatusBar.isVisibleLw() && !statusBarTransient) {
- // Status bar may go away, so the screen area it occupies
- // is available to apps but just covering them when the
- // status bar is visible.
- mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
+ // If the status bar is hidden, we don't want to cause windows behind it to scroll.
+ if (mStatusBar.isVisibleLw() && !statusBarTransient) {
+ // Status bar may go away, so the screen area it occupies is available to apps but just
+ // covering them when the status bar is visible.
+ final Rect dockFrame = displayFrames.mDock;
+ dockFrame.top = displayFrames.mStable.top;
+ displayFrames.mContent.set(dockFrame);
+ displayFrames.mVoiceContent.set(dockFrame);
- mContentTop = mVoiceContentTop = mCurTop = mDockTop;
- mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
- mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
- mContentRight = mVoiceContentRight = mCurRight = mDockRight;
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
+ "dock=%s content=%s cur=%s", dockFrame.toString(),
+ displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
- if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
- String.format(
- "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
- mDockLeft, mDockTop, mDockRight, mDockBottom,
- mContentLeft, mContentTop, mContentRight, mContentBottom,
- mCurLeft, mCurTop, mCurRight, mCurBottom));
- }
- if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()
- && !statusBarTransient && !statusBarTranslucent
+ if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
&& !mStatusBarController.wasRecentlyTranslucent()) {
- // If the opaque status bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
- }
- if (mStatusBarController.checkHiddenLw()) {
- return true;
+ // If the opaque status bar is currently requested to be visible, and not in the
+ // process of animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.top = displayFrames.mStable.top;
}
}
- return false;
+ return mStatusBarController.checkHiddenLw();
}
- private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation,
- int uiMode, int overscanLeft, int overscanRight, int overscanBottom, Rect dcf,
+ private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,
boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
boolean statusBarExpandedNotKeyguard) {
- if (mNavigationBar != null) {
- boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
- // Force the navigation bar to its appropriate place and
- // size. We need to do this directly, instead of relying on
- // it to bubble up from the nav bar, because this needs to
- // change atomically with screen rotations.
- mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight,
- displayRotation);
- if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
- // It's a system nav bar or a portrait screen; nav bar goes on bottom.
- int top = displayHeight - overscanBottom
- - getNavigationBarHeight(displayRotation, uiMode);
- mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
- mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- mDockBottom = mTmpNavigationFrame.top;
- mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
- mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status
- // bar.
- mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the opaque nav bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemBottom = mTmpNavigationFrame.top;
- }
- } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
- // Landscape screen; nav bar goes to the right.
- int left = displayWidth - overscanRight
- - getNavigationBarWidth(displayRotation, uiMode);
- mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
- mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- mDockRight = mTmpNavigationFrame.left;
- mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
- mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status
- // bar.
- mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the nav bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemRight = mTmpNavigationFrame.left;
- }
- } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
- // Seascape screen; nav bar goes to the left.
- int right = overscanLeft + getNavigationBarWidth(displayRotation, uiMode);
- mTmpNavigationFrame.set(overscanLeft, 0, right, displayHeight);
- mStableLeft = mStableFullscreenLeft = mTmpNavigationFrame.right;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- mDockLeft = mTmpNavigationFrame.right;
- // TODO: not so sure about those:
- mRestrictedScreenLeft = mRestrictedOverscanScreenLeft = mDockLeft;
- mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
- mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status
- // bar.
- mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the nav bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemLeft = mTmpNavigationFrame.right;
- }
+ if (mNavigationBar == null) {
+ return false;
+ }
+ boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
+ // Force the navigation bar to its appropriate place and size. We need to do this directly,
+ // instead of relying on it to bubble up from the nav bar, because this needs to change
+ // atomically with screen rotations.
+ final int rotation = displayFrames.mRotation;
+ final int displayHeight = displayFrames.mDisplayHeight;
+ final int displayWidth = displayFrames.mDisplayWidth;
+ final Rect dockFrame = displayFrames.mDock;
+ mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
+
+ if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ // It's a system nav bar or a portrait screen; nav bar goes on bottom.
+ final int top = displayFrames.mUnrestricted.bottom
+ - getNavigationBarHeight(rotation, uiMode);
+ mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
+ displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ dockFrame.bottom = displayFrames.mRestricted.bottom
+ = displayFrames.mRestrictedOverscan.bottom = top;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status bar.
+ mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
}
- // Make sure the content and current rectangles are updated to
- // account for the restrictions from the navigation bar.
- mContentTop = mVoiceContentTop = mCurTop = mDockTop;
- mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
- mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
- mContentRight = mVoiceContentRight = mCurRight = mDockRight;
- mStatusBarLayer = mNavigationBar.getSurfaceLayer();
- // And compute the final frame.
- mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
- mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
- mTmpNavigationFrame, mTmpNavigationFrame);
- if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
- if (mNavigationBarController.checkHiddenLw()) {
- return true;
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the opaque nav bar is currently requested to be visible and not in the process
+ // of animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.bottom = top;
+ }
+ } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+ // Landscape screen; nav bar goes to the right.
+ final int left = displayFrames.mUnrestricted.right
+ - getNavigationBarWidth(rotation, uiMode);
+ mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
+ displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ dockFrame.right = displayFrames.mRestricted.right
+ = displayFrames.mRestrictedOverscan.right = left;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status bar.
+ mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible, and not in the process of
+ // animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.right = left;
+ }
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ // Seascape screen; nav bar goes to the left.
+ final int right = displayFrames.mUnrestricted.left
+ + getNavigationBarWidth(rotation, uiMode);
+ mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
+ displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ dockFrame.left = displayFrames.mRestricted.left =
+ displayFrames.mRestrictedOverscan.left = right;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status bar.
+ mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible, and not in the process of
+ // animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.left = right;
}
}
- return false;
+
+ // Make sure the content and current rectangles are updated to account for the restrictions
+ // from the navigation bar.
+ displayFrames.mCurrent.set(dockFrame);
+ displayFrames.mVoiceContent.set(dockFrame);
+ displayFrames.mContent.set(dockFrame);
+ mStatusBarLayer = mNavigationBar.getSurfaceLayer();
+ // And compute the final frame.
+ mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
+ mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
+ mTmpNavigationFrame, mTmpNavigationFrame);
+ if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
+ return mNavigationBarController.checkHiddenLw();
}
private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
@@ -4813,32 +4674,26 @@
return 0;
}
- @Override
- public void getContentRectLw(Rect r) {
- r.set(mContentLeft, mContentTop, mContentRight, mContentBottom);
- }
-
- void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
- boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf) {
+ private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
+ boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf,
+ DisplayFrames displayFrames) {
if (win.getSurfaceLayer() > mDockLayer && attached.getSurfaceLayer() < mDockLayer) {
- // Here's a special case: if this attached window is a panel that is
- // above the dock window, and the window it is attached to is below
- // the dock window, then the frames we computed for the window it is
- // attached to can not be used because the dock is effectively part
- // of the underlying window and the attached window is floating on top
- // of the whole thing. So, we ignore the attached window and explicitly
- // compute the frames that would be appropriate without the dock.
- df.left = of.left = cf.left = vf.left = mDockLeft;
- df.top = of.top = cf.top = vf.top = mDockTop;
- df.right = of.right = cf.right = vf.right = mDockRight;
- df.bottom = of.bottom = cf.bottom = vf.bottom = mDockBottom;
+ // Here's a special case: if this attached window is a panel that is above the dock
+ // window, and the window it is attached to is below the dock window, then the frames we
+ // computed for the window it is attached to can not be used because the dock is
+ // effectively part of the underlying window and the attached window is floating on top
+ // of the whole thing. So, we ignore the attached window and explicitly compute the
+ // frames that would be appropriate without the dock.
+ vf.set(displayFrames.mDock);
+ cf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
} else {
- // The effective display frame of the attached window depends on
- // whether it is taking care of insetting its content. If not,
- // we need to use the parent's content frame so that the entire
- // window is positioned within that content. Otherwise we can use
- // the overscan frame and let the attached window take care of
- // positioning its content appropriately.
+ // The effective display frame of the attached window depends on whether it is taking
+ // care of insetting its content. If not, we need to use the parent's content frame so
+ // that the entire window is positioned within that content. Otherwise we can use the
+ // overscan frame and let the attached window take care of positioning its content
+ // appropriately.
if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
// Set the content frame of the attached window to the parent's decor frame
// (same as content frame when IME isn't present) if specifically requested by
@@ -4847,51 +4702,37 @@
cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0
? attached.getContentFrameLw() : attached.getOverscanFrameLw());
} else {
- // If the window is resizing, then we want to base the content
- // frame on our attached content frame to resize... however,
- // things can be tricky if the attached window is NOT in resize
- // mode, in which case its content frame will be larger.
- // Ungh. So to deal with that, make sure the content frame
- // we end up using is not covering the IM dock.
+ // If the window is resizing, then we want to base the content frame on our attached
+ // content frame to resize...however, things can be tricky if the attached window is
+ // NOT in resize mode, in which case its content frame will be larger.
+ // Ungh. So to deal with that, make sure the content frame we end up using is not
+ // covering the IM dock.
cf.set(attached.getContentFrameLw());
if (attached.isVoiceInteraction()) {
- if (cf.left < mVoiceContentLeft) cf.left = mVoiceContentLeft;
- if (cf.top < mVoiceContentTop) cf.top = mVoiceContentTop;
- if (cf.right > mVoiceContentRight) cf.right = mVoiceContentRight;
- if (cf.bottom > mVoiceContentBottom) cf.bottom = mVoiceContentBottom;
+ cf.intersectUnchecked(displayFrames.mVoiceContent);
} else if (attached.getSurfaceLayer() < mDockLayer) {
- if (cf.left < mContentLeft) cf.left = mContentLeft;
- if (cf.top < mContentTop) cf.top = mContentTop;
- if (cf.right > mContentRight) cf.right = mContentRight;
- if (cf.bottom > mContentBottom) cf.bottom = mContentBottom;
+ cf.intersectUnchecked(displayFrames.mContent);
}
}
df.set(insetDecors ? attached.getDisplayFrameLw() : cf);
of.set(insetDecors ? attached.getOverscanFrameLw() : cf);
vf.set(attached.getVisibleFrameLw());
}
- // The LAYOUT_IN_SCREEN flag is used to determine whether the attached
- // window should be positioned relative to its parent or the entire
- // screen.
- pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0
- ? attached.getFrameLw() : df);
+ // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be
+ // positioned relative to its parent or the entire screen.
+ pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df);
}
- private void applyStableConstraints(int sysui, int fl, Rect r) {
- if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
- // If app is requesting a stable layout, don't let the
- // content insets go below the stable values.
- if ((fl & FLAG_FULLSCREEN) != 0) {
- if (r.left < mStableFullscreenLeft) r.left = mStableFullscreenLeft;
- if (r.top < mStableFullscreenTop) r.top = mStableFullscreenTop;
- if (r.right > mStableFullscreenRight) r.right = mStableFullscreenRight;
- if (r.bottom > mStableFullscreenBottom) r.bottom = mStableFullscreenBottom;
- } else {
- if (r.left < mStableLeft) r.left = mStableLeft;
- if (r.top < mStableTop) r.top = mStableTop;
- if (r.right > mStableRight) r.right = mStableRight;
- if (r.bottom > mStableBottom) r.bottom = mStableBottom;
- }
+ private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) {
+ if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) {
+ return;
+ }
+ // If app is requesting a stable layout, don't let the content insets go below the stable
+ // values.
+ if ((fl & FLAG_FULLSCREEN) != 0) {
+ r.intersectUnchecked(displayFrames.mStableFullscreen);
+ } else {
+ r.intersectUnchecked(displayFrames.mStable);
}
}
@@ -4906,7 +4747,7 @@
/** {@inheritDoc} */
@Override
- public void layoutWindowLw(WindowState win, WindowState attached) {
+ public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
// We've already done the navigation bar, status bar, and all screen decor windows. If the
// status bar can receive input, we need to layout it again to accommodate for the IME
// window.
@@ -4920,9 +4761,10 @@
(win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
if (needsToOffsetInputMethodTarget) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
- offsetInputMethodWindowLw(mLastInputMethodWindow);
+ offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames);
}
+ final int type = attrs.type;
final int fl = PolicyControl.getWindowFlags(win, attrs);
final int pfl = attrs.privateFlags;
final int sim = attrs.softInputMode;
@@ -4943,120 +4785,83 @@
final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
- if (isDefaultDisplay) {
- sf.set(mStableLeft, mStableTop, mStableRight, mStableBottom);
- } else {
- sf.set(mOverscanLeft, mOverscanTop, mOverscanRight, mOverscanBottom);
- }
+ sf.set(displayFrames.mStable);
- if (!isDefaultDisplay) {
- // TODO: Need to fix this and above to take into account decor windows.
- if (attached != null) {
- // If this window is attached to another, our display
- // frame is the same as the one we are attached to.
- setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf);
- } else {
- // Give the window full screen.
- pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
- pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
- pf.right = df.right = of.right = cf.right
- = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom
- = mOverscanScreenTop + mOverscanScreenHeight;
- }
- } else if (attrs.type == TYPE_INPUT_METHOD) {
- pf.left = df.left = of.left = cf.left = vf.left = mDockLeft;
- pf.top = df.top = of.top = cf.top = vf.top = mDockTop;
- pf.right = df.right = of.right = cf.right = vf.right = mDockRight;
+ if (type == TYPE_INPUT_METHOD) {
+ vf.set(displayFrames.mDock);
+ cf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
+ pf.set(displayFrames.mDock);
// IM dock windows layout below the nav bar...
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
// ...with content insets above the nav bar
- cf.bottom = vf.bottom = mStableBottom;
+ cf.bottom = vf.bottom = displayFrames.mStable.bottom;
if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
// The status bar forces the navigation bar while it's visible. Make sure the IME
// avoids the navigation bar in that case.
if (mNavigationBarPosition == NAV_BAR_RIGHT) {
- pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
+ pf.right = df.right = of.right = cf.right = vf.right =
+ displayFrames.mStable.right;
} else if (mNavigationBarPosition == NAV_BAR_LEFT) {
- pf.left = df.left = of.left = cf.left = vf.left = mStableLeft;
+ pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
}
}
// IM dock windows always go to the bottom of the screen.
attrs.gravity = Gravity.BOTTOM;
mDockLayer = win.getSurfaceLayer();
- } else if (attrs.type == TYPE_VOICE_INTERACTION) {
- pf.left = df.left = of.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ } else if (type == TYPE_VOICE_INTERACTION) {
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- cf.left = mDockLeft;
- cf.top = mDockTop;
- cf.right = mDockRight;
- cf.bottom = mDockBottom;
+ cf.set(displayFrames.mDock);
} else {
- cf.left = mContentLeft;
- cf.top = mContentTop;
- cf.right = mContentRight;
- cf.bottom = mContentBottom;
+ cf.set(displayFrames.mContent);
}
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.left = mCurLeft;
- vf.top = mCurTop;
- vf.right = mCurRight;
- vf.bottom = mCurBottom;
+ vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
}
- } else if (attrs.type == TYPE_WALLPAPER) {
- layoutWallpaper(win, pf, df, of, cf);
+ } else if (type == TYPE_WALLPAPER) {
+ layoutWallpaper(displayFrames, pf, df, of, cf);
} else if (win == mStatusBar) {
- pf.left = df.left = of.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight + mUnrestrictedScreenTop;
- cf.left = vf.left = mStableLeft;
- cf.top = vf.top = mStableTop;
- cf.right = vf.right = mStableRight;
- vf.bottom = mStableBottom;
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ cf.set(displayFrames.mStable);
+ vf.set(displayFrames.mStable);
if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
- cf.bottom = mContentBottom;
+ cf.bottom = displayFrames.mContent.bottom;
} else {
- cf.bottom = mDockBottom;
- vf.bottom = mContentBottom;
+ cf.bottom = displayFrames.mDock.bottom;
+ vf.bottom = displayFrames.mContent.bottom;
}
} else {
-
- // Default policy decor for the default display
- dcf.left = mSystemLeft;
- dcf.top = mSystemTop;
- dcf.right = mSystemRight;
- dcf.bottom = mSystemBottom;
- final boolean inheritTranslucentDecor = (attrs.privateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
+ dcf.set(displayFrames.mSystem);
+ final boolean inheritTranslucentDecor =
+ (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
final boolean isAppWindow =
- attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW &&
- attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+ type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
final boolean topAtRest =
win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
- && (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0
- && (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0
- && (fl & WindowManager.LayoutParams.
- FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+ && (fl & FLAG_FULLSCREEN) == 0
+ && (fl & FLAG_TRANSLUCENT_STATUS) == 0
+ && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
&& (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) {
// Ensure policy decor includes status bar
- dcf.top = mStableTop;
+ dcf.top = displayFrames.mStable.top;
}
- if ((fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) == 0
+ if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
&& (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
- && (fl & WindowManager.LayoutParams.
- FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
+ && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
// Ensure policy decor includes navigation bar
- dcf.bottom = mStableBottom;
- dcf.right = mStableRight;
+ dcf.bottom = displayFrames.mStable.bottom;
+ dcf.right = displayFrames.mStable.right;
}
}
@@ -5064,117 +4869,83 @@
== (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ "): IN_SCREEN, INSET_DECOR");
- // This is the case for a normal activity window: we want it
- // to cover all of the screen space, and it can take care of
- // moving its contents to account for screen decorations that
- // intrude into that space.
+ // This is the case for a normal activity window: we want it to cover all of the
+ // screen space, and it can take care of moving its contents to account for screen
+ // decorations that intrude into that space.
if (attached != null) {
// If this window is attached to another, our display
// frame is the same as the one we are attached to.
- setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf);
+ setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf,
+ displayFrames);
} else {
- if (attrs.type == TYPE_STATUS_BAR_PANEL
- || attrs.type == TYPE_STATUS_BAR_SUB_PANEL) {
- // Status bar panels are the only windows who can go on top of
- // the status bar. They are protected by the STATUS_BAR_SERVICE
- // permission, so they have the same privileges as the status
- // bar itself.
+ if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
+ // Status bar panels are the only windows who can go on top of the status
+ // bar. They are protected by the STATUS_BAR_SERVICE permission, so they
+ // have the same privileges as the status bar itself.
//
// However, they should still dodge the navigation bar if it exists.
pf.left = df.left = of.left = hasNavBar
- ? mDockLeft : mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
+ ? displayFrames.mDock.left : displayFrames.mUnrestricted.left;
+ pf.top = df.top = of.top = displayFrames.mUnrestricted.top;
pf.right = df.right = of.right = hasNavBar
- ? mRestrictedScreenLeft+mRestrictedScreenWidth
- : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
+ ? displayFrames.mRestricted.right
+ : displayFrames.mUnrestricted.right;
pf.bottom = df.bottom = of.bottom = hasNavBar
- ? mRestrictedScreenTop+mRestrictedScreenHeight
- : mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ ? displayFrames.mRestricted.bottom
+ : displayFrames.mUnrestricted.bottom;
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
- && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+ && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
// Asking to layout into the overscan region, so give it that pure
// unrestricted area.
- pf.left = df.left = of.left = mOverscanScreenLeft;
- pf.top = df.top = of.top = mOverscanScreenTop;
- pf.right = df.right = of.right = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = of.bottom = mOverscanScreenTop
- + mOverscanScreenHeight;
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
} else if (canHideNavigationBar()
&& (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
- && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
- // Asking for layout as if the nav bar is hidden, lets the
- // application extend into the unrestricted overscan screen area. We
- // only do this for application windows to ensure no window that
- // can be above the nav bar can do this.
- pf.left = df.left = mOverscanScreenLeft;
- pf.top = df.top = mOverscanScreenTop;
- pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
- // We need to tell the app about where the frame inside the overscan
- // is, so it can inset its content by that amount -- it didn't ask
- // to actually extend itself into the overscan region.
- of.left = mUnrestrictedScreenLeft;
- of.top = mUnrestrictedScreenTop;
- of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+ // Asking for layout as if the nav bar is hidden, lets the application
+ // extend into the unrestricted overscan screen area. We only do this for
+ // application windows to ensure no window that can be above the nav bar can
+ // do this.
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ // We need to tell the app about where the frame inside the overscan is, so
+ // it can inset its content by that amount -- it didn't ask to actually
+ // extend itself into the overscan region.
+ of.set(displayFrames.mUnrestricted);
} else {
- pf.left = df.left = mRestrictedOverscanScreenLeft;
- pf.top = df.top = mRestrictedOverscanScreenTop;
- pf.right = df.right = mRestrictedOverscanScreenLeft
- + mRestrictedOverscanScreenWidth;
- pf.bottom = df.bottom = mRestrictedOverscanScreenTop
- + mRestrictedOverscanScreenHeight;
+ df.set(displayFrames.mRestrictedOverscan);
+ pf.set(displayFrames.mRestrictedOverscan);
// We need to tell the app about where the frame inside the overscan
// is, so it can inset its content by that amount -- it didn't ask
// to actually extend itself into the overscan region.
- of.left = mUnrestrictedScreenLeft;
- of.top = mUnrestrictedScreenTop;
- of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ of.set(displayFrames.mUnrestricted);
}
if ((fl & FLAG_FULLSCREEN) == 0) {
if (win.isVoiceInteraction()) {
- cf.left = mVoiceContentLeft;
- cf.top = mVoiceContentTop;
- cf.right = mVoiceContentRight;
- cf.bottom = mVoiceContentBottom;
+ cf.set(displayFrames.mVoiceContent);
} else {
if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- cf.left = mDockLeft;
- cf.top = mDockTop;
- cf.right = mDockRight;
- cf.bottom = mDockBottom;
+ cf.set(displayFrames.mDock);
} else {
- cf.left = mContentLeft;
- cf.top = mContentTop;
- cf.right = mContentRight;
- cf.bottom = mContentBottom;
+ cf.set(displayFrames.mContent);
}
}
} else {
- // Full screen windows are always given a layout that is as if the
- // status bar and other transient decors are gone. This is to avoid
- // bad states when moving from a window that is not hding the
- // status bar to one that is.
- cf.left = mRestrictedScreenLeft;
- cf.top = mRestrictedScreenTop;
- cf.right = mRestrictedScreenLeft + mRestrictedScreenWidth;
- cf.bottom = mRestrictedScreenTop + mRestrictedScreenHeight;
+ // Full screen windows are always given a layout that is as if the status
+ // bar and other transient decors are gone. This is to avoid bad states when
+ // moving from a window that is not hiding the status bar to one that is.
+ cf.set(displayFrames.mRestricted);
}
- applyStableConstraints(sysUiFl, fl, cf);
+ applyStableConstraints(sysUiFl, fl, cf, displayFrames);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.left = mCurLeft;
- vf.top = mCurTop;
- vf.right = mCurRight;
- vf.bottom = mCurBottom;
+ vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
}
@@ -5182,86 +4953,70 @@
} else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl
& (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
- "): IN_SCREEN");
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ + "): IN_SCREEN");
// A window that has requested to fill the entire screen just
// gets everything, period.
- if (attrs.type == TYPE_STATUS_BAR_PANEL
- || attrs.type == TYPE_STATUS_BAR_SUB_PANEL) {
- pf.left = df.left = of.left = cf.left = hasNavBar
- ? mDockLeft : mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = cf.right = hasNavBar
- ? mRestrictedScreenLeft + mRestrictedScreenWidth
- : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = hasNavBar
- ? mRestrictedScreenTop + mRestrictedScreenHeight
- : mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ if (hasNavBar) {
+ pf.left = df.left = of.left = cf.left = displayFrames.mDock.left;
+ pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right;
+ pf.bottom = df.bottom = of.bottom = cf.bottom =
+ displayFrames.mRestricted.bottom;
+ }
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
- } else if (attrs.type == TYPE_VOLUME_OVERLAY) {
+ } else if (type == TYPE_VOLUME_OVERLAY) {
// Volume overlay covers everything, including the status and navbar
- pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = cf.right =
- mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom =
- mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
- } else if (attrs.type == TYPE_NAVIGATION_BAR
- || attrs.type == TYPE_NAVIGATION_BAR_PANEL) {
+ } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
// The navigation bar has Real Ultimate Power.
- pf.left = df.left = of.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = mUnrestrictedScreenLeft
- + mUnrestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop
- + mUnrestrictedScreenHeight;
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out navigation bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
- } else if ((attrs.type == TYPE_SECURE_SYSTEM_OVERLAY
- || attrs.type == TYPE_BOOT_PROGRESS
- || attrs.type == TYPE_SCREENSHOT)
+ } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
&& ((fl & FLAG_FULLSCREEN) != 0)) {
// Fullscreen secure system overlays get what they ask for. Screenshot region
// selection overlay should also expand to full screen.
- pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
- pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
- pf.right = df.right = of.right = cf.right = mOverscanScreenLeft
- + mOverscanScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
- + mOverscanScreenHeight;
- } else if (attrs.type == TYPE_BOOT_PROGRESS) {
+ cf.set(displayFrames.mOverscan);
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ } else if (type == TYPE_BOOT_PROGRESS) {
// Boot progress screen always covers entire display.
- pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
- pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
- pf.right = df.right = of.right = cf.right = mOverscanScreenLeft
- + mOverscanScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
- + mOverscanScreenHeight;
+ cf.set(displayFrames.mOverscan);
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
- && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
- // Asking to layout into the overscan region, so give it that pure
- // unrestricted area.
- pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
- pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
- pf.right = df.right = of.right = cf.right
- = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom
- = mOverscanScreenTop + mOverscanScreenHeight;
+ && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+ // Asking to layout into the overscan region, so give it that pure unrestricted
+ // area.
+ cf.set(displayFrames.mOverscan);
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
} else if (canHideNavigationBar()
&& (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
- && (attrs.type == TYPE_STATUS_BAR
- || attrs.type == TYPE_TOAST
- || attrs.type == TYPE_DOCK_DIVIDER
- || attrs.type == TYPE_VOICE_INTERACTION_STARTING
- || (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW))) {
+ && (type == TYPE_STATUS_BAR
+ || type == TYPE_TOAST
+ || type == TYPE_DOCK_DIVIDER
+ || type == TYPE_VOICE_INTERACTION_STARTING
+ || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) {
// Asking for layout as if the nav bar is hidden, lets the
// application extend into the unrestricted screen area. We
// only do this for application windows (or toasts) to ensure no window that
@@ -5269,102 +5024,76 @@
// XXX This assumes that an app asking for this will also
// ask for layout in only content. We can't currently figure out
// what the screen would be if only laying out to hide the nav bar.
- pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = cf.right = mUnrestrictedScreenLeft
- + mUnrestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mUnrestrictedScreenTop
- + mUnrestrictedScreenHeight;
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
} else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
- pf.left = df.left = of.left = mRestrictedScreenLeft;
- pf.top = df.top = of.top = mRestrictedScreenTop;
- pf.right = df.right = of.right = mRestrictedScreenLeft + mRestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = mRestrictedScreenTop
- + mRestrictedScreenHeight;
+ of.set(displayFrames.mRestricted);
+ df.set(displayFrames.mRestricted);
+ pf.set(displayFrames.mRestricted);
if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- cf.left = mDockLeft;
- cf.top = mDockTop;
- cf.right = mDockRight;
- cf.bottom = mDockBottom;
+ cf.set(displayFrames.mDock);
} else {
- cf.left = mContentLeft;
- cf.top = mContentTop;
- cf.right = mContentRight;
- cf.bottom = mContentBottom;
+ cf.set(displayFrames.mContent);
}
} else {
- pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft;
- pf.top = df.top = of.top = cf.top = mRestrictedScreenTop;
- pf.right = df.right = of.right = cf.right = mRestrictedScreenLeft
- + mRestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop
- + mRestrictedScreenHeight;
+ cf.set(displayFrames.mRestricted);
+ of.set(displayFrames.mRestricted);
+ df.set(displayFrames.mRestricted);
+ pf.set(displayFrames.mRestricted);
}
- applyStableConstraints(sysUiFl, fl, cf);
+ applyStableConstraints(sysUiFl, fl, cf,displayFrames);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.left = mCurLeft;
- vf.top = mCurTop;
- vf.right = mCurRight;
- vf.bottom = mCurBottom;
+ vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
}
} else if (attached != null) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
- "): attached to " + attached);
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ + "): attached to " + attached);
// A child window should be placed inside of the same visible
// frame that its parent had.
- setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf);
+ setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf,
+ displayFrames);
} else {
if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
"): normal window");
// Otherwise, a normal window must be placed inside the content
// of all screen decorations.
- if (attrs.type == TYPE_STATUS_BAR_PANEL || attrs.type == TYPE_VOLUME_OVERLAY) {
+ if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_VOLUME_OVERLAY) {
// Status bar panels and the volume dialog are the only windows who can go on
- // top of the status bar. They are protected by the STATUS_BAR_SERVICE
- // permission, so they have the same privileges as the status
- // bar itself.
- pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft;
- pf.top = df.top = of.top = cf.top = mRestrictedScreenTop;
- pf.right = df.right = of.right = cf.right = mRestrictedScreenLeft
- + mRestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop
- + mRestrictedScreenHeight;
- } else if (attrs.type == TYPE_TOAST || attrs.type == TYPE_SYSTEM_ALERT) {
+ // top of the status bar. They are protected by the STATUS_BAR_SERVICE
+ // permission, so they have the same privileges as the status bar itself.
+ cf.set(displayFrames.mRestricted);
+ of.set(displayFrames.mRestricted);
+ df.set(displayFrames.mRestricted);
+ pf.set(displayFrames.mRestricted);
+ } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
// These dialogs are stable to interim decor changes.
- pf.left = df.left = of.left = cf.left = mStableLeft;
- pf.top = df.top = of.top = cf.top = mStableTop;
- pf.right = df.right = of.right = cf.right = mStableRight;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mStableBottom;
+ cf.set(displayFrames.mStable);
+ of.set(displayFrames.mStable);
+ df.set(displayFrames.mStable);
+ pf.set(displayFrames.mStable);
} else {
- pf.left = mContentLeft;
- pf.top = mContentTop;
- pf.right = mContentRight;
- pf.bottom = mContentBottom;
+ pf.set(displayFrames.mContent);
if (win.isVoiceInteraction()) {
- df.left = of.left = cf.left = mVoiceContentLeft;
- df.top = of.top = cf.top = mVoiceContentTop;
- df.right = of.right = cf.right = mVoiceContentRight;
- df.bottom = of.bottom = cf.bottom = mVoiceContentBottom;
+ cf.set(displayFrames.mVoiceContent);
+ of.set(displayFrames.mVoiceContent);
+ df.set(displayFrames.mVoiceContent);
} else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- df.left = of.left = cf.left = mDockLeft;
- df.top = of.top = cf.top = mDockTop;
- df.right = of.right = cf.right = mDockRight;
- df.bottom = of.bottom = cf.bottom = mDockBottom;
+ cf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
} else {
- df.left = of.left = cf.left = mContentLeft;
- df.top = of.top = cf.top = mContentTop;
- df.right = of.right = cf.right = mContentRight;
- df.bottom = of.bottom = cf.bottom = mContentBottom;
+ cf.set(displayFrames.mContent);
+ of.set(displayFrames.mContent);
+ df.set(displayFrames.mContent);
}
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.left = mCurLeft;
- vf.top = mCurTop;
- vf.right = mCurRight;
- vf.bottom = mCurBottom;
+ vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
}
@@ -5374,11 +5103,11 @@
// TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
// Also, we don't allow windows in multi-window mode to extend out of the screen.
- if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR
+ if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
&& !win.isInMultiWindowMode()) {
df.left = df.top = -10000;
df.right = df.bottom = 10000;
- if (attrs.type != TYPE_WALLPAPER) {
+ if (type != TYPE_WALLPAPER) {
of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000;
of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
}
@@ -5394,7 +5123,7 @@
osf.set(cf.left, cf.top, cf.right, cf.bottom);
int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
if (outset > 0) {
- int rotation = mDisplayRotation;
+ int rotation = displayFrames.mRotation;
if (rotation == Surface.ROTATION_0) {
osf.bottom += outset;
} else if (rotation == Surface.ROTATION_90) {
@@ -5411,7 +5140,7 @@
if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
+ ": sim=#" + Integer.toHexString(sim)
- + " attach=" + attached + " type=" + attrs.type
+ + " attach=" + attached + " type=" + type
+ String.format(" flags=0x%08x", fl)
+ " pf=" + pf.toShortString() + " df=" + df.toShortString()
+ " of=" + of.toShortString()
@@ -5424,62 +5153,42 @@
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
- if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleLw()
+ if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
&& !win.getGivenInsetsPendingLw()) {
setLastInputMethodWindowLw(null, null);
- offsetInputMethodWindowLw(win);
+ offsetInputMethodWindowLw(win, displayFrames);
}
- if (attrs.type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
+ if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
&& !win.getGivenInsetsPendingLw()) {
- offsetVoiceInputWindowLw(win);
+ offsetVoiceInputWindowLw(win, displayFrames);
}
}
- private void layoutWallpaper(WindowState win, Rect pf, Rect df, Rect of, Rect cf) {
-
- // The wallpaper also has Real Ultimate Power, but we want to tell
- // it about the overscan area.
- pf.left = df.left = mOverscanScreenLeft;
- pf.top = df.top = mOverscanScreenTop;
- pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
- of.left = cf.left = mUnrestrictedScreenLeft;
- of.top = cf.top = mUnrestrictedScreenTop;
- of.right = cf.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- of.bottom = cf.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) {
+ // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area.
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
}
- private void offsetInputMethodWindowLw(WindowState win) {
+ private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
top += win.getGivenContentInsetsLw().top;
- if (mContentBottom > top) {
- mContentBottom = top;
- }
- if (mVoiceContentBottom > top) {
- mVoiceContentBottom = top;
- }
+ displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
+ displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
top = win.getVisibleFrameLw().top;
top += win.getGivenVisibleInsetsLw().top;
- if (mCurBottom > top) {
- mCurBottom = top;
- }
+ displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top);
if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom="
- + mDockBottom + " mContentBottom="
- + mContentBottom + " mCurBottom=" + mCurBottom);
+ + displayFrames.mDock.bottom + " mContentBottom="
+ + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom);
}
- private void offsetVoiceInputWindowLw(WindowState win) {
+ private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) {
int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
top += win.getGivenContentInsetsLw().top;
- if (mVoiceContentBottom > top) {
- mVoiceContentBottom = top;
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void finishLayoutLw() {
- return;
+ displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
}
/** {@inheritDoc} */
@@ -8332,11 +8041,6 @@
}
@Override
- public int getInputMethodWindowVisibleHeightLw() {
- return mDockBottom - mCurBottom;
- }
-
- @Override
public void setCurrentUserLw(int newUserId) {
mCurrentUserId = newUserId;
if (mKeyguardDelegate != null) {
@@ -8358,19 +8062,6 @@
}
@Override
- public boolean canMagnifyWindow(int windowType) {
- switch (windowType) {
- case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
- case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG:
- case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
- case WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY: {
- return false;
- }
- }
- return true;
- }
-
- @Override
public boolean isTopLevelWindow(int windowType) {
if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
&& windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
@@ -8425,8 +8116,6 @@
@Override
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- new Rect(mStableLeft, mStableTop, mStableRight, mStableBottom).writeToProto(proto,
- STABLE_BOUNDS);
proto.end(token);
}
@@ -8532,58 +8221,6 @@
pw.print(" mScreenOnFully="); pw.println(mScreenOnFully);
pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
- pw.print(prefix); pw.print("mOverscanScreen=("); pw.print(mOverscanScreenLeft);
- pw.print(","); pw.print(mOverscanScreenTop);
- pw.print(") "); pw.print(mOverscanScreenWidth);
- pw.print("x"); pw.println(mOverscanScreenHeight);
- if (mOverscanLeft != 0 || mOverscanTop != 0
- || mOverscanRight != 0 || mOverscanBottom != 0) {
- pw.print(prefix); pw.print("mOverscan left="); pw.print(mOverscanLeft);
- pw.print(" top="); pw.print(mOverscanTop);
- pw.print(" right="); pw.print(mOverscanRight);
- pw.print(" bottom="); pw.println(mOverscanBottom);
- }
- pw.print(prefix); pw.print("mRestrictedOverscanScreen=(");
- pw.print(mRestrictedOverscanScreenLeft);
- pw.print(","); pw.print(mRestrictedOverscanScreenTop);
- pw.print(") "); pw.print(mRestrictedOverscanScreenWidth);
- pw.print("x"); pw.println(mRestrictedOverscanScreenHeight);
- pw.print(prefix); pw.print("mUnrestrictedScreen=("); pw.print(mUnrestrictedScreenLeft);
- pw.print(","); pw.print(mUnrestrictedScreenTop);
- pw.print(") "); pw.print(mUnrestrictedScreenWidth);
- pw.print("x"); pw.println(mUnrestrictedScreenHeight);
- pw.print(prefix); pw.print("mRestrictedScreen=("); pw.print(mRestrictedScreenLeft);
- pw.print(","); pw.print(mRestrictedScreenTop);
- pw.print(") "); pw.print(mRestrictedScreenWidth);
- pw.print("x"); pw.println(mRestrictedScreenHeight);
- pw.print(prefix); pw.print("mStableFullscreen=("); pw.print(mStableFullscreenLeft);
- pw.print(","); pw.print(mStableFullscreenTop);
- pw.print(")-("); pw.print(mStableFullscreenRight);
- pw.print(","); pw.print(mStableFullscreenBottom); pw.println(")");
- pw.print(prefix); pw.print("mStable=("); pw.print(mStableLeft);
- pw.print(","); pw.print(mStableTop);
- pw.print(")-("); pw.print(mStableRight);
- pw.print(","); pw.print(mStableBottom); pw.println(")");
- pw.print(prefix); pw.print("mSystem=("); pw.print(mSystemLeft);
- pw.print(","); pw.print(mSystemTop);
- pw.print(")-("); pw.print(mSystemRight);
- pw.print(","); pw.print(mSystemBottom); pw.println(")");
- pw.print(prefix); pw.print("mCur=("); pw.print(mCurLeft);
- pw.print(","); pw.print(mCurTop);
- pw.print(")-("); pw.print(mCurRight);
- pw.print(","); pw.print(mCurBottom); pw.println(")");
- pw.print(prefix); pw.print("mContent=("); pw.print(mContentLeft);
- pw.print(","); pw.print(mContentTop);
- pw.print(")-("); pw.print(mContentRight);
- pw.print(","); pw.print(mContentBottom); pw.println(")");
- pw.print(prefix); pw.print("mVoiceContent=("); pw.print(mVoiceContentLeft);
- pw.print(","); pw.print(mVoiceContentTop);
- pw.print(")-("); pw.print(mVoiceContentRight);
- pw.print(","); pw.print(mVoiceContentBottom); pw.println(")");
- pw.print(prefix); pw.print("mDock=("); pw.print(mDockLeft);
- pw.print(","); pw.print(mDockTop);
- pw.print(")-("); pw.print(mDockRight);
- pw.print(","); pw.print(mDockBottom); pw.println(")");
pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer);
pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer);
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 0c73fe8..3992f8a 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -15,25 +15,36 @@
*/
package com.android.server.power;
-import android.annotation.IntDef;
import android.content.ContentResolver;
+import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
-import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
+import android.os.PowerSaveState;
import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.KeyValueListParser;
import android.util.Slog;
-import android.os.PowerSaveState;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* Class to decide whether to turn on battery saver mode for specific service
+ *
+ * TODO: We should probably make {@link #mFilesForInteractive} and {@link #mFilesForNoninteractive}
+ * less flexible and just take a list of "CPU number - frequency" pairs. Being able to write
+ * anything under /sys/ and /proc/ is too loose.
+ *
+ * Test: atest BatterySaverPolicyTest
*/
public class BatterySaverPolicy extends ContentObserver {
private static final String TAG = "BatterySaverPolicy";
@@ -56,11 +67,15 @@
private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
- private static final String KEY_FORCE_ALL_APPS_STANDBY_JOBS = "force_all_apps_standby_jobs";
- private static final String KEY_FORCE_ALL_APPS_STANDBY_ALARMS = "force_all_apps_standby_alarms";
+ private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
- private final KeyValueListParser mParser = new KeyValueListParser(',');
+ private static final String KEY_FILE_FOR_INTERACTIVE_PREFIX = "file-on:";
+ private static final String KEY_FILE_FOR_NONINTERACTIVE_PREFIX = "file-off:";
+
+ private static String mSettings;
+ private static String mDeviceSpecificSettings;
+ private static String mDeviceSpecificSettingsSource; // For dump() only.
/**
* {@code true} if vibration is disabled in battery saver mode.
@@ -145,69 +160,176 @@
private float mAdjustBrightnessFactor;
/**
- * Whether to put all apps in the stand-by mode or not for job scheduler.
+ * Whether to put all apps in the stand-by mode.
*/
- private boolean mForceAllAppsStandbyJobs;
-
- /**
- * Whether to put all apps in the stand-by mode or not for alarms.
- */
- private boolean mForceAllAppsStandbyAlarms;
+ private boolean mForceAllAppsStandby;
/**
* Weather to show non-essential sensors (e.g. edge sensors) or not.
*/
private boolean mOptionalSensorsDisabled;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private Context mContext;
+
+ @GuardedBy("mLock")
private ContentResolver mContentResolver;
+ @GuardedBy("mLock")
+ private final List<BatterySaverPolicyListener> mListeners = new ArrayList<>();
+
+ /**
+ * List of [Filename -> content] that should be written when battery saver is activated
+ * and the device is interactive.
+ *
+ * We use this to change the max CPU frequencies.
+ */
+ @GuardedBy("mLock")
+ private ArrayMap<String, String> mFilesForInteractive;
+
+ /**
+ * List of [Filename -> content] that should be written when battery saver is activated
+ * and the device is non-interactive.
+ *
+ * We use this to change the max CPU frequencies.
+ */
+ @GuardedBy("mLock")
+ private ArrayMap<String, String> mFilesForNoninteractive;
+
+ public interface BatterySaverPolicyListener {
+ void onBatterySaverPolicyChanged(BatterySaverPolicy policy);
+ }
+
public BatterySaverPolicy(Handler handler) {
super(handler);
}
- public void start(ContentResolver contentResolver) {
- mContentResolver = contentResolver;
+ public void systemReady(Context context) {
+ synchronized (mLock) {
+ mContext = context;
+ mContentResolver = context.getContentResolver();
- mContentResolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.BATTERY_SAVER_CONSTANTS), false, this);
+ mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.BATTERY_SAVER_CONSTANTS), false, this);
+ mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+ Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this);
+ }
onChange(true, null);
}
+ public void addListener(BatterySaverPolicyListener listener) {
+ synchronized (mLock) {
+ mListeners.add(listener);
+ }
+ }
+
+ @VisibleForTesting
+ String getGlobalSetting(String key) {
+ return Settings.Global.getString(mContentResolver, key);
+ }
+
+ @VisibleForTesting
+ int getDeviceSpecificConfigResId() {
+ return R.string.config_batterySaverDeviceSpecificConfig;
+ }
+
@Override
public void onChange(boolean selfChange, Uri uri) {
- final String value = Settings.Global.getString(mContentResolver,
- Settings.Global.BATTERY_SAVER_CONSTANTS);
- updateConstants(value);
+ final BatterySaverPolicyListener[] listeners;
+ synchronized (mLock) {
+ // Load the non-device-specific setting.
+ final String setting = getGlobalSetting(Settings.Global.BATTERY_SAVER_CONSTANTS);
+
+ // Load the device specific setting.
+ // We first check the global setting, and if it's empty or the string "null" is set,
+ // use the default value from config.xml.
+ String deviceSpecificSetting = getGlobalSetting(
+ Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS);
+ mDeviceSpecificSettingsSource =
+ Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS;
+
+ if (TextUtils.isEmpty(deviceSpecificSetting) || "null".equals(deviceSpecificSetting)) {
+ deviceSpecificSetting =
+ mContext.getString(getDeviceSpecificConfigResId());
+ mDeviceSpecificSettingsSource = "(overlay)";
+ }
+
+ // Update.
+ updateConstantsLocked(setting, deviceSpecificSetting);
+
+ listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]);
+ }
+
+ // Notify the listeners.
+ for (BatterySaverPolicyListener listener : listeners) {
+ listener.onBatterySaverPolicyChanged(this);
+ }
}
@VisibleForTesting
- void updateConstants(final String value) {
- synchronized (BatterySaverPolicy.this) {
- try {
- mParser.setString(value);
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Bad battery saver constants");
+ void updateConstantsLocked(final String setting, final String deviceSpecificSetting) {
+ mSettings = setting;
+ mDeviceSpecificSettings = deviceSpecificSetting;
+
+ final KeyValueListParser parser = new KeyValueListParser(',');
+
+ // Non-device-specific parameters.
+ try {
+ parser.setString(setting);
+ } catch (IllegalArgumentException e) {
+ Slog.wtf(TAG, "Bad battery saver constants: " + setting);
+ }
+
+ mVibrationDisabled = parser.getBoolean(KEY_VIBRATION_DISABLED, true);
+ mAnimationDisabled = parser.getBoolean(KEY_ANIMATION_DISABLED, true);
+ mSoundTriggerDisabled = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, true);
+ mFullBackupDeferred = parser.getBoolean(KEY_FULLBACKUP_DEFERRED, true);
+ mKeyValueBackupDeferred = parser.getBoolean(KEY_KEYVALUE_DEFERRED, true);
+ mFireWallDisabled = parser.getBoolean(KEY_FIREWALL_DISABLED, false);
+ mAdjustBrightnessDisabled = parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
+ mAdjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
+ mDataSaverDisabled = parser.getBoolean(KEY_DATASAVER_DISABLED, true);
+ mForceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY, true);
+ mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
+
+ // Get default value from Settings.Secure
+ final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
+ GPS_MODE_NO_CHANGE);
+ mGpsMode = parser.getInt(KEY_GPS_MODE, defaultGpsMode);
+
+ // Non-device-specific parameters.
+ try {
+ parser.setString(deviceSpecificSetting);
+ } catch (IllegalArgumentException e) {
+ Slog.wtf(TAG, "Bad device specific battery saver constants: "
+ + deviceSpecificSetting);
+ }
+
+ mFilesForInteractive = collectParams(parser, KEY_FILE_FOR_INTERACTIVE_PREFIX);
+ mFilesForNoninteractive = collectParams(parser, KEY_FILE_FOR_NONINTERACTIVE_PREFIX);
+ }
+
+ private static ArrayMap<String, String> collectParams(
+ KeyValueListParser parser, String prefix) {
+ final ArrayMap<String, String> ret = new ArrayMap<>();
+
+ for (int i = parser.size() - 1; i >= 0; i--) {
+ final String key = parser.keyAt(i);
+ if (!key.startsWith(prefix)) {
+ continue;
+ }
+ final String path = key.substring(prefix.length());
+
+ if (!(path.startsWith("/sys/") || path.startsWith("/proc/"))) {
+ Slog.wtf(TAG, "Invalid path: " + path);
+ continue;
}
- mVibrationDisabled = mParser.getBoolean(KEY_VIBRATION_DISABLED, true);
- mAnimationDisabled = mParser.getBoolean(KEY_ANIMATION_DISABLED, true);
- mSoundTriggerDisabled = mParser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, true);
- mFullBackupDeferred = mParser.getBoolean(KEY_FULLBACKUP_DEFERRED, true);
- mKeyValueBackupDeferred = mParser.getBoolean(KEY_KEYVALUE_DEFERRED, true);
- mFireWallDisabled = mParser.getBoolean(KEY_FIREWALL_DISABLED, false);
- mAdjustBrightnessDisabled = mParser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
- mAdjustBrightnessFactor = mParser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
- mDataSaverDisabled = mParser.getBoolean(KEY_DATASAVER_DISABLED, true);
- mForceAllAppsStandbyJobs = mParser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true);
- mForceAllAppsStandbyAlarms =
- mParser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true);
- mOptionalSensorsDisabled = mParser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
-
- // Get default value from Settings.Secure
- final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
- GPS_MODE_NO_CHANGE);
- mGpsMode = mParser.getInt(KEY_GPS_MODE, defaultGpsMode);
+ ret.put(path, parser.getString(key, ""));
}
+ return ret;
}
/**
@@ -220,7 +342,7 @@
* @return State data that contains battery saver data
*/
public PowerSaveState getBatterySaverPolicy(@ServiceType int type, boolean realMode) {
- synchronized (BatterySaverPolicy.this) {
+ synchronized (mLock) {
final PowerSaveState.Builder builder = new PowerSaveState.Builder()
.setGlobalBatterySaverEnabled(realMode);
if (!realMode) {
@@ -257,12 +379,6 @@
case ServiceType.VIBRATION:
return builder.setBatterySaverEnabled(mVibrationDisabled)
.build();
- case ServiceType.FORCE_ALL_APPS_STANDBY_JOBS:
- return builder.setBatterySaverEnabled(mForceAllAppsStandbyJobs)
- .build();
- case ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS:
- return builder.setBatterySaverEnabled(mForceAllAppsStandbyAlarms)
- .build();
case ServiceType.OPTIONAL_SENSORS:
return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
.build();
@@ -273,25 +389,56 @@
}
}
- public void dump(PrintWriter pw) {
- pw.println();
- pw.println("Battery saver policy");
- pw.println(" Settings " + Settings.Global.BATTERY_SAVER_CONSTANTS);
- pw.println(" value: " + Settings.Global.getString(mContentResolver,
- Settings.Global.BATTERY_SAVER_CONSTANTS));
+ public ArrayMap<String, String> getFileValues(boolean interactive) {
+ synchronized (mLock) {
+ return interactive ? mFilesForInteractive : mFilesForNoninteractive;
+ }
+ }
- pw.println();
- pw.println(" " + KEY_VIBRATION_DISABLED + "=" + mVibrationDisabled);
- pw.println(" " + KEY_ANIMATION_DISABLED + "=" + mAnimationDisabled);
- pw.println(" " + KEY_FULLBACKUP_DEFERRED + "=" + mFullBackupDeferred);
- pw.println(" " + KEY_KEYVALUE_DEFERRED + "=" + mKeyValueBackupDeferred);
- pw.println(" " + KEY_FIREWALL_DISABLED + "=" + mFireWallDisabled);
- pw.println(" " + KEY_DATASAVER_DISABLED + "=" + mDataSaverDisabled);
- pw.println(" " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
- pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
- pw.println(" " + KEY_GPS_MODE + "=" + mGpsMode);
- pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_JOBS + "=" + mForceAllAppsStandbyJobs);
- pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_ALARMS + "=" + mForceAllAppsStandbyAlarms);
- pw.println(" " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
+ public void dump(PrintWriter pw) {
+ synchronized (mLock) {
+ pw.println();
+ pw.println("Battery saver policy");
+ pw.println(" Settings " + Settings.Global.BATTERY_SAVER_CONSTANTS);
+ pw.println(" value: " + mSettings);
+ pw.println(" Settings " + mDeviceSpecificSettingsSource);
+ pw.println(" value: " + mDeviceSpecificSettings);
+
+ pw.println();
+ pw.println(" " + KEY_VIBRATION_DISABLED + "=" + mVibrationDisabled);
+ pw.println(" " + KEY_ANIMATION_DISABLED + "=" + mAnimationDisabled);
+ pw.println(" " + KEY_FULLBACKUP_DEFERRED + "=" + mFullBackupDeferred);
+ pw.println(" " + KEY_KEYVALUE_DEFERRED + "=" + mKeyValueBackupDeferred);
+ pw.println(" " + KEY_FIREWALL_DISABLED + "=" + mFireWallDisabled);
+ pw.println(" " + KEY_DATASAVER_DISABLED + "=" + mDataSaverDisabled);
+ pw.println(" " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
+ pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
+ pw.println(" " + KEY_GPS_MODE + "=" + mGpsMode);
+ pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY + "=" + mForceAllAppsStandby);
+ pw.println(" " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
+ pw.println();
+
+ pw.print(" Interactive File values:\n");
+ dumpMap(pw, " ", mFilesForInteractive);
+ pw.println();
+
+ pw.print(" Noninteractive File values:\n");
+ dumpMap(pw, " ", mFilesForNoninteractive);
+ pw.println();
+ }
+ }
+
+ private void dumpMap(PrintWriter pw, String prefix, ArrayMap<String, String> map) {
+ if (map == null) {
+ return;
+ }
+ final int size = map.size();
+ for (int i = 0; i < size; i++) {
+ pw.print(prefix);
+ pw.print(map.keyAt(i));
+ pw.print(": '");
+ pw.print(map.valueAt(i));
+ pw.println("'");
+ }
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a153fdf..584761c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -63,7 +63,6 @@
import android.service.vr.IVrStateCallbacks;
import android.util.EventLog;
import android.util.KeyValueListParser;
-import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -71,7 +70,6 @@
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.WindowManagerPolicy;
-import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
@@ -92,10 +90,8 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
+import com.android.server.power.batterysaver.BatterySaverController;
+
import libcore.util.Objects;
import java.io.FileDescriptor;
@@ -228,6 +224,7 @@
private final PowerManagerHandler mHandler;
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private final BatterySaverPolicy mBatterySaverPolicy;
+ private final BatterySaverController mBatterySaverController;
private LightsManager mLightsManager;
private BatteryManagerInternal mBatteryManagerInternal;
@@ -555,9 +552,6 @@
// True if double tap to wake is enabled
private boolean mDoubleTapWakeEnabled;
- private final ArrayList<PowerManagerInternal.LowPowerModeListener> mLowPowerModeListeners
- = new ArrayList<PowerManagerInternal.LowPowerModeListener>();
-
// True if we are currently in VR Mode.
private boolean mIsVrModeEnabled;
@@ -645,7 +639,10 @@
mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
mConstants = new Constants(mHandler);
mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
+
mBatterySaverPolicy = new BatterySaverPolicy(mHandler);
+ mBatterySaverController = new BatterySaverController(mContext,
+ BackgroundThread.get().getLooper(), mBatterySaverPolicy);
synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
@@ -670,7 +667,6 @@
PowerManagerService(Context context, BatterySaverPolicy batterySaverPolicy) {
super(context);
- mBatterySaverPolicy = batterySaverPolicy;
mContext = context;
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
@@ -680,6 +676,10 @@
mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
mDisplaySuspendBlocker = null;
mWakeLockSuspendBlocker = null;
+
+ mBatterySaverPolicy = batterySaverPolicy;
+ mBatterySaverController = new BatterySaverController(context,
+ BackgroundThread.getHandler().getLooper(), batterySaverPolicy);
}
@Override
@@ -752,6 +752,7 @@
mDisplayManagerInternal.initPowerManagement(
mDisplayPowerCallbacks, mHandler, sensorManager);
+
// Go.
readConfigurationLocked();
updateSettingsLocked();
@@ -761,7 +762,9 @@
final ContentResolver resolver = mContext.getContentResolver();
mConstants.start(resolver);
- mBatterySaverPolicy.start(resolver);
+
+ mBatterySaverController.systemReady();
+ mBatterySaverPolicy.systemReady(mContext);
// Register for settings changes.
resolver.registerContentObserver(Settings.Secure.getUriFor(
@@ -996,43 +999,9 @@
if (mLowPowerModeEnabled != lowPowerModeEnabled) {
mLowPowerModeEnabled = lowPowerModeEnabled;
- powerHintInternal(PowerHint.LOW_POWER, lowPowerModeEnabled ? 1 : 0);
- postAfterBootCompleted(new Runnable() {
- @Override
- public void run() {
- Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
- .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, mLowPowerModeEnabled)
- .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcast(intent);
- ArrayList<PowerManagerInternal.LowPowerModeListener> listeners;
- synchronized (mLock) {
- listeners = new ArrayList<PowerManagerInternal.LowPowerModeListener>(
- mLowPowerModeListeners);
- }
- for (int i = 0; i < listeners.size(); i++) {
- final PowerManagerInternal.LowPowerModeListener listener = listeners.get(i);
- final PowerSaveState result =
- mBatterySaverPolicy.getBatterySaverPolicy(
- listener.getServiceType(), lowPowerModeEnabled);
- listener.onLowPowerModeChanged(result);
- }
- intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- // Send internal version that requires signature permission.
- intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
- Manifest.permission.DEVICE_POWER);
- // STOPSHIP Remove the toast.
- if (mLowPowerModeEnabled) {
- Toast.makeText(mContext,
- com.android.internal.R.string.battery_saver_warning,
- Toast.LENGTH_LONG).show();
- }
- }
- });
+ postAfterBootCompleted(() ->
+ mBatterySaverController.enableBatterySaver(mLowPowerModeEnabled));
}
}
@@ -3136,7 +3105,7 @@
mIsVrModeEnabled = enabled;
}
- private void powerHintInternal(int hintId, int data) {
+ private static void powerHintInternal(int hintId, int data) {
nativeSendPowerHint(hintId, data);
}
@@ -4405,7 +4374,7 @@
* Gets the reason for the last time the phone had to reboot.
*
* @return The reason the phone last shut down as an int or
- * {@link PowerManager.SHUTDOWN_REASON_UNKNOWN} if the file could not be opened.
+ * {@link PowerManager#SHUTDOWN_REASON_UNKNOWN} if the file could not be opened.
*/
@Override // Binder call
public int getLastShutdownReason() {
@@ -4728,9 +4697,7 @@
@Override
public void registerLowPowerModeObserver(LowPowerModeListener listener) {
- synchronized (mLock) {
- mLowPowerModeListeners.add(listener);
- }
+ mBatterySaverController.addListener(listener);
}
@Override
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 755c5f0..6bf725e 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -68,7 +68,8 @@
public final class ShutdownThread extends Thread {
// constants
private static final String TAG = "ShutdownThread";
- private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
+ private static final int ACTION_DONE_POLL_WAIT_MS = 500;
+ private static final int RADIOS_STATE_POLL_SLEEP_MS = 100;
// maximum time we wait for the shutdown broadcast before going on.
private static final int MAX_BROADCAST_TIME = 10*1000;
private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
@@ -471,7 +472,7 @@
sInstance.setRebootProgress(status, null);
}
try {
- mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
+ mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
} catch (InterruptedException e) {
}
}
@@ -565,7 +566,7 @@
sInstance.setRebootProgress(status, null);
}
try {
- mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
+ mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
} catch (InterruptedException e) {
}
}
@@ -710,8 +711,7 @@
done[0] = true;
break;
}
- SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
-
+ SystemClock.sleep(RADIOS_STATE_POLL_SLEEP_MS);
delay = endTime - SystemClock.elapsedRealtime();
}
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
new file mode 100644
index 0000000..3db6a25
--- /dev/null
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.batterysaver;
+
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.PowerManagerInternal.LowPowerModeListener;
+import android.os.PowerSaveState;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.widget.Toast;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.power.BatterySaverPolicy;
+import com.android.server.power.BatterySaverPolicy.BatterySaverPolicyListener;
+import com.android.server.power.PowerManagerService;
+
+import java.util.ArrayList;
+
+/**
+ * Responsible for battery saver mode transition logic.
+ */
+public class BatterySaverController implements BatterySaverPolicyListener {
+ static final String TAG = "BatterySaverController";
+
+ static final boolean DEBUG = false; // DO NOT MERGE WITH TRUE
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final MyHandler mHandler;
+ private final FileUpdater mFileUpdater;
+
+ private PowerManager mPowerManager;
+
+ private final BatterySaverPolicy mBatterySaverPolicy;
+
+ @GuardedBy("mLock")
+ private final ArrayList<LowPowerModeListener> mListeners = new ArrayList<>();
+
+ @GuardedBy("mLock")
+ private boolean mEnabled;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_SCREEN_ON:
+ case Intent.ACTION_SCREEN_OFF:
+ if (!isEnabled()) {
+ return; // No need to send it if not enabled.
+ }
+ // Don't send the broadcast, because we never did so in this case.
+ mHandler.postStateChanged(/*sendBroadcast=*/ false);
+ break;
+ }
+ }
+ };
+
+ /**
+ * Constructor.
+ */
+ public BatterySaverController(Context context, Looper looper, BatterySaverPolicy policy) {
+ mContext = context;
+ mHandler = new MyHandler(looper);
+ mBatterySaverPolicy = policy;
+ mBatterySaverPolicy.addListener(this);
+ mFileUpdater = new FileUpdater(context);
+ }
+
+ /**
+ * Add a listener.
+ */
+ public void addListener(LowPowerModeListener listener) {
+ synchronized (mLock) {
+ mListeners.add(listener);
+ }
+ }
+
+ /**
+ * Called by {@link PowerManagerService} on system ready..
+ */
+ public void systemReady() {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private PowerManager getPowerManager() {
+ if (mPowerManager == null) {
+ mPowerManager =
+ Preconditions.checkNotNull(mContext.getSystemService(PowerManager.class));
+ }
+ return mPowerManager;
+ }
+
+ @Override
+ public void onBatterySaverPolicyChanged(BatterySaverPolicy policy) {
+ if (!isEnabled()) {
+ return; // No need to send it if not enabled.
+ }
+ mHandler.postStateChanged(/*sendBroadcast=*/ true);
+ }
+
+ private class MyHandler extends Handler {
+ private static final int MSG_STATE_CHANGED = 1;
+
+ private static final int ARG_DONT_SEND_BROADCAST = 0;
+ private static final int ARG_SEND_BROADCAST = 1;
+
+ public MyHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void postStateChanged(boolean sendBroadcast) {
+ obtainMessage(MSG_STATE_CHANGED, sendBroadcast ?
+ ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, 0).sendToTarget();
+ }
+
+ @Override
+ public void dispatchMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_STATE_CHANGED:
+ handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Called by {@link PowerManagerService} to update the battery saver stete.
+ */
+ public void enableBatterySaver(boolean enable) {
+ synchronized (mLock) {
+ if (mEnabled == enable) {
+ return;
+ }
+ mEnabled = enable;
+
+ mHandler.postStateChanged(/*sendBroadcast=*/ true);
+ }
+ }
+
+ /** @return whether battery saver is enabled or not. */
+ boolean isEnabled() {
+ synchronized (mLock) {
+ return mEnabled;
+ }
+ }
+
+ /**
+ * Dispatch power save events to the listeners.
+ *
+ * This method is always called on the handler thread.
+ *
+ * This method is called only in the following cases:
+ * - When battery saver becomes activated.
+ * - When battery saver becomes deactivated.
+ * - When battery saver is on the interactive state changes.
+ * - When battery saver is on the battery saver policy changes.
+ */
+ void handleBatterySaverStateChanged(boolean sendBroadcast) {
+ final LowPowerModeListener[] listeners;
+
+ final boolean enabled;
+ final boolean isInteractive = getPowerManager().isInteractive();
+ final ArrayMap<String, String> fileValues;
+
+ synchronized (mLock) {
+ Slog.i(TAG, "Battery saver " + (mEnabled ? "enabled" : "disabled")
+ + ": isInteractive=" + isInteractive);
+
+ listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
+ enabled = mEnabled;
+
+ if (enabled) {
+ fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
+ } else {
+ fileValues = null;
+ }
+ }
+
+ final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
+ if (pmi != null) {
+ pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0);
+ }
+
+ if (enabled) {
+ // STOPSHIP Remove the toast.
+ Toast.makeText(mContext,
+ com.android.internal.R.string.battery_saver_warning,
+ Toast.LENGTH_LONG).show();
+ }
+
+ if (ArrayUtils.isEmpty(fileValues)) {
+ mFileUpdater.restoreDefault();
+ } else {
+ mFileUpdater.writeFiles(fileValues);
+ }
+
+ if (sendBroadcast) {
+ if (DEBUG) {
+ Slog.i(TAG, "Sending broadcasts for mode: " + enabled);
+ }
+
+ // Send the broadcasts and notify the listeners. We only do this when the battery saver
+ // mode changes, but not when only the screen state changes.
+ Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
+ .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, enabled)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcast(intent);
+
+ intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+
+ // Send internal version that requires signature permission.
+ intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ Manifest.permission.DEVICE_POWER);
+
+
+ for (LowPowerModeListener listener : listeners) {
+ final PowerSaveState result =
+ mBatterySaverPolicy.getBatterySaverPolicy(
+ listener.getServiceType(), enabled);
+ listener.onLowPowerModeChanged(result);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
new file mode 100644
index 0000000..cfe8fc4
--- /dev/null
+++ b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.batterysaver;
+
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+/**
+ * Used by {@link BatterySaverController} to write values to /sys/ (and possibly /proc/ too) files
+ * with retry and to restore the original values.
+ *
+ * TODO Implement it
+ */
+public class FileUpdater {
+ private static final String TAG = BatterySaverController.TAG;
+
+ private static final boolean DEBUG = BatterySaverController.DEBUG;
+
+ private final Object mLock = new Object();
+ private final Context mContext;
+
+ public FileUpdater(Context context) {
+ mContext = context;
+ }
+
+ public void writeFiles(ArrayMap<String, String> fileValues) {
+ if (DEBUG) {
+ final int size = fileValues.size();
+ for (int i = 0; i < size; i++) {
+ Slog.d(TAG, "Writing '" + fileValues.valueAt(i)
+ + "' to '" + fileValues.keyAt(i) + "'");
+ }
+ }
+ }
+
+ public void restoreDefault() {
+ if (DEBUG) {
+ Slog.d(TAG, "Resetting file default values");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 2488723..dafab77 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -44,6 +44,8 @@
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
+import com.android.internal.os.KernelCpuSpeedReader;
+import com.android.internal.os.PowerProfile;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -60,6 +62,8 @@
public class StatsCompanionService extends IStatsCompanionService.Stub {
static final String TAG = "StatsCompanionService";
static final boolean DEBUG = true;
+ public static final String ACTION_TRIGGER_COLLECTION =
+ "com.android.server.stats.action.TRIGGER_COLLECTION";
private final Context mContext;
private final AlarmManager mAlarmManager;
@@ -71,6 +75,9 @@
private final PendingIntent mPullingAlarmIntent;
private final BroadcastReceiver mAppUpdateReceiver;
private final BroadcastReceiver mUserUpdateReceiver;
+ private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+ private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+ private final KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
public StatsCompanionService(Context context) {
super();
@@ -103,6 +110,22 @@
}
};
Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
+ PowerProfile powerProfile = new PowerProfile(context);
+ final int numClusters = powerProfile.getNumCpuClusters();
+ mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
+ int firstCpuOfCluster = 0;
+ for (int i = 0; i < numClusters; i++) {
+ final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
+ mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
+ numSpeedSteps);
+ firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
+ }
+ }
+
+ @Override
+ public void sendBroadcast(String pkg, String cls) {
+ mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls),
+ UserHandle.SYSTEM);
}
private final static int[] toIntArray(List<Integer> list) {
@@ -159,7 +182,7 @@
Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
synchronized (sStatsdLock) {
if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
+ Slog.w(TAG, "Could not access statsd to inform it of an app update");
return;
}
try {
@@ -286,9 +309,6 @@
}
}
- private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
- private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
-
private StatsLogEventWrapper[] addNetworkStats(int tag, NetworkStats stats, boolean withFGBG) {
List<StatsLogEventWrapper> ret = new ArrayList<>();
int size = stats.size();
@@ -446,6 +466,22 @@
}
return ret.toArray(new StatsLogEventWrapper[ret.size()]);
}
+ case StatsLog.CPU_TIME_PER_FREQ_PULLED: {
+ List<StatsLogEventWrapper> ret = new ArrayList();
+ for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
+ long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readDelta();
+ if (clusterTimeMs != null) {
+ for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+ e.writeInt(tagId);
+ e.writeInt(speed);
+ e.writeLong(clusterTimeMs[speed]);
+ ret.add(e);
+ }
+ }
+ }
+ return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index e8ebbe4..5493207 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -756,7 +756,7 @@
mBootsToVr = SystemProperties.getBoolean("ro.boot.vr", false);
mUseStandbyToExitVrMode = mBootsToVr
- && SystemProperties.getBoolean("persist.vr.use_standby_to_exit_vr_mode", false);
+ && SystemProperties.getBoolean("persist.vr.use_standby_to_exit_vr_mode", true);
publishLocalService(VrManagerInternal.class, new LocalService());
publishBinderService(Context.VR_SERVICE, mVrManager.asBinder());
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index de4fd7cd..88d1e55 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -292,6 +292,8 @@
public void setMagnificationSpecLocked(MagnificationSpec spec) {
mMagnifedViewport.updateMagnificationSpecLocked(spec);
mMagnifedViewport.recomputeBoundsLocked();
+
+ mService.applyMagnificationSpec(spec);
mService.scheduleAnimationLocked();
}
@@ -421,7 +423,7 @@
public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
if (spec != null && !spec.isNop()) {
- if (!mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+ if (!windowState.shouldMagnify()) {
return null;
}
}
@@ -476,6 +478,7 @@
private final ViewportWindow mWindow;
private boolean mFullRedrawNeeded;
+ private int mTempLayer = 0;
public MagnifiedViewport() {
mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
@@ -565,7 +568,7 @@
portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
- if (mService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+ if (windowState.shouldMagnify()) {
mMagnificationRegion.op(windowBounds, Region.Op.UNION);
mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
} else {
@@ -676,10 +679,12 @@
private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+ mTempLayer = 0;
dc.forAllWindows((w) -> {
if (w.isOnScreen() && w.isVisibleLw()
&& !w.mWinAnimator.mEnterAnimationPending) {
- outWindows.put(w.mLayer, w);
+ mTempLayer++;
+ outWindows.put(mTempLayer, w);
}
}, false /* traverseTopToBottom */ );
}
@@ -705,7 +710,7 @@
SurfaceControl surfaceControl = null;
try {
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+ surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
.setName(SURFACE_TITLE)
.setSize(mTempPoint.x, mTempPoint.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
@@ -714,8 +719,6 @@
/* ignore */
}
mSurfaceControl = surfaceControl;
- mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
- .getLayerStack());
mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
TYPE_MAGNIFICATION_OVERLAY)
* WindowManagerService.TYPE_LAYER_MULTIPLIER);
@@ -1005,6 +1008,8 @@
private final long mRecurringAccessibilityEventsIntervalMillis;
+ private int mTempLayer = 0;
+
public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
WindowsForAccessibilityCallback callback) {
mContext = windowManagerService.mContext;
@@ -1090,6 +1095,7 @@
if (isReportedWindowType(windowState.mAttrs.type)) {
// Add the window to the ones to be reported.
WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
+ window.layer = addedWindows.size();
addedWindows.add(window.token);
windows.add(window);
if (windowState.isFocused()) {
@@ -1323,9 +1329,10 @@
private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
final DisplayContent dc = mService.getDefaultDisplayContentLocked();
+ mTempLayer = 0;
dc.forAllWindows((w) -> {
if (w.isVisibleLw()) {
- outWindows.put(w.mLayer, w);
+ outWindows.put(mTempLayer++, w);
}
}, false /* traverseTopToBottom */ );
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 2ef7f25..accfc65 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -253,7 +253,6 @@
private void updateLayers() {
mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
- updateThumbnailLayer();
}
private void stepThumbnailAnimation(long currentTime) {
@@ -283,27 +282,12 @@
+ "][" + tmpFloats[Matrix.MSKEW_X]
+ "," + tmpFloats[Matrix.MSCALE_Y] + "]");
thumbnail.setAlpha(thumbnailTransformation.getAlpha());
- updateThumbnailLayer();
thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
}
/**
- * Updates the thumbnail layer z order to just above the highest animation layer if changed
- */
- void updateThumbnailLayer() {
- if (thumbnail != null) {
- final int layer = mAppToken.getHighestAnimLayer();
- if (DEBUG_LAYERS) Slog.v(TAG,
- "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
- thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
- - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
- mThumbnailLayer = layer;
- }
- }
-
- /**
* Sometimes we need to synchronize the first frame of animation with some external event, e.g.
* Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
* and keep producing the first frame of the animation.
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 9729e50..f19cd0f 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -30,7 +30,6 @@
import android.util.Slog;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
/**
* Four black surfaces put together to make a black frame.
@@ -42,22 +41,22 @@
final int layer;
final SurfaceControl surface;
- BlackSurface(SurfaceSession session, int layer, int l, int t, int r, int b, int layerStack)
- throws OutOfResourcesException {
+ BlackSurface(int layer,
+ int l, int t, int r, int b, DisplayContent dc) throws OutOfResourcesException {
left = l;
top = t;
this.layer = layer;
int w = r-l;
int h = b-t;
- surface = new SurfaceControl.Builder(session)
+ surface = dc.makeOverlay()
.setName("BlackSurface")
.setSize(w, h)
.setColorLayer(true)
+ .setParent(null) // TODO: Work-around for b/69259549
.build();
surface.setAlpha(1);
- surface.setLayerStack(layerStack);
surface.setLayer(layer);
surface.show();
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
@@ -114,30 +113,32 @@
}
}
- public BlackFrame(SurfaceSession session, Rect outer, Rect inner, int layer, int layerStack,
+ public BlackFrame(Rect outer, Rect inner, int layer, DisplayContent dc,
boolean forceDefaultOrientation) throws OutOfResourcesException {
boolean success = false;
mForceDefaultOrientation = forceDefaultOrientation;
+ // TODO: Why do we use 4 surfaces instead of just one big one behind the screenshot?
+ // b/68253229
mOuterRect = new Rect(outer);
mInnerRect = new Rect(inner);
try {
if (outer.top < inner.top) {
- mBlackSurfaces[0] = new BlackSurface(session, layer,
- outer.left, outer.top, inner.right, inner.top, layerStack);
+ mBlackSurfaces[0] = new BlackSurface(layer,
+ outer.left, outer.top, inner.right, inner.top, dc);
}
if (outer.left < inner.left) {
- mBlackSurfaces[1] = new BlackSurface(session, layer,
- outer.left, inner.top, inner.left, outer.bottom, layerStack);
+ mBlackSurfaces[1] = new BlackSurface(layer,
+ outer.left, inner.top, inner.left, outer.bottom, dc);
}
if (outer.bottom > inner.bottom) {
- mBlackSurfaces[2] = new BlackSurface(session, layer,
- inner.left, inner.bottom, outer.right, outer.bottom, layerStack);
+ mBlackSurfaces[2] = new BlackSurface(layer,
+ inner.left, inner.bottom, outer.right, outer.bottom, dc);
}
if (outer.right > inner.right) {
- mBlackSurfaces[3] = new BlackSurface(session, layer,
- inner.right, outer.top, outer.right, inner.bottom, layerStack);
+ mBlackSurfaces[3] = new BlackSurface(layer,
+ inner.right, outer.top, outer.right, inner.bottom, dc);
}
success = true;
} finally {
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 2d5d1b2..2a216ab 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -33,7 +33,6 @@
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
class CircularDisplayMask {
private static final String TAG = TAG_WITH_CLASS_NAME ? "CircularDisplayMask" : TAG_WM;
@@ -54,8 +53,10 @@
private boolean mDimensionsUnequal = false;
private int mMaskThickness;
- public CircularDisplayMask(Display display, SurfaceSession session, int zOrder,
+ public CircularDisplayMask(DisplayContent dc, int zOrder,
int screenOffset, int maskThickness) {
+ final Display display = dc.getDisplay();
+
mScreenSize = new Point();
display.getSize(mScreenSize);
if (mScreenSize.x != mScreenSize.y + screenOffset) {
@@ -66,7 +67,7 @@
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("CircularDisplayMask")
.setSize(mScreenSize.x, mScreenSize.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index cc94807..b534b8a 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -375,6 +375,10 @@
return toString();
}
+ boolean isAlwaysOnTop() {
+ return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
+ }
+
abstract protected int getChildCount();
abstract protected E getChildAt(int index);
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
deleted file mode 100644
index 8fb2be8..0000000
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Copyright (C) 2014 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.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.view.DisplayInfo;
-import android.view.SurfaceControl;
-
-import java.io.PrintWriter;
-
-public class DimLayer {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayer" : TAG_WM;
- private final WindowManagerService mService;
-
- /** Actual surface that dims */
- private SurfaceControl mDimSurface;
-
- /** Last value passed to mDimSurface.setAlpha() */
- private float mAlpha = 0;
-
- /** Last value passed to mDimSurface.setLayer() */
- private int mLayer = -1;
-
- /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
- private final Rect mBounds = new Rect();
-
- /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
- private final Rect mLastBounds = new Rect();
-
- /** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
- private boolean mShowing = false;
-
- /** Value of mAlpha when beginning transition to mTargetAlpha */
- private float mStartAlpha = 0;
-
- /** Final value of mAlpha following transition */
- private float mTargetAlpha = 0;
-
- /** Time in units of SystemClock.uptimeMillis() at which the current transition started */
- private long mStartTime;
-
- /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */
- private long mDuration;
-
- private boolean mDestroyed = false;
-
- private final int mDisplayId;
-
-
- /** Interface implemented by users of the dim layer */
- interface DimLayerUser {
- /** Returns true if the dim should be fullscreen. */
- boolean dimFullscreen();
- /** Returns the display info. of the dim layer user. */
- DisplayInfo getDisplayInfo();
- /** Returns true if the dim layer user is currently attached to a display */
- boolean isAttachedToDisplay();
- /** Gets the bounds of the dim layer user. */
- void getDimBounds(Rect outBounds);
- /** Returns the layer to place a dim layer. */
- default int getLayerForDim(WindowStateAnimator animator, int layerOffset,
- int defaultLayer) {
- return defaultLayer;
- }
-
- String toShortString();
- }
- /** The user of this dim layer. */
- private final DimLayerUser mUser;
-
- private final String mName;
-
- DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
- mUser = user;
- mDisplayId = displayId;
- mService = service;
- mName = name;
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
- }
-
- private void constructSurface(WindowManagerService service) {
- service.openSurfaceTransaction();
- try {
- mDimSurface = new SurfaceControl.Builder(service.mFxSession)
- .setName(mName)
- .setSize(16, 16)
- .setColorLayer(true)
- .build();
-
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
- " DIM " + mDimSurface + ": CREATE");
- mDimSurface.setLayerStack(mDisplayId);
- adjustBounds();
- adjustAlpha(mAlpha);
- adjustLayer(mLayer);
- } catch (Exception e) {
- Slog.e(TAG_WM, "Exception creating Dim surface", e);
- } finally {
- service.closeSurfaceTransaction("DimLayer.constructSurface");
- }
- }
-
- /** Return true if dim layer is showing */
- boolean isDimming() {
- return mTargetAlpha != 0;
- }
-
- /** Return true if in a transition period */
- boolean isAnimating() {
- return mTargetAlpha != mAlpha;
- }
-
- float getTargetAlpha() {
- return mTargetAlpha;
- }
-
- void setLayer(int layer) {
- if (mLayer == layer) {
- return;
- }
- mLayer = layer;
- adjustLayer(layer);
- }
-
- private void adjustLayer(int layer) {
- if (mDimSurface != null) {
- mDimSurface.setLayer(layer);
- }
- }
-
- int getLayer() {
- return mLayer;
- }
-
- private void setAlpha(float alpha) {
- if (mAlpha == alpha) {
- return;
- }
- mAlpha = alpha;
- adjustAlpha(alpha);
- }
-
- private void adjustAlpha(float alpha) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha alpha=" + alpha);
- try {
- if (mDimSurface != null) {
- mDimSurface.setAlpha(alpha);
- }
- if (alpha == 0 && mShowing) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha hiding");
- if (mDimSurface != null) {
- mDimSurface.hide();
- mShowing = false;
- }
- } else if (alpha > 0 && !mShowing) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha showing");
- if (mDimSurface != null) {
- mDimSurface.show();
- mShowing = true;
- }
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure setting alpha immediately", e);
- }
- }
-
- /**
- * NOTE: Must be called with Surface transaction open.
- */
- private void adjustBounds() {
- if (mUser.dimFullscreen()) {
- getBoundsForFullscreen(mBounds);
- }
-
- if (mDimSurface != null) {
- mDimSurface.setPosition(mBounds.left, mBounds.top);
- mDimSurface.setSize(mBounds.width(), mBounds.height());
- if (DEBUG_DIM_LAYER) Slog.v(TAG,
- "adjustBounds user=" + mUser.toShortString() + " mBounds=" + mBounds);
- }
-
- mLastBounds.set(mBounds);
- }
-
- private void getBoundsForFullscreen(Rect outBounds) {
- final int dw, dh;
- final float xPos, yPos;
- // Set surface size to screen size.
- final DisplayInfo info = mUser.getDisplayInfo();
- // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
- // a corner.
- dw = (int) (info.logicalWidth * 1.5);
- dh = (int) (info.logicalHeight * 1.5);
- // back off position so 1/4 of Surface is before and 1/4 is after.
- xPos = -1 * dw / 6;
- yPos = -1 * dh / 6;
- outBounds.set((int) xPos, (int) yPos, (int) xPos + dw, (int) yPos + dh);
- }
-
- void setBoundsForFullscreen() {
- getBoundsForFullscreen(mBounds);
- setBounds(mBounds);
- }
-
- /** @param bounds The new bounds to set */
- void setBounds(Rect bounds) {
- mBounds.set(bounds);
- if (isDimming() && !mLastBounds.equals(bounds)) {
- try {
- mService.openSurfaceTransaction();
- adjustBounds();
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure setting size", e);
- } finally {
- mService.closeSurfaceTransaction("DimLayer.setBounds");
- }
- }
- }
-
- /**
- * @param duration The time to test.
- * @return True if the duration would lead to an earlier end to the current animation.
- */
- private boolean durationEndsEarlier(long duration) {
- return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
- }
-
- /** Jump to the end of the animation.
- * NOTE: Must be called with Surface transaction open. */
- void show() {
- if (isAnimating()) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: immediate");
- show(mLayer, mTargetAlpha, 0);
- }
- }
-
- /**
- * Begin an animation to a new dim value.
- * NOTE: Must be called with Surface transaction open.
- *
- * @param layer The layer to set the surface to.
- * @param alpha The dim value to end at.
- * @param duration How long to take to get there in milliseconds.
- */
- void show(int layer, float alpha, long duration) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
- + " duration=" + duration + ", mDestroyed=" + mDestroyed);
- if (mDestroyed) {
- Slog.e(TAG, "show: no Surface");
- // Make sure isAnimating() returns false.
- mTargetAlpha = mAlpha = 0;
- return;
- }
-
- if (mDimSurface == null) {
- constructSurface(mService);
- }
-
- if (!mLastBounds.equals(mBounds)) {
- adjustBounds();
- }
- setLayer(layer);
-
- long curTime = SystemClock.uptimeMillis();
- final boolean animating = isAnimating();
- if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
- || (!animating && mAlpha != alpha)) {
- if (duration <= 0) {
- // No animation required, just set values.
- setAlpha(alpha);
- } else {
- // Start or continue animation with new parameters.
- mStartAlpha = mAlpha;
- mStartTime = curTime;
- mDuration = duration;
- }
- }
- mTargetAlpha = alpha;
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime="
- + mStartTime + " mTargetAlpha=" + mTargetAlpha);
- }
-
- /** Immediate hide.
- * NOTE: Must be called with Surface transaction open. */
- void hide() {
- if (mShowing) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: immediate");
- hide(0);
- }
- }
-
- /**
- * Gradually fade to transparent.
- * NOTE: Must be called with Surface transaction open.
- *
- * @param duration Time to fade in milliseconds.
- */
- void hide(long duration) {
- if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: duration=" + duration);
- show(mLayer, 0, duration);
- }
- }
-
- /**
- * Advance the dimming per the last #show(int, float, long) call.
- * NOTE: Must be called with Surface transaction open.
- *
- * @return True if animation is still required after this step.
- */
- boolean stepAnimation() {
- if (mDestroyed) {
- Slog.e(TAG, "stepAnimation: surface destroyed");
- // Ensure that isAnimating() returns false;
- mTargetAlpha = mAlpha = 0;
- return false;
- }
- if (isAnimating()) {
- final long curTime = SystemClock.uptimeMillis();
- final float alphaDelta = mTargetAlpha - mStartAlpha;
- float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
- if (alphaDelta > 0 && alpha > mTargetAlpha ||
- alphaDelta < 0 && alpha < mTargetAlpha) {
- // Don't exceed limits.
- alpha = mTargetAlpha;
- }
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
- setAlpha(alpha);
- }
-
- return isAnimating();
- }
-
- /** Cleanup */
- void destroySurface() {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "destroySurface.");
- if (mDimSurface != null) {
- mDimSurface.destroy();
- mDimSurface = null;
- }
- mDestroyed = true;
- }
-
- public void printTo(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
- pw.print(" mLayer="); pw.print(mLayer);
- pw.print(" mAlpha="); pw.println(mAlpha);
- pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
- pw.print(" mBounds="); pw.println(mBounds.toShortString());
- pw.print(prefix); pw.print("Last animation: ");
- pw.print(" mDuration="); pw.print(mDuration);
- pw.print(" mStartTime="); pw.print(mStartTime);
- pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
- pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
- pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
- }
-}
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
deleted file mode 100644
index 6f9e45a..0000000
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ /dev/null
@@ -1,403 +0,0 @@
-package com.android.server.wm;
-
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
-
-import android.graphics.Rect;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.util.TypedValue;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.DimLayer.DimLayerUser;
-
-import java.io.PrintWriter;
-
-/**
- * Centralizes the control of dim layers used for
- * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
- * as well as other use cases (such as dimming above a dead window).
- */
-class DimLayerController {
- private static final String TAG_LOCAL = "DimLayerController";
- private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
-
- /** Amount of time in milliseconds to animate the dim surface from one value to another,
- * when no window animation is driving it. */
- private static final int DEFAULT_DIM_DURATION = 200;
-
- /**
- * The default amount of dim applied over a dead window
- */
- private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
-
- // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this
- // instead of creating a new object per fullscreen task on a display.
- private DimLayer mSharedFullScreenDimLayer;
-
- private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
-
- private DisplayContent mDisplayContent;
-
- private Rect mTmpBounds = new Rect();
-
- DimLayerController(DisplayContent displayContent) {
- mDisplayContent = displayContent;
- }
-
- /** Updates the dim layer bounds, recreating it if needed. */
- void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
- final DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
- final boolean previousFullscreen = state.dimLayer != null
- && state.dimLayer == mSharedFullScreenDimLayer;
- DimLayer newDimLayer;
- final int displayId = mDisplayContent.getDisplayId();
- if (dimLayerUser.dimFullscreen()) {
- if (previousFullscreen && mSharedFullScreenDimLayer != null) {
- // Update the bounds for fullscreen in case of rotation.
- mSharedFullScreenDimLayer.setBoundsForFullscreen();
- return;
- }
- // Use shared fullscreen dim layer
- newDimLayer = mSharedFullScreenDimLayer;
- if (newDimLayer == null) {
- if (state.dimLayer != null) {
- // Re-purpose the previous dim layer.
- newDimLayer = state.dimLayer;
- } else {
- // Create new full screen dim layer.
- newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
- getDimLayerTag(dimLayerUser));
- }
- dimLayerUser.getDimBounds(mTmpBounds);
- newDimLayer.setBounds(mTmpBounds);
- mSharedFullScreenDimLayer = newDimLayer;
- } else if (state.dimLayer != null) {
- state.dimLayer.destroySurface();
- }
- } else {
- newDimLayer = (state.dimLayer == null || previousFullscreen)
- ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
- getDimLayerTag(dimLayerUser))
- : state.dimLayer;
- dimLayerUser.getDimBounds(mTmpBounds);
- newDimLayer.setBounds(mTmpBounds);
- }
- state.dimLayer = newDimLayer;
- }
-
- private static String getDimLayerTag(DimLayerUser dimLayerUser) {
- return TAG_LOCAL + "/" + dimLayerUser.toShortString();
- }
-
- private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
- + dimLayerUser.toShortString());
- DimLayerState state = mState.get(dimLayerUser);
- if (state == null) {
- state = new DimLayerState();
- mState.put(dimLayerUser, state);
- }
- return state;
- }
-
- private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- if (state == null) {
- if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
- + dimLayerUser.toShortString());
- return;
- }
- state.continueDimming = true;
- }
-
- boolean isDimming() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayerState state = mState.valueAt(i);
- if (state.dimLayer != null && state.dimLayer.isDimming()) {
- return true;
- }
- }
- return false;
- }
-
- void resetDimming() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- mState.valueAt(i).continueDimming = false;
- }
- }
-
- private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- return state != null && state.continueDimming;
- }
-
- void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
- WindowStateAnimator newWinAnimator, boolean aboveApp) {
- // Only set dim params on the highest dimmed layer.
- // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
- DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
- state.dimAbove = aboveApp;
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " newWinAnimator=" + newWinAnimator
- + " state.animator=" + state.animator);
- if (newWinAnimator.getShown() && (state.animator == null
- || !state.animator.getShown()
- || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
- state.animator = newWinAnimator;
- if (state.animator.mWin.mAppToken == null && !dimLayerUser.dimFullscreen()) {
- // Dim should cover the entire screen for system windows.
- mDisplayContent.getLogicalDisplayRect(mTmpBounds);
- } else {
- dimLayerUser.getDimBounds(mTmpBounds);
- }
- state.dimLayer.setBounds(mTmpBounds);
- }
- }
-
- void stopDimmingIfNeeded() {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
- stopDimmingIfNeeded(dimLayerUser);
- }
- }
-
- private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
- // No need to check if state is null, we know the key has a value.
- DimLayerState state = mState.get(dimLayerUser);
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " state.continueDimming=" + state.continueDimming
- + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
- if (state.animator != null && state.animator.mWin.mWillReplaceWindow) {
- return;
- }
-
- if (!state.continueDimming && state.dimLayer.isDimming()) {
- state.animator = null;
- dimLayerUser.getDimBounds(mTmpBounds);
- state.dimLayer.setBounds(mTmpBounds);
- }
- }
-
- boolean animateDimLayers() {
- int fullScreen = -1;
- int fullScreenAndDimming = -1;
- int topFullScreenUserLayer = 0;
- boolean result = false;
-
- for (int i = mState.size() - 1; i >= 0; i--) {
- final DimLayer.DimLayerUser user = mState.keyAt(i);
- final DimLayerState state = mState.valueAt(i);
-
- if (!user.isAttachedToDisplay()) {
- // Leaked dim user that is no longer attached to the display. Go ahead and clean it
- // clean-up and log what happened.
- // TODO: This is a work around for b/34395537 as the dim user should have cleaned-up
- // it self when it was detached from the display. Need to investigate how the dim
- // user is leaking...
- //Slog.wtfStack(TAG_WM, "Leaked dim user=" + user.toShortString()
- // + " state=" + state);
- Slog.w(TAG_WM, "Leaked dim user=" + user.toShortString() + " state=" + state);
- removeDimLayerUser(user);
- continue;
- }
-
- // We have to check that we are actually the shared fullscreen layer
- // for this path. If we began as non fullscreen and became fullscreen
- // (e.g. Docked stack closing), then we may not be the shared layer
- // and we have to make sure we always animate the layer.
- if (user.dimFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) {
- fullScreen = i;
- if (!state.continueDimming) {
- continue;
- }
-
- // When choosing which user to assign the shared fullscreen layer to
- // we need to look at Z-order.
- if (topFullScreenUserLayer == 0 ||
- (state.animator != null && state.animator.mAnimLayer > topFullScreenUserLayer)) {
- fullScreenAndDimming = i;
- if (state.animator != null) {
- topFullScreenUserLayer = state.animator.mAnimLayer;
- }
- }
- } else {
- // We always want to animate the non fullscreen windows, they don't share their
- // dim layers.
- result |= animateDimLayers(user);
- }
- }
- // For the shared, full screen dim layer, we prefer the animation that is causing it to
- // appear.
- if (fullScreenAndDimming != -1) {
- result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
- } else if (fullScreen != -1) {
- // If there is no animation for the full screen dim layer to appear, we can use any of
- // the animators that will cause it to disappear.
- result |= animateDimLayers(mState.keyAt(fullScreen));
- }
- return result;
- }
-
- private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " state.animator=" + state.animator
- + " state.continueDimming=" + state.continueDimming);
- final int dimLayer;
- final float dimAmount;
- if (state.animator == null) {
- dimLayer = state.dimLayer.getLayer();
- dimAmount = 0;
- } else {
- if (state.dimAbove) {
- dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
- dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
- } else {
- dimLayer = dimLayerUser.getLayerForDim(state.animator, LAYER_OFFSET_DIM,
- state.animator.mAnimLayer - LAYER_OFFSET_DIM);
- dimAmount = state.animator.mWin.mAttrs.dimAmount;
- }
- }
- final float targetAlpha = state.dimLayer.getTargetAlpha();
- if (targetAlpha != dimAmount) {
- if (state.animator == null) {
- state.dimLayer.hide(DEFAULT_DIM_DURATION);
- } else {
- long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
- ? state.animator.mAnimation.computeDurationHint()
- : DEFAULT_DIM_DURATION;
- if (targetAlpha > dimAmount) {
- duration = getDimLayerFadeDuration(duration);
- }
- state.dimLayer.show(dimLayer, dimAmount, duration);
-
- // If we showed a dim layer, make sure to redo the layout because some things depend
- // on whether a dim layer is showing or not.
- if (targetAlpha == 0) {
- mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
- mDisplayContent.setLayoutNeeded();
- }
- }
- } else if (state.dimLayer.getLayer() != dimLayer) {
- state.dimLayer.setLayer(dimLayer);
- }
- if (state.dimLayer.isAnimating()) {
- if (!mDisplayContent.okToAnimate()) {
- // Jump to the end of the animation.
- state.dimLayer.show();
- } else {
- return state.dimLayer.stepAnimation();
- }
- }
- return false;
- }
-
- boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
- DimLayerState state = mState.get(dimLayerUser);
- return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
- }
-
- private long getDimLayerFadeDuration(long duration) {
- TypedValue tv = new TypedValue();
- mDisplayContent.mService.mContext.getResources().getValue(
- com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
- if (tv.type == TypedValue.TYPE_FRACTION) {
- duration = (long) tv.getFraction(duration, duration);
- } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
- duration = tv.data;
- }
- return duration;
- }
-
- void close() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayerState state = mState.valueAt(i);
- state.dimLayer.destroySurface();
- }
- mState.clear();
- mSharedFullScreenDimLayer = null;
- }
-
- void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- if (state != null) {
- // Destroy the surface, unless it's the shared fullscreen dim.
- if (state.dimLayer != mSharedFullScreenDimLayer) {
- state.dimLayer.destroySurface();
- }
- mState.remove(dimLayerUser);
- }
- if (mState.isEmpty()) {
- mSharedFullScreenDimLayer = null;
- }
- }
-
- @VisibleForTesting
- boolean hasDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
- return mState.containsKey(dimLayerUser);
- }
-
- @VisibleForTesting
- boolean hasSharedFullScreenDimLayer() {
- return mSharedFullScreenDimLayer != null;
- }
-
- void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
- applyDim(dimLayerUser, animator, false /* aboveApp */);
- }
-
- void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
- applyDim(dimLayerUser, animator, true /* aboveApp */);
- }
-
- void applyDim(
- DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
- if (dimLayerUser == null) {
- Slog.e(TAG, "Trying to apply dim layer for: " + this
- + ", but no dim layer user found.");
- return;
- }
- if (!getContinueDimming(dimLayerUser)) {
- setContinueDimming(dimLayerUser);
- if (!isDimming(dimLayerUser, animator)) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
- startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
- }
- }
- }
-
- private static class DimLayerState {
- // The particular window requesting a dim layer. If null, hide dimLayer.
- WindowStateAnimator animator;
- // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
- // end then stop any dimming.
- boolean continueDimming;
- DimLayer dimLayer;
- boolean dimAbove;
- }
-
- void dump(String prefix, PrintWriter pw) {
- pw.println(prefix + "DimLayerController");
- final String doubleSpace = " ";
- final String prefixPlusDoubleSpace = prefix + doubleSpace;
-
- for (int i = 0, n = mState.size(); i < n; i++) {
- pw.println(prefixPlusDoubleSpace + mState.keyAt(i).toShortString());
- DimLayerState state = mState.valueAt(i);
- pw.println(prefixPlusDoubleSpace + doubleSpace + "dimLayer="
- + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" : state.dimLayer)
- + ", animator=" + state.animator + ", continueDimming=" + state.continueDimming);
- if (state.dimLayer != null) {
- state.dimLayer.printTo(prefixPlusDoubleSpace + doubleSpace, pw);
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
new file mode 100644
index 0000000..9fe16ae
--- /dev/null
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.graphics.Rect;
+
+/**
+ * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
+ * black layers of varying opacity at various Z-levels which create the effect of a Dim.
+ */
+class Dimmer {
+ private static final String TAG = "WindowManager";
+
+ private class DimState {
+ SurfaceControl mSurfaceControl;
+ boolean mDimming;
+
+ /**
+ * Used for Dims not assosciated with a WindowContainer. See {@link Dimmer#dimAbove} for
+ * details on Dim lifecycle.
+ */
+ boolean mDontReset;
+
+ DimState(SurfaceControl ctl) {
+ mSurfaceControl = ctl;
+ mDimming = true;
+ }
+ };
+
+ private ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
+
+ /**
+ * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
+ * host, some controller of it, or one of the hosts children.
+ */
+ private WindowContainer mHost;
+
+ Dimmer(WindowContainer host) {
+ mHost = host;
+ }
+
+ SurfaceControl makeDimLayer() {
+ final SurfaceControl control = mHost.makeChildSurface(null)
+ .setParent(mHost.getSurfaceControl())
+ .setColorLayer(true)
+ .setName("Dim Layer for - " + mHost.getName())
+ .build();
+ return control;
+ }
+
+ /**
+ * Retreive the DimState for a given child of the host.
+ */
+ DimState getDimState(WindowContainer container) {
+ DimState state = mDimLayerUsers.get(container);
+ if (state == null) {
+ final SurfaceControl ctl = makeDimLayer();
+ state = new DimState(ctl);
+ /**
+ * See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
+ * via state resetting for Dim's with containers.
+ */
+ if (container == null) {
+ state.mDontReset = true;
+ }
+ mDimLayerUsers.put(container, state);
+ }
+ return state;
+ }
+
+ private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
+ float alpha) {
+ final DimState d = getDimState(container);
+ t.show(d.mSurfaceControl);
+ if (container != null) {
+ t.setRelativeLayer(d.mSurfaceControl,
+ container.getSurfaceControl(), relativeLayer);
+ } else {
+ t.setLayer(d.mSurfaceControl, Integer.MAX_VALUE);
+ }
+ t.setAlpha(d.mSurfaceControl, alpha);
+
+ d.mDimming = true;
+ }
+
+ /**
+ * Finish a dim started by dimAbove in the case there was no call to dimAbove.
+ *
+ * @param t A Transaction in which to finish the dim.
+ */
+ void stopDim(SurfaceControl.Transaction t) {
+ DimState d = getDimState(null);
+ t.hide(d.mSurfaceControl);
+ d.mDontReset = false;
+ }
+ /**
+ * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
+ * remove this effect. If the Dim can be assosciated with a particular child of the host
+ * consider using the other variant of dimAbove which ties the Dim lifetime to the child
+ * lifetime more explicitly.
+ *
+ * @param t A transaction in which to apply the Dim.
+ * @param alpha The alpha at which to Dim.
+ */
+ void dimAbove(SurfaceControl.Transaction t, float alpha) {
+ dim(t, null, 1, alpha);
+ }
+
+ /**
+ * Place a dim above the given container, which should be a child of the host container.
+ * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
+ * and the child should call dimAbove again to request the Dim to continue.
+ *
+ * @param t A transaction in which to apply the Dim.
+ * @param container The container which to dim above. Should be a child of our host.
+ * @param alpha The alpha at which to Dim.
+ */
+ void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
+ dim(t, container, 1, alpha);
+ }
+
+ /**
+ * Like {@link #dimAbove} but places the dim below the given container.
+ *
+ * @param t A transaction in which to apply the Dim.
+ * @param container The container which to dim below. Should be a child of our host.
+ * @param alpha The alpha at which to Dim.
+ */
+
+ void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
+ dim(t, container, -1, alpha);
+ }
+
+ /**
+ * Mark all dims as pending completion on the next call to {@link #updateDims}
+ *
+ * This is intended for us by the host container, to be called at the beginning of
+ * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
+ * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
+ * a chance to request dims to continue.
+ */
+ void resetDimStates() {
+ for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
+ final DimState state = mDimLayerUsers.valueAt(i);
+ if (state.mDontReset == false) {
+ state.mDimming = false;
+ }
+ }
+ }
+
+ /**
+ * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
+ * described in {@link #resetDimStates}.
+ *
+ * @param t A transaction in which to update the dims.
+ * @param bounds The bounds at which to dim.
+ * @return true if any Dims were updated.
+ */
+ boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
+ boolean didSomething = false;
+ for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
+ DimState state = mDimLayerUsers.valueAt(i);
+ // TODO: We want to animate the addition and removal of Dim's instead of immediately
+ // acting. When we do this we need to take care to account for the "Replacing Windows"
+ // case (and seamless dim transfer).
+ if (state.mDimming == false) {
+ mDimLayerUsers.removeAt(i);
+ state.mSurfaceControl.destroy();
+ } else {
+ didSomething = true;
+ // TODO: Once we use geometry from hierarchy this falls away.
+ t.setSize(state.mSurfaceControl, bounds.width(), bounds.height());
+ t.setPosition(state.mSurfaceControl, bounds.left, bounds.top);
+ }
+ }
+ return didSomething;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 67d62e1..17312b2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -104,6 +105,7 @@
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
import static com.android.server.wm.proto.DisplayProto.ABOVE_APP_WINDOWS;
import static com.android.server.wm.proto.DisplayProto.BELOW_APP_WINDOWS;
+import static com.android.server.wm.proto.DisplayProto.DISPLAY_FRAMES;
import static com.android.server.wm.proto.DisplayProto.DISPLAY_INFO;
import static com.android.server.wm.proto.DisplayProto.DOCKED_STACK_DIVIDER_CONTROLLER;
import static com.android.server.wm.proto.DisplayProto.DPI;
@@ -140,13 +142,16 @@
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InputDevice;
+import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.WindowManagerPolicy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.view.IInputMethodClient;
+import android.view.DisplayFrames;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -222,6 +227,8 @@
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Display mDisplay;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ DisplayFrames mDisplayFrames;
+
/**
* For default display it contains real metrics, empty for others.
* @see WindowManagerService#createWatermarkInTransaction()
@@ -285,7 +292,6 @@
private boolean mLastWallpaperVisible = false;
private Rect mBaseDisplayRect = new Rect();
- private Rect mContentRect = new Rect();
// Accessed directly by all users.
private boolean mLayoutNeeded;
@@ -317,8 +323,6 @@
final DockedStackDividerController mDividerControllerLocked;
final PinnedStackController mPinnedStackControllerLocked;
- DimLayerController mDimLayerController;
-
final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>();
private boolean mHaveBootMsg = false;
@@ -342,10 +346,37 @@
// {@code false} if this display is in the processing of being created.
private boolean mDisplayReady = false;
- private final WindowLayersController mLayersController;
WallpaperController mWallpaperController;
int mInputMethodAnimLayerAdjustment;
+ private final SurfaceSession mSession = new SurfaceSession();
+
+ /**
+ * We organize all top-level Surfaces in to the following layers.
+ * mOverlayLayer contains a few Surfaces which are always on top of others
+ * and omitted from Screen-Magnification ({@link WindowState#isScreenOverlay})
+ * {@link #mWindowingLayer} contains everything else.
+ */
+ private SurfaceControl mOverlayLayer;
+
+ /**
+ * See {@link #mOverlayLayer}
+ */
+ private SurfaceControl mWindowingLayer;
+
+ /**
+ * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}.
+ * <p>
+ * For these surfaces currently we use a surface based on the larger of width or height so we
+ * don't have to resize when rotating the display.
+ */
+ private int mSurfaceSize;
+
+ /**
+ * A list of surfaces to be destroyed after {@link #mPendingTransaction} is applied.
+ */
+ private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
if (winAnimator.hasSurface()) {
@@ -499,9 +530,6 @@
return true;
};
- private final Consumer<WindowState> mPrepareWindowSurfaces =
- w -> w.mWinAnimator.prepareSurfaceLocked(true);
-
private final Consumer<WindowState> mPerformLayout = w -> {
// Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
// wasting time and funky changes while a window is animating away.
@@ -545,7 +573,7 @@
w.mLayoutNeeded = false;
w.prelayout();
final boolean firstLayout = !w.isLaidOut();
- mService.mPolicy.layoutWindowLw(w, null);
+ mService.mPolicy.layoutWindowLw(w, null, mDisplayFrames);
w.mLayoutSeq = mService.mLayoutSeq;
// If this is the first layout, we need to initialize the last inset values as
@@ -554,12 +582,6 @@
w.updateLastInsetValues();
}
- // Window frames may have changed. Update dim layer with the new bounds.
- final Task task = w.getTask();
- if (task != null) {
- mDimLayerController.updateDimLayer(task);
- }
-
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
+ " mContainingFrame=" + w.mContainingFrame
+ " mDisplayFrame=" + w.mDisplayFrame);
@@ -586,7 +608,7 @@
}
w.mLayoutNeeded = false;
w.prelayout();
- mService.mPolicy.layoutWindowLw(w, w.getParentWindow());
+ mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
w.mLayoutSeq = mService.mLayoutSeq;
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
+ " mContainingFrame=" + w.mContainingFrame
@@ -653,8 +675,6 @@
}
}
- w.applyDimLayerIfNeeded();
-
if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
&& mWallpaperController.isWallpaperTarget(w)) {
// This is the wallpaper target and its obscured state changed... make sure the
@@ -737,13 +757,11 @@
* initialize direct children.
* @param display May not be null.
* @param service You know.
- * @param layersController window layer controller used to assign layer to the windows on this
- * display.
* @param wallpaperController wallpaper windows controller used to adjust the positioning of the
* wallpaper windows in the window list.
*/
DisplayContent(Display display, WindowManagerService service,
- WindowLayersController layersController, WallpaperController wallpaperController) {
+ WallpaperController wallpaperController) {
if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+ " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -752,16 +770,31 @@
mDisplay = display;
mDisplayId = display.getDisplayId();
- mLayersController = layersController;
mWallpaperController = wallpaperController;
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mService = service;
+ mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo);
initializeDisplayBaseInfo();
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
- mDimLayerController = new DimLayerController(this);
+
+ mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth);
+
+ final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
+ .setSize(mSurfaceSize, mSurfaceSize)
+ .setOpaque(true);
+ mWindowingLayer = b.setName("Display Root").build();
+ mOverlayLayer = b.setName("Display Overlays").build();
+
+ getPendingTransaction().setLayer(mWindowingLayer, 0)
+ .setLayerStack(mWindowingLayer, mDisplayId)
+ .show(mWindowingLayer)
+ .setLayer(mOverlayLayer, 1)
+ .setLayerStack(mOverlayLayer, mDisplayId)
+ .show(mOverlayLayer);
+ getPendingTransaction().apply();
// These are the only direct children we should ever have and they are permanent.
super.addChild(mBelowAppWindowsContainers, null);
@@ -1025,11 +1058,7 @@
setLayoutNeeded();
final int[] anim = new int[2];
- if (isDimming()) {
- anim[0] = anim[1] = 0;
- } else {
- mService.mPolicy.selectRotationAnimationLw(anim);
- }
+ mService.mPolicy.selectRotationAnimationLw(anim);
if (!rotateSeamlessly) {
mService.startFreezingDisplayLocked(inTransaction, anim[0], anim[1], this);
@@ -1066,8 +1095,7 @@
// it doesn't support hardware OpenGL emulation yet.
if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
&& screenRotationAnimation.hasScreenshot()) {
- if (screenRotationAnimation.setRotationInTransaction(
- rotation, mService.mFxSession,
+ if (screenRotationAnimation.setRotationInTransaction(rotation,
MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(),
mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
mService.scheduleAnimationLocked();
@@ -1129,6 +1157,13 @@
return true;
}
+ void configureDisplayPolicy() {
+ mService.mPolicy.setInitialDisplaySize(getDisplay(),
+ mBaseDisplayWidth, mBaseDisplayHeight, mBaseDisplayDensity);
+
+ mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo);
+ }
+
/**
* Update {@link #mDisplayInfo} and other internal variables when display is rotated or config
* changed.
@@ -1477,7 +1512,7 @@
* Returns the topmost stack on the display that is compatible with the input windowing mode.
* Null is no compatible stack on the display.
*/
- TaskStack getStack(int windowingMode) {
+ TaskStack getTopStackInWindowingMode(int windowingMode) {
return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
}
@@ -1744,7 +1779,7 @@
}
void getContentRect(Rect out) {
- out.set(mContentRect);
+ out.set(mDisplayFrames.mContent);
}
TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) {
@@ -1753,10 +1788,6 @@
final TaskStack stack = new TaskStack(mService, stackId, controller);
mTaskStackContainers.addStackToDisplay(stack, onTop);
-
- if (stack.inSplitScreenPrimaryWindowingMode()) {
- mDividerControllerLocked.notifyDockedStackExistsChanged(true);
- }
return stack;
}
@@ -1847,8 +1878,8 @@
mTmpRect2.setEmpty();
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
- stack.setTouchExcludeRegion(
- focusedTask, delta, mTouchExcludeRegion, mContentRect, mTmpRect2);
+ stack.setTouchExcludeRegion(focusedTask, delta, mTouchExcludeRegion,
+ mDisplayFrames.mContent, mTmpRect2);
}
// If we removed the focused task above, add it back and only leave its
// outside touch area in the exclusion. TapDectector is not interested in
@@ -1899,22 +1930,6 @@
}
}
- boolean animateDimLayers() {
- return mDimLayerController.animateDimLayers();
- }
-
- private void resetDimming() {
- mDimLayerController.resetDimming();
- }
-
- boolean isDimming() {
- return mDimLayerController.isDimming();
- }
-
- private void stopDimmingIfNeeded() {
- mDimLayerController.stopDimmingIfNeeded();
- }
-
@Override
void removeIfPossible() {
if (isAnimating()) {
@@ -1930,7 +1945,6 @@
try {
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
- mDimLayerController.close();
if (mService.canDispatchPointerEvents()) {
if (mTapDetector != null) {
mService.unregisterPointerEventListener(mTapDetector);
@@ -1939,6 +1953,9 @@
mService.unregisterPointerEventListener(mService.mMousePositionTracker);
}
}
+ // The pending transaction won't be applied so we should
+ // just clean up any surfaces pending destruction.
+ onPendingTransactionApplied();
} finally {
mRemovingDisplay = false;
}
@@ -2025,7 +2042,7 @@
final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock();
- final int imeHeight = mService.mPolicy.getInputMethodWindowVisibleHeightLw();
+ final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight();
final boolean imeHeightChanged = imeVisible &&
imeHeight != mDividerControllerLocked.getImeHeightAdjustedFor();
@@ -2167,6 +2184,7 @@
if (screenRotationAnimation != null) {
screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
}
+ mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
proto.end(token);
}
@@ -2219,8 +2237,7 @@
token.dump(pw, " ");
}
}
- pw.println();
- mDimLayerController.dump(prefix, pw);
+
pw.println();
// Dump stack references
@@ -2246,6 +2263,9 @@
pw.println(subPrefix
+ "mInputMethodAnimLayerAdjustment=" + mInputMethodAnimLayerAdjustment);
}
+
+ pw.println();
+ mDisplayFrames.dump(prefix, pw);
}
@Override
@@ -2259,7 +2279,7 @@
/** Returns true if the stack in the windowing mode is visible. */
boolean isStackVisible(int windowingMode) {
- final TaskStack stack = getStack(windowingMode);
+ final TaskStack stack = getTopStackInWindowingMode(windowingMode);
return stack != null && stack.isVisible();
}
@@ -2330,10 +2350,16 @@
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
- mLayersController.assignWindowLayers(this);
+ assignChildLayers(getPendingTransaction());
if (setLayoutNeeded) {
setLayoutNeeded();
}
+
+ // We accumlate the layer changes in-to "getPendingTransaction()" but we defer
+ // the application of this transaction until the animation pass triggers
+ // prepareSurfaces. This allows us to synchronize Z-ordering changes with
+ // the hiding and showing of surfaces.
+ scheduleAnimation();
}
// TODO: This should probably be called any time a visual change is made to the hierarchy like
@@ -2689,10 +2715,6 @@
}
}
- void prepareWindowSurfaces() {
- forAllWindows(mPrepareWindowSurfaces, false /* traverseTopToBottom */);
- }
-
boolean inputMethodClientHasFocus(IInputMethodClient client) {
final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
if (imFocus == null) {
@@ -2834,7 +2856,6 @@
} while (pendingLayoutChanges != 0);
mTmpApplySurfaceChangesTransactionState.reset();
- resetDimming();
mTmpRecoveringMemory = recoveringMemory;
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
@@ -2845,8 +2866,6 @@
mTmpApplySurfaceChangesTransactionState.preferredModeId,
true /* inTraversal, must call performTraversalInTrans... below */);
- stopDimmingIfNeeded();
-
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
if (wallpaperVisible != mLastWallpaperVisible) {
mLastWallpaperVisible = wallpaperVisible;
@@ -2871,21 +2890,22 @@
final int dw = mDisplayInfo.logicalWidth;
final int dh = mDisplayInfo.logicalHeight;
-
if (DEBUG_LAYOUT) {
Slog.v(TAG, "-------------------------------------");
Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
}
- mService.mPolicy.beginLayoutLw(mDisplayId, dw, dh, mRotation, getConfiguration().uiMode);
+ mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo);
+ // TODO: Not sure if we really need to set the rotation here since we are updating from the
+ // display info above...
+ mDisplayFrames.mRotation = mRotation;
+ mService.mPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
if (isDefaultDisplay) {
// Not needed on non-default displays.
mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
mService.mScreenRect.set(0, 0, dw, dh);
}
- mService.mPolicy.getContentRectLw(mContentRect);
-
int seq = mService.mLayoutSeq + 1;
if (seq < 0) seq = 0;
mService.mLayoutSeq = seq;
@@ -2915,7 +2935,6 @@
mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
}
- mService.mPolicy.finishLayoutLw();
mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
}
@@ -3050,13 +3069,6 @@
// Include this window.
final WindowStateAnimator winAnim = w.mWinAnimator;
- int layer = winAnim.mSurfaceController.getLayer();
- if (mScreenshotApplicationState.maxLayer < layer) {
- mScreenshotApplicationState.maxLayer = layer;
- }
- if (mScreenshotApplicationState.minLayer > layer) {
- mScreenshotApplicationState.minLayer = layer;
- }
// Don't include wallpaper in bounds calculation
if (!w.mIsWallpaper && !mutableIncludeFullDisplay.value) {
@@ -3100,8 +3112,6 @@
final WindowState appWin = mScreenshotApplicationState.appWin;
final boolean screenshotReady = mScreenshotApplicationState.screenshotReady;
- final int maxLayer = mScreenshotApplicationState.maxLayer;
- final int minLayer = mScreenshotApplicationState.minLayer;
if (appToken != null && appWin == null) {
// Can't find a window to snapshot.
@@ -3122,11 +3132,6 @@
// because we don't want to release the mWindowMap lock until the screenshot is
// taken.
- if (maxLayer == 0) {
- if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
- + ": returning null maxLayer=" + maxLayer);
- return null;
- }
if (!mutableIncludeFullDisplay.value) {
// Constrain frame to the screen size.
@@ -3171,8 +3176,6 @@
convertCropForSurfaceFlinger(crop, rot, dw, dh);
if (DEBUG_SCREENSHOT) {
- Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
- + maxLayer + " appToken=" + appToken);
forAllWindows(w -> {
final WindowSurfaceController controller = w.mWinAnimator.mSurfaceController;
Slog.i(TAG_WM, w + ": " + w.mLayer
@@ -3194,11 +3197,13 @@
SurfaceControl.openTransaction();
SurfaceControl.closeTransactionSync();
- bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer,
+ // TODO(b/68392460): We should screenshot Task controls directly
+ // but it's difficult at the moment as the Task doesn't have the
+ // correct size set.
+ bitmap = screenshoter.screenshot(crop, width, height, 0, 1,
inRotation, rot);
if (bitmap == null) {
- Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
- + ") to layer " + maxLayer);
+ Slog.w(TAG_WM, "Failed to take screenshot");
return null;
}
}
@@ -3354,6 +3359,10 @@
* I.e Activities.
*/
private final class TaskStackContainers extends DisplayChildWindowContainer<TaskStack> {
+ /**
+ * A control placed at the appropriate level for transitions to occur.
+ */
+ SurfaceControl mAnimationLayer = null;
// Cached reference to some special stacks we tend to get a lot so we don't need to loop
// through the list to find them.
@@ -3376,6 +3385,12 @@
}
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
final TaskStack stack = mTaskStackContainers.getChildAt(i);
+ if (activityType == ACTIVITY_TYPE_UNDEFINED
+ && windowingMode == stack.getWindowingMode()) {
+ // Passing in undefined type means we want to match the topmost stack with the
+ // windowing mode.
+ return stack;
+ }
if (stack.isCompatible(windowingMode, activityType)) {
return stack;
}
@@ -3446,6 +3461,7 @@
+ " already exist on display=" + this + " stack=" + stack);
}
mSplitScreenPrimaryStack = stack;
+ mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
}
@@ -3456,6 +3472,10 @@
mPinnedStack = null;
} else if (stack == mSplitScreenPrimaryStack) {
mSplitScreenPrimaryStack = null;
+ // Re-set the split-screen create mode whenever the split-screen stack is removed.
+ mService.setDockedStackCreateStateLocked(
+ SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
+ mDividerControllerLocked.notifyDockedStackExistsChanged(false);
}
}
@@ -3654,6 +3674,50 @@
// to prevent freezing/unfreezing the display too early.
return mLastOrientation;
}
+
+ @Override
+ void assignChildLayers(SurfaceControl.Transaction t) {
+ final int NORMAL_STACK_STATE = 0;
+ final int BOOSTED_STATE = 1;
+ final int ALWAYS_ON_TOP_STATE = 2;
+
+ // We allow stacks to change visual order from the AM specified order due to
+ // Z-boosting during animations. However we must take care to ensure TaskStacks
+ // which are marked as alwaysOnTop remain that way.
+ int layer = 0;
+ for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
+ for (int i = 0; i < mChildren.size(); i++) {
+ final TaskStack s = mChildren.get(i);
+ layer++;
+ if (state == NORMAL_STACK_STATE) {
+ s.assignLayer(t, layer);
+ } else if (state == BOOSTED_STATE && s.needsZBoost()) {
+ s.assignLayer(t, layer);
+ } else if (state == ALWAYS_ON_TOP_STATE &&
+ s.isAlwaysOnTop()) {
+ s.assignLayer(t, layer);
+ }
+ s.assignChildLayers(t);
+ }
+ // The appropriate place for App-Transitions to occur is right
+ // above all other animations but still below things in the Picture-and-Picture
+ // windowing mode.
+ if (state == BOOSTED_STATE && mAnimationLayer != null) {
+ t.setLayer(mAnimationLayer, layer + 1);
+ }
+ }
+ }
+
+ @Override
+ void onParentSet() {
+ super.onParentSet();
+ if (getParent() != null) {
+ mAnimationLayer = makeSurface().build();
+ } else {
+ mAnimationLayer.destroy();
+ mAnimationLayer = null;
+ }
+ }
}
/**
@@ -3737,4 +3801,119 @@
E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
boolean useIdentityTransform, int rotation);
}
+
+ SurfaceControl.Builder makeSurface(SurfaceSession s) {
+ return mService.makeSurfaceBuilder(s)
+ .setParent(mWindowingLayer);
+ }
+
+ @Override
+ SurfaceSession getSession() {
+ return mSession;
+ }
+
+ @Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ SurfaceSession s = child != null ? child.getSession() : getSession();
+ final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s);
+ b.setSize(mSurfaceSize, mSurfaceSize);
+
+ if (child == null) {
+ return b;
+ }
+
+ b.setName(child.getName());
+ if (child.isScreenOverlay()) {
+ return b.setParent(mOverlayLayer);
+ } else {
+ return b.setParent(mWindowingLayer);
+ }
+ }
+
+ /**
+ * The makeSurface variants are for use by the window-container
+ * hierarchy. makeOverlay here is a function for various non windowing
+ * overlays like the ScreenRotation screenshot, the Strict Mode Flash
+ * and other potpourii.
+ */
+ SurfaceControl.Builder makeOverlay() {
+ return mService.makeSurfaceBuilder(mSession)
+ .setParent(mOverlayLayer);
+ }
+
+ void applyMagnificationSpec(MagnificationSpec spec) {
+ applyMagnificationSpec(getPendingTransaction(), spec);
+ getPendingTransaction().apply();
+ }
+
+ @Override
+ void onParentSet() {
+ // Since we are the top of the SurfaceControl hierarchy here
+ // we create the root surfaces explicitly rather than chaining
+ // up as the default implementation in onParentSet does. So we
+ // explicitly do NOT call super here.
+ }
+
+ @Override
+ void assignChildLayers(SurfaceControl.Transaction t) {
+ t.setLayer(mOverlayLayer, 1)
+ .setLayer(mWindowingLayer, 0);
+
+ // These are layers as children of "mWindowingLayer"
+ mBelowAppWindowsContainers.assignLayer(t, 0);
+ mTaskStackContainers.assignLayer(t, 1);
+ mAboveAppWindowsContainers.assignLayer(t, 2);
+
+ WindowState imeTarget = mService.mInputMethodTarget;
+ if (imeTarget == null || imeTarget.inSplitScreenWindowingMode()) {
+ // In split-screen windowing mode we can't layer the
+ // IME relative to the IME target because it needs to
+ // go over the docked divider, so instead we place it on top
+ // of everything and use relative layering of windows which need
+ // to go above it (see special logic in WindowState#assignLayer)
+ mImeWindowsContainers.assignLayer(t, 3);
+ } else {
+ t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(),
+ imeTarget.getSurfaceControl(),
+ // TODO: We need to use an extra level on the app surface to ensure
+ // this is always above SurfaceView but always below attached window.
+ 1);
+ }
+
+ // Above we have assigned layers to our children, now we ask them to assign
+ // layers to their children.
+ mBelowAppWindowsContainers.assignChildLayers(t);
+ mTaskStackContainers.assignChildLayers(t);
+ mAboveAppWindowsContainers.assignChildLayers(t);
+ mImeWindowsContainers.assignChildLayers(t);
+ }
+
+ /**
+ * Here we satisfy an unfortunate special case of the IME in split-screen mode. Imagine
+ * that the IME target is one of the docked applications. We'd like the docked divider to be
+ * above both of the applications, and we'd like the IME to be above the docked divider.
+ * However we need child windows of the applications to be above the IME (Text drag handles).
+ * This is a non-strictly hierarcical layering and we need to break out of the Z ordering
+ * somehow. We do this by relatively ordering children of the target to the IME in cooperation
+ * with {@link #WindowState#assignLayer}
+ */
+ void assignRelativeLayerForImeTargetChild(SurfaceControl.Transaction t, WindowContainer child) {
+ t.setRelativeLayer(child.getSurfaceControl(), mImeWindowsContainers.getSurfaceControl(), 1);
+ }
+
+ @Override
+ void destroyAfterPendingTransaction(SurfaceControl surface) {
+ mPendingDestroyingSurfaces.add(surface);
+ }
+
+ /**
+ * Destroys any surfaces that have been put into the pending list with
+ * {@link #destroyAfterTransaction}.
+ */
+ void onPendingTransactionApplied() {
+ for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+ mPendingDestroyingSurfaces.get(i).destroy();
+ }
+ mPendingDestroyingSurfaces.clear();
+ }
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 5ea0e1d..8308417 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -23,6 +23,7 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
@@ -53,7 +54,6 @@
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
import com.android.server.LocalServices;
-import com.android.server.wm.DimLayer.DimLayerUser;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
@@ -61,7 +61,7 @@
/**
* Keeps information about the docked stack divider.
*/
-public class DockedStackDividerController implements DimLayerUser {
+public class DockedStackDividerController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
@@ -113,9 +113,9 @@
private boolean mLastVisibility = false;
private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
= new RemoteCallbackList<>();
- private final DimLayer mDimLayer;
private boolean mMinimizedDock;
+ private int mOriginalDockedSide = DOCKED_INVALID;
private boolean mAnimatingForMinimizedDockedStack;
private boolean mAnimationStarted;
private long mAnimationStartTime;
@@ -139,13 +139,12 @@
private boolean mImeHideRequested;
private final Rect mLastDimLayerRect = new Rect();
private float mLastDimLayerAlpha;
+ private TaskStack mDimmedStack;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
final Context context = service.mContext;
- mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
- "DockedStackDim");
mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
context, android.R.interpolator.fast_out_slow_in);
loadDimens();
@@ -408,6 +407,31 @@
mDockedStackListeners.finishBroadcast();
}
+ /**
+ * Checks if the primary stack is allowed to dock to a specific side based on its original dock
+ * side.
+ *
+ * @param dockSide the side to see if it is valid
+ * @return true if the side provided is valid
+ */
+ boolean canPrimaryStackDockTo(int dockSide) {
+ if (mService.mPolicy.isDockSideAllowed(dockSide)) {
+ // Side is the same as original side
+ if (dockSide == mOriginalDockedSide) {
+ return true;
+ }
+ // Special rule that the top in portrait is always valid
+ if (dockSide == DOCKED_TOP) {
+ return true;
+ }
+ // Only if original docked side was top in portrait will allow left side for landscape
+ if (dockSide == DOCKED_LEFT && mOriginalDockedSide == DOCKED_TOP) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void notifyDockedStackExistsChanged(boolean exists) {
// TODO(multi-display): Perform all actions only for current display.
final int size = mDockedStackListeners.beginBroadcast();
@@ -430,9 +454,17 @@
inputMethodManagerInternal.hideCurrentInputMethod();
mImeHideRequested = true;
}
+ final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ mOriginalDockedSide = stack.getDockSide();
return;
}
+ mOriginalDockedSide = DOCKED_INVALID;
setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);
+
+ if (mDimmedStack != null) {
+ mDimmedStack.stopDimming();
+ mDimmedStack = null;
+ }
}
/**
@@ -529,39 +561,17 @@
void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
// TODO: Maybe only allow split-screen windowing modes?
final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
- ? mDisplayContent.getStack(targetWindowingMode)
+ ? mDisplayContent.getTopStackInWindowingMode(targetWindowingMode)
: null;
final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
boolean visibleAndValid = visible && stack != null && dockedStack != null;
if (visibleAndValid) {
- stack.getDimBounds(mTmpRect);
- if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
- if (!mLastDimLayerRect.equals(mTmpRect) || mLastDimLayerAlpha != alpha) {
- try {
- // TODO: This should use the regular animation transaction - here and below
- mService.openSurfaceTransaction();
- mDimLayer.setBounds(mTmpRect);
- mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
- } finally {
- mService.closeSurfaceTransaction("setResizeDimLayer");
- }
- }
- mLastDimLayerRect.set(mTmpRect);
- mLastDimLayerAlpha = alpha;
- } else {
- visibleAndValid = false;
- }
+ mDimmedStack = stack;
+ stack.dim(alpha);
}
- if (!visibleAndValid) {
- if (mLastDimLayerAlpha != 0f) {
- try {
- mService.openSurfaceTransaction();
- mDimLayer.hide();
- } finally {
- mService.closeSurfaceTransaction("setResizeDimLayer");
- }
- }
- mLastDimLayerAlpha = 0f;
+ if (!visibleAndValid && stack != null) {
+ mDimmedStack = null;
+ stack.stopDimming();
}
}
@@ -633,11 +643,15 @@
if (mMinimizedDock && mService.mPolicy.isKeyguardShowingAndNotOccluded()) {
return;
}
- final TaskStack fullscreenStack = mDisplayContent.getStack(
+ final TaskStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
- final boolean homeBehind = fullscreenStack != null && fullscreenStack.isVisible();
- setMinimizedDockedStack(homeVisible && !homeBehind, animate);
+ boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
+ if (homeVisible && topSecondaryStack != null) {
+ // Home should only be considered visible if it is greater or equal to the top secondary
+ // stack in terms of z-order.
+ homeVisible = homeStack.compareTo(topSecondaryStack) >= 0;
+ }
+ setMinimizedDockedStack(homeVisible, animate);
}
private boolean isWithinDisplay(Task task) {
@@ -795,12 +809,8 @@
return animateForMinimizedDockedStack(now);
} else if (mAnimatingForIme) {
return animateForIme(now);
- } else {
- if (mDimLayer != null && mDimLayer.isDimming()) {
- mDimLayer.setLayer(getResizeDimLayer());
- }
- return false;
}
+ return false;
}
private boolean animateForIme(long now) {
@@ -908,27 +918,6 @@
+ (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
}
- @Override
- public boolean dimFullscreen() {
- return false;
- }
-
- @Override
- public DisplayInfo getDisplayInfo() {
- return mDisplayContent.getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return mDisplayContent != null;
- }
-
- @Override
- public void getDimBounds(Rect outBounds) {
- // This dim layer user doesn't need this.
- }
-
- @Override
public String toShortString() {
return TAG;
}
@@ -943,10 +932,6 @@
pw.println(prefix + " mMinimizedDock=" + mMinimizedDock);
pw.println(prefix + " mAdjustedForIme=" + mAdjustedForIme);
pw.println(prefix + " mAdjustedForDivider=" + mAdjustedForDivider);
- if (mDimLayer.isDimming()) {
- pw.println(prefix + " Dim layer is dimming: ");
- mDimLayer.printTo(prefix + " ", pw);
- }
}
void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index 8bec8d7..fddf6ca 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -49,19 +49,19 @@
private int mRotation;
private boolean mVisible;
- public EmulatorDisplayOverlay(Context context, Display display, SurfaceSession session,
+ public EmulatorDisplayOverlay(Context context, DisplayContent dc,
int zOrder) {
+ final Display display = dc.getDisplay();
mScreenSize = new Point();
display.getSize(mScreenSize);
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("EmulatorDisplayOverlay")
.setSize(mScreenSize.x, mScreenSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- ctrl.setLayerStack(display.getLayerStack());
ctrl.setLayer(zOrder);
ctrl.setPosition(0, 0);
ctrl.show();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bcb6e673..43dfccc 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -138,7 +138,6 @@
ParcelFileDescriptor mSurfaceTraceFd;
RemoteEventTrace mRemoteEventTrace;
- private final WindowLayersController mLayersController;
final WallpaperController mWallpaperController;
private final Handler mHandler;
@@ -163,7 +162,6 @@
RootWindowContainer(WindowManagerService service) {
mService = service;
mHandler = new MyHandler(service.mH.getLooper());
- mLayersController = new WindowLayersController(mService);
mWallpaperController = new WallpaperController(mService);
}
@@ -231,7 +229,7 @@
}
private DisplayContent createDisplayContent(final Display display) {
- final DisplayContent dc = new DisplayContent(display, mService, mLayersController,
+ final DisplayContent dc = new DisplayContent(display, mService,
mWallpaperController);
final int displayId = display.getDisplayId();
@@ -247,7 +245,7 @@
if (mService.mDisplayManagerInternal != null) {
mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
displayId, displayInfo);
- mService.configureDisplayPolicyLocked(dc);
+ dc.configureDisplayPolicy();
// Tap Listeners are supported for:
// 1. All physical displays (multi-display).
@@ -1103,4 +1101,9 @@
String getName() {
return "ROOT";
}
+
+ @Override
+ void scheduleAnimation() {
+ mService.scheduleAnimationLocked();
+ }
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 3350fea..70bf15c 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -225,7 +225,7 @@
}
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
- SurfaceSession session, boolean inTransaction, boolean forceDefaultOrientation,
+ boolean inTransaction, boolean forceDefaultOrientation,
boolean isSecure, WindowManagerService service) {
mService = service;
mContext = context;
@@ -269,7 +269,7 @@
try {
try {
- mSurfaceControl = new SurfaceControl.Builder(session)
+ mSurfaceControl = displayContent.makeOverlay()
.setName("ScreenshotSurface")
.setSize(mWidth, mHeight)
.setSecure(isSecure)
@@ -281,7 +281,6 @@
// TODO(multidisplay): we should use the proper display
SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
- mSurfaceControl.setLayerStack(display.getLayerStack());
mSurfaceControl.setLayer(SCREEN_FREEZE_LAYER_SCREENSHOT);
mSurfaceControl.setAlpha(0);
mSurfaceControl.show();
@@ -370,11 +369,11 @@
}
// Must be called while in a transaction.
- public boolean setRotationInTransaction(int rotation, SurfaceSession session,
+ public boolean setRotationInTransaction(int rotation,
long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
setRotationInTransaction(rotation);
if (TWO_PHASE_ANIMATION) {
- return startAnimation(session, maxAnimationDuration, animationScale,
+ return startAnimation(maxAnimationDuration, animationScale,
finalWidth, finalHeight, false, 0, 0);
}
@@ -385,7 +384,7 @@
/**
* Returns true if animating.
*/
- private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
+ private boolean startAnimation(long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, boolean dismissing,
int exitAnim, int enterAnim) {
if (mSurfaceControl == null) {
@@ -561,8 +560,8 @@
Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
mOriginalWidth*2, mOriginalHeight*2);
Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
- mCustomBlackFrame = new BlackFrame(session, outer, inner,
- SCREEN_FREEZE_LAYER_CUSTOM, layerStack, false);
+ mCustomBlackFrame = new BlackFrame(outer, inner,
+ SCREEN_FREEZE_LAYER_CUSTOM, mDisplayContent, false);
mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
@@ -601,8 +600,8 @@
mOriginalWidth*2, mOriginalHeight*2);
inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
}
- mExitingBlackFrame = new BlackFrame(session, outer, inner,
- SCREEN_FREEZE_LAYER_EXIT, layerStack, mForceDefaultOrientation);
+ mExitingBlackFrame = new BlackFrame(outer, inner,
+ SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
@@ -624,8 +623,8 @@
Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
finalWidth*2, finalHeight*2);
Rect inner = new Rect(0, 0, finalWidth, finalHeight);
- mEnteringBlackFrame = new BlackFrame(session, outer, inner,
- SCREEN_FREEZE_LAYER_ENTER, layerStack, false);
+ mEnteringBlackFrame = new BlackFrame(outer, inner,
+ SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
@@ -642,7 +641,7 @@
/**
* Returns true if animating.
*/
- public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
+ public boolean dismiss(long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
if (mSurfaceControl == null) {
@@ -650,7 +649,7 @@
return false;
}
if (!mStarted) {
- startAnimation(session, maxAnimationDuration, animationScale, finalWidth, finalHeight,
+ startAnimation(maxAnimationDuration, animationScale, finalWidth, finalHeight,
true, exitAnim, enterAnim);
}
if (!mStarted) {
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index aff1bc6..95c1d53 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -225,11 +225,13 @@
}
}
- private void getRawBounds(Rect outBounds) {
- if (mContainer.getRawFullscreen()) {
- outBounds.setEmpty();
- } else {
- mContainer.getRawBounds(outBounds);
+ public void getRawBounds(Rect outBounds) {
+ synchronized (mWindowMap) {
+ if (mContainer.getRawFullscreen()) {
+ outBounds.setEmpty();
+ } else {
+ mContainer.getRawBounds(outBounds);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index eb8ee69..f51a6a9 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -41,15 +41,14 @@
private boolean mDrawNeeded;
private final int mThickness = 20;
- public StrictModeFlash(Display display, SurfaceSession session) {
+ public StrictModeFlash(DisplayContent dc) {
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("StrictModeFlash")
.setSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- ctrl.setLayerStack(display.getLayerStack());
ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary.
ctrl.setPosition(0, 0);
ctrl.show();
diff --git a/core/java/com/android/internal/app/IAssistDataReceiver.aidl b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
similarity index 70%
copy from core/java/com/android/internal/app/IAssistDataReceiver.aidl
copy to services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
index 9c9ffef..5390e5a 100644
--- a/core/java/com/android/internal/app/IAssistDataReceiver.aidl
+++ b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package com.android.internal.app;
+package com.android.server.wm;
-import android.graphics.Bitmap;
-import android.os.Bundle;
+import android.view.SurfaceSession;
+import android.view.SurfaceControl;
-/** @hide */
-oneway interface IAssistDataReceiver {
- void onHandleAssistData(in Bundle resultData);
- void onHandleAssistScreenshot(in Bitmap screenshot);
-}
+interface SurfaceBuilderFactory {
+ SurfaceControl.Builder make(SurfaceSession s);
+};
+
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 13435d7..f70845e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -51,7 +51,7 @@
import java.io.PrintWriter;
import java.util.function.Consumer;
-class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerUser {
+class Task extends WindowContainer<AppWindowToken> {
static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
// Return value from {@link setBounds} indicating no change was made to the Task bounds.
private static final int BOUNDS_CHANGE_NONE = 0;
@@ -105,6 +105,9 @@
// stack moves and we in fact do so when moving from full screen to pinned.
private boolean mPreserveNonFloatingState = false;
+ private Dimmer mDimmer = new Dimmer(this);
+ private final Rect mTmpDimBoundsRect = new Rect();
+
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription,
TaskWindowContainerController controller) {
@@ -188,12 +191,6 @@
EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeTask");
mDeferRemoval = false;
- // Make sure to remove dim layer user first before removing task its from parent.
- DisplayContent content = getDisplayContent();
- if (content != null) {
- content.mDimLayerController.removeDimLayerUser(this);
- }
-
super.removeImmediately();
}
@@ -237,6 +234,8 @@
@Override
void onParentSet() {
+ super.onParentSet();
+
// Update task bounds if needed.
updateDisplayInfo(getDisplayContent());
@@ -312,9 +311,7 @@
mBounds.set(bounds);
mRotation = rotation;
- if (displayContent != null) {
- displayContent.mDimLayerController.updateDimLayer(this);
- }
+
onOverrideConfigurationChanged(overrideConfig);
return boundsChange;
}
@@ -482,7 +479,6 @@
}
/** Bounds of the task to be used for dimming, as well as touch related tests. */
- @Override
public void getDimBounds(Rect out) {
final DisplayContent displayContent = mStack.getDisplayContent();
// It doesn't matter if we in particular are part of the resize, since we couldn't have
@@ -634,23 +630,6 @@
return null;
}
- @Override
- public boolean dimFullscreen() {
- return isFullscreen();
- }
-
- @Override
- public int getLayerForDim(WindowStateAnimator animator, int layerOffset, int defaultLayer) {
- // If the dim layer is for a starting window, move the dim layer back in the z-order behind
- // the lowest activity window to ensure it does not occlude the main window if it is
- // translucent
- final AppWindowToken appToken = animator.mWin.mAppToken;
- if (animator.mAttrType == TYPE_APPLICATION_STARTING && hasChild(appToken) ) {
- return Math.min(defaultLayer, appToken.getLowestAnimLayer() - layerOffset);
- }
- return defaultLayer;
- }
-
boolean isFullscreen() {
if (useCurrentBounds()) {
return mFillsParent;
@@ -661,16 +640,6 @@
return true;
}
- @Override
- public DisplayInfo getDisplayInfo() {
- return getDisplayContent().getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return getDisplayContent() != null;
- }
-
void forceWindowsScaleable(boolean force) {
mService.openSurfaceTransaction();
try {
@@ -718,9 +687,18 @@
mPreserveNonFloatingState = false;
}
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
@Override
- public String toShortString() {
- return "Task=" + mTaskId;
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getDimBounds(mTmpDimBoundsRect);
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
}
@CallSuper
@@ -757,4 +735,8 @@
wtoken.dump(pw, triplePrefix);
}
}
+
+ String toShortString() {
+ return "Task=" + mTaskId;
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 12f6b5a..5d4ba09 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -59,7 +59,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-class TaskPositioner implements DimLayer.DimLayerUser {
+class TaskPositioner {
private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
private static final String TAG_LOCAL = "TaskPositioner";
private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
@@ -99,9 +99,6 @@
private WindowPositionerEventReceiver mInputEventReceiver;
private Display mDisplay;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
- private DimLayer mDimLayer;
- @CtrlType
- private int mCurrentDimSide;
private Rect mTmpRect = new Rect();
private int mSideMargin;
private int mMinVisibleWidth;
@@ -207,15 +204,6 @@
mService.mActivityManager.resizeTask(
mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
}
-
- if (mCurrentDimSide != CTRL_NONE) {
- final int createMode = mCurrentDimSide == CTRL_LEFT
- ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
- : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
- mService.mActivityManager.setTaskWindowingModeSplitScreenPrimary(
- mTask.mTaskId, createMode, true /*toTop*/, true /* animate */,
- null /* initialBounds */);
- }
} catch(RemoteException e) {}
// Post back to WM to handle clean-ups. We still need the input
@@ -243,7 +231,9 @@
/**
* @param display The Display that the window being dragged is on.
*/
- void register(Display display) {
+ void register(DisplayContent displayContent) {
+ final Display display = displayContent.getDisplay();
+
if (DEBUG_TASK_POSITIONING) {
Slog.d(TAG, "Registering task positioner");
}
@@ -305,7 +295,6 @@
}
mService.pauseRotationLocked();
- mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
@@ -336,12 +325,6 @@
mDragWindowHandle = null;
mDragApplicationHandle = null;
mDisplay = null;
-
- if (mDimLayer != null) {
- mDimLayer.destroySurface();
- mDimLayer = null;
- }
- mCurrentDimSide = CTRL_NONE;
mDragEnded = true;
// Resume rotations after a drag.
@@ -434,7 +417,6 @@
}
updateWindowDragBounds(nX, nY, mTmpRect);
- updateDimLayerVisibility(nX);
return false;
}
@@ -621,88 +603,6 @@
"updateWindowDragBounds: " + mWindowDragBounds);
}
- private void updateDimLayerVisibility(int x) {
- @CtrlType
- int dimSide = getDimSide(x);
- if (dimSide == mCurrentDimSide) {
- return;
- }
-
- mCurrentDimSide = dimSide;
-
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
- mService.openSurfaceTransaction();
- if (mCurrentDimSide == CTRL_NONE) {
- mDimLayer.hide();
- } else {
- showDimLayer();
- }
- mService.closeSurfaceTransaction("updateDimLayerVisibility");
- }
-
- /**
- * Returns the side of the screen the dim layer should be shown.
- * @param x horizontal coordinate used to determine if the dim layer should be shown
- * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
- * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
- * shouldn't be shown.
- */
- private int getDimSide(int x) {
- if (!mTask.mStack.inFreeformWindowingMode()
- || !mTask.mStack.fillsParent()
- || mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
- return CTRL_NONE;
- }
-
- mTask.mStack.getDimBounds(mTmpRect);
- if (x - mSideMargin <= mTmpRect.left) {
- return CTRL_LEFT;
- }
- if (x + mSideMargin >= mTmpRect.right) {
- return CTRL_RIGHT;
- }
-
- return CTRL_NONE;
- }
-
- private void showDimLayer() {
- mTask.mStack.getDimBounds(mTmpRect);
- if (mCurrentDimSide == CTRL_LEFT) {
- mTmpRect.right = mTmpRect.centerX();
- } else if (mCurrentDimSide == CTRL_RIGHT) {
- mTmpRect.left = mTmpRect.centerX();
- }
-
- mDimLayer.setBounds(mTmpRect);
- mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
- RESIZING_HINT_DURATION_MS);
- }
-
- @Override /** {@link DimLayer.DimLayerUser} */
- public boolean dimFullscreen() {
- return isFullscreen();
- }
-
- boolean isFullscreen() {
- return false;
- }
-
- @Override /** {@link DimLayer.DimLayerUser} */
- public DisplayInfo getDisplayInfo() {
- return mTask.mStack.getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return mTask != null && mTask.getDisplayContent() != null;
- }
-
- @Override
- public void getDimBounds(Rect out) {
- // This dim layer user doesn't need this.
- }
-
- @Override
public String toShortString() {
return TAG;
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 170feac..f9062a8 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -55,6 +55,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.Surface;
+import android.view.SurfaceControl;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
@@ -63,7 +64,7 @@
import java.io.PrintWriter;
-public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser,
+public class TaskStack extends WindowContainer<Task> implements
BoundsAnimationTarget {
/** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
* restrict IME adjustment so that a min portion of top stack remains visible.*/
@@ -108,8 +109,8 @@
/** Density as of last time {@link #mBounds} was set. */
private int mDensity;
- /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
- private DimLayer mAnimationBackgroundSurface;
+ private SurfaceControl mAnimationBackgroundSurface;
+ private boolean mAnimationBackgroundSurfaceIsShown = false;
/** The particular window with an Animation with non-zero background color. */
private WindowStateAnimator mAnimationBackgroundAnimator;
@@ -149,6 +150,13 @@
Rect mPreAnimationBounds = new Rect();
+ private Dimmer mDimmer = new Dimmer(this);
+
+ /**
+ * For {@link #prepareSurfaces}.
+ */
+ final Rect mTmpDimBoundsRect = new Rect();
+
TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
mService = service;
mStackId = stackId;
@@ -245,6 +253,35 @@
}
}
+ private void setAnimationBackgroundBounds(Rect bounds) {
+ if (mAnimationBackgroundSurface == null) {
+ return;
+ }
+ getPendingTransaction().setSize(mAnimationBackgroundSurface, bounds.width(), bounds.height())
+ .setPosition(mAnimationBackgroundSurface, 0, 0);
+ scheduleAnimation();
+ }
+
+ private void hideAnimationSurface() {
+ if (mAnimationBackgroundSurface == null) {
+ return;
+ }
+ getPendingTransaction().hide(mAnimationBackgroundSurface);
+ mAnimationBackgroundSurfaceIsShown = false;
+ scheduleAnimation();
+ }
+
+ private void showAnimationSurface(float alpha) {
+ if (mAnimationBackgroundSurface == null) {
+ return;
+ }
+ getPendingTransaction().setLayer(mAnimationBackgroundSurface, Integer.MIN_VALUE)
+ .setAlpha(mAnimationBackgroundSurface, alpha)
+ .show(mAnimationBackgroundSurface);
+ mAnimationBackgroundSurfaceIsShown = true;
+ scheduleAnimation();
+ }
+
private boolean setBounds(Rect bounds) {
boolean oldFullscreen = mFillsParent;
int rotation = Surface.ROTATION_0;
@@ -267,10 +304,7 @@
return false;
}
- if (mDisplayContent != null) {
- mDisplayContent.mDimLayerController.updateDimLayer(this);
- mAnimationBackgroundSurface.setBounds(bounds);
- }
+ setAnimationBackgroundBounds(bounds);
mBounds.set(bounds);
mRotation = rotation;
@@ -368,7 +402,6 @@
}
/** Bounds of the stack with other system factors taken into consideration. */
- @Override
public void getDimBounds(Rect out) {
getBounds(out);
}
@@ -442,7 +475,7 @@
mTmpRect2.set(mBounds);
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
if (inSplitScreenPrimaryWindowingMode()) {
- repositionDockedStackAfterRotation(mTmpRect2);
+ repositionPrimarySplitScreenStackAfterRotation(mTmpRect2);
snapDockedStackAfterRotation(mTmpRect2);
final int newDockSide = getDockSide(mTmpRect2);
@@ -466,14 +499,14 @@
}
/**
- * Some dock sides are not allowed by the policy. This method queries the policy and moves
- * the docked stack around if needed.
+ * Some primary split screen sides are not allowed by the policy. This method queries the policy
+ * and moves the primary stack around if needed.
*
- * @param inOutBounds the bounds of the docked stack to adjust
+ * @param inOutBounds the bounds of the primary stack to adjust
*/
- private void repositionDockedStackAfterRotation(Rect inOutBounds) {
+ private void repositionPrimarySplitScreenStackAfterRotation(Rect inOutBounds) {
int dockSide = getDockSide(inOutBounds);
- if (mService.mPolicy.isDockSideAllowed(dockSide)) {
+ if (mDisplayContent.getDockedDividerController().canPrimaryStackDockTo(dockSide)) {
return;
}
mDisplayContent.getLogicalDisplayRect(mTmpRect);
@@ -685,9 +718,12 @@
public void onConfigurationChanged(Configuration newParentConfig) {
final int prevWindowingMode = getWindowingMode();
super.onConfigurationChanged(newParentConfig);
- if (mDisplayContent != null && prevWindowingMode != getWindowingMode()) {
- mDisplayContent.onStackWindowingModeChanged(this);
+ final int windowingMode = getWindowingMode();
+ if (mDisplayContent == null || prevWindowingMode == windowingMode) {
+ return;
}
+ mDisplayContent.onStackWindowingModeChanged(this);
+ updateBoundsForWindowModeChange();
}
@Override
@@ -697,41 +733,46 @@
}
mDisplayContent = dc;
- mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
- "animation background stackId=" + mStackId);
+ updateBoundsForWindowModeChange();
+ mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer(true)
+ .setName("animation background stackId=" + mStackId)
+ .build();
+
+ super.onDisplayChanged(dc);
+ }
+
+ private void updateBoundsForWindowModeChange() {
Rect bounds = null;
- final TaskStack dockedStack = dc.getSplitScreenPrimaryStackIgnoringVisibility();
- if (inSplitScreenPrimaryWindowingMode()
- || (dockedStack != null && inSplitScreenSecondaryWindowingMode()
- && !dockedStack.fillsParent())) {
+ final boolean inSplitScreenPrimary = inSplitScreenPrimaryWindowingMode();
+ final TaskStack splitScreenStack =
+ mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ if (inSplitScreenPrimary || (splitScreenStack != null
+ && inSplitScreenSecondaryWindowingMode() && !splitScreenStack.fillsParent())) {
// The existence of a docked stack affects the size of other static stack created since
// the docked stack occupies a dedicated region on screen, but only if the dock stack is
// not fullscreen. If it's fullscreen, it means that we are in the transition of
// dismissing it, so we must not resize this stack.
bounds = new Rect();
- dc.getLogicalDisplayRect(mTmpRect);
+ mDisplayContent.getLogicalDisplayRect(mTmpRect);
mTmpRect2.setEmpty();
- if (dockedStack != null) {
- dockedStack.getRawBounds(mTmpRect2);
+ if (splitScreenStack != null) {
+ splitScreenStack.getRawBounds(mTmpRect2);
}
final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
== SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(mTmpRect, bounds, mTmpRect2,
- mDisplayContent.mDividerControllerLocked.getContentWidth(),
- dockedOnTopOrLeft);
+ mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
} else if (inPinnedWindowingMode()) {
// Update the bounds based on any changes to the display info
getAnimationOrCurrentBounds(mTmpRect2);
- boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
- mTmpRect2, mTmpRect3);
- if (updated) {
+ if (mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
+ mTmpRect2, mTmpRect3)) {
bounds = new Rect(mTmpRect3);
}
}
updateDisplayInfo(bounds);
- super.onDisplayChanged(dc);
}
/**
@@ -909,32 +950,26 @@
@Override
void onParentSet() {
+ super.onParentSet();
+
if (getParent() != null || mDisplayContent == null) {
return;
}
- // Looks like the stack was removed from the display. Go ahead and clean things up.
- mDisplayContent.mDimLayerController.removeDimLayerUser(this);
EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
if (mAnimationBackgroundSurface != null) {
- mAnimationBackgroundSurface.destroySurface();
+ mAnimationBackgroundSurface.destroy();
mAnimationBackgroundSurface = null;
}
- if (inSplitScreenPrimaryWindowingMode()) {
- mDisplayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(false);
- }
-
mDisplayContent = null;
mService.mWindowPlacerLocked.requestTraversal();
}
void resetAnimationBackgroundAnimator() {
mAnimationBackgroundAnimator = null;
- if (mAnimationBackgroundSurface != null) {
- mAnimationBackgroundSurface.hide();
- }
+ hideAnimationSurface();
}
void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
@@ -943,8 +978,7 @@
|| animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
mAnimationBackgroundAnimator = winAnimator;
animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator);
- mAnimationBackgroundSurface.show(animLayer - LAYER_OFFSET_DIM,
- ((color >> 24) & 0xff) / 255f, 0);
+ showAnimationSurface(((color >> 24) & 0xff) / 255f);
}
}
@@ -1249,7 +1283,7 @@
}
proto.write(FILLS_PARENT, mFillsParent);
mBounds.writeToProto(proto, BOUNDS);
- proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurface.isDimming());
+ proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown);
proto.end(token);
}
@@ -1272,9 +1306,8 @@
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
mChildren.get(taskNdx).dump(prefix + " ", pw);
}
- if (mAnimationBackgroundSurface.isDimming()) {
- pw.println(prefix + "mWindowAnimationBackgroundSurface:");
- mAnimationBackgroundSurface.printTo(prefix + " ", pw);
+ if (mAnimationBackgroundSurfaceIsShown) {
+ pw.println(prefix + "mWindowAnimationBackgroundSurface is shown");
}
if (!mExitingAppTokens.isEmpty()) {
pw.println();
@@ -1298,11 +1331,6 @@
}
@Override
- public boolean dimFullscreen() {
- return !isActivityTypeStandard() || fillsParent();
- }
-
- @Override
boolean fillsParent() {
if (useCurrentBounds()) {
return mFillsParent;
@@ -1314,16 +1342,6 @@
}
@Override
- public DisplayInfo getDisplayInfo() {
- return mDisplayContent.getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return mDisplayContent != null;
- }
-
- @Override
public String toString() {
return "{stackId=" + mStackId + " tasks=" + mChildren + "}";
}
@@ -1332,7 +1350,6 @@
return toShortString();
}
- @Override
public String toShortString() {
return "Stack=" + mStackId;
}
@@ -1690,4 +1707,32 @@
|| activityType == ACTIVITY_TYPE_RECENTS
|| activityType == ACTIVITY_TYPE_ASSISTANT;
}
+
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getDimBounds(mTmpDimBoundsRect);
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
+ }
+
+ public DisplayInfo getDisplayInfo() {
+ return mDisplayContent.getDisplayInfo();
+ }
+
+ void dim(float alpha) {
+ mDimmer.dimAbove(getPendingTransaction(), alpha);
+ scheduleAnimation();
+ }
+
+ void stopDimming() {
+ mDimmer.stopDim(getPendingTransaction());
+ scheduleAnimation();
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 65f8cdf..b3bb0b7 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -103,6 +103,10 @@
}
}
+ public void positionChildAtTop(AppWindowContainerController childController) {
+ positionChildAt(childController, POSITION_TOP);
+ }
+
public void positionChildAt(AppWindowContainerController childController, int position) {
synchronized(mService.mWindowMap) {
final AppWindowToken aToken = childController.mContainer;
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index d97aaac..9216b66 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -53,7 +53,7 @@
private int mLastDH;
private boolean mDrawNeeded;
- Watermark(Display display, DisplayMetrics dm, SurfaceSession session, String[] tokens) {
+ Watermark(DisplayContent dc, DisplayMetrics dm, String[] tokens) {
if (false) {
Log.i(TAG_WM, "*********************** WATERMARK");
for (int i=0; i<tokens.length; i++) {
@@ -61,7 +61,7 @@
}
}
- mDisplay = display;
+ mDisplay = dc.getDisplay();
mTokens = tokens;
StringBuilder builder = new StringBuilder(32);
@@ -114,7 +114,7 @@
SurfaceControl ctrl = null;
try {
- ctrl = new SurfaceControl.Builder(session)
+ ctrl = dc.makeOverlay()
.setName("WatermarkSurface")
.setSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 1912095..20bade67 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -200,7 +200,7 @@
++mAnimTransactionSequence;
dc.updateWindowsForAnimator(this);
dc.updateWallpaperForAnimator(this);
- dc.prepareWindowSurfaces();
+ dc.prepareSurfaces();
}
for (int i = 0; i < numDisplays; i++) {
@@ -214,8 +214,6 @@
if (screenRotationAnimation != null) {
screenRotationAnimation.updateSurfacesInTransaction();
}
-
- orAnimating(dc.animateDimLayers());
orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
//TODO (multidisplay): Magnification is supported only for the default display.
if (accessibilityController != null && dc.isDefaultDisplay) {
@@ -237,6 +235,13 @@
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
}
+ final int numDisplays = mDisplayContentsAnimators.size();
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+ dc.onPendingTransactionApplied();
+ }
+
boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
boolean doRequest = false;
if (mBulkUpdateParams != 0) {
@@ -271,6 +276,7 @@
mService.destroyPreservedSurfaceLocked();
mService.mWindowPlacerLocked.destroyPendingSurfaces();
+
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8f4b897..a5e6288 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -21,9 +21,13 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
+import static android.view.SurfaceControl.Transaction;
import android.annotation.CallSuper;
import android.content.res.Configuration;
+import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.util.Pools;
import android.util.proto.ProtoOutputStream;
@@ -64,7 +68,14 @@
new Pools.SynchronizedPool<>(3);
// The owner/creator for this container. No controller if null.
- private WindowContainerController mController;
+ WindowContainerController mController;
+
+ protected SurfaceControl mSurfaceControl;
+
+ /**
+ * Applied as part of the animation pass in "prepareSurfaces".
+ */
+ private Transaction mPendingTransaction = new Transaction();
@Override
final protected WindowContainer getParent() {
@@ -101,7 +112,22 @@
* Supposed to be overridden and contain actions that should be executed after parent was set.
*/
void onParentSet() {
- // Do nothing by default.
+ if (mParent == null) {
+ return;
+ }
+ if (mSurfaceControl == null) {
+ // If we don't yet have a surface, but we now have a parent, we should
+ // build a surface.
+ mSurfaceControl = makeSurface().build();
+ getPendingTransaction().show(mSurfaceControl);
+ } else {
+ // If we have a surface but a new parent, we just need to perform a reparent.
+ getPendingTransaction().reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
+ }
+
+ // Either way we need to ask the parent to assign us a Z-order.
+ mParent.assignChildLayers();
+ scheduleAnimation();
}
// Temp. holders for a chain of containers we are currently processing.
@@ -188,6 +214,11 @@
mChildren.remove(child);
}
+ if (mSurfaceControl != null) {
+ destroyAfterPendingTransaction(mSurfaceControl);
+ mSurfaceControl = null;
+ }
+
if (mParent != null) {
mParent.removeChild(this);
}
@@ -195,6 +226,7 @@
if (mController != null) {
setController(null);
}
+
}
/**
@@ -407,7 +439,7 @@
}
/**
-a * Returns whether this child is on top of the window hierarchy.
+ * @return Whether this child is on top of the window hierarchy.
*/
boolean isOnTop() {
return getParent().getTopChild() == this && getParent().isOnTop();
@@ -673,6 +705,103 @@
mController = controller;
}
+ SurfaceControl.Builder makeSurface() {
+ final WindowContainer p = getParent();
+ return p.makeChildSurface(this);
+ }
+
+ /**
+ * @param child The WindowContainer this child surface is for, or null if the Surface
+ * is not assosciated with a WindowContainer (e.g. a surface used for Dimming).
+ */
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ final WindowContainer p = getParent();
+ // Give the parent a chance to set properties. In hierarchy v1 we rely
+ // on this to set full-screen dimensions on all our Surface-less Layers.
+ final SurfaceControl.Builder b = p.makeChildSurface(child);
+ if (child != null && child.isScreenOverlay()) {
+ // If it's a screen overlay it's been promoted in the hierarchy (wrt to the
+ // WindowContainer hierarchy vs the SurfaceControl hierarchy)
+ // and we shouldn't set ourselves as the parent.
+ return b;
+ } else {
+ return b.setParent(mSurfaceControl);
+ }
+ }
+
+ /**
+ * There are various layers which require promotion from the WindowContainer
+ * hierarchy to the Overlay layer described in {@link DisplayContent}. See {@link WindowState}
+ * for the particular usage.
+ *
+ * TODO: Perhaps this should be eliminated, either through modifying
+ * the window container hierarchy or through modifying the way we express these overlay
+ * Surfaces (for example, the Magnification Overlay could be implemented like the Strict-mode
+ * Flash and not actually use a WindowState).
+ */
+ boolean isScreenOverlay() {
+ return false;
+ }
+
+ /**
+ * @return Whether this WindowContainer should be magnified by the accessibility magnifier.
+ */
+ boolean shouldMagnify() {
+ for (int i = 0; i < mChildren.size(); i++) {
+ if (!mChildren.get(i).shouldMagnify()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ SurfaceSession getSession() {
+ if (getParent() != null) {
+ return getParent().getSession();
+ }
+ return null;
+ }
+
+ void assignLayer(Transaction t, int layer) {
+ if (mSurfaceControl != null) {
+ t.setLayer(mSurfaceControl, layer);
+ }
+ }
+
+ void assignChildLayers(Transaction t) {
+ int layer = 0;
+ boolean boosting = false;
+
+ // We use two passes as a way to promote children which
+ // need Z-boosting to the end of the list.
+ for (int i = 0; i < 2; i++ ) {
+ for (int j = 0; j < mChildren.size(); ++j) {
+ final WindowContainer wc = mChildren.get(j);
+ if (wc.needsZBoost() && !boosting) {
+ continue;
+ }
+ wc.assignLayer(t, layer);
+ wc.assignChildLayers(t);
+
+ layer++;
+ }
+ boosting = true;
+ }
+ }
+
+ void assignChildLayers() {
+ assignChildLayers(getPendingTransaction());
+ }
+
+ boolean needsZBoost() {
+ for (int i = 0; i < mChildren.size(); i++) {
+ if (mChildren.get(i).needsZBoost()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Write to a protocol buffer output stream. Protocol buffer message definition is at
* {@link com.android.server.wm.proto.WindowContainerProto}.
@@ -719,4 +848,59 @@
mConsumerWrapperPool.release(this);
}
}
+
+ // TODO(b/68336570): Should this really be on WindowContainer since it
+ // can only be used on the top-level nodes that aren't animated?
+ // (otherwise we would be fighting other callers of setMatrix).
+ void applyMagnificationSpec(Transaction t, MagnificationSpec spec) {
+ if (shouldMagnify()) {
+ t.setMatrix(mSurfaceControl, spec.scale, 0, 0, spec.scale)
+ .setPosition(mSurfaceControl, spec.offsetX, spec.offsetY);
+ } else {
+ for (int i = 0; i < mChildren.size(); i++) {
+ mChildren.get(i).applyMagnificationSpec(t, spec);
+ }
+ }
+ }
+
+ /**
+ * TODO: Once we totally eliminate global transaction we will pass transaction in here
+ * rather than merging to global.
+ */
+ void prepareSurfaces() {
+ SurfaceControl.mergeToGlobalTransaction(getPendingTransaction());
+ for (int i = 0; i < mChildren.size(); i++) {
+ mChildren.get(i).prepareSurfaces();
+ }
+ }
+
+ /**
+ * Trigger a call to prepareSurfaces from the animation thread, such that
+ * mPendingTransaction will be applied.
+ */
+ void scheduleAnimation() {
+ if (mParent != null) {
+ mParent.scheduleAnimation();
+ }
+ }
+
+ SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
+ /**
+ * Destroy a given surface after executing mPendingTransaction. This is
+ * largely a workaround for destroy not being part of transactions
+ * rather than an intentional design, so please take care when
+ * expanding use.
+ */
+ void destroyAfterPendingTransaction(SurfaceControl surface) {
+ if (mParent != null) {
+ mParent.destroyAfterPendingTransaction(surface);
+ }
+ }
+
+ Transaction getPendingTransaction() {
+ return mPendingTransaction;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
deleted file mode 100644
index 7caf2fe..0000000
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.util.Slog;
-
-import java.util.ArrayDeque;
-import java.util.function.Consumer;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
-import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
-
-/**
- * Controller for assigning layers to windows on the display.
- *
- * This class encapsulates general algorithm for assigning layers and special rules that we need to
- * apply on top. The general algorithm goes through windows from bottom to the top and the higher
- * the window is, the higher layer is assigned. The final layer is equal to base layer +
- * adjustment from the order. This means that the window list is assumed to be ordered roughly by
- * the base layer (there are exceptions, e.g. due to keyguard and wallpaper and they need to be
- * handled with care, because they break the algorithm).
- *
- * On top of the general algorithm we add special rules, that govern such amazing things as:
- * <li>IME (which has higher base layer, but will be positioned above application windows)</li>
- * <li>docked/pinned windows (that need to be lifted above other application windows, including
- * animations)
- * <li>dock divider (which needs to live above applications, but below IME)</li>
- * <li>replaced windows, which need to live above their normal level, because they anticipate
- * an animation</li>.
- */
-class WindowLayersController {
- private final WindowManagerService mService;
-
- WindowLayersController(WindowManagerService service) {
- mService = service;
- }
-
- private ArrayDeque<WindowState> mPinnedWindows = new ArrayDeque<>();
- private ArrayDeque<WindowState> mDockedWindows = new ArrayDeque<>();
- private ArrayDeque<WindowState> mAssistantWindows = new ArrayDeque<>();
- private ArrayDeque<WindowState> mInputMethodWindows = new ArrayDeque<>();
- private WindowState mDockDivider = null;
- private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
- private int mCurBaseLayer;
- private int mCurLayer;
- private boolean mAnyLayerChanged;
- private int mHighestApplicationLayer;
- private int mHighestDockedAffectedLayer;
- private int mHighestLayerInImeTargetBaseLayer;
- private WindowState mImeTarget;
- private boolean mAboveImeTarget;
- private ArrayDeque<WindowState> mAboveImeTargetAppWindows = new ArrayDeque();
-
- private final Consumer<WindowState> mAssignWindowLayersConsumer = w -> {
- boolean layerChanged = false;
-
- int oldLayer = w.mLayer;
- if (w.mBaseLayer == mCurBaseLayer) {
- mCurLayer += WINDOW_LAYER_MULTIPLIER;
- } else {
- mCurBaseLayer = mCurLayer = w.mBaseLayer;
- }
- assignAnimLayer(w, mCurLayer);
-
- // TODO: Preserved old behavior of code here but not sure comparing oldLayer to
- // mAnimLayer and mLayer makes sense...though the worst case would be unintentional
- // layer reassignment.
- if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
- layerChanged = true;
- mAnyLayerChanged = true;
- }
-
- if (w.mAppToken != null) {
- mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
- w.mWinAnimator.mAnimLayer);
- }
- if (mImeTarget != null && w.mBaseLayer == mImeTarget.mBaseLayer) {
- mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer,
- w.mWinAnimator.mAnimLayer);
- }
- if (w.getAppToken() != null && w.inSplitScreenSecondaryWindowingMode()) {
- mHighestDockedAffectedLayer = Math.max(mHighestDockedAffectedLayer,
- w.mWinAnimator.mAnimLayer);
- }
-
- collectSpecialWindows(w);
-
- if (layerChanged) {
- w.scheduleAnimationIfDimming();
- }
- };
-
- final void assignWindowLayers(DisplayContent dc) {
- if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based",
- new RuntimeException("here").fillInStackTrace());
-
- reset();
- dc.forAllWindows(mAssignWindowLayersConsumer, false /* traverseTopToBottom */);
-
- adjustSpecialWindows();
-
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && mAnyLayerChanged
- && dc.getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onWindowLayersChangedLocked();
- }
-
- if (DEBUG_LAYERS) logDebugLayers(dc);
- }
-
- private void logDebugLayers(DisplayContent dc) {
- dc.forAllWindows((w) -> {
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- Slog.v(TAG_WM, "Assign layer " + w + ": " + "mBase=" + w.mBaseLayer
- + " mLayer=" + w.mLayer + (w.mAppToken == null
- ? "" : " mAppLayer=" + w.mAppToken.getAnimLayerAdjustment())
- + " =mAnimLayer=" + winAnimator.mAnimLayer);
- }, false /* traverseTopToBottom */);
- }
-
- private void reset() {
- mPinnedWindows.clear();
- mInputMethodWindows.clear();
- mDockedWindows.clear();
- mAssistantWindows.clear();
- mReplacingWindows.clear();
- mDockDivider = null;
-
- mCurBaseLayer = 0;
- mCurLayer = 0;
- mAnyLayerChanged = false;
-
- mHighestApplicationLayer = 0;
- mHighestDockedAffectedLayer = 0;
- mHighestLayerInImeTargetBaseLayer = (mImeTarget != null) ? mImeTarget.mBaseLayer : 0;
- mImeTarget = mService.mInputMethodTarget;
- mAboveImeTarget = false;
- mAboveImeTargetAppWindows.clear();
- }
-
- private void collectSpecialWindows(WindowState w) {
- if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
- mDockDivider = w;
- return;
- }
- if (w.mWillReplaceWindow) {
- mReplacingWindows.add(w);
- }
- if (w.mIsImWindow) {
- mInputMethodWindows.add(w);
- return;
- }
- if (mImeTarget != null) {
- if (w.getParentWindow() == mImeTarget && w.mSubLayer > 0) {
- // Child windows of the ime target with a positive sub-layer should be placed above
- // the IME.
- mAboveImeTargetAppWindows.add(w);
- } else if (mAboveImeTarget && w.mAppToken != null) {
- // windows of apps above the IME target should be placed above the IME.
- mAboveImeTargetAppWindows.add(w);
- }
- if (w == mImeTarget) {
- mAboveImeTarget = true;
- }
- }
-
- final int windowingMode = w.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_PINNED) {
- mPinnedWindows.add(w);
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mDockedWindows.add(w);
- }
- if (w.isActivityTypeAssistant()) {
- mAssistantWindows.add(w);
- }
- }
-
- private void adjustSpecialWindows() {
- // The following adjustments are beyond the highest docked-affected layer
- int layer = mHighestDockedAffectedLayer + TYPE_LAYER_OFFSET;
-
- // Adjust the docked stack windows and dock divider above only the windows that are affected
- // by the docked stack. When this happens, also boost the assistant window layers, otherwise
- // the docked stack windows & divider would be promoted above the assistant.
- if (!mDockedWindows.isEmpty() && mHighestDockedAffectedLayer > 0) {
- while (!mDockedWindows.isEmpty()) {
- final WindowState window = mDockedWindows.remove();
- layer = assignAndIncreaseLayerIfNeeded(window, layer);
- }
-
- layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
-
- while (!mAssistantWindows.isEmpty()) {
- final WindowState window = mAssistantWindows.remove();
- if (window.mLayer > mHighestDockedAffectedLayer) {
- layer = assignAndIncreaseLayerIfNeeded(window, layer);
- }
- }
- }
-
- // The following adjustments are beyond the highest app layer or boosted layer
- layer = Math.max(layer, mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER);
-
- // We know that we will be animating a relaunching window in the near future, which will
- // receive a z-order increase. We want the replaced window to immediately receive the same
- // treatment, e.g. to be above the dock divider.
- while (!mReplacingWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
- }
-
- while (!mPinnedWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
- }
-
- // Make sure IME is the highest window in the base layer of it's target.
- if (mImeTarget != null) {
- if (mImeTarget.mAppToken == null) {
- // For non-app ime targets adjust the layer we start from to match what we found
- // when assigning layers. Otherwise, just use the highest app layer we have some far.
- layer = mHighestLayerInImeTargetBaseLayer + WINDOW_LAYER_MULTIPLIER;
- }
-
- while (!mInputMethodWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mInputMethodWindows.remove(), layer);
- }
-
- // Adjust app windows the should be displayed above the IME since they are above the IME
- // target.
- while (!mAboveImeTargetAppWindows.isEmpty()) {
- layer = assignAndIncreaseLayerIfNeeded(mAboveImeTargetAppWindows.remove(), layer);
- }
- }
-
- }
-
- private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
- if (win != null) {
- assignAnimLayer(win, layer);
- // Make sure we leave space in-between normal windows for dims and such.
- layer += WINDOW_LAYER_MULTIPLIER;
- }
- return layer;
- }
-
- private void assignAnimLayer(WindowState w, int layer) {
- w.mLayer = layer;
- w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
- + w.getSpecialWindowAnimLayerAdjustment();
- if (w.mAppToken != null) {
- w.mAppToken.mAppAnimator.updateThumbnailLayer();
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0652767..4656539 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -124,6 +124,7 @@
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.IActivityManager;
+import android.app.IAssistDataReceiver;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ContentResolver;
@@ -226,7 +227,6 @@
import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.R;
-import com.android.internal.app.IAssistDataReceiver;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -245,6 +245,7 @@
import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.input.InputManagerService;
+import android.view.DisplayFrames;
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
@@ -528,7 +529,6 @@
AccessibilityController mAccessibilityController;
- final SurfaceSession mFxSession;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
CircularDisplayMask mCircularDisplayMask;
@@ -803,6 +803,13 @@
static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
new WindowManagerThreadPriorityBooster();
+ class DefaultSurfaceBuilderFactory implements SurfaceBuilderFactory {
+ public SurfaceControl.Builder make(SurfaceSession s) {
+ return new SurfaceControl.Builder(s);
+ }
+ };
+ SurfaceBuilderFactory mSurfaceBuilderFactory = new DefaultSurfaceBuilderFactory();
+
static void boostPriorityForLockedSection() {
sThreadPriorityBooster.boost();
}
@@ -991,7 +998,6 @@
mPointerEventDispatcher = null;
}
- mFxSession = new SurfaceSession();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
mDisplays = mDisplayManager.getDisplays();
for (Display display : mDisplays) {
@@ -1452,23 +1458,19 @@
prepareNoneTransitionForRelaunching(atoken);
}
- if (displayContent.isDefaultDisplay) {
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final Rect taskBounds;
- if (atoken != null && atoken.getTask() != null) {
- taskBounds = mTmpRect;
- atoken.getTask().getBounds(mTmpRect);
- } else {
- taskBounds = null;
- }
- if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayInfo.rotation,
- displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets,
- outStableInsets, outOutsets)) {
- res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
- }
+ final DisplayFrames displayFrames = displayContent.mDisplayFrames;
+ // TODO: Not sure if onDisplayInfoUpdated() call is needed.
+ displayFrames.onDisplayInfoUpdated(displayContent.getDisplayInfo());
+ final Rect taskBounds;
+ if (atoken != null && atoken.getTask() != null) {
+ taskBounds = mTmpRect;
+ atoken.getTask().getBounds(mTmpRect);
} else {
- outContentInsets.setEmpty();
- outStableInsets.setEmpty();
+ taskBounds = null;
+ }
+ if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayFrames, outContentInsets,
+ outStableInsets, outOutsets)) {
+ res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
}
if (mInTouchMode) {
@@ -2481,11 +2483,8 @@
mWaitingForConfig = true;
displayContent.setLayoutNeeded();
int anim[] = new int[2];
- if (displayContent.isDimming()) {
- anim[0] = anim[1] = 0;
- } else {
- mPolicy.selectRotationAnimationLw(anim);
- }
+ mPolicy.selectRotationAnimationLw(anim);
+
startFreezingDisplayLocked(false, anim[0], anim[1], displayContent);
config = new Configuration(mTempConfiguration);
}
@@ -3595,8 +3594,7 @@
com.android.internal.R.dimen.circular_display_mask_thickness);
mCircularDisplayMask = new CircularDisplayMask(
- getDefaultDisplayContentLocked().getDisplay(),
- mFxSession,
+ getDefaultDisplayContentLocked(),
mPolicy.getWindowLayerFromTypeLw(
WindowManager.LayoutParams.TYPE_POINTER)
* TYPE_LAYER_MULTIPLIER + 10, screenOffset, maskThickness);
@@ -3624,8 +3622,7 @@
if (mEmulatorDisplayOverlay == null) {
mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
mContext,
- getDefaultDisplayContentLocked().getDisplay(),
- mFxSession,
+ getDefaultDisplayContentLocked(),
mPolicy.getWindowLayerFromTypeLw(
WindowManager.LayoutParams.TYPE_POINTER)
* TYPE_LAYER_MULTIPLIER + 10);
@@ -3673,7 +3670,7 @@
// TODO(multi-display): support multiple displays
if (mStrictModeFlash == null) {
mStrictModeFlash = new StrictModeFlash(
- getDefaultDisplayContentLocked().getDisplay(), mFxSession);
+ getDefaultDisplayContentLocked());
}
mStrictModeFlash.setVisibility(on);
} finally {
@@ -4525,7 +4522,7 @@
Display display = displayContent.getDisplay();
mTaskPositioner = new TaskPositioner(this);
- mTaskPositioner.register(display);
+ mTaskPositioner.register(displayContent);
mInputMonitor.updateInputWindowsLw(true /*force*/);
// We need to grab the touch focus so that the touch events during the
@@ -4668,7 +4665,7 @@
synchronized(mWindowMap) {
mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TOUCHSCREEN);
- configureDisplayPolicyLocked(getDefaultDisplayContentLocked());
+ getDefaultDisplayContentLocked().configureDisplayPolicy();
}
try {
@@ -5525,7 +5522,7 @@
if (!mDisplayReady) {
return;
}
- configureDisplayPolicyLocked(displayContent);
+ displayContent.configureDisplayPolicy();
displayContent.setLayoutNeeded();
final int displayId = displayContent.getDisplayId();
@@ -5546,18 +5543,6 @@
mWindowPlacerLocked.performSurfacePlacement();
}
- void configureDisplayPolicyLocked(DisplayContent displayContent) {
- mPolicy.setInitialDisplaySize(displayContent.getDisplay(),
- displayContent.mBaseDisplayWidth,
- displayContent.mBaseDisplayHeight,
- displayContent.mBaseDisplayDensity);
-
- DisplayInfo displayInfo = displayContent.getDisplayInfo();
- mPolicy.setDisplayOverscan(displayContent.getDisplay(),
- displayInfo.overscanLeft, displayInfo.overscanTop,
- displayInfo.overscanRight, displayInfo.overscanBottom);
- }
-
/**
* Get an array with display ids ordered by focus priority - last items should be given
* focus first. Sparse array just maps position to displayId.
@@ -5907,7 +5892,7 @@
displayContent.updateDisplayInfo();
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
- mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
+ inTransaction, mPolicy.isDefaultOrientationForced(), isSecure,
this);
mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
screenRotationAnimation);
@@ -5967,11 +5952,10 @@
// TODO(multidisplay): rotation on main screen only.
DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Get rotation animation again, with new top window
- boolean isDimming = displayContent.isDimming();
- if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
+ if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
mExitAnimId = mEnterAnimId = 0;
}
- if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
+ if (screenRotationAnimation.dismiss(MAX_ANIMATION_DURATION,
getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
scheduleAnimationLocked();
@@ -6055,8 +6039,8 @@
if (toks != null && toks.length > 0) {
// TODO(multi-display): Show watermarks on secondary displays.
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- mWatermark = new Watermark(displayContent.getDisplay(),
- displayContent.mRealDisplayMetrics, mFxSession, toks);
+ mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics,
+ toks);
}
}
} catch (FileNotFoundException e) {
@@ -6875,11 +6859,16 @@
public void setWillReplaceWindow(IBinder token, boolean animate) {
synchronized (mWindowMap) {
final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null || !appWindowToken.hasContentToDisplay()) {
+ if (appWindowToken == null) {
Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
+ token);
return;
}
+ if (!appWindowToken.hasContentToDisplay()) {
+ Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
+ + token);
+ return;
+ }
appWindowToken.setWillReplaceWindows(animate);
}
}
@@ -6899,11 +6888,16 @@
void setWillReplaceWindows(IBinder token, boolean childrenOnly) {
synchronized (mWindowMap) {
final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null || !appWindowToken.hasContentToDisplay()) {
+ if (appWindowToken == null) {
Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
+ token);
return;
}
+ if (!appWindowToken.hasContentToDisplay()) {
+ Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
+ + token);
+ return;
+ }
if (childrenOnly) {
appWindowToken.setWillReplaceChildWindows();
@@ -7370,7 +7364,9 @@
@Override
public int getInputMethodWindowVisibleHeight() {
synchronized (mWindowMap) {
- return mPolicy.getInputMethodWindowVisibleHeightLw();
+ // TODO(multi-display): Have caller pass in the display they are interested in.
+ final DisplayContent dc = getDefaultDisplayContentLocked();
+ return dc.mDisplayFrames.getInputMethodWindowVisibleHeight();
}
}
@@ -7560,4 +7556,13 @@
w.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
}, false /* traverseTopToBottom */);
}
+
+ public void applyMagnificationSpec(MagnificationSpec spec) {
+ getDefaultDisplayContentLocked().applyMagnificationSpec(spec);
+ }
+
+ SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
+ return mSurfaceBuilderFactory.make(s);
+ }
}
+
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6b1932d..52b7a25 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -23,6 +23,7 @@
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+import static android.view.SurfaceControl.Transaction;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -41,6 +42,7 @@
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
@@ -54,6 +56,9 @@
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
@@ -147,6 +152,8 @@
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowInfo;
@@ -602,6 +609,14 @@
};
};
+ /**
+ * Indicates whether we have requested a Dim (in the sense of {@link Dimmer}) from our host
+ * container.
+ */
+ private boolean mIsDimming = false;
+
+ private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
@@ -2034,23 +2049,6 @@
return isVisibleOrAdding();
}
- void scheduleAnimationIfDimming() {
- final DisplayContent dc = getDisplayContent();
- if (dc == null) {
- return;
- }
-
- // If layout is currently deferred, we want to hold of with updating the layers.
- if (mService.mWindowPlacerLocked.isLayoutDeferred()) {
- return;
- }
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- if (dimLayerUser != null && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator)) {
- // Force an animation pass just to update the mDimLayer layer.
- mService.scheduleAnimationLocked();
- }
- }
-
private final class DeadWindowEventReceiver extends InputEventReceiver {
DeadWindowEventReceiver(InputChannel inputChannel) {
super(inputChannel, mService.mH.getLooper());
@@ -2106,31 +2104,12 @@
mInputWindowHandle.inputChannel = null;
}
- void applyDimLayerIfNeeded() {
- // When the app is terminated (eg. from Recents), the task might have already been
- // removed with the window pending removal. Don't apply dim in such cases, as there
- // will be no more updateDimLayer() calls, which leaves the dimlayer invalid.
- final AppWindowToken token = mAppToken;
- if (token != null && token.removed) {
- return;
- }
-
- final DisplayContent dc = getDisplayContent();
- if (!mAnimatingExit && mAppDied) {
- // If app died visible, apply a dim over the window to indicate that it's inactive
- dc.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
- } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
- && dc != null && !mAnimatingExit && isVisible()) {
- dc.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
- }
- }
-
- private DimLayer.DimLayerUser getDimLayerUser() {
+ private Dimmer getDimmer() {
Task task = getTask();
if (task != null) {
- return task;
+ return task.getDimmer();
}
- return getStack();
+ return getStack().getDimmer();
}
/** Returns true if the replacement window was removed. */
@@ -2152,9 +2131,6 @@
private void removeReplacedWindow() {
if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + this);
- if (isDimming()) {
- transferDimToReplacement();
- }
mWillReplaceWindow = false;
mAnimateReplacingWindow = false;
mReplacingRemoveRequested = false;
@@ -2217,11 +2193,11 @@
// need to intercept touches outside of that window. The dim layer user
// associated with the window (task or stack) will give us the good bounds, as
// they would be used to display the dim layer.
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- if (dimLayerUser != null) {
- dimLayerUser.getDimBounds(mTmpRect);
+ final Task task = getTask();
+ if (task != null) {
+ task.getDimBounds(mTmpRect);
} else {
- getVisibleBounds(mTmpRect);
+ getStack().getDimBounds(mTmpRect);
}
if (inFreeformWindowingMode()) {
// For freeform windows we the touch region to include the whole surface for the
@@ -2729,14 +2705,6 @@
return displayContent.isDefaultDisplay;
}
- @Override
- public boolean isDimming() {
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- final DisplayContent dc = getDisplayContent();
- return dimLayerUser != null && dc != null
- && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator);
- }
-
void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
mShowToOwnerOnly = showToOwnerOnly;
}
@@ -3071,7 +3039,7 @@
if (task == null) {
return false;
}
- if (!inSplitScreenWindowingMode()) {
+ if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()) {
return false;
}
if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) {
@@ -3591,15 +3559,6 @@
return winY;
}
- private void transferDimToReplacement() {
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- final DisplayContent dc = getDisplayContent();
- if (dimLayerUser != null && dc != null) {
- dc.mDimLayerController.applyDim(dimLayerUser,
- mReplacementWindow.mWinAnimator, (mAttrs.flags & FLAG_DIM_BEHIND) != 0);
- }
- }
-
// During activity relaunch due to resize, we sometimes use window replacement
// for only child windows (as the main window is handled by window preservation)
// and the big surface.
@@ -4388,4 +4347,80 @@
return false;
}
}
+
+ @Override
+ boolean shouldMagnify() {
+ if (mAttrs.type == TYPE_INPUT_METHOD ||
+ mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
+ return false;
+ } else if (isScreenOverlay()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ boolean isScreenOverlay() {
+ // It's tempting to wonder: Have we forgotten the rounded corners overlay?
+ // worry not: it's a fake TYPE_NAVIGATION_BAR.
+ if (mAttrs.type == TYPE_MAGNIFICATION_OVERLAY ||
+ mAttrs.type == TYPE_NAVIGATION_BAR ||
+ mAttrs.type == TYPE_STATUS_BAR) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ SurfaceSession getSession() {
+ if (mSession.mSurfaceSession != null) {
+ return mSession.mSurfaceSession;
+ } else {
+ return getParent().getSession();
+ }
+ }
+
+ @Override
+ boolean needsZBoost() {
+ return getAnimLayerAdjustment() > 0 || mWillReplaceWindow;
+ }
+
+ @Override
+ SurfaceControl.Builder makeSurface() {
+ return mToken.makeChildSurface(this);
+ }
+
+
+ @Override
+ void prepareSurfaces() {
+ mIsDimming = false;
+ if (!mAnimatingExit && mAppDied) {
+ mIsDimming = true;
+ getDimmer().dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
+ } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
+ && !mAnimatingExit && isVisible()) {
+ mIsDimming = true;
+ getDimmer().dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
+ }
+
+ mWinAnimator.prepareSurfaceLocked(true);
+ super.prepareSurfaces();
+ }
+
+ @Override
+ void assignLayer(Transaction t, int layer) {
+ // See comment in assignRelativeLayerForImeTargetChild
+ if (!isChildWindow()
+ || (mService.mInputMethodTarget != getParentWindow())
+ || !inSplitScreenWindowingMode()) {
+ super.assignLayer(t, layer);
+ return;
+ }
+ getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
+ }
+
+ @Override
+ public boolean isDimming() {
+ return mIsDimming;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 86397ae..840cc40 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -60,7 +60,6 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
-import android.view.MagnificationSpec;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -559,7 +558,10 @@
}
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", false);
if (mSurfaceController != null) {
- mSurfaceController.setLayer(mAnimLayer + 1);
+ // Our SurfaceControl is always at layer 0 within the parent Surface managed by
+ // window-state. We want this old Surface to stay on top of the new one
+ // until we do the swap, so we place it at layer 1.
+ mSurfaceController.mSurfaceControl.setLayer(1);
}
mDestroyPreservedSurfaceUponRedraw = true;
mSurfaceDestroyDeferred = true;
@@ -730,7 +732,6 @@
try {
mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top, false);
mSurfaceController.setLayerStackInTransaction(getLayerStack());
- mSurfaceController.setLayer(mAnimLayer);
} finally {
mService.closeSurfaceTransaction("createSurfaceLocked");
}
@@ -867,22 +868,6 @@
mPendingDestroySurface = null;
}
- void applyMagnificationSpec(MagnificationSpec spec, Matrix transform) {
- final int surfaceInsetLeft = mWin.mAttrs.surfaceInsets.left;
- final int surfaceInsetTop = mWin.mAttrs.surfaceInsets.top;
-
- if (spec != null && !spec.isNop()) {
- float scale = spec.scale;
- transform.postScale(scale, scale);
- transform.postTranslate(spec.offsetX, spec.offsetY);
-
- // As we are scaling the whole surface, to keep the content
- // in the same position we will also have to scale the surfaceInsets.
- transform.postTranslate(-(surfaceInsetLeft*scale - surfaceInsetLeft),
- -(surfaceInsetTop*scale - surfaceInsetTop));
- }
- }
-
void computeShownFrameLocked() {
final boolean selfTransformation = mHasLocalTransformation;
Transformation attachedTransformation =
@@ -969,11 +954,6 @@
tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
}
- MagnificationSpec spec = getMagnificationSpec();
- if (spec != null) {
- applyMagnificationSpec(spec, tmpMatrix);
- }
-
// "convert" it into SurfaceFlinger's format
// (a 2x2 matrix + an offset)
// Here we must not transform the position of the surface
@@ -1057,49 +1037,16 @@
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
- MagnificationSpec spec = getMagnificationSpec();
- if (spec != null) {
- final Rect frame = mWin.mFrame;
- final float tmpFloats[] = mService.mTmpFloats;
- final Matrix tmpMatrix = mWin.mTmpMatrix;
-
- tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
- tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
-
- applyMagnificationSpec(spec, tmpMatrix);
-
- tmpMatrix.getValues(tmpFloats);
-
- mHaveMatrix = true;
- mDsDx = tmpFloats[Matrix.MSCALE_X];
- mDtDx = tmpFloats[Matrix.MSKEW_Y];
- mDtDy = tmpFloats[Matrix.MSKEW_X];
- mDsDy = tmpFloats[Matrix.MSCALE_Y];
- float x = tmpFloats[Matrix.MTRANS_X];
- float y = tmpFloats[Matrix.MTRANS_Y];
- mWin.mShownPosition.set(Math.round(x), Math.round(y));
-
- mShownAlpha = mAlpha;
- } else {
- mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
- if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
- mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
- }
- mShownAlpha = mAlpha;
- mHaveMatrix = false;
- mDsDx = mWin.mGlobalScale;
- mDtDx = 0;
- mDtDy = 0;
- mDsDy = mWin.mGlobalScale;
+ mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
+ if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
+ mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
}
- }
-
- private MagnificationSpec getMagnificationSpec() {
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && mWin.getDisplayId() == DEFAULT_DISPLAY) {
- return mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
- }
- return null;
+ mShownAlpha = mAlpha;
+ mHaveMatrix = false;
+ mDsDx = mWin.mGlobalScale;
+ mDtDx = 0;
+ mDtDy = 0;
+ mDsDy = mWin.mGlobalScale;
}
/**
@@ -1140,26 +1087,6 @@
w.expandForSurfaceInsets(finalClipRect);
}
- // We may be applying a magnification spec to all windows,
- // simulating a transformation in screen space, in which case
- // we need to transform all other screen space values...including
- // the final crop. This is kind of messed up and we should look
- // in to actually transforming screen-space via a parent-layer.
- // b/38322835
- MagnificationSpec spec = getMagnificationSpec();
- if (spec != null && !spec.isNop()) {
- Matrix transform = mWin.mTmpMatrix;
- RectF finalCrop = mService.mTmpRectF;
- transform.reset();
- transform.postScale(spec.scale, spec.scale);
- transform.postTranslate(-spec.offsetX, -spec.offsetY);
- transform.mapRect(finalCrop);
- finalClipRect.top = (int) finalCrop.top;
- finalClipRect.left = (int) finalCrop.left;
- finalClipRect.right = (int) finalCrop.right;
- finalClipRect.bottom = (int) finalCrop.bottom;
- }
-
return true;
}
@@ -1517,7 +1444,6 @@
mReportSurfaceResized = true;
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- w.applyDimLayerIfNeeded();
}
}
@@ -1615,7 +1541,6 @@
mDtDy * w.mHScale * mExtraHScale,
mDsDy * w.mVScale * mExtraVScale,
recoveringMemory);
- mSurfaceController.setLayer(mAnimLayer);
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index a214523..6746754 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -53,7 +53,7 @@
final WindowStateAnimator mAnimator;
- private SurfaceControlWithBackground mSurfaceControl;
+ SurfaceControlWithBackground mSurfaceControl;
// Should only be set from within setShown().
private boolean mSurfaceShown = false;
@@ -101,7 +101,8 @@
mWindowSession = win.mSession;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
- final SurfaceControl.Builder b = new SurfaceControl.Builder(s)
+ final SurfaceControl.Builder b = win.makeSurface()
+ .setParent(win.getSurfaceControl())
.setName(name)
.setSize(w, h)
.setFormat(format)
@@ -245,25 +246,6 @@
}
}
- void setLayer(int layer) {
- if (mSurfaceControl != null) {
- mService.openSurfaceTransaction();
- try {
- if (mAnimator.mWin.usesRelativeZOrdering()) {
- mSurfaceControl.setRelativeLayer(
- mAnimator.mWin.getParentWindow()
- .mWinAnimator.mSurfaceController.mSurfaceControl,
- -1);
- } else {
- mSurfaceLayer = layer;
- mSurfaceControl.setLayer(layer);
- }
- } finally {
- mService.closeSurfaceTransaction("setLayer");
- }
- }
- }
-
void setLayerStackInTransaction(int layerStack) {
if (mSurfaceControl != null) {
mSurfaceControl.setLayerStack(layerStack);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index cd5e475..5081868 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -704,7 +704,7 @@
// Create a new surface for the thumbnail
WindowState window = appToken.findMainWindow();
- final SurfaceControl surfaceControl = new SurfaceControl.Builder(mService.mFxSession)
+ final SurfaceControl surfaceControl = appToken.makeSurface()
.setName("thumbnail anim")
.setSize(dirty.width(), dirty.height())
.setFormat(PixelFormat.TRANSLUCENT)
@@ -712,7 +712,6 @@
window != null ? window.mOwnerUid : Binder.getCallingUid())
.build();
- surfaceControl.setLayerStack(display.getLayerStack());
if (SHOW_TRANSACTIONS) {
Slog.i(TAG, " THUMBNAIL " + surfaceControl + ": CREATE");
}
@@ -750,10 +749,13 @@
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
- openingAppAnimator.updateThumbnailLayer();
openingAppAnimator.thumbnail = surfaceControl;
openingAppAnimator.thumbnailAnimation = anim;
mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
+
+ // We parent the thumbnail to the app token, and just place it
+ // on top of anything else in the app token.
+ surfaceControl.setLayer(Integer.MAX_VALUE);
} catch (Surface.OutOfResourcesException e) {
Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w="
+ dirty.width() + " h=" + dirty.height(), e);
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 5657f6c..6aa5101 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -67,6 +67,7 @@
mWriteQueue.clear();
mTraceFile.delete();
try (OutputStream os = new FileOutputStream(mTraceFile)) {
+ mTraceFile.setReadable(true, false);
ProtoOutputStream proto = new ProtoOutputStream(os);
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
proto.flush();
@@ -147,7 +148,7 @@
}
static WindowTracing createDefaultAndStartLooper(Context context) {
- File file = new File("/data/system/window_trace.proto");
+ File file = new File("/data/misc/wmtrace/wm_trace.pb");
WindowTracing windowTracing = new WindowTracing(file);
new Thread(windowTracing::loop, "window_tracing").start();
return windowTracing;
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 36e9e7f..4a2da37 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -23,7 +23,6 @@
$(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
$(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
- $(LOCAL_REL_DIR)/com_android_server_location_ContextHubService.cpp \
$(LOCAL_REL_DIR)/com_android_server_location_GnssLocationProvider.cpp \
$(LOCAL_REL_DIR)/com_android_server_locksettings_SyntheticPasswordManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index 6403a5a..df53fee 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -51,8 +51,9 @@
using V1_0::BandConfig;
using V1_0::MetaData;
using V1_0::Result;
-using V1_2::ITunerCallback;
using V1_1::ProgramListResult;
+using V1_1::VendorKeyValue;
+using V1_2::ITunerCallback;
using utils::HalRevision;
static mutex gContextMutex;
@@ -93,6 +94,7 @@
wp<V1_1::IBroadcastRadio> mHalModule11;
sp<V1_0::ITuner> mHalTuner;
sp<V1_1::ITuner> mHalTuner11;
+ sp<V1_2::ITuner> mHalTuner12;
sp<HalDeathRecipient> mHalDeathRecipient;
private:
@@ -179,8 +181,11 @@
ctx.mHalTuner = halTuner;
ctx.mHalTuner11 = V1_1::ITuner::castFrom(halTuner).withDefault(nullptr);
+ ctx.mHalTuner12 = V1_2::ITuner::castFrom(halTuner).withDefault(nullptr);
ALOGW_IF(ctx.mHalRev >= HalRevision::V1_1 && ctx.mHalTuner11 == nullptr,
"Provided tuner does not implement 1.1 HAL");
+ ALOGW_IF(ctx.mHalRev >= HalRevision::V1_2 && ctx.mHalTuner12 == nullptr,
+ "Provided tuner does not implement 1.2 HAL");
ctx.mHalDeathRecipient = new HalDeathRecipient(getNativeCallback(env, jTuner));
halTuner->linkToDeath(ctx.mHalDeathRecipient, 0);
@@ -194,16 +199,21 @@
return tuner;
}
-sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
+static sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
lock_guard<mutex> lk(gContextMutex);
return getHalTuner(getNativeContext(nativeContext));
}
-sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
+static sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
lock_guard<mutex> lk(gContextMutex);
return getNativeContext(nativeContext).mHalTuner11;
}
+static sp<V1_2::ITuner> getHalTuner12(jlong nativeContext) {
+ lock_guard<mutex> lk(gContextMutex);
+ return getNativeContext(nativeContext).mHalTuner12;
+}
+
sp<ITunerCallback> getNativeCallback(JNIEnv *env, JavaRef<jobject> const &tuner) {
return TunerCallback::getNativeCallback(env,
env->GetObjectField(tuner.get(), gjni.Tuner.tunerCallback));
@@ -233,6 +243,7 @@
ctx.mHalDeathRecipient = nullptr;
ctx.mHalTuner11 = nullptr;
+ ctx.mHalTuner12 = nullptr;
ctx.mHalTuner = nullptr;
}
@@ -488,6 +499,48 @@
convert::ThrowIfFailed(env, halResult);
}
+static jobject nativeSetParameters(JNIEnv *env, jobject obj, jlong nativeContext, jobject jParameters) {
+ ALOGV("%s", __func__);
+
+ auto halTuner = getHalTuner12(nativeContext);
+ if (halTuner == nullptr) {
+ ALOGI("Parameters are not supported with HAL < 1.2");
+ return nullptr;
+ }
+
+ JavaRef<jobject> jResults = nullptr;
+ auto parameters = convert::VendorInfoToHal(env, jParameters);
+ auto hidlResult = halTuner->setParameters(parameters,
+ [&](const hidl_vec<VendorKeyValue> results) {
+ jResults = convert::VendorInfoFromHal(env, results);
+ });
+
+ if (convert::ThrowIfFailed(env, hidlResult)) return nullptr;
+
+ return jResults.release();
+}
+
+static jobject nativeGetParameters(JNIEnv *env, jobject obj, jlong nativeContext, jobject jKeys) {
+ ALOGV("%s", __func__);
+
+ auto halTuner = getHalTuner12(nativeContext);
+ if (halTuner == nullptr) {
+ ALOGI("Parameters are not supported with HAL < 1.2");
+ return nullptr;
+ }
+
+ JavaRef<jobject> jResults = nullptr;
+ auto keys = convert::StringListToHal(env, jKeys);
+ auto hidlResult = halTuner->getParameters(keys,
+ [&](const hidl_vec<VendorKeyValue> parameters) {
+ jResults = convert::VendorInfoFromHal(env, parameters);
+ });
+
+ if (convert::ThrowIfFailed(env, hidlResult)) return nullptr;
+
+ return jResults.release();
+}
+
static bool nativeIsAntennaConnected(JNIEnv *env, jobject obj, jlong nativeContext) {
ALOGV("%s", __func__);
auto halTuner = getHalTuner(nativeContext);
@@ -525,6 +578,8 @@
{ "nativeGetImage", "(JI)[B", (void*)nativeGetImage},
{ "nativeIsAnalogForced", "(J)Z", (void*)nativeIsAnalogForced },
{ "nativeSetAnalogForced", "(JZ)V", (void*)nativeSetAnalogForced },
+ { "nativeSetParameters", "(JLjava/util/Map;)Ljava/util/Map;", (void*)nativeSetParameters },
+ { "nativeGetParameters", "(JLjava/util/List;)Ljava/util/Map;", (void*)nativeGetParameters },
{ "nativeIsAntennaConnected", "(J)Z", (void*)nativeIsAntennaConnected },
};
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.cpp b/services/core/jni/BroadcastRadio/TunerCallback.cpp
index ed7c9c4..d624df6 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.cpp
+++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp
@@ -70,6 +70,7 @@
jmethodID onBackgroundScanAvailabilityChange;
jmethodID onBackgroundScanComplete;
jmethodID onProgramListChanged;
+ jmethodID onParametersUpdated;
} TunerCallback;
} gjni;
@@ -346,7 +347,10 @@
Return<void> NativeCallback::parametersUpdated(const hidl_vec<VendorKeyValue>& parameters) {
ALOGV("%s", __func__);
- // TODO(b/65862441): pass this callback to the front-end
+ mCallbackThread.enqueue([this, parameters](JNIEnv *env) {
+ auto jParameters = convert::VendorInfoFromHal(env, parameters);
+ env->CallVoidMethod(mJCallback, gjni.TunerCallback.onParametersUpdated, jParameters.get());
+ });
return {};
}
@@ -437,6 +441,8 @@
"onBackgroundScanComplete", "()V");
gjni.TunerCallback.onProgramListChanged = GetMethodIDOrDie(env, tunerCbClass,
"onProgramListChanged", "()V");
+ gjni.TunerCallback.onParametersUpdated = GetMethodIDOrDie(env, tunerCbClass,
+ "onParametersUpdated", "(Ljava/util/Map;)V");
auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/TunerCallback",
gTunerCallbackMethods, NELEM(gTunerCallbackMethods));
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index 8dfa14f..734ce79 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -34,6 +34,7 @@
namespace utils = hardware::broadcastradio::utils;
using hardware::Return;
+using hardware::hidl_string;
using hardware::hidl_vec;
using regions::RegionalBandConfig;
@@ -98,6 +99,11 @@
} HashMap;
struct {
+ jmethodID get;
+ jmethodID size;
+ } List;
+
+ struct {
jmethodID put;
} Map;
@@ -145,8 +151,21 @@
jclass clazz;
jmethodID cstor;
} ParcelableException;
+
+ struct {
+ jclass clazz;
+ } String;
} gjni;
+static jstring CastToString(JNIEnv *env, jobject obj) {
+ if (env->IsInstanceOf(obj, gjni.String.clazz)) {
+ return static_cast<jstring>(obj);
+ } else {
+ ALOGE("Cast failed, object is not a string");
+ return nullptr;
+ }
+}
+
template <>
bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult) {
return __ThrowIfFailedHidl(env, hidlResult);
@@ -250,12 +269,26 @@
}
static std::string StringFromJava(JNIEnv *env, JavaRef<jstring> &jStr) {
- auto cstr = (jStr == nullptr) ? nullptr : env->GetStringUTFChars(jStr.get(), nullptr);
+ if (jStr == nullptr) return {};
+ auto cstr = env->GetStringUTFChars(jStr.get(), nullptr);
std::string str(cstr);
env->ReleaseStringUTFChars(jStr.get(), cstr);
return str;
}
+hidl_vec<hidl_string> StringListToHal(JNIEnv *env, jobject jList) {
+ auto len = (jList == nullptr) ? 0 : env->CallIntMethod(jList, gjni.List.size);
+ hidl_vec<hidl_string> list(len);
+
+ for (decltype(len) i = 0; i < len; i++) {
+ auto jString = make_javaref(env, CastToString(env, env->CallObjectMethod(
+ jList, gjni.List.get, i)));
+ list[i] = StringFromJava(env, jString);
+ }
+
+ return list;
+}
+
JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hidl_vec<VendorKeyValue> &info) {
ALOGV("%s(%s)", __func__, toString(info).substr(0, 100).c_str());
@@ -275,7 +308,10 @@
auto jInfoArr = make_javaref(env, static_cast<jobjectArray>(env->CallStaticObjectMethod(
gjni.Convert.clazz, gjni.Convert.stringMapToNative, jInfo)));
- LOG_FATAL_IF(jInfoArr == nullptr, "Converted array is null");
+ if (jInfoArr == nullptr) {
+ ALOGE("Converted array is null");
+ return {};
+ }
auto len = env->GetArrayLength(jInfoArr.get());
hidl_vec<VendorKeyValue> vec;
@@ -651,6 +687,10 @@
gjni.HashMap.clazz = MakeGlobalRefOrDie(env, hashMapClass);
gjni.HashMap.cstor = GetMethodIDOrDie(env, hashMapClass, "<init>", "()V");
+ auto listClass = FindClassOrDie(env, "java/util/List");
+ gjni.List.get = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
+ gjni.List.size = GetMethodIDOrDie(env, listClass, "size", "()I");
+
auto mapClass = FindClassOrDie(env, "java/util/Map");
gjni.Map.put = GetMethodIDOrDie(env, mapClass, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
@@ -713,6 +753,9 @@
gjni.ParcelableException.clazz = MakeGlobalRefOrDie(env, parcelableExcClass);
gjni.ParcelableException.cstor = GetMethodIDOrDie(env, parcelableExcClass, "<init>",
"(Ljava/lang/Throwable;)V");
+
+ auto stringClass = FindClassOrDie(env, "java/lang/String");
+ gjni.String.clazz = MakeGlobalRefOrDie(env, stringClass);
}
} // namespace android
diff --git a/services/core/jni/BroadcastRadio/convert.h b/services/core/jni/BroadcastRadio/convert.h
index 1fc75f0..b8c55c1 100644
--- a/services/core/jni/BroadcastRadio/convert.h
+++ b/services/core/jni/BroadcastRadio/convert.h
@@ -35,6 +35,8 @@
namespace V1_0 = hardware::broadcastradio::V1_0;
namespace V1_1 = hardware::broadcastradio::V1_1;
+hardware::hidl_vec<hardware::hidl_string> StringListToHal(JNIEnv *env, jobject jList);
+
JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hardware::hidl_vec<V1_1::VendorKeyValue> &info);
hardware::hidl_vec<V1_1::VendorKeyValue> VendorInfoToHal(JNIEnv *env, jobject jInfo);
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
index 35e65bc..df85e31 100644
--- a/services/core/jni/com_android_server_UsbDescriptorParser.cpp
+++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
@@ -17,18 +17,16 @@
#define LOG_TAG "UsbHostManagerJNI"
#include "utils/Log.h"
-#include <stdlib.h>
-
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include <usbhost/usbhost.h>
-#define MAX_DESCRIPTORS_LENGTH 4096
+#define MAX_DESCRIPTORS_LENGTH 16384
// com.android.server.usb.descriptors
extern "C" {
-jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors_1native(
+jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors(
JNIEnv* env, jobject thiz, jstring deviceAddr) {
const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL);
struct usb_device* device = usb_device_open(deviceAddrStr);
@@ -41,7 +39,6 @@
int fd = usb_device_get_fd(device);
if (fd < 0) {
- usb_device_close(device);
return NULL;
}
@@ -49,48 +46,17 @@
jbyte buffer[MAX_DESCRIPTORS_LENGTH];
lseek(fd, 0, SEEK_SET);
int numBytes = read(fd, buffer, sizeof(buffer));
- jbyteArray ret = NULL;
+
usb_device_close(device);
- if (numBytes > 0) {
+ jbyteArray ret = NULL;
+ if (numBytes != 0) {
ret = env->NewByteArray(numBytes);
env->SetByteArrayRegion(ret, 0, numBytes, buffer);
- } else {
- ALOGE("error reading descriptors\n");
}
-
return ret;
}
-jstring JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getDescriptorString_1native(
- JNIEnv* env, jobject thiz, jstring deviceAddr, jint stringId) {
-
- const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL);
- struct usb_device* device = usb_device_open(deviceAddrStr);
- env->ReleaseStringUTFChars(deviceAddr, deviceAddrStr);
-
- if (!device) {
- ALOGE("usb_device_open failed");
- return NULL;
- }
-
- int fd = usb_device_get_fd(device);
- if (fd < 0) {
- ALOGE("usb_device_get_fd failed");
- usb_device_close(device);
- return NULL;
- }
-
- char* c_str = usb_device_get_string(device, stringId, 0 /*timeout*/);
-
- jstring j_str = env->NewStringUTF(c_str);
-
- free(c_str);
- usb_device_close(device);
-
- return j_str;
-}
-
} // extern "C"
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index 11f508b..88ae824 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -22,6 +22,8 @@
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
+#include <usbhost/usbhost.h>
+
#include <stdio.h>
#include <asm/byteorder.h>
#include <sys/types.h>
@@ -29,20 +31,22 @@
#include <fcntl.h>
#include <sys/ioctl.h>
-#include <usbhost/usbhost.h>
-
-#define MAX_DESCRIPTORS_LENGTH 4096
-
namespace android
{
+static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
+
static struct parcel_file_descriptor_offsets_t
{
jclass mClass;
jmethodID mConstructor;
} gParcelFileDescriptorOffsets;
-static jmethodID method_usbDeviceAdded;
+static jmethodID method_beginUsbDeviceAdded;
+static jmethodID method_addUsbConfiguration;
+static jmethodID method_addUsbInterface;
+static jmethodID method_addUsbEndpoint;
+static jmethodID method_endUsbDeviceAdded;
static jmethodID method_usbDeviceRemoved;
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -53,63 +57,101 @@
}
}
-static int usb_device_added(const char *devAddress, void* clientData) {
- struct usb_device *device = usb_device_open(devAddress);
+static int usb_device_added(const char *devname, void* client_data) {
+ struct usb_descriptor_header* desc;
+ struct usb_descriptor_iter iter;
+
+ struct usb_device *device = usb_device_open(devname);
if (!device) {
ALOGE("usb_device_open failed\n");
return 0;
}
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject thiz = (jobject)client_data;
const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
- int classID = deviceDesc->bDeviceClass;
- int subClassID = deviceDesc->bDeviceSubClass;
- // get the raw descriptors
- int fd = usb_device_get_fd(device);
- if (fd < 0) {
- ALOGE("usb_device_get_fd failed\n");
- usb_device_close(device);
- // TODO return an error code here?
- return 0;
+ char *manufacturer = usb_device_get_manufacturer_name(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ char *product = usb_device_get_product_name(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ int version = usb_device_get_version(device);
+ char *serial = usb_device_get_serial(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+
+ jstring deviceName = env->NewStringUTF(devname);
+ jstring manufacturerName = AndroidRuntime::NewStringLatin1(env, manufacturer);
+ jstring productName = AndroidRuntime::NewStringLatin1(env, product);
+ jstring serialNumber = AndroidRuntime::NewStringLatin1(env, serial);
+
+ jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded,
+ deviceName, usb_device_get_vendor_id(device), usb_device_get_product_id(device),
+ deviceDesc->bDeviceClass, deviceDesc->bDeviceSubClass, deviceDesc->bDeviceProtocol,
+ manufacturerName, productName, version, serialNumber);
+
+ env->DeleteLocalRef(serialNumber);
+ env->DeleteLocalRef(productName);
+ env->DeleteLocalRef(manufacturerName);
+ env->DeleteLocalRef(deviceName);
+ free(manufacturer);
+ free(product);
+ free(serial);
+
+ if (!result) goto fail;
+
+ usb_descriptor_iter_init(device, &iter);
+
+ while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+ if (desc->bDescriptorType == USB_DT_CONFIG) {
+ struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc;
+ char *name = usb_device_get_string(device, config->iConfiguration,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ jstring configName = AndroidRuntime::NewStringLatin1(env, name);
+
+ env->CallVoidMethod(thiz, method_addUsbConfiguration,
+ config->bConfigurationValue, configName, config->bmAttributes,
+ config->bMaxPower);
+
+ env->DeleteLocalRef(configName);
+ free(name);
+ } else if (desc->bDescriptorType == USB_DT_INTERFACE) {
+ struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+ char *name = usb_device_get_string(device, interface->iInterface,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ jstring interfaceName = AndroidRuntime::NewStringLatin1(env, name);
+
+ env->CallVoidMethod(thiz, method_addUsbInterface,
+ interface->bInterfaceNumber, interfaceName, interface->bAlternateSetting,
+ interface->bInterfaceClass, interface->bInterfaceSubClass,
+ interface->bInterfaceProtocol);
+
+ env->DeleteLocalRef(interfaceName);
+ free(name);
+ } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
+ struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;
+
+ env->CallVoidMethod(thiz, method_addUsbEndpoint,
+ endpoint->bEndpointAddress, endpoint->bmAttributes,
+ __le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval);
+ }
}
- // from android_hardware_UsbDeviceConnection_get_desc()
- jbyte rawdescriptors[MAX_DESCRIPTORS_LENGTH];
- lseek(fd, 0, SEEK_SET);
- int numBytes = read(fd, rawdescriptors, sizeof(rawdescriptors));
+ env->CallVoidMethod(thiz, method_endUsbDeviceAdded);
+fail:
usb_device_close(device);
-
- if (numBytes > 0) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jobject thiz = (jobject)clientData;
- jstring deviceAddress = env->NewStringUTF(devAddress);
-
- jbyteArray descriptorsArray = env->NewByteArray(numBytes);
- env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawdescriptors);
-
- env->CallBooleanMethod(thiz, method_usbDeviceAdded,
- deviceAddress, classID, subClassID, descriptorsArray);
-
- env->DeleteLocalRef(descriptorsArray);
- env->DeleteLocalRef(deviceAddress);
-
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- } else {
- // TODO return an error code here?
- ALOGE("error reading descriptors\n");
- }
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
return 0;
}
-static int usb_device_removed(const char *devAddress, void* clientData) {
+static int usb_device_removed(const char *devname, void* client_data) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- jobject thiz = (jobject)clientData;
+ jobject thiz = (jobject)client_data;
- jstring deviceAddress = env->NewStringUTF(devAddress);
- env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceAddress);
- env->DeleteLocalRef(deviceAddress);
+ jstring deviceName = env->NewStringUTF(devname);
+ env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceName);
+ env->DeleteLocalRef(deviceName);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return 0;
}
@@ -126,11 +168,11 @@
}
static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject /* thiz */,
- jstring deviceAddress)
+ jstring deviceName)
{
- const char *deviceAddressStr = env->GetStringUTFChars(deviceAddress, NULL);
- struct usb_device* device = usb_device_open(deviceAddressStr);
- env->ReleaseStringUTFChars(deviceAddress, deviceAddressStr);
+ const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
+ struct usb_device* device = usb_device_open(deviceNameStr);
+ env->ReleaseStringUTFChars(deviceName, deviceNameStr);
if (!device)
return NULL;
@@ -164,12 +206,34 @@
ALOGE("Can't find com/android/server/usb/UsbHostManager");
return -1;
}
- method_usbDeviceAdded =
- env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;II[B)Z");
- if (method_usbDeviceAdded == NULL) {
+ method_beginUsbDeviceAdded = env->GetMethodID(clazz, "beginUsbDeviceAdded",
+ "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;ILjava/lang/String;)Z");
+ if (method_beginUsbDeviceAdded == NULL) {
ALOGE("Can't find beginUsbDeviceAdded");
return -1;
}
+ method_addUsbConfiguration = env->GetMethodID(clazz, "addUsbConfiguration",
+ "(ILjava/lang/String;II)V");
+ if (method_addUsbConfiguration == NULL) {
+ ALOGE("Can't find addUsbConfiguration");
+ return -1;
+ }
+ method_addUsbInterface = env->GetMethodID(clazz, "addUsbInterface",
+ "(ILjava/lang/String;IIII)V");
+ if (method_addUsbInterface == NULL) {
+ ALOGE("Can't find addUsbInterface");
+ return -1;
+ }
+ method_addUsbEndpoint = env->GetMethodID(clazz, "addUsbEndpoint", "(IIII)V");
+ if (method_addUsbEndpoint == NULL) {
+ ALOGE("Can't find addUsbEndpoint");
+ return -1;
+ }
+ method_endUsbDeviceAdded = env->GetMethodID(clazz, "endUsbDeviceAdded", "()V");
+ if (method_endUsbDeviceAdded == NULL) {
+ ALOGE("Can't find endUsbDeviceAdded");
+ return -1;
+ }
method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved",
"(Ljava/lang/String;)V");
if (method_usbDeviceRemoved == NULL) {
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
deleted file mode 100644
index ad372de..0000000
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ /dev/null
@@ -1,1201 +0,0 @@
-/*
- * Copyright 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_NDEBUG
-#undef LOG_TAG
-#define LOG_NDEBUG 0
-#define LOG_TAG "ContextHubService"
-
-#include <inttypes.h>
-#include <jni.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/endian.h>
-
-#include <chrono>
-#include <mutex>
-#include <queue>
-#include <unordered_map>
-#include <utility>
-
-#include <android-base/macros.h>
-#include <android/hardware/contexthub/1.0/IContexthub.h>
-#include <cutils/log.h>
-
-#include "core_jni_helpers.h"
-#include <nativehelper/JNIHelp.h>
-
-using android::hardware::contexthub::V1_0::AsyncEventType;
-using android::hardware::contexthub::V1_0::ContextHub;
-using android::hardware::contexthub::V1_0::ContextHubMsg;
-using android::hardware::contexthub::V1_0::HubAppInfo;
-using android::hardware::contexthub::V1_0::IContexthub;
-using android::hardware::contexthub::V1_0::IContexthubCallback;
-using android::hardware::contexthub::V1_0::NanoAppBinary;
-using android::hardware::contexthub::V1_0::Result;
-using android::hardware::contexthub::V1_0::TransactionResult;
-
-using android::hardware::Return;
-
-using std::chrono::steady_clock;
-
-// If a transaction takes longer than this, we'll allow it to be
-// canceled by a new transaction. Note we do _not_ automatically
-// cancel a transaction after this much time. We can have a
-// legal transaction which takes longer than this amount of time,
-// as long as no other new transactions are attempted after this
-// time has expired.
-constexpr auto kMinTransactionCancelTime = std::chrono::seconds(29);
-
-namespace android {
-
-constexpr uint32_t kNanoAppBinaryHeaderVersion = 1;
-
-// Important: this header is explicitly defined as little endian byte order, and
-// therefore may not match host endianness
-struct NanoAppBinaryHeader {
- uint32_t headerVersion; // 0x1 for this version
- uint32_t magic; // "NANO" (see NANOAPP_MAGIC in context_hub.h)
- uint64_t appId; // App Id, contains vendor id
- uint32_t appVersion; // Version of the app
- uint32_t flags; // Signed, encrypted
- uint64_t hwHubType; // Which hub type is this compiled for
- uint8_t targetChreApiMajorVersion; // Which CHRE API version this is compiled for
- uint8_t targetChreApiMinorVersion;
- uint8_t reserved[6];
-} __attribute__((packed));
-
-enum HubMessageType {
- CONTEXT_HUB_APPS_ENABLE = 1, // Enables loaded nano-app(s)
- CONTEXT_HUB_APPS_DISABLE = 2, // Disables loaded nano-app(s)
- CONTEXT_HUB_LOAD_APP = 3, // Load a supplied app
- CONTEXT_HUB_UNLOAD_APP = 4, // Unload a specified app
- CONTEXT_HUB_QUERY_APPS = 5, // Query for app(s) info on hub
- CONTEXT_HUB_QUERY_MEMORY = 6, // Query for memory info
- CONTEXT_HUB_OS_REBOOT = 7, // Request to reboot context HUB OS
-};
-
-constexpr jint OS_APP_ID = -1;
-constexpr jint INVALID_APP_ID = -2;
-
-constexpr jint MIN_APP_ID = 1;
-constexpr jint MAX_APP_ID = 128;
-
-constexpr size_t MSG_HEADER_SIZE = 4;
-constexpr size_t HEADER_FIELD_MSG_TYPE = 0;
-constexpr size_t HEADER_FIELD_MSG_VERSION = 1;
-constexpr size_t HEADER_FIELD_HUB_HANDLE = 2;
-constexpr size_t HEADER_FIELD_APP_INSTANCE = 3;
-
-constexpr size_t HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
-constexpr size_t HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
-constexpr size_t MSG_HEADER_SIZE_LOAD_APP = MSG_HEADER_SIZE + 2;
-
-jint getAppInstanceForAppId(uint64_t app_id);
-int onMessageReceipt(const uint32_t *header,
- size_t headerLen,
- const char *msg,
- size_t msgLen);
-void onHubReset(uint32_t hubId);
-void queryHubForApps(uint32_t hubId);
-void passOnOsResponse(uint32_t hubHandle,
- uint32_t msgType,
- TransactionResult result,
- const int8_t *additionalData,
- size_t additionalDataLen);
-
-bool closeLoadTxn(bool success, jint *appInstanceHandle);
-void closeUnloadTxn(bool success);
-int handleQueryAppsResponse(const std::vector<HubAppInfo> apps,
- uint32_t hubHandle);
-
-struct JniInfo {
- JavaVM *vm;
- jclass contextHubInfoClass;
- jclass contextHubServiceClass;
- jclass memoryRegionsClass;
-
- jobject jContextHubService;
-
- jmethodID msgReceiptCallBack;
-
- jmethodID contextHubInfoCtor;
- jmethodID contextHubInfoSetId;
- jmethodID contextHubInfoSetName;
- jmethodID contextHubInfoSetVendor;
- jmethodID contextHubInfoSetToolchain;
- jmethodID contextHubInfoSetPlatformVersion;
- jmethodID contextHubInfoSetStaticSwVersion;
- jmethodID contextHubInfoSetToolchainVersion;
- jmethodID contextHubInfoSetPeakMips;
- jmethodID contextHubInfoSetStoppedPowerDrawMw;
- jmethodID contextHubInfoSetSleepPowerDrawMw;
- jmethodID contextHubInfoSetPeakPowerDrawMw;
- jmethodID contextHubInfoSetSupportedSensors;
- jmethodID contextHubInfoSetMemoryRegions;
- jmethodID contextHubInfoSetMaxPacketLenBytes;
-
- jmethodID contextHubServiceMsgReceiptCallback;
- jmethodID contextHubServiceAddAppInstance;
- jmethodID contextHubServiceDeleteAppInstance;
-};
-
-
-
-class TxnManager {
-public:
- TxnManager() {
- mData = nullptr;
- mIsPending = false;
- }
-
- ~TxnManager() {
- closeTxn();
- }
-
- int addTxn(HubMessageType txnIdentifier, void *txnData) {
- std::lock_guard<std::mutex>lock(mLock);
- if (mIsPending) {
- ALOGW("Transaction already found pending when trying to add a new one.");
- return -1;
- }
- mIsPending = true;
- mFirstTimeTxnCanBeCanceled = steady_clock::now() + kMinTransactionCancelTime;
- mData = txnData;
- mIdentifier = txnIdentifier;
-
- return 0;
- }
-
- int closeTxn() {
- std::lock_guard<std::mutex>lock(mLock);
- closeTxnUnlocked();
- return 0;
- }
-
- bool isTxnPending() {
- std::lock_guard<std::mutex>lock(mLock);
- return mIsPending;
- }
-
- void closeAnyStaleTxns() {
- std::lock_guard<std::mutex>lock(mLock);
- if (mIsPending && steady_clock::now() >= mFirstTimeTxnCanBeCanceled) {
- ALOGW("Stale transaction canceled");
- closeTxnUnlocked();
- }
- }
-
- int fetchTxnData(HubMessageType *id, void **data) {
- if (id == nullptr || data == nullptr) {
- ALOGW("Null Params isNull{id, data} {%d, %d}",
- id == nullptr ? 1 : 0,
- data == nullptr ? 1 : 0);
- return -1;
- }
-
- std::lock_guard<std::mutex>lock(mLock);
- if (!mIsPending) {
- ALOGW("No Transactions pending");
- return -1;
- }
-
- *id = mIdentifier;
- *data = mData;
- return 0;
- }
-
- private:
- bool mIsPending; // Is a transaction pending
- std::mutex mLock; // mutex for manager
- HubMessageType mIdentifier; // What are we doing
- void *mData; // Details
- steady_clock::time_point mFirstTimeTxnCanBeCanceled;
-
- // Only call this if you hold the lock.
- void closeTxnUnlocked() {
- mIsPending = false;
- free(mData);
- mData = nullptr;
- }
-};
-
-
-struct ContextHubServiceCallback : IContexthubCallback {
- uint32_t mContextHubId;
-
- ContextHubServiceCallback(uint32_t hubId) {
- mContextHubId = hubId;
- }
-
- virtual Return<void> handleClientMsg(const ContextHubMsg &msg) {
- jint appHandle = getAppInstanceForAppId(msg.appName);
- if (appHandle < 0) {
- ALOGE("Filtering out message due to invalid App Instance.");
- } else {
- uint32_t msgHeader[MSG_HEADER_SIZE] = {};
- msgHeader[HEADER_FIELD_MSG_TYPE] = msg.msgType;
- msgHeader[HEADER_FIELD_HUB_HANDLE] = mContextHubId;
- msgHeader[HEADER_FIELD_APP_INSTANCE] = appHandle;
- onMessageReceipt(msgHeader,
- MSG_HEADER_SIZE,
- reinterpret_cast<const char *>(msg.msg.data()),
- msg.msg.size());
- }
-
- return android::hardware::Void();
- }
-
- virtual Return<void> handleHubEvent(AsyncEventType evt) {
- if (evt == AsyncEventType::RESTARTED) {
- ALOGW("Context Hub handle %d restarted", mContextHubId);
- onHubReset(mContextHubId);
- } else {
- ALOGW("Cannot handle event %u from hub %d", evt, mContextHubId);
- }
-
- return android::hardware::Void();
- }
-
- virtual Return<void> handleTxnResult(uint32_t txnId,
- TransactionResult result) {
- ALOGI("Handle transaction result , hubId %" PRIu32 ", txnId %" PRIu32 ", result %" PRIu32,
- mContextHubId,
- txnId,
- result);
-
- switch(txnId) {
- case CONTEXT_HUB_APPS_ENABLE:
- case CONTEXT_HUB_APPS_DISABLE:
- passOnOsResponse(mContextHubId, txnId, result, nullptr, 0);
- break;
-
- case CONTEXT_HUB_UNLOAD_APP:
- closeUnloadTxn(result == TransactionResult::SUCCESS);
- passOnOsResponse(mContextHubId, txnId, result, nullptr, 0);
- break;
-
- case CONTEXT_HUB_LOAD_APP:
- {
- jint appInstanceHandle = INVALID_APP_ID;
- bool appRunningOnHub = (result == TransactionResult::SUCCESS);
- if (!(closeLoadTxn(appRunningOnHub, &appInstanceHandle))) {
- if (appRunningOnHub) {
- // Now we're in an odd situation. Our nanoapp
- // is up and running on the Context Hub. However,
- // something went wrong in our Service code so that
- // we're not able to properly track this nanoapp
- // in our Service code. If we tell the Java layer
- // things are good, it's a lie because the handle
- // we give them will fail when used with the Service.
- // If we tell the Java layer this failed, it's kind
- // of a lie as well, since this nanoapp is running.
- //
- // We leave a more robust fix for later, and for
- // now just tell the user things have failed.
- //
- // TODO(b/30835981): Make this situation better.
- result = TransactionResult::FAILURE;
- }
- }
-
- passOnOsResponse(mContextHubId,
- txnId,
- result,
- reinterpret_cast<int8_t *>(&appInstanceHandle),
- sizeof(appInstanceHandle));
- break;
- }
-
- default:
- ALOGI("unrecognized transction id %" PRIu32, txnId);
- break;
- }
- return android::hardware::Void();
- }
-
- virtual Return<void> handleAppsInfo(
- const android::hardware::hidl_vec<HubAppInfo>& apps) {
- TransactionResult result = TransactionResult::SUCCESS;
- handleQueryAppsResponse(apps,mContextHubId);
- passOnOsResponse(mContextHubId, CONTEXT_HUB_QUERY_APPS, result, nullptr, 0);
- return android::hardware::Void();
- }
-
- virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode) {
- ALOGI("Handle app aport called from %" PRIx64 " with abort code %" PRIu32,
- appId,
- abortCode);
-
- // TODO: Plumb this to the clients interested in this app
- return android::hardware::Void();
- }
-
- void setContextHubId(uint32_t id) {
- mContextHubId = id;
- }
-
- uint32_t getContextHubId() {
- return(mContextHubId);
- }
-};
-
-struct AppInstanceInfo {
- HubAppInfo appInfo; // returned from the HAL
- uint64_t truncName; // Possibly truncated name for logging
- uint32_t hubHandle; // Id of the hub this app is on
- jint instanceId; // system wide unique instance id - assigned
-};
-
-struct ContextHubInfo {
- int numHubs;
- Vector<ContextHub> hubs;
- sp<IContexthub> contextHub;
-};
-
-struct ContextHubServiceDb {
- int initialized;
- ContextHubInfo hubInfo;
- JniInfo jniInfo;
- std::queue<jint> freeIds;
- std::unordered_map<jint, AppInstanceInfo> appInstances;
- TxnManager txnManager;
- std::vector<ContextHubServiceCallback *> regCallBacks;
-};
-
-ContextHubServiceDb db;
-
-bool getHubIdForHubHandle(int hubHandle, uint32_t *hubId) {
- if (hubHandle < 0 || hubHandle >= db.hubInfo.numHubs || hubId == nullptr) {
- return false;
- } else {
- *hubId = db.hubInfo.hubs[hubHandle].hubId;
- return true;
- }
-}
-
-int getHubHandleForAppInstance(jint id) {
- if (!db.appInstances.count(id)) {
- ALOGD("%s: Cannot find app for app instance %" PRId32,
- __FUNCTION__,
- id);
- return -1;
- }
-
- return db.appInstances[id].hubHandle;
-}
-
-jint getAppInstanceForAppId(uint64_t app_id) {
- auto end = db.appInstances.end();
- for (auto current = db.appInstances.begin(); current != end; ++current) {
- if (current->second.appInfo.appId == app_id) {
- return current->first;
- }
- }
- ALOGD("Cannot find app for app id %" PRIu64 ".", app_id);
- return -1;
-}
-
-uint64_t getAppIdForAppInstance(jint id) {
- if (!db.appInstances.count(id)) {
- return INVALID_APP_ID;
- }
- return db.appInstances[id].appInfo.appId;
-}
-
-void queryHubForApps(uint32_t hubId) {
- Result r = db.hubInfo.contextHub->queryApps(hubId);
- ALOGD("Sent query for apps to hub %" PRIu32 " with result %" PRIu32, hubId, r);
-}
-
-void sendQueryForApps() {
- for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
- queryHubForApps(db.hubInfo.hubs[i].hubId);
- }
-}
-
-int returnId(jint id) {
- // Note : This method is not thread safe.
- // id returned is guaranteed to be in use
- if (id >= 0) {
- db.freeIds.push(id);
- return 0;
- }
-
- return -1;
-}
-
-jint generateId() {
- // Note : This method is not thread safe.
- jint retVal = -1;
-
- if (!db.freeIds.empty()) {
- retVal = db.freeIds.front();
- db.freeIds.pop();
- }
-
- return retVal;
-}
-
-jint addAppInstance(const HubAppInfo *appInfo, uint32_t hubHandle,
- jint appInstanceHandle, JNIEnv *env) {
- // Not checking if the apps are indeed distinct
- AppInstanceInfo entry;
- assert(appInfo);
-
-
- entry.appInfo = *appInfo;
-
- entry.instanceId = appInstanceHandle;
- entry.truncName = appInfo->appId;
- entry.hubHandle = hubHandle;
- db.appInstances[appInstanceHandle] = entry;
- // Finally - let the service know of this app instance, to populate
- // the Java cache.
- env->CallIntMethod(db.jniInfo.jContextHubService,
- db.jniInfo.contextHubServiceAddAppInstance,
- hubHandle, entry.instanceId,
- entry.truncName,
- entry.appInfo.version);
-
- const char *action = (db.appInstances.count(appInstanceHandle) == 0) ? "Added" : "Updated";
- ALOGI("%s App 0x%" PRIx64 " on hub Handle %" PRId32
- " as appInstance %" PRId32, action, entry.truncName,
- entry.hubHandle, appInstanceHandle);
-
- return appInstanceHandle;
-}
-
-int deleteAppInstance(jint id, JNIEnv *env) {
- bool fullyDeleted = true;
-
- if (db.appInstances.count(id)) {
- db.appInstances.erase(id);
- } else {
- ALOGW("Cannot delete App id (%" PRId32 ") from the JNI C++ cache", id);
- fullyDeleted = false;
- }
- returnId(id);
-
- if ((env == nullptr) ||
- (env->CallIntMethod(db.jniInfo.jContextHubService,
- db.jniInfo.contextHubServiceDeleteAppInstance,
- id) != 0)) {
- ALOGW("Cannot delete App id (%" PRId32 ") from Java cache", id);
- fullyDeleted = false;
- }
-
- if (fullyDeleted) {
- ALOGI("Deleted App id : %" PRId32, id);
- return 0;
- }
- return -1;
-}
-
-int startLoadAppTxn(uint64_t appId, int hubHandle) {
- AppInstanceInfo *txnInfo = new AppInstanceInfo();
- jint instanceId = generateId();
-
- if (!txnInfo || instanceId < 0) {
- returnId(instanceId);
- delete txnInfo;
- return -1;
- }
-
- txnInfo->truncName = appId;
- txnInfo->hubHandle = hubHandle;
- txnInfo->instanceId = instanceId;
-
- txnInfo->appInfo.appId = appId;
- txnInfo->appInfo.version = -1; // Awaited
-
- if (db.txnManager.addTxn(CONTEXT_HUB_LOAD_APP, txnInfo) != 0) {
- returnId(instanceId);
- delete txnInfo;
- return -1;
- }
-
- return 0;
-}
-
-int startUnloadAppTxn(jint appInstanceHandle) {
- jint *txnData = new(jint);
- if (!txnData) {
- ALOGW("Cannot allocate memory to start unload transaction");
- return -1;
- }
-
- *txnData = appInstanceHandle;
-
- if (db.txnManager.addTxn(CONTEXT_HUB_UNLOAD_APP, txnData) != 0) {
- delete txnData;
- ALOGW("Cannot start transaction to unload app");
- return -1;
- }
-
- return 0;
-}
-
-void getHubsCb(const ::android::hardware::hidl_vec<ContextHub>& hubs) {
- for (size_t i = 0; i < hubs.size(); i++) {
- db.hubInfo.hubs.push_back(hubs[i]);
- }
-}
-
-void initContextHubService() {
- db.hubInfo.numHubs = 0;
-
- db.hubInfo.contextHub = IContexthub::getService();
-
- if (db.hubInfo.contextHub == nullptr) {
- ALOGE("Could not load context hub hal");
- } else {
- ALOGI("Loaded context hub hal, isRemote %s", db.hubInfo.contextHub->isRemote() ? "TRUE" : "FALSE");
- }
-
- // Prep for storing app info
- for (jint i = MIN_APP_ID; i <= MAX_APP_ID; i++) {
- db.freeIds.push(i);
- }
-
- if (db.hubInfo.contextHub != nullptr) {
- std::function<void(const ::android::hardware::hidl_vec<ContextHub>& hubs)> f = getHubsCb;
- if(!db.hubInfo.contextHub->getHubs(f).isOk()) {
- ALOGW("GetHubs Failed! transport error.");
- return;
- };
-
- int retNumHubs = db.hubInfo.hubs.size();
- ALOGD("ContextHubModule returned %d hubs ", retNumHubs);
- db.hubInfo.numHubs = retNumHubs;
-
- for (int i = 0; i < db.hubInfo.numHubs; i++) {
- ALOGI("Subscribing to hubHandle %d", i);
-
- ContextHubServiceCallback *callBackPtr =
- new ContextHubServiceCallback(db.hubInfo.hubs[i].hubId);
- db.hubInfo.contextHub->registerCallback(db.hubInfo.hubs[i].hubId,
- callBackPtr);
- db.regCallBacks.push_back(callBackPtr);
- }
-
- sendQueryForApps();
-
- } else {
- ALOGW("No Context Hub Module present");
- }
-}
-
-void onHubReset(uint32_t hubId) {
- TransactionResult result = TransactionResult::SUCCESS;
- db.txnManager.closeTxn();
- // TODO : Expose this through an api
- passOnOsResponse(hubId, CONTEXT_HUB_OS_REBOOT, result, nullptr, 0);
- queryHubForApps(hubId);
-}
-
-int onMessageReceipt(const uint32_t *header,
- size_t headerLen,
- const char *msg,
- size_t msgLen) {
- JNIEnv *env;
-
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
- return -1;
- }
-
- jbyteArray jmsg = env->NewByteArray(msgLen);
- if (jmsg == nullptr) {
- ALOGW("Can't allocate %zu byte array", msgLen);
- return -1;
- }
- jintArray jheader = env->NewIntArray(headerLen);
- if (jheader == nullptr) {
- env->DeleteLocalRef(jmsg);
- ALOGW("Can't allocate %zu int array", headerLen);
- return -1;
- }
-
- env->SetByteArrayRegion(jmsg, 0, msgLen, reinterpret_cast<const jbyte *>(msg));
- env->SetIntArrayRegion(jheader, 0, headerLen, reinterpret_cast<const jint *>(header));
-
- int ret = (env->CallIntMethod(db.jniInfo.jContextHubService,
- db.jniInfo.contextHubServiceMsgReceiptCallback,
- jheader,
- jmsg) != 0);
- env->DeleteLocalRef(jmsg);
- env->DeleteLocalRef(jheader);
-
- return ret;
-}
-
-int handleQueryAppsResponse(const std::vector<HubAppInfo> apps,
- uint32_t hubHandle) {
- JNIEnv *env;
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
- return -1;
- }
-
- int numApps = apps.size();
-
- // We use this information to sync our JNI and Java caches of nanoapp info.
- // We want to accomplish two things here:
- // 1) Remove entries from our caches which are stale, and pertained to
- // apps no longer running on Context Hub.
- // 2) Populate our caches with the latest information of all these apps.
-
- // We make a couple of assumptions here:
- // A) The JNI and Java caches are in sync with each other (this isn't
- // necessarily true; any failure of a single call into Java land to
- // update its cache will leave that cache in a bad state. For NYC,
- // we're willing to tolerate this for now).
- // B) The total number of apps is relatively small, so horribly inefficent
- // algorithms aren't too painful.
- // C) We're going to call this relatively infrequently, so its inefficency
- // isn't a big impact.
-
-
- // (1). Looking for stale cache entries. Yes, this is O(N^2). See
- // assumption (B). Per assumption (A), it is sufficient to iterate
- // over just the JNI cache.
- auto end = db.appInstances.end();
- for (auto current = db.appInstances.begin(); current != end; ) {
- AppInstanceInfo cacheEntry = current->second;
- // We perform our iteration here because if we call
- // delete_app_instance() below, it will erase() this entry.
- current++;
- bool entryIsStale = true;
- for (int i = 0; i < numApps; i++) {
- if (apps[i].appId == cacheEntry.appInfo.appId) {
- // We found a match; this entry is current.
- entryIsStale = false;
- break;
- }
- }
-
- if (entryIsStale) {
- deleteAppInstance(cacheEntry.instanceId, env);
- }
- }
-
- // (2). Update our caches with the latest.
- for (int i = 0; i < numApps; i++) {
- // We will only have one instance of the app
- // TODO : Change this logic once we support multiple instances of the same app
- jint appInstance = getAppInstanceForAppId(apps[i].appId);
- if (appInstance == -1) {
- // This is a previously unknown app, let's allocate an "id" for it.
- appInstance = generateId();
- }
- addAppInstance(&apps[i], hubHandle, appInstance, env);
- }
- return 0;
-}
-
-// TODO(b/30807327): Do not use raw bytes for additional data. Use the
-// JNI interfaces for the appropriate types.
-void passOnOsResponse(uint32_t hubHandle,
- uint32_t msgType,
- TransactionResult result,
- const int8_t *additionalData,
- size_t additionalDataLen) {
- JNIEnv *env;
-
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
- ALOGW("Cannot latch to JNI env, dropping OS response %" PRIu32,
- msgType);
- return;
- }
-
- uint32_t header[MSG_HEADER_SIZE];
- memset(header, 0, sizeof(header));
-
- if (!additionalData) {
- additionalDataLen = 0; // clamp
- }
- int msgLen = 1 + additionalDataLen;
-
- int8_t *msg = new int8_t[msgLen];
-
- if (!msg) {
- ALOGW("Unexpected : Ran out of memory, cannot send response");
- return;
- }
-
- header[HEADER_FIELD_MSG_TYPE] = msgType;
- header[HEADER_FIELD_MSG_VERSION] = 0;
- header[HEADER_FIELD_HUB_HANDLE] = hubHandle;
- header[HEADER_FIELD_APP_INSTANCE] = OS_APP_ID;
-
- // Due to API constraints, at the moment we can't change the fact that
- // we're changing our 4-byte response to a 1-byte value. But we can prevent
- // the possible change in sign (and thus meaning) that would happen from
- // a naive cast. Further, we can log when we're losing part of the value.
- // TODO(b/30918279): Don't truncate this result.
- int8_t truncatedResult;
- truncatedResult = static_cast<int8_t>(result);
- msg[0] = truncatedResult;
-
- if (additionalData) {
- memcpy(&msg[1], additionalData, additionalDataLen);
- }
-
- jbyteArray jmsg = env->NewByteArray(msgLen);
- jintArray jheader = env->NewIntArray(arraysize(header));
-
- env->SetByteArrayRegion(jmsg, 0, msgLen, reinterpret_cast<jbyte *>(msg));
- env->SetIntArrayRegion(jheader, 0, arraysize(header), reinterpret_cast<jint *>(header));
-
- ALOGI("Passing msg type %" PRIu32 " from app %" PRIu32 " from hub %" PRIu32,
- header[HEADER_FIELD_MSG_TYPE],
- header[HEADER_FIELD_APP_INSTANCE],
- header[HEADER_FIELD_HUB_HANDLE]);
-
- env->CallIntMethod(db.jniInfo.jContextHubService,
- db.jniInfo.contextHubServiceMsgReceiptCallback,
- jheader,
- jmsg);
-
- env->DeleteLocalRef(jmsg);
- env->DeleteLocalRef(jheader);
-
- delete[] msg;
-}
-
-void closeUnloadTxn(bool success) {
- void *txnData = nullptr;
- HubMessageType txnId;
-
- if (success && db.txnManager.fetchTxnData(&txnId, &txnData) == 0 &&
- txnId == CONTEXT_HUB_UNLOAD_APP) {
- JNIEnv *env;
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
- ALOGW("Could not attach to JVM !");
- env = nullptr;
- }
- jint handle = *reinterpret_cast<jint *>(txnData);
- deleteAppInstance(handle, env);
- } else {
- ALOGW("Could not unload the app successfully ! success %d, txnData %p",
- success,
- txnData);
- }
-
- db.txnManager.closeTxn();
-}
-
-bool closeLoadTxn(bool success, jint *appInstanceHandle) {
- void *txnData;
- HubMessageType txnId;
-
- if (success && db.txnManager.fetchTxnData(&txnId, &txnData) == 0 &&
- txnId == CONTEXT_HUB_LOAD_APP) {
- AppInstanceInfo *info = static_cast<AppInstanceInfo *>(txnData);
- *appInstanceHandle = info->instanceId;
-
- JNIEnv *env;
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) == JNI_OK) {
- addAppInstance(&info->appInfo, info->hubHandle, info->instanceId, env);
- } else {
- ALOGW("Could not attach to JVM !");
- success = false;
- }
- // While we just called addAppInstance above, our info->appInfo was
- // incomplete (for example, the 'version' is hardcoded to -1). So we
- // trigger an additional query to the CHRE, so we'll be able to get
- // all the app "info", and have our JNI and Java caches with the
- // full information.
- sendQueryForApps();
- } else {
- ALOGW("Could not load the app successfully ! Unexpected failure");
- *appInstanceHandle = INVALID_APP_ID;
- success = false;
- }
-
- db.txnManager.closeTxn();
- return success;
-}
-
-int initJni(JNIEnv *env, jobject instance) {
- if (env->GetJavaVM(&db.jniInfo.vm) != JNI_OK) {
- return -1;
- }
-
- db.jniInfo.jContextHubService = env->NewGlobalRef(instance);
-
- db.jniInfo.contextHubInfoClass =
- env->FindClass("android/hardware/location/ContextHubInfo");
- db.jniInfo.contextHubServiceClass =
- env->FindClass("com/android/server/location/ContextHubService");
-
- db.jniInfo.memoryRegionsClass =
- env->FindClass("android/hardware/location/MemoryRegion");
-
- db.jniInfo.contextHubInfoCtor =
- env->GetMethodID(db.jniInfo.contextHubInfoClass, "<init>", "()V");
- db.jniInfo.contextHubInfoSetId =
- env->GetMethodID(db.jniInfo.contextHubInfoClass, "setId", "(I)V");
- db.jniInfo.contextHubInfoSetName =
- env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName", "(Ljava/lang/String;)V");
- db.jniInfo.contextHubInfoSetVendor =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setVendor",
- "(Ljava/lang/String;)V");
- db.jniInfo.contextHubInfoSetToolchain =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setToolchain",
- "(Ljava/lang/String;)V");
- db.jniInfo.contextHubInfoSetPlatformVersion =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setPlatformVersion",
- "(I)V");
- db.jniInfo.contextHubInfoSetStaticSwVersion =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setStaticSwVersion",
- "(I)V");
- db.jniInfo.contextHubInfoSetToolchainVersion =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setToolchainVersion",
- "(I)V");
- db.jniInfo.contextHubInfoSetPeakMips =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setPeakMips",
- "(F)V");
- db.jniInfo.contextHubInfoSetStoppedPowerDrawMw =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setStoppedPowerDrawMw",
- "(F)V");
- db.jniInfo.contextHubInfoSetSleepPowerDrawMw =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setSleepPowerDrawMw",
- "(F)V");
- db.jniInfo.contextHubInfoSetPeakPowerDrawMw =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setPeakPowerDrawMw",
- "(F)V");
- db.jniInfo.contextHubInfoSetSupportedSensors =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setSupportedSensors",
- "([I)V");
- db.jniInfo.contextHubInfoSetMemoryRegions =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setMemoryRegions",
- "([Landroid/hardware/location/MemoryRegion;)V");
- db.jniInfo.contextHubInfoSetMaxPacketLenBytes =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setMaxPacketLenBytes",
- "(I)V");
- db.jniInfo.contextHubServiceMsgReceiptCallback =
- env->GetMethodID(db.jniInfo.contextHubServiceClass,
- "onMessageReceipt",
- "([I[B)I");
- db.jniInfo.contextHubInfoSetName =
- env->GetMethodID(db.jniInfo.contextHubInfoClass,
- "setName",
- "(Ljava/lang/String;)V");
- db.jniInfo.contextHubServiceAddAppInstance =
- env->GetMethodID(db.jniInfo.contextHubServiceClass,
- "addAppInstance",
- "(IIJI)I");
- db.jniInfo.contextHubServiceDeleteAppInstance =
- env->GetMethodID(db.jniInfo.contextHubServiceClass,
- "deleteAppInstance",
- "(I)I");
-
- return 0;
-}
-
-jobject constructJContextHubInfo(JNIEnv *env, const ContextHub &hub) {
- jstring jstrBuf;
- jintArray jintBuf;
- jobjectArray jmemBuf;
-
- jobject jHub = env->NewObject(db.jniInfo.contextHubInfoClass,
- db.jniInfo.contextHubInfoCtor);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetId, hub.hubId);
-
- jstrBuf = env->NewStringUTF(hub.name.c_str());
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetName, jstrBuf);
- env->DeleteLocalRef(jstrBuf);
-
- jstrBuf = env->NewStringUTF(hub.vendor.c_str());
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetVendor, jstrBuf);
- env->DeleteLocalRef(jstrBuf);
-
- jstrBuf = env->NewStringUTF(hub.toolchain.c_str());
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchain, jstrBuf);
- env->DeleteLocalRef(jstrBuf);
-
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPlatformVersion, hub.platformVersion);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchainVersion, hub.toolchainVersion);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakMips, hub.peakMips);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw,
- hub.stoppedPowerDrawMw);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw,
- hub.sleepPowerDrawMw);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw,
- hub.peakPowerDrawMw);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMaxPacketLenBytes,
- hub.maxSupportedMsgLen);
-
-
- jintBuf = env->NewIntArray(hub.connectedSensors.size());
- int *connectedSensors = new int[hub.connectedSensors.size()];
-
- if (!connectedSensors) {
- ALOGW("Cannot allocate memory! Unexpected");
- assert(false);
- } else {
- for (unsigned int i = 0; i < hub.connectedSensors.size(); i++) {
- // TODO :: Populate connected sensors.
- //connectedSensors[i] = hub.connectedSensors[i].sensorType;
- connectedSensors[i] = 0;
- }
- }
-
- env->SetIntArrayRegion(jintBuf, 0, hub.connectedSensors.size(),
- connectedSensors);
-
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSupportedSensors, jintBuf);
- env->DeleteLocalRef(jintBuf);
-
- // We are not getting the memory regions from the CH Hal - change this when it is available
- jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, nullptr);
- // Note the zero size above. We do not need to set any elements
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMemoryRegions, jmemBuf);
- env->DeleteLocalRef(jmemBuf);
-
-
- delete[] connectedSensors;
- return jHub;
-}
-
-jobjectArray nativeInitialize(JNIEnv *env, jobject instance) {
- jobject hub;
- jobjectArray retArray;
-
- if (initJni(env, instance) < 0) {
- return nullptr;
- }
-
- initContextHubService();
-
- if (db.hubInfo.numHubs > 1) {
- ALOGW("Clamping the number of hubs to 1");
- db.hubInfo.numHubs = 1;
- }
-
- retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, nullptr);
-
- for(int i = 0; i < db.hubInfo.numHubs; i++) {
- hub = constructJContextHubInfo(env, db.hubInfo.hubs[i]);
- env->SetObjectArrayElement(retArray, i, hub);
- }
-
- return retArray;
-}
-
-Result sendLoadNanoAppRequest(uint32_t hubId,
- jbyte *data,
- size_t dataBufferLength) {
- auto header = reinterpret_cast<const NanoAppBinaryHeader *>(data);
- Result result;
-
- if (dataBufferLength < sizeof(NanoAppBinaryHeader)) {
- ALOGE("Got short NanoApp, length %zu", dataBufferLength);
- result = Result::BAD_PARAMS;
- } else if (header->headerVersion != htole32(kNanoAppBinaryHeaderVersion)) {
- ALOGE("Got unexpected NanoApp header version %" PRIu32,
- letoh32(header->headerVersion));
- result = Result::BAD_PARAMS;
- } else {
- NanoAppBinary nanoapp;
-
- // Data from the common nanoapp header goes into explicit fields
- nanoapp.appId = letoh64(header->appId);
- nanoapp.appVersion = letoh32(header->appVersion);
- nanoapp.flags = letoh32(header->flags);
- nanoapp.targetChreApiMajorVersion = header->targetChreApiMajorVersion;
- nanoapp.targetChreApiMinorVersion = header->targetChreApiMinorVersion;
-
- // Everything past the header goes in customBinary
- auto dataBytes = reinterpret_cast<const uint8_t *>(data);
- std::vector<uint8_t> customBinary(
- dataBytes + sizeof(NanoAppBinaryHeader),
- dataBytes + dataBufferLength);
- nanoapp.customBinary = std::move(customBinary);
-
- ALOGW("Calling Load NanoApp on hub %d", hubId);
- result = db.hubInfo.contextHub->loadNanoApp(hubId,
- nanoapp,
- CONTEXT_HUB_LOAD_APP);
- }
-
- return result;
-}
-
-jint nativeSendMessage(JNIEnv *env,
- jobject instance,
- jintArray header_,
- jbyteArray data_) {
- // With the new binderized HAL definition, this function can be made much simpler.
- // All the magic can be removed. This is not however needed for the default implementation
- // TODO :: Change the JNI interface to conform to the new HAL interface and clean up this
- // function
- jint retVal = -1; // Default to failure
-
- jint *header = env->GetIntArrayElements(header_, 0);
- size_t numHeaderElements = env->GetArrayLength(header_);
- jbyte *data = env->GetByteArrayElements(data_, 0);
- size_t dataBufferLength = env->GetArrayLength(data_);
-
- if (numHeaderElements < MSG_HEADER_SIZE) {
- ALOGW("Malformed header len");
- return -1;
- }
-
- jint appInstanceHandle = header[HEADER_FIELD_APP_INSTANCE];
- uint32_t msgType = header[HEADER_FIELD_MSG_TYPE];
- int hubHandle = -1;
- uint64_t appId;
-
- if (msgType == CONTEXT_HUB_UNLOAD_APP) {
- hubHandle = getHubHandleForAppInstance(appInstanceHandle);
- } else if (msgType == CONTEXT_HUB_LOAD_APP) {
- if (numHeaderElements < MSG_HEADER_SIZE_LOAD_APP) {
- return -1;
- }
- uint64_t appIdLo = header[HEADER_FIELD_LOAD_APP_ID_LO];
- uint64_t appIdHi = header[HEADER_FIELD_LOAD_APP_ID_HI];
- appId = appIdHi << 32 | appIdLo;
-
- hubHandle = header[HEADER_FIELD_HUB_HANDLE];
- } else {
- hubHandle = header[HEADER_FIELD_HUB_HANDLE];
- }
-
- uint32_t hubId = -1;
- if (!getHubIdForHubHandle(hubHandle, &hubId)) {
- ALOGD("Invalid hub Handle %d", hubHandle);
- return -1;
- }
-
- if (msgType == CONTEXT_HUB_LOAD_APP ||
- msgType == CONTEXT_HUB_UNLOAD_APP) {
-
- db.txnManager.closeAnyStaleTxns();
-
- if (db.txnManager.isTxnPending()) {
- // TODO : There is a race conditio
- ALOGW("Cannot load or unload app while a transaction is pending !");
- return -1;
- } else if (msgType == CONTEXT_HUB_LOAD_APP) {
- if (startLoadAppTxn(appId, hubHandle) != 0) {
- ALOGW("Cannot Start Load Transaction");
- return -1;
- }
- } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
- if (startUnloadAppTxn(appInstanceHandle) != 0) {
- ALOGW("Cannot Start UnLoad Transaction");
- return -1;
- }
- }
- }
-
- Result result;
-
- if (msgType == CONTEXT_HUB_UNLOAD_APP) {
- ALOGW("Calling UnLoad NanoApp for app %" PRIx64 " on hub %" PRIu32,
- db.appInstances[appInstanceHandle].appInfo.appId,
- hubId);
- result = db.hubInfo.contextHub->unloadNanoApp(
- hubId, db.appInstances[appInstanceHandle].appInfo.appId, CONTEXT_HUB_UNLOAD_APP);
- } else {
- if (appInstanceHandle == OS_APP_ID) {
- if (msgType == CONTEXT_HUB_LOAD_APP) {
- result = sendLoadNanoAppRequest(hubId, data, dataBufferLength);
- } else if (msgType == CONTEXT_HUB_QUERY_APPS) {
- result = db.hubInfo.contextHub->queryApps(hubId);
- } else {
- ALOGD("Dropping OS addresses message of type - %" PRIu32, msgType);
- result = Result::BAD_PARAMS;
- }
- } else {
- appId = getAppIdForAppInstance(appInstanceHandle);
- if (appId == static_cast<uint64_t>(INVALID_APP_ID)) {
- ALOGD("Cannot find application instance %d", appInstanceHandle);
- result = Result::BAD_PARAMS;
- } else if (hubHandle != getHubHandleForAppInstance(appInstanceHandle)) {
- ALOGE("Given hubHandle (%d) doesn't match expected for app instance (%d)",
- hubHandle,
- getHubHandleForAppInstance(appInstanceHandle));
- result = Result::BAD_PARAMS;
- } else {
- ContextHubMsg msg;
- msg.appName = appId;
- msg.msgType = msgType;
- msg.msg.setToExternal((unsigned char *)data, dataBufferLength);
-
- ALOGW("Sending msg of type %" PRIu32 " len %zu to app %" PRIx64 " on hub %" PRIu32,
- msgType,
- dataBufferLength,
- appId,
- hubId);
- result = db.hubInfo.contextHub->sendMessageToHub(hubId, msg);
- }
- }
- }
-
- if (result != Result::OK) {
- ALOGD("Send Message failure - %d", result);
- if (msgType == CONTEXT_HUB_LOAD_APP) {
- jint ignored;
- closeLoadTxn(false, &ignored);
- } else if (msgType == CONTEXT_HUB_UNLOAD_APP) {
- closeUnloadTxn(false);
- }
- } else {
- retVal = 0;
- }
-
- env->ReleaseIntArrayElements(header_, header, 0);
- env->ReleaseByteArrayElements(data_, data, 0);
-
- return retVal;
-}
-
-//--------------------------------------------------------------------------------------------------
-//
-const JNINativeMethod gContextHubServiceMethods[] = {
- {"nativeInitialize",
- "()[Landroid/hardware/location/ContextHubInfo;",
- reinterpret_cast<void*>(nativeInitialize)},
- {"nativeSendMessage",
- "([I[B)I",
- reinterpret_cast<void*>(nativeSendMessage)}
-};
-
-int register_android_server_location_ContextHubService(JNIEnv *env)
-{
- RegisterMethodsOrDie(env, "com/android/server/location/ContextHubService",
- gContextHubServiceMethods, NELEM(gContextHubServiceMethods));
-
- return 0;
-}
-
-}//namespace android
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 1f1324a..daf3f2f 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1207,6 +1207,13 @@
ALOGE("Unable to initialize GNSS NI interface\n");
}
+ sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback();
+ if (agnssRilIface != nullptr) {
+ agnssRilIface->setCallback(aGnssRilCbIface);
+ } else {
+ ALOGI("Unable to Initialize AGnss Ril interface\n");
+ }
+
return JNI_TRUE;
}
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 2f45181..46d5043 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -39,7 +39,6 @@
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_vr_VrManagerService(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
-int register_android_server_location_ContextHubService(JNIEnv* env);
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv*);
@@ -82,7 +81,6 @@
register_android_server_vr_VrManagerService(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
- register_android_server_location_ContextHubService(env);
register_android_server_location_GnssLocationProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_connectivity_tethering_OffloadHardwareInterface(env);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 90df82a..33f4e34 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -35,6 +35,7 @@
import android.os.IIncidentManager;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.PowerManager;
import android.os.Process;
import android.os.ServiceManager;
@@ -95,6 +96,7 @@
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.ShortcutService;
import com.android.server.pm.UserManagerService;
+import com.android.server.pm.crossprofile.CrossProfileAppsService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
@@ -359,6 +361,9 @@
// to avoid throwing BadParcelableException.
BaseBundle.setShouldDefuse(true);
+ // Within the system server, when parceling exceptions, include the stack trace
+ Parcel.setStackTraceParceling(true);
+
// Ensure binder calls into the system always run at foreground priority.
BinderInternal.disableBackgroundScheduling(true);
@@ -1489,6 +1494,10 @@
traceBeginAndSlog("StartLauncherAppsService");
mSystemServiceManager.startService(LauncherAppsService.class);
traceEnd();
+
+ traceBeginAndSlog("StartCrossProfileAppsService");
+ mSystemServiceManager.startService(CrossProfileAppsService.class);
+ traceEnd();
}
if (!disableMediaProjection) {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 58833be..e8ae020 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -597,6 +597,7 @@
PrintJobStateChangeListenerRecord record =
mPrintJobStateChangeListenerRecords.get(i);
if (record.listener.asBinder().equals(listener.asBinder())) {
+ record.destroy();
mPrintJobStateChangeListenerRecords.remove(i);
break;
}
@@ -639,6 +640,7 @@
ListenerRecord<IPrintServicesChangeListener> record =
mPrintServicesChangeListenerRecords.get(i);
if (record.listener.asBinder().equals(listener.asBinder())) {
+ record.destroy();
mPrintServicesChangeListenerRecords.remove(i);
break;
}
@@ -686,6 +688,7 @@
ListenerRecord<IRecommendationsChangeListener> record =
mPrintServiceRecommendationsChangeListenerRecords.get(i);
if (record.listener.asBinder().equals(listener.asBinder())) {
+ record.destroy();
mPrintServiceRecommendationsChangeListenerRecords.remove(i);
break;
}
@@ -1285,6 +1288,10 @@
listener.asBinder().linkToDeath(this, 0);
}
+ public void destroy() {
+ listener.asBinder().unlinkToDeath(this, 0);
+ }
+
@Override
public void binderDied() {
listener.asBinder().unlinkToDeath(this, 0);
@@ -1302,6 +1309,10 @@
listener.asBinder().linkToDeath(this, 0);
}
+ public void destroy() {
+ listener.asBinder().unlinkToDeath(this, 0);
+ }
+
@Override
public void binderDied() {
listener.asBinder().unlinkToDeath(this, 0);
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 59d2a24..114929c 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -45,9 +45,10 @@
platform-test-annotations \
truth-prebuilt
+# TODO(b/69254249): Migrate to Robolectric 3.4.2
LOCAL_JAVA_LIBRARIES := \
junit \
- platform-robolectric-prebuilt
+ platform-robolectric-3.1.1-prebuilt
LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib
LOCAL_MODULE := FrameworksServicesRoboTests
@@ -72,4 +73,5 @@
LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))backup/java
-include prebuilts/misc/common/robolectric/run_robotests.mk
+# TODO(b/69254249): Migrate to Robolectric 3.4.2
+include prebuilts/misc/common/robolectric/3.1.1/run_robotests.mk
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 70e8a16..c145e82 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -144,7 +144,9 @@
// Use a Testable subclass so we can simulate calls from the system without failing.
private static class TestableNotificationManagerService extends NotificationManagerService {
- public TestableNotificationManagerService(Context context) { super(context); }
+ public TestableNotificationManagerService(Context context) {
+ super(context);
+ }
@Override
protected boolean isCallingUidSystem() {
@@ -160,6 +162,11 @@
protected ICompanionDeviceManager getCompanionManager() {
return null;
}
+
+ @Override
+ protected void reportSeen(NotificationRecord r) {
+ return;
+ }
}
@Before
@@ -1865,8 +1872,8 @@
}
@Test
- public void testStats_updatedOnExpansion() throws Exception {
- final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ public void testStats_updatedOnUserExpansion() throws Exception {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
@@ -1876,6 +1883,17 @@
}
@Test
+ public void testStats_notUpdatedOnAutoExpansion() throws Exception {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+ assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
+ assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ }
+
+ @Test
public void testStats_updatedOnViewSettings() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index df989f7..3c02e23 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -25,6 +25,20 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -74,20 +88,6 @@
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RankingHelperTest extends NotificationTestCase {
diff --git a/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
index cbe9650..8ac6481 100644
--- a/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
@@ -17,7 +17,7 @@
package com.android.server.notification;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -100,5 +100,34 @@
AudioAttributes.USAGE_GAME);
verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION);
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ AudioAttributes.USAGE_UNKNOWN);
+ }
+
+ @Test
+ public void testZenAllCannotBypass() {
+ // Only audio attributes with SUPPRESIBLE_NEVER can bypass
+ mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mConfig.allowAlarms = false;
+ mZenModeHelperSpy.mConfig.allowMediaSystemOther = false;
+ mZenModeHelperSpy.mConfig.allowReminders = false;
+ mZenModeHelperSpy.mConfig.allowCalls = false;
+ mZenModeHelperSpy.mConfig.allowMessages = false;
+ mZenModeHelperSpy.mConfig.allowEvents = false;
+ mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
+ assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
+ assertFalse(mZenModeHelperSpy.mConfig.allowMediaSystemOther);
+ assertFalse(mZenModeHelperSpy.mConfig.allowReminders);
+ assertFalse(mZenModeHelperSpy.mConfig.allowCalls);
+ assertFalse(mZenModeHelperSpy.mConfig.allowMessages);
+ assertFalse(mZenModeHelperSpy.mConfig.allowEvents);
+ assertFalse(mZenModeHelperSpy.mConfig.allowRepeatCallers);
+ mZenModeHelperSpy.applyRestrictions();
+
+ for (int usage : AudioAttributes.SDK_USAGES) {
+ boolean shouldMute = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage)
+ != AudioAttributes.SUPPRESSIBLE_NEVER;
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage);
+ }
}
}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 8e41a55..a2ec234 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -25,7 +25,8 @@
mockito-target-minus-junit4 \
platform-test-annotations \
ShortcutManagerTestUtils \
- truth-prebuilt
+ truth-prebuilt \
+ testng
LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index f1e76ab..9a5ebed 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -57,6 +57,7 @@
<uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/res/values/strings.xml b/services/tests/servicestests/res/values/strings.xml
index 1253d44..3ac56bb 100644
--- a/services/tests/servicestests/res/values/strings.xml
+++ b/services/tests/servicestests/res/values/strings.xml
@@ -28,4 +28,8 @@
<string name="test_account_type2_authenticator_label">AccountManagerService Test Account Type2</string>
<string name="test_account_type1">com.android.server.accounts.account_manager_service_test.account.type1</string>
<string name="test_account_type2">com.android.server.accounts.account_manager_service_test.account.type2</string>
+
+ <string name="config_batterySaverDeviceSpecificConfig_1"></string>
+ <string name="config_batterySaverDeviceSpecificConfig_2">file-off:/sys/a=1,file-off:/sys/b=2</string>
+ <string name="config_batterySaverDeviceSpecificConfig_3">file-off:/sys/a=3,file-on:/proc/c=4,/abc=3</string>
</resources>
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index cb13e85..2b770ac 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -29,6 +29,11 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
@@ -156,7 +161,8 @@
record.canBeLaunchedOnDisplay(DEFAULT_DISPLAY);
- assertEquals(((TestActivityStackSupervisor) mService.mStackSupervisor)
- .getLastResizeableFromCanPlaceEntityOnDisplay(), expected);
+
+ verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected), anyInt(), anyInt(),
+ eq(record.info));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 480b210..6b09363 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -166,6 +166,8 @@
final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
final TestActivityStack homeStack = createStackForShouldBeVisibleTest(display,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ // Home stack should always be fullscreen for this test.
+ homeStack.setSupportsSplitScreen(false);
final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(display,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(display,
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 5b1e4b7..f9933fb6 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -16,13 +16,28 @@
package com.android.server.am;
+import static android.app.ActivityManager.START_ABORTED;
+import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
+import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityManager.START_SWITCHES_CANCELED;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import android.app.ActivityOptions;
+import android.app.IApplicationThread;
import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.service.voice.IVoiceInteractionSession;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -36,10 +51,20 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.times;
+import static android.app.ActivityManager.START_PERMISSION_DENIED;
+import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
+
+import com.android.internal.os.BatteryStatsImpl;
+
/**
* Tests for the {@link ActivityStack} class.
*
@@ -52,12 +77,26 @@
public class ActivityStarterTests extends ActivityTestsBase {
private ActivityManagerService mService;
private ActivityStarter mStarter;
+ private IPackageManager mPackageManager;
+
+ private static final int PRECONDITION_NO_CALLER_APP = 1;
+ private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
+ private static final int PRECONDITION_NO_ACTIVITY_INFO = 1 << 2;
+ private static final int PRECONDITION_SOURCE_PRESENT = 1 << 3;
+ private static final int PRECONDITION_REQUEST_CODE = 1 << 4;
+ private static final int PRECONDITION_SOURCE_VOICE_SESSION = 1 << 5;
+ private static final int PRECONDITION_NO_VOICE_SESSION_SUPPORT = 1 << 6;
+ private static final int PRECONDITION_DIFFERENT_UID = 1 << 7;
+ private static final int PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION = 1 << 8;
+ private static final int PRECONDITION_CANNOT_START_ANY_ACTIVITY = 1 << 9;
+ private static final int PRECONDITION_DISALLOW_APP_SWITCHING = 1 << 10;
@Override
public void setUp() throws Exception {
super.setUp();
mService = createActivityManagerService();
- mStarter = new ActivityStarter(mService);
+ mPackageManager = mock(IPackageManager.class);
+ mStarter = new ActivityStarter(mService, mPackageManager);
}
@Test
@@ -92,4 +131,152 @@
assertEquals(task2.mBounds, null);
}
}
+
+ @Test
+ public void testStartActivityPreconditions() throws Exception {
+ verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
+ verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
+ START_INTENT_NOT_RESOLVED);
+ verifyStartActivityPreconditions(PRECONDITION_NO_ACTIVITY_INFO, START_CLASS_NOT_FOUND);
+ verifyStartActivityPreconditions(PRECONDITION_SOURCE_PRESENT | PRECONDITION_REQUEST_CODE,
+ Intent.FLAG_ACTIVITY_FORWARD_RESULT, START_FORWARD_AND_REQUEST_CONFLICT);
+ verifyStartActivityPreconditions(
+ PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
+ | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID,
+ START_NOT_VOICE_COMPATIBLE);
+ verifyStartActivityPreconditions(
+ PRECONDITION_SOURCE_PRESENT | PRECONDITION_NO_VOICE_SESSION_SUPPORT
+ | PRECONDITION_SOURCE_VOICE_SESSION | PRECONDITION_DIFFERENT_UID
+ | PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION,
+ START_NOT_VOICE_COMPATIBLE);
+ verifyStartActivityPreconditions(PRECONDITION_CANNOT_START_ANY_ACTIVITY, START_ABORTED);
+ verifyStartActivityPreconditions(PRECONDITION_DISALLOW_APP_SWITCHING,
+ START_SWITCHES_CANCELED);
+ }
+
+ private static boolean containsConditions(int preconditions, int mask) {
+ return (preconditions & mask) == mask;
+ }
+
+ private void verifyStartActivityPreconditions(int preconditions, int expectedResult) {
+ verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
+ }
+
+ /**
+ * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
+ * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
+ * and the launch flags specified in the intent. The method constructs a call to
+ * {@link ActivityStarter#startActivityLocked} based on these preconditions and ensures the
+ * result matches the expected. It is important to note that the method also checks side effects
+ * of the start, such as ensuring {@link ActivityOptions#abort()} is called in the relevant
+ * scenarios.
+ * @param preconditions A bitmask representing the preconditions for the launch
+ * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
+ * @param expectedResult The expected result from the launch.
+ */
+ private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
+ int expectedResult) {
+ final ActivityManagerService service = createActivityManagerService();
+ final IPackageManager packageManager = mock(IPackageManager.class);
+ final ActivityStarter starter = new ActivityStarter(service, packageManager);
+
+ final IApplicationThread caller = mock(IApplicationThread.class);
+
+ // If no caller app, return {@code null} {@link ProcessRecord}.
+ final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
+ ? null : new ProcessRecord(mock(BatteryStatsImpl.class),
+ mock(ApplicationInfo.class), null, 0);
+
+ doReturn(record).when(service).getRecordForAppLocked(anyObject());
+
+ final Intent intent = new Intent();
+ intent.setFlags(launchFlags);
+
+ final ActivityInfo aInfo = containsConditions(preconditions, PRECONDITION_NO_ACTIVITY_INFO)
+ ? null : new ActivityInfo();
+
+ if (aInfo != null) {
+ aInfo.applicationInfo = new ApplicationInfo();
+ aInfo.applicationInfo.packageName = ActivityBuilder.DEFAULT_PACKAGE;
+ }
+
+ IVoiceInteractionSession voiceSession =
+ containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
+ ? mock(IVoiceInteractionSession.class) : null;
+
+ // Create source token
+ final ActivityBuilder builder = new ActivityBuilder(service).setTask(
+ new TaskBuilder(service.mStackSupervisor).setVoiceSession(voiceSession).build());
+
+ // Offset uid by one from {@link ActivityInfo} to simulate different uids.
+ if (containsConditions(preconditions, PRECONDITION_DIFFERENT_UID)) {
+ builder.setUid(aInfo.applicationInfo.uid + 1);
+ }
+
+ final ActivityRecord source = builder.build();
+
+ if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
+ intent.setComponent(source.realActivity);
+ }
+
+ if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
+ doReturn(false).when(service).checkAppSwitchAllowedLocked(anyInt(), anyInt(), anyInt(),
+ anyInt(), any());
+ }
+
+ if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
+ doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
+ any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), anyBoolean(),
+ any(), any(), any(), any());
+ }
+
+ try {
+ if (containsConditions(preconditions,
+ PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
+ doAnswer((inv) -> {
+ throw new RemoteException();
+ }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
+ any());
+ } else {
+ doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
+ .when(packageManager).activitySupportsIntent(eq(source.realActivity),
+ eq(intent), any());
+ }
+ } catch (RemoteException e) {
+ }
+
+ final IBinder resultTo = containsConditions(preconditions, PRECONDITION_SOURCE_PRESENT)
+ || containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
+ ? source.appToken : null;
+
+ final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
+ ? 1 : 0;
+
+ final int result = starter.startActivityLocked(caller, intent,
+ null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
+ null /*voiceSession*/, null /*voiceInteractor*/, resultTo,
+ null /*resultWho*/, requestCode, 0 /*callingPid*/, 0 /*callingUid*/,
+ null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
+ 0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
+ false /*componentSpecified*/, null /*outActivity*/,
+ null /*inTask*/, "testLaunchActivityPermissionDenied");
+
+ // In some cases the expected result internally is different than the published result. We
+ // must use ActivityStarter#getExternalResult to translate.
+ assertEquals(ActivityStarter.getExternalResult(expectedResult), result);
+
+ // Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
+ if (expectedResult != START_SUCCESS) {
+ final ActivityOptions options = spy(ActivityOptions.makeBasic());
+ final int optionResult = starter.startActivityLocked(caller, intent,
+ null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
+ null /*voiceSession*/, null /*voiceInteractor*/, resultTo,
+ null /*resultWho*/, requestCode, 0 /*callingPid*/, 0 /*callingUid*/,
+ null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
+ 0 /*startFlags*/, options /*options*/, false /*ignoreTargetSecurity*/,
+ false /*componentSpecified*/, null /*outActivity*/,
+ null /*inTask*/, "testLaunchActivityPermissionDenied");
+ verify(options, times(1)).abort();
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 198cc6d..9c949ad 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -21,8 +21,11 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
@@ -38,6 +41,7 @@
import android.hardware.display.DisplayManager;
import android.os.HandlerThread;
import android.os.Looper;
+import android.service.voice.IVoiceInteractionSession;
import android.support.test.InstrumentationRegistry;
import com.android.server.AttributeCache;
import com.android.server.wm.AppWindowContainerController;
@@ -78,7 +82,10 @@
}
protected ActivityManagerService createActivityManagerService() {
- return setupActivityManagerService(new TestActivityManagerService(mContext));
+ final ActivityManagerService service =
+ setupActivityManagerService(new TestActivityManagerService(mContext));
+ AttributeCache.init(mContext);
+ return service;
}
protected ActivityManagerService setupActivityManagerService(ActivityManagerService service) {
@@ -95,7 +102,7 @@
private static int sCurrentActivityId = 0;
// Default package name
- private static final String DEFAULT_PACKAGE = "com.foo";
+ static final String DEFAULT_PACKAGE = "com.foo";
// Default base activity name
private static final String DEFAULT_BASE_ACTIVITY_NAME = ".BarActivity";
@@ -156,7 +163,6 @@
aInfo.applicationInfo = new ApplicationInfo();
aInfo.applicationInfo.packageName = mComponent.getPackageName();
aInfo.applicationInfo.uid = mUid;
- AttributeCache.init(mService.mContext);
final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
0 /* launchedFromPid */, 0, null, intent, null,
aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
@@ -182,6 +188,7 @@
private String mPackage;
private int mFlags = 0;
private int mTaskId = 0;
+ private IVoiceInteractionSession mVoiceSession;
private ActivityStack mStack;
@@ -199,6 +206,11 @@
return this;
}
+ TaskBuilder setVoiceSession(IVoiceInteractionSession session) {
+ mVoiceSession = session;
+ return this;
+ }
+
TaskBuilder setFlags(int flags) {
mFlags = flags;
return this;
@@ -229,7 +241,7 @@
intent.setFlags(mFlags);
final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
- intent /*intent*/, null /*_taskDescription*/);
+ intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
mSupervisor.setFocusStackUnchecked("test", mStack);
mStack.addTask(task, true, "creating test task");
task.setStack(mStack);
@@ -255,7 +267,27 @@
}
@Override
- protected ActivityStackSupervisor createStackSupervisor() {
+ final protected ActivityStackSupervisor createStackSupervisor() {
+ final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
+
+ // No home stack is set.
+ doNothing().when(supervisor).moveHomeStackToFront(any());
+ doReturn(true).when(supervisor).moveHomeStackTaskToTop(any());
+ // Invoked during {@link ActivityStack} creation.
+ doNothing().when(supervisor).updateUIDsPresentOnDisplay();
+ // Always keep things awake.
+ doReturn(true).when(supervisor).hasAwakeDisplay();
+ // Called when moving activity to pinned stack.
+ doNothing().when(supervisor).ensureActivitiesVisibleLocked(any(), anyInt(), anyBoolean());
+ // Do not schedule idle timeouts
+ doNothing().when(supervisor).scheduleIdleTimeoutLocked(any());
+
+ supervisor.initialize();
+
+ return supervisor;
+ }
+
+ protected ActivityStackSupervisor createTestSupervisor() {
return new TestActivityStackSupervisor(this, mHandlerThread.getLooper());
}
@@ -269,14 +301,18 @@
* setup not available in the test environment. Also specifies an injector for
*/
protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
- private final ActivityDisplay mDisplay;
- private boolean mLastResizeable;
+ private ActivityDisplay mDisplay;
public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
super(service, looper);
mDisplayManager =
(DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
mWindowManager = prepareMockWindowManager();
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
attachDisplay(mDisplay);
}
@@ -286,54 +322,11 @@
return mDisplay;
}
- // TODO: Use Mockito spy instead. Currently not possible due to TestActivityStackSupervisor
- // access to ActivityDisplay
- @Override
- boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable, int callingPid,
- int callingUid, ActivityInfo activityInfo) {
- mLastResizeable = resizeable;
- return super.canPlaceEntityOnDisplay(displayId, resizeable, callingPid, callingUid,
- activityInfo);
- }
-
- // TODO: remove and use Mockito verify once {@link #canPlaceEntityOnDisplay} override is
- // removed.
- public boolean getLastResizeableFromCanPlaceEntityOnDisplay() {
- return mLastResizeable;
- }
-
- // No home stack is set.
- @Override
- void moveHomeStackToFront(String reason) {
- }
-
- @Override
- boolean moveHomeStackTaskToTop(String reason) {
- return true;
- }
-
- // Invoked during {@link ActivityStack} creation.
- @Override
- void updateUIDsPresentOnDisplay() {
- }
-
- // Just return the current front task.
+ // Just return the current front task. This is called internally so we cannot use spy to mock this out.
@Override
ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus) {
return mFocusedStack;
}
-
- // Called when moving activity to pinned stack.
- @Override
- void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
- boolean preserveWindows) {
- }
-
- // Always keep things awake
- @Override
- boolean hasAwakeDisplay() {
- return true;
- }
}
private static class TestActivityDisplay extends ActivityDisplay {
@@ -396,6 +389,11 @@
static final int IS_TRANSLUCENT_TRUE = 2;
private int mIsTranslucent = IS_TRANSLUCENT_UNSET;
+ static final int SUPPORTS_SPLIT_SCREEN_UNSET = 0;
+ static final int SUPPORTS_SPLIT_SCREEN_FALSE = 1;
+ static final int SUPPORTS_SPLIT_SCREEN_TRUE = 2;
+ private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET;
+
TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
int windowingMode, int activityType, boolean onTop) {
super(display, stackId, supervisor, windowingMode, activityType, onTop);
@@ -439,5 +437,23 @@
return super.isStackTranslucent(starting);
}
}
+
+ void setSupportsSplitScreen(boolean supportsSplitScreen) {
+ mSupportsSplitScreen = supportsSplitScreen
+ ? SUPPORTS_SPLIT_SCREEN_TRUE : SUPPORTS_SPLIT_SCREEN_FALSE;
+ }
+
+ @Override
+ public boolean supportsSplitScreenWindowingMode() {
+ switch (mSupportsSplitScreen) {
+ case SUPPORTS_SPLIT_SCREEN_TRUE:
+ return true;
+ case SUPPORTS_SPLIT_SCREEN_FALSE:
+ return false;
+ case SUPPORTS_SPLIT_SCREEN_UNSET:
+ default:
+ return super.supportsSplitScreenWindowingMode();
+ }
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index f5ea60f..afece5d 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -428,7 +428,8 @@
assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
assertSecurityException(expectCallable, () -> mService.cancelTaskThumbnailTransition(0));
- assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null, 0));
+ assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
+ null, 0));
}
private void testGetTasksApis(boolean expectCallable) {
@@ -510,7 +511,7 @@
}
@Override
- protected ActivityStackSupervisor createStackSupervisor() {
+ protected ActivityStackSupervisor createTestSupervisor() {
return new MyTestActivityStackSupervisor(this, mHandlerThread.getLooper());
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index f22dfdc..c7fd551 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -11,7 +11,6 @@
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstantAppInfo;
import android.content.pm.InstrumentationInfo;
@@ -623,12 +622,6 @@
}
@Override
- public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,
- String installerPackageName) {
-
- }
-
- @Override
public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags,
String installerPackageName) {
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
new file mode 100644
index 0000000..d9fac87
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -0,0 +1,640 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.display.BrightnessChangeEvent;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.MessageQueue;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.AtomicFile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BrightnessTrackerTest {
+
+ private BrightnessTracker mTracker;
+ private TestInjector mInjector;
+
+ private static Object sHandlerLock = new Object();
+ private static Handler sHandler;
+ private static HandlerThread sThread =
+ new HandlerThread("brightness.test", android.os.Process.THREAD_PRIORITY_BACKGROUND);
+
+ private static Handler ensureHandler() {
+ synchronized (sHandlerLock) {
+ if (sHandler == null) {
+ sThread.start();
+ sHandler = new Handler(sThread.getLooper());
+ }
+ return sHandler;
+ }
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ mInjector = new TestInjector(ensureHandler());
+
+ mTracker = new BrightnessTracker(InstrumentationRegistry.getContext(), mInjector);
+ }
+
+ @Test
+ public void testStartStopTracker() {
+ startTracker(mTracker);
+ assertNotNull(mInjector.mSensorListener);
+ assertNotNull(mInjector.mSettingsObserver);
+ assertNotNull(mInjector.mBroadcastReceiver);
+ mTracker.stop();
+ assertNull(mInjector.mSensorListener);
+ assertNull(mInjector.mSettingsObserver);
+ assertNull(mInjector.mBroadcastReceiver);
+ }
+
+ @Test
+ public void testBrightnessEvent() {
+ final int brightness = 20;
+
+ mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
+ startTracker(mTracker);
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
+ mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS));
+ List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+ mTracker.stop();
+
+ assertEquals(1, events.size());
+ BrightnessChangeEvent event = events.get(0);
+ assertEquals(mInjector.currentTimeMillis(), event.timeStamp);
+ assertEquals(1, event.luxValues.length);
+ assertEquals(1.0f, event.luxValues[0], 0.1f);
+ assertEquals(mInjector.currentTimeMillis() - TimeUnit.SECONDS.toMillis(2),
+ event.luxTimestamps[0]);
+ assertEquals(brightness, event.brightness);
+
+ // System had no data so these should all be at defaults.
+ assertEquals(Float.NaN, event.batteryLevel, 0.0);
+ assertFalse(event.nightMode);
+ assertEquals(0, event.colorTemperature);
+ }
+
+ @Test
+ public void testBrightnessFullPopulatedEvent() {
+ final int lastBrightness = 230;
+ final int brightness = 130;
+
+ mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, lastBrightness);
+ mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
+ mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3333);
+
+ startTracker(mTracker);
+ mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
+ mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
+ batteryChangeEvent(30, 60));
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f));
+ final long sensorTime = mInjector.currentTimeMillis();
+ mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS));
+ List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+ mTracker.stop();
+
+ assertEquals(1, events.size());
+ BrightnessChangeEvent event = events.get(0);
+ assertEquals(event.timeStamp, mInjector.currentTimeMillis());
+ assertArrayEquals(new float[] {1000.0f}, event.luxValues, 0.01f);
+ assertArrayEquals(new long[] {sensorTime}, event.luxTimestamps);
+ assertEquals(brightness, event.brightness);
+ assertEquals(lastBrightness, event.lastBrightness);
+ assertEquals(0.5, event.batteryLevel, 0.01);
+ assertTrue(event.nightMode);
+ assertEquals(3333, event.colorTemperature);
+ assertEquals("a.package", event.packageName);
+ assertEquals(0, event.userId);
+ }
+
+ @Test
+ public void testIgnoreSelfChange() {
+ final int initialBrightness = 30;
+ mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, initialBrightness);
+ startTracker(mTracker);
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
+
+ final int systemUpdatedBrightness = 20;
+ mTracker.setBrightness(systemUpdatedBrightness, 0);
+ assertEquals(systemUpdatedBrightness,
+ (int) mInjector.mSystemIntSettings.get(Settings.System.SCREEN_BRIGHTNESS));
+ mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS));
+ List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+ // No events because we filtered out our change.
+ assertEquals(0, events.size());
+
+ final int firstUserUpdateBrightness = 20;
+ // Then change comes from somewhere else so we shouldn't filter.
+ mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS,
+ firstUserUpdateBrightness);
+ mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS));
+
+ // and with a different brightness value.
+ final int secondUserUpdateBrightness = 34;
+ mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS,
+ secondUserUpdateBrightness);
+ mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS));
+ events = mTracker.getEvents(0).getList();
+
+ assertEquals(2, events.size());
+ // First event is change from system update (20) to first user update (20)
+ assertEquals(systemUpdatedBrightness, events.get(0).lastBrightness);
+ assertEquals(firstUserUpdateBrightness, events.get(0).brightness);
+ // Second event is from first to second user update.
+ assertEquals(firstUserUpdateBrightness, events.get(1).lastBrightness);
+ assertEquals(secondUserUpdateBrightness, events.get(1).brightness);
+
+ mTracker.stop();
+ }
+
+ @Test
+ public void testLimitedBufferSize() {
+ startTracker(mTracker);
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
+
+ for (int brightness = 0; brightness <= 255; ++brightness) {
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
+ mInjector.incrementTime(TimeUnit.SECONDS.toNanos(1));
+ mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
+ mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS));
+ }
+ List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+ mTracker.stop();
+
+ // Should be capped at 100 events, and they should be the most recent 100.
+ assertEquals(100, events.size());
+ for (int i = 0; i < events.size(); i++) {
+ BrightnessChangeEvent event = events.get(i);
+ assertEquals(156 + i, event.brightness);
+ }
+ }
+
+ @Test
+ public void testLimitedSensorEvents() {
+ final int brightness = 20;
+ mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
+
+ startTracker(mTracker);
+ // 20 Sensor events 1 second apart.
+ for (int i = 0; i < 20; ++i) {
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(i + 1.0f));
+ }
+ mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS));
+ List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+ mTracker.stop();
+
+ assertEquals(1, events.size());
+ BrightnessChangeEvent event = events.get(0);
+ assertEquals(mInjector.currentTimeMillis(), event.timeStamp);
+
+ // 12 sensor events, 11 for 0->10 seconds + 1 previous event.
+ assertEquals(12, event.luxValues.length);
+ for (int i = 0; i < 12; ++i) {
+ assertEquals(event.luxTimestamps[11 - i],
+ mInjector.currentTimeMillis() - i * TimeUnit.SECONDS.toMillis(1));
+ }
+ assertEquals(brightness, event.brightness);
+ }
+
+ @Test
+ public void testReadEvents() throws Exception {
+ BrightnessTracker tracker = new BrightnessTracker(InstrumentationRegistry.getContext(),
+ mInjector);
+ mInjector.mCurrentTimeMillis = System.currentTimeMillis();
+ long someTimeAgo = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(12);
+ long twoMonthsAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60);
+ // 3 Events in the file but one too old to read.
+ String eventFile =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<events>\n"
+ + "<event brightness=\"194\" timestamp=\""
+ + Long.toString(someTimeAgo) + "\" packageName=\""
+ + "com.example.app\" user=\"10\" "
+ + "lastBrightness=\"32\" "
+ + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n"
+ + "lux=\"32.2,31.1\" luxTimestamps=\""
+ + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>"
+ + "<event brightness=\"71\" timestamp=\""
+ + Long.toString(someTimeAgo) + "\" packageName=\""
+ + "com.android.anapp\" user=\"11\" "
+ + "lastBrightness=\"32\" "
+ + "batteryLevel=\"0.5\" nightMode=\"true\" colorTemperature=\"3235\"\n"
+ + "lux=\"132.2,131.1\" luxTimestamps=\""
+ + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>"
+ // Event that is too old so shouldn't show up.
+ + "<event brightness=\"142\" timestamp=\""
+ + Long.toString(twoMonthsAgo) + "\" packageName=\""
+ + "com.example.app\" user=\"10\" "
+ + "lastBrightness=\"32\" "
+ + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n"
+ + "lux=\"32.2,31.1\" luxTimestamps=\""
+ + Long.toString(twoMonthsAgo) + "," + Long.toString(twoMonthsAgo) + "\"/>"
+ + "</events>";
+ tracker.readEventsLocked(getInputStream(eventFile));
+ List<BrightnessChangeEvent> events = tracker.getEvents(0).getList();
+ assertEquals(1, events.size());
+ BrightnessChangeEvent event = events.get(0);
+ assertEquals(someTimeAgo, event.timeStamp);
+ assertEquals(194, event.brightness);
+ assertArrayEquals(new float[] {32.2f, 31.1f}, event.luxValues, 0.01f);
+ assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps);
+ assertEquals(32, event.lastBrightness);
+ assertEquals(0, event.userId);
+ assertFalse(event.nightMode);
+ assertEquals(1.0f, event.batteryLevel, 0.01);
+ assertEquals("com.example.app", event.packageName);
+
+ events = tracker.getEvents(1).getList();
+ assertEquals(1, events.size());
+ event = events.get(0);
+ assertEquals(someTimeAgo, event.timeStamp);
+ assertEquals(71, event.brightness);
+ assertArrayEquals(new float[] {132.2f, 131.1f}, event.luxValues, 0.01f);
+ assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps);
+ assertEquals(32, event.lastBrightness);
+ assertEquals(1, event.userId);
+ assertTrue(event.nightMode);
+ assertEquals(3235, event.colorTemperature);
+ assertEquals(0.5f, event.batteryLevel, 0.01);
+ assertEquals("com.android.anapp", event.packageName);
+ }
+
+ @Test
+ public void testFailedRead() {
+ String someTimeAgo =
+ Long.toString(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(12));
+ mInjector.mCurrentTimeMillis = System.currentTimeMillis();
+
+ BrightnessTracker tracker = new BrightnessTracker(InstrumentationRegistry.getContext(),
+ mInjector);
+ String eventFile = "junk in the file";
+ try {
+ tracker.readEventsLocked(getInputStream(eventFile));
+ } catch (IOException e) {
+ // Expected;
+ }
+ assertEquals(0, tracker.getEvents(0).getList().size());
+
+ // Missing lux value.
+ eventFile =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<events>\n"
+ + "<event brightness=\"194\" timestamp=\"" + someTimeAgo + "\" packageName=\""
+ + "com.example.app\" user=\"10\" "
+ + "batteryLevel=\"0.7\" nightMode=\"false\" colorTemperature=\"0\" />\n"
+ + "</events>";
+ try {
+ tracker.readEventsLocked(getInputStream(eventFile));
+ } catch (IOException e) {
+ // Expected;
+ }
+ assertEquals(0, tracker.getEvents(0).getList().size());
+ }
+
+ @Test
+ public void testWriteThenRead() throws Exception {
+ final int brightness = 20;
+
+ mInjector.mSystemIntSettings.put(Settings.System.SCREEN_BRIGHTNESS, brightness);
+ mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
+ mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
+
+ startTracker(mTracker);
+ mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
+ batteryChangeEvent(30, 100));
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(2000.0f));
+ final long firstSensorTime = mInjector.currentTimeMillis();
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
+ mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f));
+ final long secondSensorTime = mInjector.currentTimeMillis();
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3));
+ mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
+ Settings.System.SCREEN_BRIGHTNESS));
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ mTracker.writeEventsLocked(baos);
+ mTracker.stop();
+
+ baos.flush();
+ ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray());
+ BrightnessTracker tracker = new BrightnessTracker(InstrumentationRegistry.getContext(),
+ mInjector);
+ tracker.readEventsLocked(input);
+ List<BrightnessChangeEvent> events = tracker.getEvents(0).getList();
+
+ assertEquals(1, events.size());
+ BrightnessChangeEvent event = events.get(0);
+ assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, 0.01f);
+ assertArrayEquals(new long[] {firstSensorTime, secondSensorTime}, event.luxTimestamps);
+ assertEquals(brightness, event.brightness);
+ assertEquals(0.3, event.batteryLevel, 0.01f);
+ assertTrue(event.nightMode);
+ assertEquals(3339, event.colorTemperature);
+ }
+
+ @Test
+ public void testParcelUnParcel() {
+ Parcel parcel = Parcel.obtain();
+ BrightnessChangeEvent event = new BrightnessChangeEvent();
+ event.brightness = 23;
+ event.timeStamp = 345L;
+ event.packageName = "com.example";
+ event.userId = 12;
+ event.luxValues = new float[2];
+ event.luxValues[0] = 3000.0f;
+ event.luxValues[1] = 4000.0f;
+ event.luxTimestamps = new long[2];
+ event.luxTimestamps[0] = 325L;
+ event.luxTimestamps[1] = 315L;
+ event.batteryLevel = 0.7f;
+ event.nightMode = false;
+ event.colorTemperature = 345;
+ event.lastBrightness = 50;
+
+ event.writeToParcel(parcel, 0);
+ byte[] parceled = parcel.marshall();
+ parcel.recycle();
+
+ parcel = Parcel.obtain();
+ parcel.unmarshall(parceled, 0, parceled.length);
+ parcel.setDataPosition(0);
+
+ BrightnessChangeEvent event2 = BrightnessChangeEvent.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ assertEquals(event.brightness, event2.brightness);
+ assertEquals(event.timeStamp, event2.timeStamp);
+ assertEquals(event.packageName, event2.packageName);
+ assertEquals(event.userId, event2.userId);
+ assertArrayEquals(event.luxValues, event2.luxValues, 0.01f);
+ assertArrayEquals(event.luxTimestamps, event2.luxTimestamps);
+ assertEquals(event.batteryLevel, event2.batteryLevel, 0.01f);
+ assertEquals(event.nightMode, event2.nightMode);
+ assertEquals(event.colorTemperature, event2.colorTemperature);
+ assertEquals(event.lastBrightness, event2.lastBrightness);
+
+ parcel = Parcel.obtain();
+ event.batteryLevel = Float.NaN;
+ event.writeToParcel(parcel, 0);
+ parceled = parcel.marshall();
+ parcel.recycle();
+
+ parcel = Parcel.obtain();
+ parcel.unmarshall(parceled, 0, parceled.length);
+ parcel.setDataPosition(0);
+ event2 = BrightnessChangeEvent.CREATOR.createFromParcel(parcel);
+ assertEquals(event.batteryLevel, event2.batteryLevel, 0.01f);
+ }
+
+ private InputStream getInputStream(String data) {
+ return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
+ }
+
+ private Intent batteryChangeEvent(int level, int scale) {
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra(BatteryManager.EXTRA_LEVEL, level);
+ intent.putExtra(BatteryManager.EXTRA_SCALE, scale);
+ return intent;
+ }
+
+ private SensorEvent createSensorEvent(float lux) {
+ SensorEvent event;
+ try {
+ Constructor<SensorEvent> constr =
+ SensorEvent.class.getDeclaredConstructor(Integer.TYPE);
+ constr.setAccessible(true);
+ event = constr.newInstance(1);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ event.values[0] = lux;
+ event.timestamp = mInjector.mElapsedRealtimeNanos;
+
+ return event;
+ }
+
+ private void startTracker(BrightnessTracker tracker) {
+ tracker.start();
+ mInjector.waitForHandler();
+ }
+
+
+ private static final class Idle implements MessageQueue.IdleHandler {
+ private boolean mIdle;
+
+ @Override
+ public boolean queueIdle() {
+ synchronized (this) {
+ mIdle = true;
+ notifyAll();
+ }
+ return false;
+ }
+
+ public synchronized void waitForIdle() {
+ while (!mIdle) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ private class TestInjector extends BrightnessTracker.Injector {
+ SensorEventListener mSensorListener;
+ ContentObserver mSettingsObserver;
+ BroadcastReceiver mBroadcastReceiver;
+ Map<String, Integer> mSystemIntSettings = new HashMap<>();
+ Map<String, Integer> mSecureIntSettings = new HashMap<>();
+ long mCurrentTimeMillis = System.currentTimeMillis();
+ long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
+ Handler mHandler;
+
+ public TestInjector(Handler handler) {
+ mHandler = handler;
+ }
+
+ public void incrementTime(long timeMillis) {
+ mCurrentTimeMillis += timeMillis;
+ mElapsedRealtimeNanos += TimeUnit.MILLISECONDS.toNanos(timeMillis);
+ }
+
+ @Override
+ public void registerSensorListener(Context context,
+ SensorEventListener sensorListener) {
+ mSensorListener = sensorListener;
+ }
+
+ @Override
+ public void unregisterSensorListener(Context context,
+ SensorEventListener sensorListener) {
+ mSensorListener = null;
+ }
+
+ @Override
+ public void registerBrightnessObserver(ContentResolver resolver,
+ ContentObserver settingsObserver) {
+ mSettingsObserver = settingsObserver;
+ }
+
+ @Override
+ public void unregisterBrightnessObserver(Context context,
+ ContentObserver settingsObserver) {
+ mSettingsObserver = null;
+ }
+
+ @Override
+ public void registerReceiver(Context context,
+ BroadcastReceiver shutdownReceiver, IntentFilter shutdownFilter) {
+ mBroadcastReceiver = shutdownReceiver;
+ }
+
+ @Override
+ public void unregisterReceiver(Context context,
+ BroadcastReceiver broadcastReceiver) {
+ assertEquals(mBroadcastReceiver, broadcastReceiver);
+ mBroadcastReceiver = null;
+ }
+
+ @Override
+ public Handler getBackgroundHandler() {
+ return mHandler;
+ }
+
+ public void waitForHandler() {
+ Idle idle = new Idle();
+ mHandler.getLooper().getQueue().addIdleHandler(idle);
+ mHandler.post(() -> {});
+ idle.waitForIdle();
+ }
+
+ @Override
+ public int getSystemIntForUser(ContentResolver resolver, String setting, int defaultValue,
+ int userId) {
+ Integer value = mSystemIntSettings.get(setting);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ return value;
+ }
+ }
+
+ @Override
+ public void putSystemIntForUser(ContentResolver resolver, String setting, int value,
+ int userId) {
+ mSystemIntSettings.put(setting, value);
+ }
+
+ @Override
+ public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue,
+ int userId) {
+ Integer value = mSecureIntSettings.get(setting);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ return value;
+ }
+ }
+
+ @Override
+ public AtomicFile getFile() {
+ // Don't have the test write / read from anywhere.
+ return null;
+ }
+
+ @Override
+ public long currentTimeMillis() {
+ return mCurrentTimeMillis;
+ }
+
+ @Override
+ public long elapsedRealtimeNanos() {
+ return mElapsedRealtimeNanos;
+ }
+
+ @Override
+ public int getUserSerialNumber(UserManager userManager, int userId) {
+ return userId + 10;
+ }
+
+ @Override
+ public int getUserId(UserManager userManager, int userSerialNumber) {
+ return userSerialNumber - 10;
+ }
+
+ @Override
+ public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
+ ActivityManager.StackInfo focusedStack = new ActivityManager.StackInfo();
+ focusedStack.userId = 0;
+ focusedStack.topActivity = new ComponentName("a.package", "a.class");
+ return focusedStack;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index afe432b..467b47a 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -37,7 +37,6 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
@@ -57,10 +56,10 @@
* To run this test from root of checkout:
* <pre>
* mmm -j32 frameworks/base/services/tests/servicestests/
- * adb install out/target/product/marlin/data/app/JobTestApp/JobTestApp.apk
- * adb install out/target/product/marlin/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * adb install -r $OUT/data/app/JobTestApp/JobTestApp.apk
+ * adb install -r $OUT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
* adb shell am instrument -e class 'com.android.server.job.BackgroundRestrictionsTest' -w \
- * 'com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner'
+ com.android.frameworks.servicestests
* </pre>
*/
@RunWith(AndroidJUnit4.class)
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 6bb5bc6..025ebc3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -453,7 +453,7 @@
}
@Override
- boolean injectCheckAccessShortcutsPermission(int callingPid, int callingUid) {
+ boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) {
return mInjectCheckAccessShortcutsPermission;
}
@@ -1648,7 +1648,6 @@
protected void assertShortcutLaunchable(@NonNull String packageName, @NonNull String shortcutId,
int userId) {
assertNotNull(launchShortcutAndGetIntent(packageName, shortcutId, userId));
- assertNotNull(launchShortcutAndGetIntent_withShortcutInfo(packageName, shortcutId, userId));
}
protected void assertShortcutNotLaunched(@NonNull String packageName,
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 b5e8e1c..f92b575 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -1918,7 +1918,7 @@
// Make sure FLAG_MATCH_ALL_PINNED will be ignored.
assertWith(mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_MATCH_PINNED
- | ShortcutQuery.FLAG_MATCH_ALL_PINNED), getCallingUser()))
+ | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER), getCallingUser()))
.isEmpty();
// Make sure the special permission works.
@@ -1928,14 +1928,18 @@
assertWith(mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_MATCH_PINNED
- | ShortcutQuery.FLAG_MATCH_ALL_PINNED), getCallingUser()))
+ | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER), getCallingUser()))
.haveIds("s1", "s2");
assertWith(mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_MATCH_PINNED), getCallingUser()))
.isEmpty();
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", getCallingUser().getIdentifier());
+
mInjectCheckAccessShortcutsPermission = false;
+ assertShortcutNotLaunched(CALLING_PACKAGE_2, "s1", getCallingUser().getIdentifier());
+
assertShortcutIds(assertAllDynamic(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED
@@ -2106,6 +2110,62 @@
});
}
+ public void testPinShortcutAndGetPinnedShortcuts_assistant() {
+ // Create some shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ // Pin some.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3", "s4"), getCallingUser());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ final ShortcutQuery allPinned = new ShortcutQuery().setQueryFlags(
+ ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER);
+
+ assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0))
+ .isEmpty();
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunched(CALLING_PACKAGE_1, "s3", USER_0);
+ assertShortcutNotLaunched(CALLING_PACKAGE_1, "s4", USER_0);
+
+ // Make it the assistant app.
+ mInternal.setShortcutHostPackage("assistant", LAUNCHER_2, USER_0);
+ assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0))
+ .haveIds("s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+ assertShortcutNotLaunched(CALLING_PACKAGE_1, "s4", USER_0);
+
+ mInternal.setShortcutHostPackage("another-type", LAUNCHER_3, USER_0);
+ assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0))
+ .haveIds("s3");
+
+ mInternal.setShortcutHostPackage("assistant", null, USER_0);
+ assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0))
+ .isEmpty();
+
+ mInternal.setShortcutHostPackage("assistant", LAUNCHER_2, USER_0);
+ assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0))
+ .haveIds("s3");
+
+ mInternal.setShortcutHostPackage("assistant", LAUNCHER_1, USER_0);
+ assertWith(mLauncherApps.getShortcuts(allPinned, HANDLE_USER_0))
+ .isEmpty();
+ });
+ }
+
public void testPinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index b656d5e..dd9a8ab 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -309,6 +309,8 @@
@MediumTest
public void testAddRestrictedProfile() throws Exception {
+ assertFalse("There should be no associated restricted profiles before the test",
+ mUserManager.hasRestrictedProfiles());
UserInfo userInfo = createRestrictedProfile("Profile");
assertNotNull(userInfo);
@@ -324,6 +326,9 @@
userInfo.id);
assertEquals("Restricted profile should have setting LOCATION_MODE set to "
+ "LOCATION_MODE_OFF by default", locationMode, Settings.Secure.LOCATION_MODE_OFF);
+
+ assertTrue("Newly created profile should be associated with the current user",
+ mUserManager.hasRestrictedProfiles());
}
@MediumTest
diff --git a/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
new file mode 100644
index 0000000..880b77e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
@@ -0,0 +1,449 @@
+package com.android.server.pm.crossprofile;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.pm.crossprofile.CrossProfileAppsServiceImplTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class CrossProfileAppsServiceImplTest {
+ private static final String PACKAGE_ONE = "com.one";
+ private static final int PACKAGE_ONE_UID = 1111;
+ private static final ComponentName ACTIVITY_COMPONENT =
+ new ComponentName("com.one", "test");
+
+ private static final String PACKAGE_TWO = "com.two";
+ private static final int PACKAGE_TWO_UID = 2222;
+
+ private static final int PRIMARY_USER = 0;
+ private static final int PROFILE_OF_PRIMARY_USER = 10;
+ private static final int SECONDARY_USER = 11;
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
+ @Mock
+ private AppOpsManager mAppOpsManager;
+
+ private TestInjector mTestInjector;
+ private ActivityInfo mActivityInfo;
+ private CrossProfileAppsServiceImpl mCrossProfileAppsServiceImpl;
+
+ private SparseArray<Boolean> mUserEnabled = new SparseArray<>();
+
+ @Before
+ public void initCrossProfileAppsServiceImpl() {
+ mTestInjector = new TestInjector();
+ mCrossProfileAppsServiceImpl = new CrossProfileAppsServiceImpl(mContext, mTestInjector);
+ }
+
+ @Before
+ public void setupEnabledProfiles() {
+ mUserEnabled.put(PRIMARY_USER, true);
+ mUserEnabled.put(PROFILE_OF_PRIMARY_USER, true);
+ mUserEnabled.put(SECONDARY_USER, true);
+
+ when(mUserManager.getEnabledProfileIds(anyInt())).thenAnswer(
+ invocation -> {
+ List<Integer> users = new ArrayList<>();
+ final int targetUser = invocation.getArgument(0);
+ users.add(targetUser);
+
+ int profileUserId = -1;
+ if (targetUser == PRIMARY_USER) {
+ profileUserId = PROFILE_OF_PRIMARY_USER;
+ } else if (targetUser == PROFILE_OF_PRIMARY_USER) {
+ profileUserId = PRIMARY_USER;
+ }
+
+ if (profileUserId != -1 && mUserEnabled.get(profileUserId)) {
+ users.add(profileUserId);
+ }
+ return users.stream().mapToInt(i -> i).toArray();
+ });
+ }
+
+ @Before
+ public void setupCaller() {
+ mTestInjector.setCallingUid(PACKAGE_ONE_UID);
+ mTestInjector.setCallingUserId(PRIMARY_USER);
+ }
+
+ @Before
+ public void setupPackage() throws Exception {
+ // PACKAGE_ONE are installed in all users.
+ mockAppsInstalled(PACKAGE_ONE, PRIMARY_USER, true);
+ mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, true);
+ mockAppsInstalled(PACKAGE_ONE, SECONDARY_USER, true);
+
+ // Packages are resolved to their corresponding UID.
+ doAnswer(invocation -> {
+ final int uid = invocation.getArgument(0);
+ final String packageName = invocation.getArgument(1);
+ if (uid == PACKAGE_ONE_UID && PACKAGE_ONE.equals(packageName)) {
+ return null;
+ } else if (uid ==PACKAGE_TWO_UID && PACKAGE_TWO.equals(packageName)) {
+ return null;
+ }
+ throw new SecurityException("Not matching");
+ }).when(mAppOpsManager).checkPackage(anyInt(), anyString());
+
+ // The intent is resolved to the ACTIVITY_COMPONENT.
+ mockActivityLaunchIntentResolvedTo(ACTIVITY_COMPONENT);
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromPrimaryUser_installed() throws Exception {
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).containsExactly(UserHandle.of(PROFILE_OF_PRIMARY_USER));
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromPrimaryUser_notInstalled() throws Exception {
+ mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, false);
+
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).isEmpty();
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromPrimaryUser_userNotEnabled() throws Exception {
+ mUserEnabled.put(PROFILE_OF_PRIMARY_USER, false);
+
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).isEmpty();
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromSecondaryUser() throws Exception {
+ mTestInjector.setCallingUserId(SECONDARY_USER);
+
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).isEmpty();
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromProfile_installed() throws Exception {
+ mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
+
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).containsExactly(UserHandle.of(PRIMARY_USER));
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromProfile_notInstalled() throws Exception {
+ mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
+ mockAppsInstalled(PACKAGE_ONE, PRIMARY_USER, false);
+
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).isEmpty();
+ }
+
+ @Test(expected = SecurityException.class)
+ public void getTargetUserProfiles_fakeCaller() throws Exception {
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_TWO);
+ }
+
+ @Test
+ public void startActivityAsUser_currentUser() throws Exception {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(PRIMARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_profile_successWithOption() throws Exception {
+ Bundle options = Bundle.forPair("test_key", "test_value");
+
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ options,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER));
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+
+ verify(mContext)
+ .startActivityAsUser(
+ intentCaptor.capture(),
+ bundleCaptor.capture(),
+ eq(UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+ Intent intent = intentCaptor.getValue();
+ assertEquals(ACTIVITY_COMPONENT, intent.getComponent());
+
+ Bundle bundle = bundleCaptor.getValue();
+ assertEquals("test_value", bundle.getString("test_key"));
+ }
+
+ @Test
+ public void startActivityAsUser_profile_notInstalled() throws Exception {
+ mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, false);
+
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_profile_fakeCaller() throws Exception {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_TWO,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_profile_notExported() throws Exception {
+ mActivityInfo.exported = false;
+
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_profile_anotherPackage() throws Exception {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ new ComponentName(PACKAGE_TWO, "test"),
+ null,
+ null,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_secondaryUser() throws Exception {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(SECONDARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_fromProfile_success() throws Exception {
+ mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
+
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(PRIMARY_USER));
+
+ verify(mContext)
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ eq(UserHandle.of(PRIMARY_USER)));
+ }
+
+ private void mockAppsInstalled(String packageName, int user, boolean installed) {
+ when(mPackageManagerInternal.getPackageInfo(
+ eq(packageName),
+ anyInt(),
+ anyInt(),
+ eq(user)))
+ .thenReturn(installed ? createInstalledPackageInfo() : null);
+ }
+
+ private PackageInfo createInstalledPackageInfo() {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.enabled = true;
+ return packageInfo;
+ }
+
+ private void mockActivityLaunchIntentResolvedTo(ComponentName componentName) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.packageName = componentName.getPackageName();
+ activityInfo.name = componentName.getClassName();
+ activityInfo.exported = true;
+ resolveInfo.activityInfo = activityInfo;
+ mActivityInfo = activityInfo;
+
+ when(mPackageManagerInternal.queryIntentActivities(
+ any(Intent.class), anyInt(), anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(resolveInfo));
+ }
+
+ private class TestInjector implements CrossProfileAppsServiceImpl.Injector {
+ private int mCallingUid;
+ private int mCallingUserId;
+
+ public void setCallingUid(int uid) {
+ mCallingUid = uid;
+ }
+
+ public void setCallingUserId(int userId) {
+ mCallingUserId = userId;
+ }
+
+ @Override
+ public int getCallingUid() {
+ return mCallingUid;
+ }
+
+ @Override
+ public int getCallingUserId() {
+ return mCallingUserId;
+ }
+
+ @Override
+ public UserHandle getCallingUserHandle() {
+ return UserHandle.of(mCallingUserId);
+ }
+
+ @Override
+ public long clearCallingIdentity() {
+ return 0;
+ }
+
+ @Override
+ public void restoreCallingIdentity(long token) {
+ }
+
+ @Override
+ public UserManager getUserManager() {
+ return mUserManager;
+ }
+
+ @Override
+ public PackageManagerInternal getPackageManagerInternal() {
+ return mPackageManagerInternal;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public AppOpsManager getAppOpsManager() {
+ return mAppOpsManager;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 4db9a30..36d0c8b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -17,12 +17,15 @@
package com.android.server.pm.dex;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.UserHandle;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.pm.Installer;
+
import dalvik.system.DelegateLastClassLoader;
import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
@@ -36,8 +39,13 @@
import java.util.Map;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -45,6 +53,12 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
@@ -56,6 +70,12 @@
private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
DelegateLastClassLoader.class.getName();
+ @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+ @Mock Installer mInstaller;
+ @Mock IPackageManager mPM;
+ private final Object mInstallLock = new Object();
+ @Mock DexManager.Listener mListener;
+
private DexManager mDexManager;
private TestData mFooUser0;
@@ -90,7 +110,8 @@
mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
DELEGATE_LAST_CLASS_LOADER_NAME);
- mDexManager = new DexManager(null, null, null, null);
+ mDexManager = new DexManager(
+ mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, mListener);
// Foo and Bar are available to user0.
// Only Bar is available to user1;
@@ -440,6 +461,20 @@
}
+ @Test
+ public void testReconcileSecondaryDexFiles_invokesListener() throws Exception {
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
+ notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+ when(mPM.getPackageInfo(mFooUser0.getPackageName(), 0, 0))
+ .thenReturn(mFooUser0.mPackageInfo);
+
+ mDexManager.reconcileSecondaryDexFiles(mFooUser0.getPackageName());
+
+ verify(mListener, times(fooSecondaries.size()))
+ .onReconcileSecondaryDexFile(any(ApplicationInfo.class),
+ any(DexUseInfo.class), anyString(), anyInt());
+ }
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
@@ -492,12 +527,12 @@
}
private PackageUseInfo getPackageUseInfo(TestData testData) {
- assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
- return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
+ assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
+ return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
}
private void assertNoUseInfo(TestData testData) {
- assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
+ assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
}
private static PackageInfo getMockPackageInfo(String packageName, int userId) {
@@ -555,8 +590,8 @@
List<String> getSecondaryDexPathsFromProtectedDirs() {
List<String> paths = new ArrayList<>();
- paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex");
- paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex");
+ paths.add(mPackageInfo.applicationInfo.deviceProtectedDataDir + "/secondary6.dex");
+ paths.add(mPackageInfo.applicationInfo.credentialProtectedDataDir + "/secondary7.dex");
return paths;
}
diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
index 50ac41c..0db19e4 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
@@ -18,8 +18,13 @@
import android.os.PowerManager.ServiceType;
import android.os.PowerSaveState;
import android.os.Handler;
+import android.provider.Settings.Global;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArrayMap;
+
+import com.android.frameworks.servicestests.R;
+import com.android.internal.annotations.VisibleForTesting;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -36,7 +41,7 @@
private static final float DEFAULT_BRIGHTNESS_FACTOR = 0.5f;
private static final float PRECISION = 0.001f;
private static final int GPS_MODE = 0;
- private static final int DEFAULT_GPS_MODE = 1;
+ private static final int DEFAULT_GPS_MODE = 0;
private static final String BATTERY_SAVER_CONSTANTS = "vibration_disabled=true,"
+ "animation_disabled=false,"
+ "soundtrigger_disabled=true,"
@@ -49,15 +54,40 @@
+ "gps_mode=0";
private static final String BATTERY_SAVER_INCORRECT_CONSTANTS = "vi*,!=,,true";
+ private class BatterySaverPolicyForTest extends BatterySaverPolicy {
+ public BatterySaverPolicyForTest(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ String getGlobalSetting(String key) {
+ return mMockGlobalSettings.get(key);
+ }
+
+ @Override
+ int getDeviceSpecificConfigResId() {
+ return mDeviceSpecificConfigResId;
+ }
+
+
+ @VisibleForTesting
+ void onChange() {
+ onChange(true, null);
+ }
+ }
+
@Mock
Handler mHandler;
- private BatterySaverPolicy mBatterySaverPolicy;
+ private BatterySaverPolicyForTest mBatterySaverPolicy;
+
+ private final ArrayMap<String, String> mMockGlobalSettings = new ArrayMap<>();
+ private int mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_1;
public void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
- mBatterySaverPolicy = new BatterySaverPolicy(mHandler);
- mBatterySaverPolicy.start(getContext().getContentResolver());
+ mBatterySaverPolicy = new BatterySaverPolicyForTest(mHandler);
+ mBatterySaverPolicy.systemReady(getContext());
}
@SmallTest
@@ -102,7 +132,7 @@
@SmallTest
public void testGetBatterySaverPolicy_PolicyDataSaver_DefaultValueCorrect() {
- mBatterySaverPolicy.updateConstants("");
+ mBatterySaverPolicy.updateConstantsLocked("", "");
final PowerSaveState batterySaverStateOn =
mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.DATA_SAVER, BATTERY_SAVER_ON);
assertThat(batterySaverStateOn.batterySaverEnabled).isFalse();
@@ -132,7 +162,7 @@
@SmallTest
public void testUpdateConstants_getCorrectData() {
- mBatterySaverPolicy.updateConstants(BATTERY_SAVER_CONSTANTS);
+ mBatterySaverPolicy.updateConstantsLocked(BATTERY_SAVER_CONSTANTS, "");
final PowerSaveState vibrationState =
mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.VIBRATION, BATTERY_SAVER_ON);
@@ -177,12 +207,12 @@
@SmallTest
public void testUpdateConstants_IncorrectData_NotCrash() {
//Should not crash
- mBatterySaverPolicy.updateConstants(BATTERY_SAVER_INCORRECT_CONSTANTS);
- mBatterySaverPolicy.updateConstants(null);
+ mBatterySaverPolicy.updateConstantsLocked(BATTERY_SAVER_INCORRECT_CONSTANTS, "");
+ mBatterySaverPolicy.updateConstantsLocked(null, "");
}
private void testServiceDefaultValue(@ServiceType int type) {
- mBatterySaverPolicy.updateConstants("");
+ mBatterySaverPolicy.updateConstantsLocked("", "");
final PowerSaveState batterySaverStateOn =
mBatterySaverPolicy.getBatterySaverPolicy(type, BATTERY_SAVER_ON);
assertThat(batterySaverStateOn.batterySaverEnabled).isTrue();
@@ -191,4 +221,37 @@
mBatterySaverPolicy.getBatterySaverPolicy(type, BATTERY_SAVER_OFF);
assertThat(batterySaverStateOff.batterySaverEnabled).isFalse();
}
+
+ public void testDeviceSpecific() {
+ mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_1;
+ mMockGlobalSettings.put(Global.BATTERY_SAVER_CONSTANTS, "");
+ mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, "");
+
+ mBatterySaverPolicy.onChange();
+ assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
+ assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
+
+
+ mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_2;
+
+ mBatterySaverPolicy.onChange();
+ assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
+ assertThat(mBatterySaverPolicy.getFileValues(false).toString())
+ .isEqualTo("{/sys/a=1, /sys/b=2}");
+
+
+ mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_3;
+
+ mBatterySaverPolicy.onChange();
+ assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{/proc/c=4}");
+ assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{/sys/a=3}");
+
+
+ mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
+ "file-on:/proc/z=4");
+
+ mBatterySaverPolicy.onChange();
+ assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{/proc/z=4}");
+ assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index b60d5bf..5039e42 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -16,32 +16,28 @@
package com.android.server.power;
-import android.content.Context;
+import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
+import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.PowerManager;
import android.os.PowerSaveState;
import android.os.SystemProperties;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import android.text.TextUtils;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
+
+import com.android.server.power.batterysaver.BatterySaverController;
+
import org.junit.Rule;
-import org.junit.rules.TemporaryFolder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
-import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
-import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
-import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.when;
-
/**
* Tests for {@link com.android.server.power.PowerManagerService}
*/
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 8531baf..40edfd2 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -17,6 +17,9 @@
package com.android.server.usage;
import static android.app.usage.AppStandby.*;
+import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
+import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -67,6 +70,10 @@
private static final long HOUR_MS = 60 * MINUTE_MS;
private static final long DAY_MS = 24 * HOUR_MS;
+ private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
+ private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
+ private static final long RARE_THRESHOLD = 48 * HOUR_MS;
+
private MyInjector mInjector;
static class MyContextWrapper extends ContextWrapper {
@@ -168,6 +175,14 @@
return packageName != null && packageName.equals(mBoundWidgetPackage);
}
+ @Override
+ String getAppIdleSettings() {
+ return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
+ + WORKING_SET_THRESHOLD + "/"
+ + FREQUENT_THRESHOLD + "/"
+ + RARE_THRESHOLD;
+ }
+
// Internal methods
void setDisplayOn(boolean on) {
@@ -225,12 +240,12 @@
AppStandbyController controller = setupController();
setChargingState(controller, true);
- mInjector.mElapsedRealtime = 8 * DAY_MS;
+ mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
mInjector.mElapsedRealtime, false));
setChargingState(controller, false);
- mInjector.mElapsedRealtime = 16 * DAY_MS;
+ mInjector.mElapsedRealtime = 2 * RARE_THRESHOLD + 2;
controller.checkIdleStates(USER_ID);
assertTrue(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
mInjector.mElapsedRealtime, false));
@@ -247,43 +262,49 @@
false));
}
- private void reportEvent(AppStandbyController controller, long elapsedTime) {
+ private void reportEvent(AppStandbyController controller, int eventType,
+ long elapsedTime) {
// Back to ACTIVE on event
UsageEvents.Event ev = new UsageEvents.Event();
ev.mPackage = PACKAGE_1;
- ev.mEventType = UsageEvents.Event.USER_INTERACTION;
+ ev.mEventType = eventType;
controller.reportEvent(ev, elapsedTime, USER_ID);
}
+ private int getStandbyBucket(AppStandbyController controller) {
+ return controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
+ true);
+ }
+
@Test
public void testBuckets() throws Exception {
AppStandbyController controller = setupController();
assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
- reportEvent(controller, 0);
+ reportEvent(controller, USER_INTERACTION, 0);
// ACTIVE bucket
- assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
+ assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
// WORKING_SET bucket
- assertTimeout(controller, 25 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+ assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
// WORKING_SET bucket
- assertTimeout(controller, 47 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+ assertTimeout(controller, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET);
// FREQUENT bucket
- assertTimeout(controller, 4 * DAY_MS, STANDBY_BUCKET_FREQUENT);
+ assertTimeout(controller, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT);
// RARE bucket
- assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_RARE);
+ assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
- reportEvent(controller, 9 * DAY_MS);
+ reportEvent(controller, USER_INTERACTION, RARE_THRESHOLD + 1);
- assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_ACTIVE);
+ assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
// RARE bucket
- assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
+ assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
}
@Test
@@ -293,23 +314,21 @@
assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
- reportEvent(controller, 0);
+ reportEvent(controller, USER_INTERACTION, 0);
// ACTIVE bucket
- assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
+ assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
// WORKING_SET bucket
- assertTimeout(controller, 25 * HOUR_MS, STANDBY_BUCKET_WORKING_SET);
+ assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
// RARE bucket, should fail because the screen wasn't ON.
- mInjector.mElapsedRealtime = 9 * DAY_MS;
+ mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
controller.checkIdleStates(USER_ID);
- assertNotEquals(STANDBY_BUCKET_RARE,
- controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
- false));
+ assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
mInjector.setDisplayOn(true);
- assertTimeout(controller, 18 * DAY_MS, STANDBY_BUCKET_RARE);
+ assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
}
@Test
@@ -318,8 +337,7 @@
setChargingState(controller, false);
controller.forceIdleState(PACKAGE_1, USER_ID, true);
- assertEquals(STANDBY_BUCKET_RARE, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
- true));
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
assertTrue(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
controller.forceIdleState(PACKAGE_1, USER_ID, false);
@@ -327,4 +345,20 @@
true));
assertFalse(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
}
+
+ @Test
+ public void testNotificationEvent() throws Exception {
+ AppStandbyController controller = setupController();
+ setChargingState(controller, false);
+
+ reportEvent(controller, USER_INTERACTION, 0);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+ mInjector.mElapsedRealtime = 1;
+ reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+
+ controller.forceIdleState(PACKAGE_1, USER_ID, true);
+ reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 6060881..b55c79b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -129,6 +129,9 @@
controller.removeStartingWindow();
waitUntilHandlersIdle();
assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
+
+ controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
+ mDisplayContent.onPendingTransactionApplied();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index bb88264..d9ab5c8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -51,71 +51,77 @@
@RunWith(AndroidJUnit4.class)
public class AppWindowTokenTests extends WindowTestsBase {
+ TaskStack mStack;
+ Task mTask;
+ WindowTestUtils.TestAppWindowToken mToken;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mStack = createTaskStackOnDisplay(mDisplayContent);
+ mTask = createTaskInStack(mStack, 0 /* userId */);
+ mToken = new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+
+ mTask.addChild(mToken, 0);
+ }
+
@Test
@Presubmit
public void testAddWindow_Order() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+ assertEquals(0, mToken.getWindowsCount());
- assertEquals(0, token.getWindowsCount());
-
- final WindowState win1 = createWindow(null, TYPE_APPLICATION, token, "win1");
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, token,
+ final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1");
+ final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
"startingWin");
- final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, token, "baseWin");
- final WindowState win4 = createWindow(null, TYPE_APPLICATION, token, "win4");
+ final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mToken, "baseWin");
+ final WindowState win4 = createWindow(null, TYPE_APPLICATION, mToken, "win4");
// Should not contain the windows that were added above.
- assertEquals(4, token.getWindowsCount());
- assertTrue(token.hasWindow(win1));
- assertTrue(token.hasWindow(startingWin));
- assertTrue(token.hasWindow(baseWin));
- assertTrue(token.hasWindow(win4));
+ assertEquals(4, mToken.getWindowsCount());
+ assertTrue(mToken.hasWindow(win1));
+ assertTrue(mToken.hasWindow(startingWin));
+ assertTrue(mToken.hasWindow(baseWin));
+ assertTrue(mToken.hasWindow(win4));
// The starting window should be on-top of all other windows.
- assertEquals(startingWin, token.getLastChild());
+ assertEquals(startingWin, mToken.getLastChild());
// The base application window should be below all other windows.
- assertEquals(baseWin, token.getFirstChild());
- token.removeImmediately();
+ assertEquals(baseWin, mToken.getFirstChild());
+ mToken.removeImmediately();
}
@Test
@Presubmit
public void testFindMainWindow() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+ assertNull(mToken.findMainWindow());
- assertNull(token.findMainWindow());
-
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
- assertEquals(window1, token.findMainWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+ final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window11");
+ final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window12");
+ assertEquals(window1, mToken.findMainWindow());
window1.mAnimatingExit = true;
- assertEquals(window1, token.findMainWindow());
- final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token, "window2");
- assertEquals(window2, token.findMainWindow());
- token.removeImmediately();
+ assertEquals(window1, mToken.findMainWindow());
+ final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2");
+ assertEquals(window2, mToken.findMainWindow());
+ mToken.removeImmediately();
}
@Test
@Presubmit
public void testGetTopFullscreenWindow() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
+ assertNull(mToken.getTopFullscreenWindow());
- assertNull(token.getTopFullscreenWindow());
-
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(null, TYPE_APPLICATION, token, "window11");
- final WindowState window12 = createWindow(null, TYPE_APPLICATION, token, "window12");
- assertEquals(window12, token.getTopFullscreenWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
+ final WindowState window11 = createWindow(null, TYPE_APPLICATION, mToken, "window11");
+ final WindowState window12 = createWindow(null, TYPE_APPLICATION, mToken, "window12");
+ assertEquals(window12, mToken.getTopFullscreenWindow());
window12.mAttrs.width = 500;
- assertEquals(window11, token.getTopFullscreenWindow());
+ assertEquals(window11, mToken.getTopFullscreenWindow());
window11.mAttrs.width = 500;
- assertEquals(window1, token.getTopFullscreenWindow());
- token.removeImmediately();
+ assertEquals(window1, mToken.getTopFullscreenWindow());
+ mToken.removeImmediately();
}
@Test
@@ -124,27 +130,21 @@
sWm.mDisplayReady = true;
sWm.mDisplayEnabled = true;
- // Create an app window with token on a display.
- final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken appWindowToken =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
- task.addChild(appWindowToken, 0);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
- appWindowToken.addWindow(appWindow);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+ mToken.addWindow(appWindow);
// Set initial orientation and update.
- appWindowToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
mDisplayContent.getDisplayId());
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
appWindow.resizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
- appWindowToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
mDisplayContent.getDisplayId());
sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
@@ -159,18 +159,11 @@
sWm.mDisplayReady = true;
sWm.mDisplayEnabled = true;
- // Create an app window with token on a display.
- final DisplayContent defaultDisplayContent = sWm.getDefaultDisplayContentLocked();
- final TaskStack stack = createTaskStackOnDisplay(defaultDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken appWindowToken =
- new WindowTestUtils.TestAppWindowToken(defaultDisplayContent);
- task.addChild(appWindowToken, 0);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken);
- appWindowToken.addWindow(appWindow);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+ mToken.addWindow(appWindow);
// Set initial orientation and update.
performRotation(Surface.ROTATION_90);
@@ -193,53 +186,49 @@
@Test
@Presubmit
public void testGetOrientation() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
- token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- token.setFillsParent(false);
+ mToken.setFillsParent(false);
// Can specify orientation if app doesn't fill parent.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
- token.setFillsParent(true);
- token.hidden = true;
- token.sendingToBottom = true;
+ mToken.setFillsParent(true);
+ mToken.hidden = true;
+ mToken.sendingToBottom = true;
// Can not specify orientation if app isn't visible even though it fills parent.
- assertEquals(SCREEN_ORIENTATION_UNSET, token.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation(SCREEN_ORIENTATION_BEHIND));
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
- token.sendingToBottom = false;
- token.setIsOnTop(true);
- // Allow for token to provide orientation hidden if on top and not being sent to bottom.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation());
+ mToken.sendingToBottom = false;
+ mToken.setIsOnTop(true);
+ // Allow for mToken to provide orientation hidden if on top and not being sent to bottom.
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
}
@Test
@Presubmit
public void testKeyguardFlagsDuringRelaunch() throws Exception {
- final WindowTestUtils.TestAppWindowToken token =
- new WindowTestUtils.TestAppWindowToken(mDisplayContent);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, token);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
// Add window with show when locked flag
- token.addWindow(appWindow);
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.addWindow(appWindow);
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Start relaunching
- token.startRelaunching();
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.startRelaunching();
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Remove window and make sure that we still report back flag
- token.removeChild(appWindow);
- assertTrue(token.containsShowWhenLockedWindow() && token.containsDismissKeyguardWindow());
+ mToken.removeChild(appWindow);
+ assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
// Finish relaunching and ensure flag is now not reported
- token.finishRelaunching();
- assertFalse(token.containsShowWhenLockedWindow() || token.containsDismissKeyguardWindow());
+ mToken.finishRelaunching();
+ assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
deleted file mode 100644
index c3a471a..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/DimLayerControllerTests.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.hardware.display.DisplayManagerGlobal;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link DimLayerController} class.
- *
- * Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.DimLayerControllerTests
- */
-@SmallTest
-@Presubmit
-@org.junit.runner.RunWith(AndroidJUnit4.class)
-public class DimLayerControllerTests extends WindowTestsBase {
-
- /**
- * This tests if shared fullscreen dim layer is added when stack is added to display
- * and is removed when the only stack on the display is removed.
- */
- @Test
- public void testSharedFullScreenDimLayer() throws Exception {
- // Create a display.
- final DisplayContent dc = createNewDisplay();
- assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-
- // Add stack with activity.
- final TaskStack stack = createTaskStackOnDisplay(dc);
- assertTrue(dc.mDimLayerController.hasDimLayerUser(stack));
- assertTrue(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-
- // Remove the only stack on the display and check if the shared dim layer clears.
- stack.removeImmediately();
- assertFalse(dc.mDimLayerController.hasDimLayerUser(stack));
- assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
new file mode 100644
index 0000000..f069d49
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import java.util.HashMap;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+
+/**
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DimmerTests extends WindowTestsBase {
+ private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
+ final SurfaceControl mControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+
+ @Override
+ SurfaceControl getSurfaceControl() {
+ return mControl;
+ }
+
+ @Override
+ SurfaceControl.Transaction getPendingTransaction() {
+ return mTransaction;
+ }
+ }
+
+ private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
+ final SurfaceSession mSession = new SurfaceSession();
+ SurfaceControl mBuiltSurface = null;
+ final SurfaceControl mHostControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
+
+ class MockSurfaceBuilder extends SurfaceControl.Builder {
+ MockSurfaceBuilder(SurfaceSession ss) {
+ super(ss);
+ }
+
+ @Override
+ public SurfaceControl build() {
+ SurfaceControl sc = mock(SurfaceControl.class);
+ mBuiltSurface = sc;
+ return sc;
+ }
+ }
+
+ @Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ return new MockSurfaceBuilder(mSession);
+ }
+
+ @Override
+ SurfaceControl getSurfaceControl() {
+ return mHostControl;
+ }
+
+ @Override
+ SurfaceControl.Transaction getPendingTransaction() {
+ return mHostTransaction;
+ }
+ }
+
+ MockSurfaceBuildingContainer mHost;
+ Dimmer mDimmer;
+ SurfaceControl.Transaction mTransaction;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mHost = new MockSurfaceBuildingContainer();
+ mTransaction = mock(SurfaceControl.Transaction.class);
+ mDimmer = new Dimmer(mHost);
+ }
+
+ @Test
+ public void testDimAboveNoChildCreatesSurface() throws Exception {
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, alpha);
+ assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+ verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+ verify(mTransaction).show(mHost.mBuiltSurface);
+ verify(mTransaction).setLayer(mHost.mBuiltSurface, Integer.MAX_VALUE);
+ }
+
+ @Test
+ public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception {
+ float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, alpha);
+ final SurfaceControl firstSurface = mHost.mBuiltSurface;
+
+ alpha = 0.9f;
+ mDimmer.dimAbove(mTransaction, alpha);
+
+ assertEquals(firstSurface, mHost.mBuiltSurface);
+ verify(mTransaction).setAlpha(firstSurface, 0.9f);
+ }
+
+ @Test
+ public void testUpdateDimsAppliesSize() throws Exception {
+ mDimmer.dimAbove(mTransaction, 0.8f);
+
+ int width = 100;
+ int height = 300;
+ Rect bounds = new Rect(0, 0, width, height);
+ mDimmer.updateDims(mTransaction, bounds);
+ verify(mTransaction).setSize(mHost.mBuiltSurface, width, height);
+ }
+
+ @Test
+ public void testDimAboveNoChildNotReset() throws Exception {
+ mDimmer.dimAbove(mTransaction, 0.8f);
+ mDimmer.resetDimStates();
+
+ mDimmer.updateDims(mTransaction, new Rect());
+ verify(mHost.mBuiltSurface, never()).destroy();
+ }
+
+ @Test
+ public void testDimAboveWithChildCreatesSurfaceAboveChild() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
+ assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+ verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+ verify(mTransaction).show(mHost.mBuiltSurface);
+ verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, 1);
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimBelow(mTransaction, child, alpha);
+ assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+ verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+ verify(mTransaction).show(mHost.mBuiltSurface);
+ verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, -1);
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceDestroyedWhenReset() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
+ mDimmer.resetDimStates();
+ mDimmer.updateDims(mTransaction, new Rect());
+ verify(mHost.mBuiltSurface).destroy();
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
+ mDimmer.resetDimStates();
+ mDimmer.dimAbove(mTransaction, child, alpha);
+
+ mDimmer.updateDims(mTransaction, new Rect());
+ verify(mHost.mBuiltSurface, never()).destroy();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 9c80544..f23bd62 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -16,8 +16,12 @@
package com.android.server.wm;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.graphics.Color.BLUE;
import static android.graphics.Color.RED;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
@@ -33,16 +37,23 @@
import static org.junit.Assert.assertEquals;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.Instrumentation;
import android.content.Context;
+import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
import android.os.Handler;
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
-import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -51,7 +62,6 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -65,7 +75,6 @@
*/
// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
// TODO: Test non-Activity windows.
-// TODO: Test secondary display.
@SmallTest
// TODO(b/68957554)
//@Presubmit
@@ -78,22 +87,26 @@
private WindowManager mWm;
private ArrayList<View> mWindows = new ArrayList<>();
- @Rule
- public ActivityTestRule<TestActivity> mTestActivityRule = new ActivityTestRule<>(
- TestActivity.class, false /* initialTouchMode */, false /* launchActivity */);
private Activity mTestActivity;
+ private VirtualDisplay mDisplay;
+ private ImageReader mImageReader;
private int mDecorThickness;
private int mHalfDecorThickness;
@Before
public void setUp() {
- mWm = mContext.getSystemService(WindowManager.class);
+ final Pair<VirtualDisplay, ImageReader> result = createDisplay();
+ mDisplay = result.first;
+ mImageReader = result.second;
+ final Display display = mDisplay.getDisplay();
+ final Context dContext = mContext.createDisplayContext(display);
+ mWm = dContext.getSystemService(WindowManager.class);
+ mTestActivity = startActivityOnDisplay(TestActivity.class, display.getDisplayId());
final Point size = new Point();
- mWm.getDefaultDisplay().getSize(size);
+ mDisplay.getDisplay().getRealSize(size);
mDecorThickness = Math.min(size.x, size.y) / 3;
mHalfDecorThickness = mDecorThickness / 2;
- mTestActivity = launchActivity(mTestActivityRule);
}
@After
@@ -101,7 +114,9 @@
while (!mWindows.isEmpty()) {
removeWindow(mWindows.get(0));
}
- finishActivity(mTestActivityRule);
+ finishActivity(mTestActivity);
+ mDisplay.release();
+ mImageReader.close();
}
@Test
@@ -259,23 +274,48 @@
Assert.assertTrue("Excepted " + first + " >= " + second, first >= second);
}
- private Activity launchActivity(ActivityTestRule activityRule) {
- final Activity activity = activityRule.launchActivity(null);
- waitForIdle();
- return activity;
- }
-
- private void finishActivity(ActivityTestRule activityRule) {
- final Activity activity = activityRule.getActivity();
- if (activity != null) {
- activity.finish();
+ private void finishActivity(Activity a) {
+ if (a == null) {
+ return;
}
+ a.finish();
+ waitForIdle();
}
private void waitForIdle() {
mInstrumentation.waitForIdleSync();
}
+ private Activity startActivityOnDisplay(Class<?> cls, int displayId) {
+ final Intent intent = new Intent(mContext, cls);
+ intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(displayId);
+ final Activity activity = mInstrumentation.startActivitySync(intent, options.toBundle());
+ waitForIdle();
+
+ assertEquals(displayId, activity.getDisplay().getDisplayId());
+ return activity;
+ }
+
+ private Pair<VirtualDisplay, ImageReader> createDisplay() {
+ final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+ final DisplayInfo displayInfo = new DisplayInfo();
+ final Display defaultDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+ defaultDisplay.getDisplayInfo(displayInfo);
+ final String name = "ScreenDecorWindowTests";
+ int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+
+ final ImageReader imageReader = ImageReader.newInstance(
+ displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
+
+ final VirtualDisplay display = dm.createVirtualDisplay(name, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, displayInfo.logicalDensityDpi, imageReader.getSurface(),
+ flags);
+
+ return Pair.create(display, imageReader);
+ }
+
public static class TestActivity extends Activity {
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
index 887def7..873a01b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
@@ -66,7 +66,7 @@
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
mPositioner = new TaskPositioner(sWm);
- mPositioner.register(display);
+ mPositioner.register(mDisplayContent);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 99f2685..96fbc14 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -32,7 +32,6 @@
import android.support.test.runner.AndroidJUnit4;
import android.util.ArraySet;
-import com.android.internal.util.Predicate;
import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
import org.junit.Test;
@@ -200,4 +199,16 @@
new File(sFilesDir.getPath() + "/snapshots/2_reduced.jpg")};
assertTrueForFiles(existsFiles, File::exists, " must exist");
}
+
+ /**
+ * Private predicate definition.
+ *
+ * This is needed because com.android.internal.util.Predicate is deprecated
+ * and can only be used with classes fron android.test.runner. This cannot
+ * use java.util.function.Predicate because that is not present on all API
+ * versions that this test must run on.
+ */
+ private interface Predicate<T> {
+ boolean apply(T t);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
index b846fd0..0ef78f4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
@@ -105,14 +105,10 @@
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
assertEquals(stack, task.mStack);
- assertTrue(mDisplayContent.mDimLayerController.hasDimLayerUser(stack));
- assertTrue(mDisplayContent.mDimLayerController.hasDimLayerUser(task));
// Remove stack and check if its child is also removed.
stack.removeImmediately();
assertNull(stack.getDisplayContent());
assertNull(task.mStack);
- assertFalse(mDisplayContent.mDimLayerController.hasDimLayerUser(stack));
- assertFalse(mDisplayContent.mDimLayerController.hasDimLayerUser(task));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 5134c26..a45695f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -133,11 +133,6 @@
}
@Override
- public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) {
-
- }
-
- @Override
public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
return 0;
}
@@ -290,40 +285,11 @@
}
@Override
- public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
- int displayRotation, int uiMode) {
-
- }
-
- @Override
public int getSystemDecorLayerLw() {
return 0;
}
@Override
- public void getContentRectLw(Rect r) {
-
- }
-
- @Override
- public void layoutWindowLw(WindowState win,
- WindowState attached) {
-
- }
-
- @Override
- public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
- int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets,
- Rect outStableInsets, Rect outOutsets) {
- return false;
- }
-
- @Override
- public void finishLayoutLw() {
-
- }
-
- @Override
public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
}
@@ -582,11 +548,6 @@
}
@Override
- public int getInputMethodWindowVisibleHeightLw() {
- return 0;
- }
-
- @Override
public void setCurrentUserLw(int newUserId) {
}
@@ -607,11 +568,6 @@
}
@Override
- public boolean canMagnifyWindow(int windowType) {
- return false;
- }
-
- @Override
public boolean isTopLevelWindow(int windowType) {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
deleted file mode 100644
index 3c3514f..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
-
-/**
- * Tests for the {@link WindowLayersController} class.
- *
- * Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.WindowLayersControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowLayersControllerTests extends WindowTestsBase {
-
- @Test
- public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
- sWm.mInputMethodTarget = null;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // The Ime has an higher base layer than app windows and lower base layer than system
- // windows, so it should be above app windows and below system windows if there isn't an IME
- // target.
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows and below system windows if it is targeting an app
- // window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
- TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
- "imeAppTargetChildAboveWindow");
- final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
- TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
- "imeAppTargetChildBelowWindow");
-
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows except for child windows that are z-ordered above it
- // and below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(imeAppTargetChildAboveWindow, mImeWindow);
- assertWindowLayerGreaterThan(mImeWindow, imeAppTargetChildBelowWindow);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
- final WindowState appBelowImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
- final WindowState imeAppTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
- final WindowState appAboveImeTarget =
- createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
-
- sWm.mInputMethodTarget = imeAppTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // Ime should be above all app windows except for non-fullscreen app window above it and
- // below system windows if it is targeting an app window.
- assertWindowLayerGreaterThan(mImeWindow, imeAppTarget);
- assertWindowLayerGreaterThan(mImeWindow, appBelowImeTarget);
- assertWindowLayerGreaterThan(appAboveImeTarget, mImeWindow);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
- assertWindowLayerGreaterThan(mStatusBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
- final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
- mDisplayContent, "imeSystemOverlayTarget",
- true /* ownerCanAddInternalSystemWindow */);
-
- sWm.mInputMethodTarget = imeSystemOverlayTarget;
- mLayersController.assignWindowLayers(mDisplayContent);
-
- // The IME target base layer is higher than all window except for the nav bar window, so the
- // IME should be above all windows except for the nav bar.
- assertWindowLayerGreaterThan(mImeWindow, imeSystemOverlayTarget);
- assertWindowLayerGreaterThan(mImeWindow, mChildAppWindowAbove);
- assertWindowLayerGreaterThan(mImeWindow, mAppWindow);
- assertWindowLayerGreaterThan(mImeWindow, mDockedDividerWindow);
- assertWindowLayerGreaterThan(mImeWindow, mStatusBarWindow);
- assertWindowLayerGreaterThan(mNavBarWindow, mImeWindow);
-
- // And, IME dialogs should always have an higher layer than the IME.
- assertWindowLayerGreaterThan(mImeDialogWindow, mImeWindow);
- }
-
- @Test
- public void testStackLayers() throws Exception {
- WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
- ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow");
- WindowState dockedStackWindow = createWindowOnStack(null,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
- mDisplayContent, "dockedStackWindow");
- WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
- mDisplayContent, "assistantStackWindow");
-
- mLayersController.assignWindowLayers(mDisplayContent);
-
- assertWindowLayerGreaterThan(dockedStackWindow, mAppWindow);
- assertWindowLayerGreaterThan(assistantStackWindow, dockedStackWindow);
- assertWindowLayerGreaterThan(pinnedStackWindow, assistantStackWindow);
- }
-
- private void assertWindowLayerGreaterThan(WindowState first, WindowState second)
- throws Exception {
- assertGreaterThan(first.mWinAnimator.mAnimLayer, second.mWinAnimator.mAnimLayer);
- }
-
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 0980f7e..4c5e291 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -73,7 +73,6 @@
private static boolean sOneTimeSetupDone = false;
DisplayContent mDisplayContent;
DisplayInfo mDisplayInfo = new DisplayInfo();
- WindowLayersController mLayersController;
WindowState mWallpaperWindow;
WindowState mImeWindow;
WindowState mImeDialogWindow;
@@ -98,8 +97,9 @@
final Context context = InstrumentationRegistry.getTargetContext();
AttributeCache.init(context);
+
sWm = TestWindowManagerPolicy.getWindowManagerService(context);
- mLayersController = new WindowLayersController(sWm);
+ beforeCreateDisplay();
context.getDisplay().getDisplayInfo(mDisplayInfo);
mDisplayContent = createNewDisplay();
@@ -126,6 +126,10 @@
waitUntilHandlersIdle();
}
+ void beforeCreateDisplay() {
+ // Called before display is created.
+ }
+
@After
public void tearDown() throws Exception {
final LinkedList<WindowState> nonCommonWindows = new LinkedList();
@@ -149,6 +153,14 @@
waitUntilHandlersIdle();
}
+ /**
+ * @return A SurfaceBuilderFactory to inject in to the WindowManagerService during
+ * set-up (or null).
+ */
+ SurfaceBuilderFactory getSurfaceBuilderFactory() {
+ return null;
+ }
+
private WindowState createCommonWindow(WindowState parent, int type, String name) {
final WindowState win = createWindow(parent, type, name);
mCommonWindows.add(win);
@@ -162,6 +174,11 @@
Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
}
+ /** Asserts that the first entry is greater than the second entry. */
+ void assertLessThan(int first, int second) throws Exception {
+ Assert.assertTrue("Excepted " + first + " to be less than " + second, first < second);
+ }
+
/**
* Waits until the main handler for WM has processed all messages.
*/
@@ -264,7 +281,7 @@
final int displayId = sNextDisplayId++;
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- return new DisplayContent(display, sWm, mLayersController, new WallpaperController(sWm));
+ return new DisplayContent(display, sWm, new WallpaperController(sWm));
}
/** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 692e08b..7219104 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -94,43 +94,6 @@
assertEquals(null, dc.getWindowToken(token.token));
}
- @Test
- public void testAdjustAnimLayer() throws Exception {
- final WindowTestUtils.TestWindowToken token =
- new WindowTestUtils.TestWindowToken(0, mDisplayContent);
- final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
- final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
- final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3");
-
- window2.mLayer = 100;
- window3.mLayer = 200;
-
- // We assign layers once, to get the base values computed by
- // the controller.
- mLayersController.assignWindowLayers(mDisplayContent);
-
- final int window1StartLayer = window1.mWinAnimator.mAnimLayer;
- final int window11StartLayer = window11.mWinAnimator.mAnimLayer;
- final int window12StartLayer = window12.mWinAnimator.mAnimLayer;
- final int window2StartLayer = window2.mWinAnimator.mAnimLayer;
- final int window3StartLayer = window3.mWinAnimator.mAnimLayer;
-
- // Then we set an adjustment, and assign them again, they should
- // be offset.
- int adj = token.adj = 50;
- mLayersController.assignWindowLayers(mDisplayContent);
- final int highestLayer = token.getHighestAnimLayer();
-
- assertEquals(window1StartLayer + adj, window1.mWinAnimator.mAnimLayer);
- assertEquals(window11StartLayer + adj, window11.mWinAnimator.mAnimLayer);
- assertEquals(window12StartLayer + adj, window12.mWinAnimator.mAnimLayer);
- assertEquals(window2StartLayer + adj, window2.mWinAnimator.mAnimLayer);
- assertEquals(window3StartLayer + adj, window3.mWinAnimator.mAnimLayer);
- assertEquals(window3StartLayer + adj, highestLayer);
- }
-
/**
* Test that a window token isn't orphaned by the system when it is requested to be removed.
* Tokens should only be removed from the system when all their windows are gone.
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
new file mode 100644
index 0000000..f7c4b1f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.util.Log;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
+/**
+ * Tests for the {@link WindowLayersController} class.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ZOrderingTests extends WindowTestsBase {
+
+ private class LayerRecordingTransaction extends SurfaceControl.Transaction {
+ HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap();
+ HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap();
+
+ @Override
+ public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) {
+ mRelativeLayersForControl.remove(sc);
+ mLayersForControl.put(sc, layer);
+ return super.setLayer(sc, layer);
+ }
+
+ @Override
+ public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc,
+ SurfaceControl relativeTo,
+ int layer) {
+ mRelativeLayersForControl.put(sc, relativeTo);
+ mLayersForControl.put(sc, layer);
+ return super.setRelativeLayer(sc, relativeTo, layer);
+ }
+
+ int getLayer(SurfaceControl sc) {
+ return mLayersForControl.get(sc);
+ }
+
+ SurfaceControl getRelativeLayer(SurfaceControl sc) {
+ return mRelativeLayersForControl.get(sc);
+ }
+ };
+
+ // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
+ // such that we can keep track of the parents of Surfaces as they are constructed.
+ private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap();
+
+ private class HierarchyRecorder extends SurfaceControl.Builder {
+ SurfaceControl mPendingParent;
+
+ HierarchyRecorder(SurfaceSession s) {
+ super(s);
+ }
+
+ public SurfaceControl.Builder setParent(SurfaceControl sc) {
+ mPendingParent = sc;
+ return super.setParent(sc);
+ }
+ public SurfaceControl build() {
+ SurfaceControl sc = super.build();
+ mParentFor.put(sc, mPendingParent);
+ mPendingParent = null;
+ return sc;
+ }
+ };
+
+ class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+ public SurfaceControl.Builder make(SurfaceSession s) {
+ return new HierarchyRecorder(s);
+ }
+ };
+
+ private LayerRecordingTransaction mTransaction;
+
+ @Override
+ void beforeCreateDisplay() {
+ // We can't use @Before here because it may happen after WindowTestsBase @Before
+ // which is after construction of the DisplayContent, meaning the HierarchyRecorder
+ // would miss construction of the top-level layers.
+ mTransaction = new LayerRecordingTransaction();
+ sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory();
+ }
+
+ @After
+ public void after() {
+ mTransaction.close();
+ mParentFor.clear();
+ }
+
+ LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) {
+ LinkedList<SurfaceControl> p = new LinkedList();
+ SurfaceControl current = sc;
+ do {
+ p.addLast(current);
+
+ SurfaceControl rs = t.getRelativeLayer(current);
+ if (rs != null) {
+ current = rs;
+ } else {
+ current = mParentFor.get(current);
+ }
+ } while (current != null);
+ return p;
+ }
+
+ void assertZOrderGreaterThan(LayerRecordingTransaction t,
+ SurfaceControl left, SurfaceControl right) throws Exception {
+ final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
+ final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
+
+ SurfaceControl commonAncestor = null;
+ SurfaceControl leftTop = leftParentChain.peekLast();
+ SurfaceControl rightTop = rightParentChain.peekLast();
+ while (leftTop != null && rightTop != null && leftTop == rightTop) {
+ commonAncestor = leftParentChain.removeLast();
+ rightParentChain.removeLast();
+ leftTop = leftParentChain.peekLast();
+ rightTop = rightParentChain.peekLast();
+ }
+
+ if (rightTop == null) { // right is the parent of left.
+ assertGreaterThan(t.getLayer(leftTop), 0);
+ } else if (leftTop == null) { // left is the parent of right.
+ assertGreaterThan(0, t.getLayer(rightTop));
+ } else {
+ assertGreaterThan(t.getLayer(leftTop),
+ t.getLayer(rightTop));
+ }
+ }
+
+ void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
+ WindowState left, WindowState right) throws Exception {
+ assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception {
+ sWm.mInputMethodTarget = null;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // The Ime has an higher base layer than app windows and lower base layer than system
+ // windows, so it should be above app windows and below system windows if there isn't an IME
+ // target.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows and below system windows if it is targeting an app
+ // window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
+ TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
+ "imeAppTargetChildAboveWindow");
+ final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
+ TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
+ "imeAppTargetChildBelowWindow");
+
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows except for child windows that are z-ordered above it
+ // and below system windows if it is targeting an app window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
+ final WindowState appBelowImeTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
+ final WindowState imeAppTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+ final WindowState appAboveImeTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
+
+ sWm.mInputMethodTarget = imeAppTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Ime should be above all app windows except for non-fullscreen app window above it and
+ // below system windows if it is targeting an app window.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
+ assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception {
+ final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
+ mDisplayContent, "imeSystemOverlayTarget",
+ true /* ownerCanAddInternalSystemWindow */);
+
+ sWm.mInputMethodTarget = imeSystemOverlayTarget;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // The IME target base layer is higher than all window except for the nav bar window, so the
+ // IME should be above all windows except for the nav bar.
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+
+ // The IME has a higher base layer than the status bar so we may expect it to go
+ // above the status bar once they are both in the Non-App layer, as past versions of this
+ // test enforced. However this seems like the wrong behavior unless the status bar is the
+ // IME target.
+ assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
+ assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testAssignWindowLayers_ForStatusBarImeTarget() throws Exception {
+ sWm.mInputMethodTarget = mStatusBarWindow;
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+ assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
+
+ // And, IME dialogs should always have an higher layer than the IME.
+ assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+ }
+
+ @Test
+ public void testStackLayers() throws Exception {
+ final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
+ "pinnedStackWindow");
+ final WindowState dockedStackWindow = createWindowOnStack(null,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ mDisplayContent, "dockedStackWindow");
+ final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+ mDisplayContent, "assistantStackWindow");
+
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow);
+ assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
+ assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index c5ca330..7ca17af 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -91,8 +91,6 @@
private long mScreenOnSnapshot; // Elapsed time snapshot when last write of mScreenOnDuration
private long mScreenOnDuration; // Total screen on duration since device was "born"
- private long mElapsedTimeThreshold;
- private long mScreenOnTimeThreshold;
private final File mStorageDir;
private boolean mScreenOn;
@@ -113,11 +111,6 @@
readScreenOnTime();
}
- public void setThresholds(long elapsedTimeThreshold, long screenOnTimeThreshold) {
- mElapsedTimeThreshold = elapsedTimeThreshold;
- mScreenOnTimeThreshold = screenOnTimeThreshold;
- }
-
public void updateDisplay(boolean screenOn, long elapsedRealtime) {
if (screenOn == mScreenOn) return;
@@ -186,7 +179,7 @@
writeScreenOnTime();
}
- public void reportUsage(String packageName, int userId, long elapsedRealtime) {
+ public int reportUsage(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
elapsedRealtime, true);
@@ -197,12 +190,33 @@
+ (elapsedRealtime - mElapsedSnapshot);
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
- appUsageHistory.currentBucket = AppStandby.STANDBY_BUCKET_ACTIVE;
- appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
- if (DEBUG) {
- Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
- + ", reason=" + appUsageHistory.bucketingReason);
+ if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) {
+ appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+ if (DEBUG) {
+ Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+ + ", reason=" + appUsageHistory.bucketingReason);
+ }
}
+ appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
+
+ return appUsageHistory.currentBucket;
+ }
+
+ public int reportMildUsage(String packageName, int userId, long elapsedRealtime) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
+ elapsedRealtime, true);
+ if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) {
+ appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET;
+ if (DEBUG) {
+ Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+ + ", reason=" + appUsageHistory.bucketingReason);
+ }
+ }
+ // TODO: Should this be a different reason for partial usage?
+ appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
+
+ return appUsageHistory.currentBucket;
}
public void setIdle(String packageName, int userId, long elapsedRealtime) {
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 5623a68..cd0fce6 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -91,14 +91,14 @@
0,
0,
COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR,
- COMPRESS_TIME ? 240 * 1000 : 8 * ONE_HOUR
+ COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR
};
static final long[] ELAPSED_TIME_THRESHOLDS = {
0,
COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
- COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_DAY,
- COMPRESS_TIME ? 16 * ONE_MINUTE : 8 * ONE_DAY
+ COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
+ COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR
};
static final int[] THRESHOLD_BUCKETS = {
@@ -140,9 +140,7 @@
static final int MSG_PAROLE_STATE_CHANGED = 9;
static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
- long mAppIdleScreenThresholdMillis;
long mCheckIdleIntervalMillis;
- long mAppIdleWallclockThresholdMillis;
long mAppIdleParoleIntervalMillis;
long mAppIdleParoleDurationMillis;
long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
@@ -229,6 +227,7 @@
// Get sync adapters for the authority
String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
authority, userId);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
for (String packageName: packages) {
// Only force the sync adapters to active if the provider is not in the same package and
// the sync adapter is a system package.
@@ -239,7 +238,12 @@
continue;
}
if (!packageName.equals(providerPkgName)) {
- setAppIdleAsync(packageName, false, userId);
+ synchronized (mAppIdleLock) {
+ int newBucket = mAppIdleHistory.reportMildUsage(packageName, userId,
+ elapsedRealtime);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ newBucket);
+ }
}
} catch (PackageManager.NameNotFoundException e) {
// Shouldn't happen
@@ -493,11 +497,21 @@
if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
|| event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
|| event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
- || event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
- mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
+ || event.mEventType == UsageEvents.Event.USER_INTERACTION
+ || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN)) {
+
+ final int newBucket;
+ if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
+ newBucket = mAppIdleHistory.reportMildUsage(event.mPackage, userId,
+ elapsedRealtime);
+ } else {
+ newBucket = mAppIdleHistory.reportUsage(event.mPackage, userId,
+ elapsedRealtime);
+ }
+
+ maybeInformListeners(event.mPackage, userId, elapsedRealtime,
+ newBucket);
if (previouslyIdle) {
- maybeInformListeners(event.mPackage, userId, elapsedRealtime,
- AppStandby.STANDBY_BUCKET_ACTIVE);
notifyBatteryStats(event.mPackage, userId, false);
}
}
@@ -896,14 +910,6 @@
pw.println();
pw.println("Settings:");
- pw.print(" mAppIdleDurationMillis=");
- TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleWallclockThresholdMillis=");
- TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
- pw.println();
-
pw.print(" mCheckIdleIntervalMillis=");
TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
pw.println();
@@ -1033,6 +1039,11 @@
int userId) {
return appWidgetManager.isBoundWidgetPackage(packageName, userId);
}
+
+ String getAppIdleSettings() {
+ return Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.APP_IDLE_CONSTANTS);
+ }
}
class AppStandbyHandler extends Handler {
@@ -1165,31 +1176,18 @@
// Look at global settings for this.
// TODO: Maybe apply different thresholds for different users.
try {
- mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.APP_IDLE_CONSTANTS));
+ mParser.setString(mInjector.getAppIdleSettings());
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
// fallthrough, mParser is empty and all defaults will be returned.
}
- // Default: 12 hours of screen-on time sans dream-time
- mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
- COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
-
- mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
- COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
-
- mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
- COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
-
// Default: 24 hours between paroles
mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
- mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
- mAppIdleScreenThresholdMillis);
String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
@@ -1199,6 +1197,9 @@
null);
mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
ELAPSED_TIME_THRESHOLDS);
+ mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
+ COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
+
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 0b10590..f02221c 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -643,6 +643,8 @@
return "SHORTCUT_INVOCATION";
case UsageEvents.Event.CHOOSER_ACTION:
return "CHOOSER_ACTION";
+ case UsageEvents.Event.NOTIFICATION_SEEN:
+ return "NOTIFICATION_SEEN";
default:
return "UNKNOWN";
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 7bea8a1..d359b70 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -255,7 +255,6 @@
}
private void alsaFileAdded(String name) {
- Slog.i(TAG, "alsaFileAdded(" + name + ")");
int type = AlsaDevice.TYPE_UNKNOWN;
int card = -1, device = -1;
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index c29a335..095fdc6 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -19,8 +19,13 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
@@ -32,6 +37,7 @@
import com.android.server.usb.descriptors.report.TextReportCanvas;
import com.android.server.usb.descriptors.tree.UsbDescriptorsTree;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -44,23 +50,28 @@
private final Context mContext;
+ // contains all connected USB devices
+ private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
+
// USB busses to exclude from USB host support
private final String[] mHostBlacklist;
+ private final Object mLock = new Object();
+
+ private UsbDevice mNewDevice;
+ private UsbConfiguration mNewConfiguration;
+ private UsbInterface mNewInterface;
+ private ArrayList<UsbConfiguration> mNewConfigurations;
+ private ArrayList<UsbInterface> mNewInterfaces;
+ private ArrayList<UsbEndpoint> mNewEndpoints;
+
private final UsbAlsaManager mUsbAlsaManager;
private final UsbSettingsManager mSettingsManager;
- private final Object mLock = new Object();
@GuardedBy("mLock")
- // contains all connected USB devices
- private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
-
- private Object mSettingsLock = new Object();
- @GuardedBy("mSettingsLock")
private UsbProfileGroupSettingsManager mCurrentSettings;
- private Object mHandlerLock = new Object();
- @GuardedBy("mHandlerLock")
+ @GuardedBy("mLock")
private ComponentName mUsbDeviceConnectionHandler;
public UsbHostManager(Context context, UsbAlsaManager alsaManager,
@@ -80,33 +91,33 @@
}
public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) {
- synchronized (mSettingsLock) {
+ synchronized (mLock) {
mCurrentSettings = settings;
}
}
private UsbProfileGroupSettingsManager getCurrentUserSettings() {
- synchronized (mSettingsLock) {
+ synchronized (mLock) {
return mCurrentSettings;
}
}
public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) {
- synchronized (mHandlerLock) {
+ synchronized (mLock) {
mUsbDeviceConnectionHandler = usbDeviceConnectionHandler;
}
}
private @Nullable ComponentName getUsbDeviceConnectionHandler() {
- synchronized (mHandlerLock) {
+ synchronized (mLock) {
return mUsbDeviceConnectionHandler;
}
}
- private boolean isBlackListed(String deviceAddress) {
+ private boolean isBlackListed(String deviceName) {
int count = mHostBlacklist.length;
for (int i = 0; i < count; i++) {
- if (deviceAddress.startsWith(mHostBlacklist[i])) {
+ if (deviceName.startsWith(mHostBlacklist[i])) {
return true;
}
}
@@ -125,73 +136,166 @@
}
/* Called from JNI in monitorUsbHostBus() to report new USB devices
- Returns true if successful, i.e. the USB Audio device descriptors are
- correctly parsed and the unique device is added to the audio device list.
+ Returns true if successful, in which case the JNI code will continue adding configurations,
+ interfaces and endpoints, and finally call endUsbDeviceAdded after all descriptors
+ have been processed
*/
@SuppressWarnings("unused")
- private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,
- byte[] descriptors) {
+ private boolean beginUsbDeviceAdded(String deviceName, int vendorID, int productID,
+ int deviceClass, int deviceSubclass, int deviceProtocol,
+ String manufacturerName, String productName, int version, String serialNumber) {
+
if (DEBUG) {
- Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start");
+ Slog.d(TAG, "usb:UsbHostManager.beginUsbDeviceAdded(" + deviceName + ")");
+ // Audio Class Codes:
+ // Audio: 0x01
+ // Audio Subclass Codes:
+ // undefined: 0x00
+ // audio control: 0x01
+ // audio streaming: 0x02
+ // midi streaming: 0x03
+
+ // some useful debugging info
+ Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:"
+ + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol);
}
- // check class/subclass first as it is more likely to be blacklisted
- if (isBlackListed(deviceClass, deviceSubclass) || isBlackListed(deviceAddress)) {
- if (DEBUG) {
- Slog.d(TAG, "device is black listed");
- }
+ // OK this is non-obvious, but true. One can't tell if the device being attached is even
+ // potentially an audio device without parsing the interface descriptors, so punt on any
+ // such test until endUsbDeviceAdded() when we have that info.
+
+ if (isBlackListed(deviceName) ||
+ isBlackListed(deviceClass, deviceSubclass)) {
return false;
}
synchronized (mLock) {
- if (mDevices.get(deviceAddress) != null) {
- Slog.w(TAG, "device already on mDevices list: " + deviceAddress);
- //TODO If this is the same peripheral as is being connected, replace
- // it with the new connection.
+ if (mDevices.get(deviceName) != null) {
+ Slog.w(TAG, "device already on mDevices list: " + deviceName);
return false;
}
- UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress);
- if (parser.parseDescriptors(descriptors)) {
-
- UsbDevice newDevice = parser.toAndroidUsbDevice();
- mDevices.put(deviceAddress, newDevice);
-
- // It is fine to call this only for the current user as all broadcasts are sent to
- // all profiles of the user and the dialogs should only show once.
- ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
- if (usbDeviceConnectionHandler == null) {
- getCurrentUserSettings().deviceAttached(newDevice);
- } else {
- getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
- usbDeviceConnectionHandler);
- }
-
- // Headset?
- boolean isInputHeadset = parser.isInputHeadset();
- boolean isOutputHeadset = parser.isOutputHeadset();
- Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
- + " , out: " + isOutputHeadset + "]");
-
- mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
- } else {
- Slog.e(TAG, "Error parsing USB device descriptors for " + deviceAddress);
+ if (mNewDevice != null) {
+ Slog.e(TAG, "mNewDevice is not null in endUsbDeviceAdded");
return false;
}
- }
- if (DEBUG) {
- Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end");
+ // Create version string in "%.%" format
+ String versionString = Integer.toString(version >> 8) + "." + (version & 0xFF);
+
+ mNewDevice = new UsbDevice(deviceName, vendorID, productID,
+ deviceClass, deviceSubclass, deviceProtocol,
+ manufacturerName, productName, versionString, serialNumber);
+
+ mNewConfigurations = new ArrayList<>();
+ mNewInterfaces = new ArrayList<>();
+ mNewEndpoints = new ArrayList<>();
}
return true;
}
+ /* Called from JNI in monitorUsbHostBus() to report new USB configuration for the device
+ currently being added. Returns true if successful, false in case of error.
+ */
+ @SuppressWarnings("unused")
+ private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
+ if (mNewConfiguration != null) {
+ mNewConfiguration.setInterfaces(
+ mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
+ mNewInterfaces.clear();
+ }
+
+ mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
+ mNewConfigurations.add(mNewConfiguration);
+ }
+
+ /* Called from JNI in monitorUsbHostBus() to report new USB interface for the device
+ currently being added. Returns true if successful, false in case of error.
+ */
+ @SuppressWarnings("unused")
+ private void addUsbInterface(int id, String name, int altSetting,
+ int Class, int subClass, int protocol) {
+ if (mNewInterface != null) {
+ mNewInterface.setEndpoints(
+ mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
+ mNewEndpoints.clear();
+ }
+
+ mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol);
+ mNewInterfaces.add(mNewInterface);
+ }
+
+ /* Called from JNI in monitorUsbHostBus() to report new USB endpoint for the device
+ currently being added. Returns true if successful, false in case of error.
+ */
+ @SuppressWarnings("unused")
+ private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
+ mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval));
+ }
+
+ /* Called from JNI in monitorUsbHostBus() to finish adding a new device */
+ @SuppressWarnings("unused")
+ private void endUsbDeviceAdded() {
+ if (DEBUG) {
+ Slog.d(TAG, "usb:UsbHostManager.endUsbDeviceAdded()");
+ }
+ if (mNewInterface != null) {
+ mNewInterface.setEndpoints(
+ mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
+ }
+ if (mNewConfiguration != null) {
+ mNewConfiguration.setInterfaces(
+ mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
+ }
+
+
+ synchronized (mLock) {
+ if (mNewDevice != null) {
+ mNewDevice.setConfigurations(
+ mNewConfigurations.toArray(
+ new UsbConfiguration[mNewConfigurations.size()]));
+ mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
+ Slog.d(TAG, "Added device " + mNewDevice);
+
+ // It is fine to call this only for the current user as all broadcasts are sent to
+ // all profiles of the user and the dialogs should only show once.
+ ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
+ if (usbDeviceConnectionHandler == null) {
+ getCurrentUserSettings().deviceAttached(mNewDevice);
+ } else {
+ getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice,
+ usbDeviceConnectionHandler);
+ }
+ // deviceName is something like: "/dev/bus/usb/001/001"
+ UsbDescriptorParser parser = new UsbDescriptorParser();
+ boolean isInputHeadset = false;
+ boolean isOutputHeadset = false;
+ if (parser.parseDevice(mNewDevice.getDeviceName())) {
+ isInputHeadset = parser.isInputHeadset();
+ isOutputHeadset = parser.isOutputHeadset();
+ Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
+ + " , out: " + isOutputHeadset + "]");
+ }
+ mUsbAlsaManager.usbDeviceAdded(mNewDevice,
+ isInputHeadset, isOutputHeadset);
+ } else {
+ Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
+ }
+ mNewDevice = null;
+ mNewConfigurations = null;
+ mNewInterfaces = null;
+ mNewEndpoints = null;
+ mNewConfiguration = null;
+ mNewInterface = null;
+ }
+ }
+
/* Called from JNI in monitorUsbHostBus to report USB device removal */
@SuppressWarnings("unused")
- private void usbDeviceRemoved(String deviceAddress) {
+ private void usbDeviceRemoved(String deviceName) {
synchronized (mLock) {
- UsbDevice device = mDevices.remove(deviceAddress);
+ UsbDevice device = mDevices.remove(deviceName);
if (device != null) {
mUsbAlsaManager.usbDeviceRemoved(device);
mSettingsManager.usbDeviceRemoved(device);
@@ -218,37 +322,32 @@
}
}
- /**
- * Opens the specified USB device
- * @hide
- */
- public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings) {
+ /* Opens the specified USB device */
+ public ParcelFileDescriptor openDevice(String deviceName, UsbUserSettingsManager settings) {
synchronized (mLock) {
- if (isBlackListed(deviceAddress)) {
+ if (isBlackListed(deviceName)) {
throw new SecurityException("USB device is on a restricted bus");
}
- UsbDevice device = mDevices.get(deviceAddress);
+ UsbDevice device = mDevices.get(deviceName);
if (device == null) {
// if it is not in mDevices, it either does not exist or is blacklisted
throw new IllegalArgumentException(
- "device " + deviceAddress + " does not exist or is restricted");
+ "device " + deviceName + " does not exist or is restricted");
}
settings.checkPermission(device);
- return nativeOpenDevice(deviceAddress);
+ return nativeOpenDevice(deviceName);
}
}
public void dump(IndentingPrintWriter pw) {
- pw.println("USB Host State:");
- synchronized (mHandlerLock) {
- if (mUsbDeviceConnectionHandler != null) {
- pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler);
- }
- }
synchronized (mLock) {
+ pw.println("USB Host State:");
for (String name : mDevices.keySet()) {
pw.println(" " + name + ": " + mDevices.get(name));
}
+ if (mUsbDeviceConnectionHandler != null) {
+ pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler);
+ }
Collection<UsbDevice> devices = mDevices.values();
if (devices.size() != 0) {
@@ -256,12 +355,17 @@
for (UsbDevice device : devices) {
StringBuilder stringBuilder = new StringBuilder();
- UsbDescriptorParser parser = new UsbDescriptorParser(device.getDeviceName());
- if (parser.parseDevice()) {
+ UsbDescriptorParser parser = new UsbDescriptorParser();
+ if (parser.parseDevice(device.getDeviceName())) {
UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
descriptorTree.parse(parser);
- descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
+ UsbManager usbManager =
+ (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+ UsbDeviceConnection connection = usbManager.openDevice(device);
+
+ descriptorTree.report(new TextReportCanvas(connection, stringBuilder));
+ connection.close();
stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
+ " , out: " + parser.isOutputHeadset() + "]");
@@ -277,5 +381,5 @@
}
private native void monitorUsbHostBus();
- private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
+ private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
}
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index ebb5a62..917e651 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -32,9 +32,10 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.XmlResourceParser;
+import android.hardware.usb.AccessoryFilter;
+import android.hardware.usb.DeviceFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.AsyncTask;
import android.os.Environment;
@@ -58,7 +59,6 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
@@ -71,7 +71,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
class UsbProfileGroupSettingsManager {
private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
@@ -157,404 +156,6 @@
}
}
- // This class is used to describe a USB device.
- // When used in HashMaps all values must be specified,
- // but wildcards can be used for any of the fields in
- // the package meta-data.
- private static class DeviceFilter {
- // USB Vendor ID (or -1 for unspecified)
- public final int mVendorId;
- // USB Product ID (or -1 for unspecified)
- public final int mProductId;
- // USB device or interface class (or -1 for unspecified)
- public final int mClass;
- // USB device subclass (or -1 for unspecified)
- public final int mSubclass;
- // USB device protocol (or -1 for unspecified)
- public final int mProtocol;
- // USB device manufacturer name string (or null for unspecified)
- public final String mManufacturerName;
- // USB device product name string (or null for unspecified)
- public final String mProductName;
- // USB device serial number string (or null for unspecified)
- public final String mSerialNumber;
-
- public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
- String manufacturer, String product, String serialnum) {
- mVendorId = vid;
- mProductId = pid;
- mClass = clasz;
- mSubclass = subclass;
- mProtocol = protocol;
- mManufacturerName = manufacturer;
- mProductName = product;
- mSerialNumber = serialnum;
- }
-
- public DeviceFilter(UsbDevice device) {
- mVendorId = device.getVendorId();
- mProductId = device.getProductId();
- mClass = device.getDeviceClass();
- mSubclass = device.getDeviceSubclass();
- mProtocol = device.getDeviceProtocol();
- mManufacturerName = device.getManufacturerName();
- mProductName = device.getProductName();
- mSerialNumber = device.getSerialNumber();
- }
-
- public static DeviceFilter read(XmlPullParser parser)
- throws XmlPullParserException, IOException {
- int vendorId = -1;
- int productId = -1;
- int deviceClass = -1;
- int deviceSubclass = -1;
- int deviceProtocol = -1;
- String manufacturerName = null;
- String productName = null;
- String serialNumber = null;
-
- int count = parser.getAttributeCount();
- for (int i = 0; i < count; i++) {
- String name = parser.getAttributeName(i);
- String value = parser.getAttributeValue(i);
- // Attribute values are ints or strings
- if ("manufacturer-name".equals(name)) {
- manufacturerName = value;
- } else if ("product-name".equals(name)) {
- productName = value;
- } else if ("serial-number".equals(name)) {
- serialNumber = value;
- } else {
- int intValue;
- int radix = 10;
- if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
- (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
- // allow hex values starting with 0x or 0X
- radix = 16;
- value = value.substring(2);
- }
- try {
- intValue = Integer.parseInt(value, radix);
- } catch (NumberFormatException e) {
- Slog.e(TAG, "invalid number for field " + name, e);
- continue;
- }
- if ("vendor-id".equals(name)) {
- vendorId = intValue;
- } else if ("product-id".equals(name)) {
- productId = intValue;
- } else if ("class".equals(name)) {
- deviceClass = intValue;
- } else if ("subclass".equals(name)) {
- deviceSubclass = intValue;
- } else if ("protocol".equals(name)) {
- deviceProtocol = intValue;
- }
- }
- }
- return new DeviceFilter(vendorId, productId,
- deviceClass, deviceSubclass, deviceProtocol,
- manufacturerName, productName, serialNumber);
- }
-
- public void write(XmlSerializer serializer) throws IOException {
- serializer.startTag(null, "usb-device");
- if (mVendorId != -1) {
- serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
- }
- if (mProductId != -1) {
- serializer.attribute(null, "product-id", Integer.toString(mProductId));
- }
- if (mClass != -1) {
- serializer.attribute(null, "class", Integer.toString(mClass));
- }
- if (mSubclass != -1) {
- serializer.attribute(null, "subclass", Integer.toString(mSubclass));
- }
- if (mProtocol != -1) {
- serializer.attribute(null, "protocol", Integer.toString(mProtocol));
- }
- if (mManufacturerName != null) {
- serializer.attribute(null, "manufacturer-name", mManufacturerName);
- }
- if (mProductName != null) {
- serializer.attribute(null, "product-name", mProductName);
- }
- if (mSerialNumber != null) {
- serializer.attribute(null, "serial-number", mSerialNumber);
- }
- serializer.endTag(null, "usb-device");
- }
-
- private boolean matches(int clasz, int subclass, int protocol) {
- return ((mClass == -1 || clasz == mClass) &&
- (mSubclass == -1 || subclass == mSubclass) &&
- (mProtocol == -1 || protocol == mProtocol));
- }
-
- public boolean matches(UsbDevice device) {
- if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
- if (mProductId != -1 && device.getProductId() != mProductId) return false;
- if (mManufacturerName != null && device.getManufacturerName() == null) return false;
- if (mProductName != null && device.getProductName() == null) return false;
- if (mSerialNumber != null && device.getSerialNumber() == null) return false;
- if (mManufacturerName != null && device.getManufacturerName() != null &&
- !mManufacturerName.equals(device.getManufacturerName())) return false;
- if (mProductName != null && device.getProductName() != null &&
- !mProductName.equals(device.getProductName())) return false;
- if (mSerialNumber != null && device.getSerialNumber() != null &&
- !mSerialNumber.equals(device.getSerialNumber())) return false;
-
- // check device class/subclass/protocol
- if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
- device.getDeviceProtocol())) return true;
-
- // if device doesn't match, check the interfaces
- int count = device.getInterfaceCount();
- for (int i = 0; i < count; i++) {
- UsbInterface intf = device.getInterface(i);
- if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
- intf.getInterfaceProtocol())) return true;
- }
-
- return false;
- }
-
- /**
- * If the device described by {@code device} covered by this filter?
- *
- * @param device The device
- *
- * @return {@code true} iff this filter covers the {@code device}
- */
- public boolean contains(DeviceFilter device) {
- // -1 and null means "match anything"
-
- if (mVendorId != -1 && device.mVendorId != mVendorId) return false;
- if (mProductId != -1 && device.mProductId != mProductId) return false;
- if (mManufacturerName != null && !Objects.equals(mManufacturerName,
- device.mManufacturerName)) {
- return false;
- }
- if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) {
- return false;
- }
- if (mSerialNumber != null
- && !Objects.equals(mSerialNumber, device.mSerialNumber)) {
- return false;
- }
-
- // check device class/subclass/protocol
- return matches(device.mClass, device.mSubclass, device.mProtocol);
- }
-
- @Override
- public boolean equals(Object obj) {
- // can't compare if we have wildcard strings
- if (mVendorId == -1 || mProductId == -1 ||
- mClass == -1 || mSubclass == -1 || mProtocol == -1) {
- return false;
- }
- if (obj instanceof DeviceFilter) {
- DeviceFilter filter = (DeviceFilter)obj;
-
- if (filter.mVendorId != mVendorId ||
- filter.mProductId != mProductId ||
- filter.mClass != mClass ||
- filter.mSubclass != mSubclass ||
- filter.mProtocol != mProtocol) {
- return(false);
- }
- if ((filter.mManufacturerName != null &&
- mManufacturerName == null) ||
- (filter.mManufacturerName == null &&
- mManufacturerName != null) ||
- (filter.mProductName != null &&
- mProductName == null) ||
- (filter.mProductName == null &&
- mProductName != null) ||
- (filter.mSerialNumber != null &&
- mSerialNumber == null) ||
- (filter.mSerialNumber == null &&
- mSerialNumber != null)) {
- return(false);
- }
- if ((filter.mManufacturerName != null &&
- mManufacturerName != null &&
- !mManufacturerName.equals(filter.mManufacturerName)) ||
- (filter.mProductName != null &&
- mProductName != null &&
- !mProductName.equals(filter.mProductName)) ||
- (filter.mSerialNumber != null &&
- mSerialNumber != null &&
- !mSerialNumber.equals(filter.mSerialNumber))) {
- return false;
- }
- return true;
- }
- if (obj instanceof UsbDevice) {
- UsbDevice device = (UsbDevice)obj;
- if (device.getVendorId() != mVendorId ||
- device.getProductId() != mProductId ||
- device.getDeviceClass() != mClass ||
- device.getDeviceSubclass() != mSubclass ||
- device.getDeviceProtocol() != mProtocol) {
- return(false);
- }
- if ((mManufacturerName != null && device.getManufacturerName() == null) ||
- (mManufacturerName == null && device.getManufacturerName() != null) ||
- (mProductName != null && device.getProductName() == null) ||
- (mProductName == null && device.getProductName() != null) ||
- (mSerialNumber != null && device.getSerialNumber() == null) ||
- (mSerialNumber == null && device.getSerialNumber() != null)) {
- return(false);
- }
- if ((device.getManufacturerName() != null &&
- !mManufacturerName.equals(device.getManufacturerName())) ||
- (device.getProductName() != null &&
- !mProductName.equals(device.getProductName())) ||
- (device.getSerialNumber() != null &&
- !mSerialNumber.equals(device.getSerialNumber()))) {
- return false;
- }
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return (((mVendorId << 16) | mProductId) ^
- ((mClass << 16) | (mSubclass << 8) | mProtocol));
- }
-
- @Override
- public String toString() {
- return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
- ",mClass=" + mClass + ",mSubclass=" + mSubclass +
- ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
- ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
- "]";
- }
- }
-
- // This class is used to describe a USB accessory.
- // When used in HashMaps all values must be specified,
- // but wildcards can be used for any of the fields in
- // the package meta-data.
- private static class AccessoryFilter {
- // USB accessory manufacturer (or null for unspecified)
- public final String mManufacturer;
- // USB accessory model (or null for unspecified)
- public final String mModel;
- // USB accessory version (or null for unspecified)
- public final String mVersion;
-
- public AccessoryFilter(String manufacturer, String model, String version) {
- mManufacturer = manufacturer;
- mModel = model;
- mVersion = version;
- }
-
- public AccessoryFilter(UsbAccessory accessory) {
- mManufacturer = accessory.getManufacturer();
- mModel = accessory.getModel();
- mVersion = accessory.getVersion();
- }
-
- public static AccessoryFilter read(XmlPullParser parser)
- throws XmlPullParserException, IOException {
- String manufacturer = null;
- String model = null;
- String version = null;
-
- int count = parser.getAttributeCount();
- for (int i = 0; i < count; i++) {
- String name = parser.getAttributeName(i);
- String value = parser.getAttributeValue(i);
-
- if ("manufacturer".equals(name)) {
- manufacturer = value;
- } else if ("model".equals(name)) {
- model = value;
- } else if ("version".equals(name)) {
- version = value;
- }
- }
- return new AccessoryFilter(manufacturer, model, version);
- }
-
- public void write(XmlSerializer serializer)throws IOException {
- serializer.startTag(null, "usb-accessory");
- if (mManufacturer != null) {
- serializer.attribute(null, "manufacturer", mManufacturer);
- }
- if (mModel != null) {
- serializer.attribute(null, "model", mModel);
- }
- if (mVersion != null) {
- serializer.attribute(null, "version", mVersion);
- }
- serializer.endTag(null, "usb-accessory");
- }
-
- public boolean matches(UsbAccessory acc) {
- if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
- if (mModel != null && !acc.getModel().equals(mModel)) return false;
- return !(mVersion != null && !acc.getVersion().equals(mVersion));
- }
-
- /**
- * Is the accessories described {@code accessory} covered by this filter?
- *
- * @param accessory A filter describing the accessory
- *
- * @return {@code true} iff this the filter covers the accessory
- */
- public boolean contains(AccessoryFilter accessory) {
- if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) {
- return false;
- }
- if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false;
- return !(mVersion != null && !Objects.equals(accessory.mVersion, mVersion));
- }
-
- @Override
- public boolean equals(Object obj) {
- // can't compare if we have wildcard strings
- if (mManufacturer == null || mModel == null || mVersion == null) {
- return false;
- }
- if (obj instanceof AccessoryFilter) {
- AccessoryFilter filter = (AccessoryFilter)obj;
- return (mManufacturer.equals(filter.mManufacturer) &&
- mModel.equals(filter.mModel) &&
- mVersion.equals(filter.mVersion));
- }
- if (obj instanceof UsbAccessory) {
- UsbAccessory accessory = (UsbAccessory)obj;
- return (mManufacturer.equals(accessory.getManufacturer()) &&
- mModel.equals(accessory.getModel()) &&
- mVersion.equals(accessory.getVersion()));
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
- (mModel == null ? 0 : mModel.hashCode()) ^
- (mVersion == null ? 0 : mVersion.hashCode()));
- }
-
- @Override
- public String toString() {
- return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
- "\", mModel=\"" + mModel +
- "\", mVersion=\"" + mVersion + "\"]";
- }
- }
-
private class MyPackageMonitor extends PackageMonitor {
@Override
public void onPackageAdded(String packageName, int uid) {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index 511e59a..75279c6 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -15,13 +15,8 @@
*/
package com.android.server.usb.descriptors;
-import android.hardware.usb.UsbConfiguration;
-import android.hardware.usb.UsbInterface;
-
import com.android.server.usb.descriptors.report.ReportCanvas;
-import java.util.ArrayList;
-
/**
* @hide
* An USB Config Descriptor.
@@ -40,9 +35,6 @@
// D4..0 Reserved, set to 0.
private byte mMaxPower; // 8:1 Maximum Power Consumption in 2mA units
- private ArrayList<UsbInterfaceDescriptor> mInterfaceDescriptors =
- new ArrayList<UsbInterfaceDescriptor>();
-
UsbConfigDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 2;
@@ -72,22 +64,6 @@
return mMaxPower;
}
- void addInterfaceDescriptor(UsbInterfaceDescriptor interfaceDesc) {
- mInterfaceDescriptors.add(interfaceDesc);
- }
-
- UsbConfiguration toAndroid(UsbDescriptorParser parser) {
- String name = parser.getDescriptorString(mConfigIndex);
- UsbConfiguration config = new
- UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower);
- UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()];
- for (int index = 0; index < mInterfaceDescriptors.size(); index++) {
- interfaces[index] = mInterfaceDescriptors.get(index).toAndroid(parser);
- }
- config.setInterfaces(interfaces);
- return config;
- }
-
@Override
public int parseRawDescriptors(ByteStream stream) {
mTotalLength = stream.unpackUsbShort();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index c5052c8..ad7bde5c 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -15,7 +15,6 @@
*/
package com.android.server.usb.descriptors;
-import android.hardware.usb.UsbDevice;
import android.util.Log;
import java.util.ArrayList;
@@ -26,16 +25,11 @@
*/
public final class UsbDescriptorParser {
private static final String TAG = "UsbDescriptorParser";
- private static final boolean DEBUG = false;
-
- private final String mDeviceAddr;
// Descriptor Objects
- private static final int DESCRIPTORS_ALLOC_SIZE = 128;
private ArrayList<UsbDescriptor> mDescriptors = new ArrayList<UsbDescriptor>();
private UsbDeviceDescriptor mDeviceDescriptor;
- private UsbConfigDescriptor mCurConfigDescriptor;
private UsbInterfaceDescriptor mCurInterfaceDescriptor;
// The AudioClass spec implemented by the AudioClass Interfaces
@@ -43,13 +37,7 @@
// Obtained from the first AudioClass Header descriptor.
private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
- public UsbDescriptorParser(String deviceAddr) {
- mDeviceAddr = deviceAddr;
- }
-
- public String getDeviceAddr() {
- return mDeviceAddr;
- }
+ public UsbDescriptorParser() {}
/**
* @return the USB Spec value associated with the Device descriptor for the
@@ -72,18 +60,6 @@
public int getACInterfaceSpec() {
return mACInterfacesSpec;
}
-
- private class UsbDescriptorsStreamFormatException extends Exception {
- String mMessage;
- UsbDescriptorsStreamFormatException(String message) {
- mMessage = message;
- }
-
- public String toString() {
- return "Descriptor Stream Format Exception: " + mMessage;
- }
- }
-
/**
* The probability (as returned by getHeadsetProbability() at which we conclude
* the peripheral is a headset.
@@ -91,8 +67,7 @@
private static final float IN_HEADSET_TRIGGER = 0.75f;
private static final float OUT_HEADSET_TRIGGER = 0.75f;
- private UsbDescriptor allocDescriptor(ByteStream stream)
- throws UsbDescriptorsStreamFormatException {
+ private UsbDescriptor allocDescriptor(ByteStream stream) {
stream.resetReadCount();
int length = stream.getUnsignedByte();
@@ -108,38 +83,15 @@
break;
case UsbDescriptor.DESCRIPTORTYPE_CONFIG:
- descriptor = mCurConfigDescriptor = new UsbConfigDescriptor(length, type);
- if (mDeviceDescriptor != null) {
- mDeviceDescriptor.addConfigDescriptor(mCurConfigDescriptor);
- } else {
- Log.e(TAG, "Config Descriptor found with no associated Device Descriptor!");
- throw new UsbDescriptorsStreamFormatException(
- "Config Descriptor found with no associated Device Descriptor!");
- }
+ descriptor = new UsbConfigDescriptor(length, type);
break;
case UsbDescriptor.DESCRIPTORTYPE_INTERFACE:
descriptor = mCurInterfaceDescriptor = new UsbInterfaceDescriptor(length, type);
- if (mCurConfigDescriptor != null) {
- mCurConfigDescriptor.addInterfaceDescriptor(mCurInterfaceDescriptor);
- } else {
- Log.e(TAG, "Interface Descriptor found with no associated Config Descriptor!");
- throw new UsbDescriptorsStreamFormatException(
- "Interface Descriptor found with no associated Config Descriptor!");
- }
break;
case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT:
descriptor = new UsbEndpointDescriptor(length, type);
- if (mCurInterfaceDescriptor != null) {
- mCurInterfaceDescriptor.addEndpointDescriptor(
- (UsbEndpointDescriptor) descriptor);
- } else {
- Log.e(TAG,
- "Endpoint Descriptor found with no associated Interface Descriptor!");
- throw new UsbDescriptorsStreamFormatException(
- "Endpoint Descriptor found with no associated Interface Descriptor!");
- }
break;
/*
@@ -192,12 +144,8 @@
/**
* @hide
*/
- public boolean parseDescriptors(byte[] descriptors) {
- if (DEBUG) {
- Log.d(TAG, "parseDescriptors() - start");
- }
- // This will allow us to (probably) alloc mDescriptors just once.
- mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
+ public void parseDescriptors(byte[] descriptors) {
+ mDescriptors.clear();
ByteStream stream = new ByteStream(descriptors);
while (stream.available() > 0) {
@@ -225,36 +173,21 @@
}
}
}
- if (DEBUG) {
- Log.d(TAG, "parseDescriptors() - end " + mDescriptors.size() + " descriptors.");
+ }
+
+ /**
+ * @hide
+ */
+ public boolean parseDevice(String deviceAddr) {
+ byte[] rawDescriptors = getRawDescriptors(deviceAddr);
+ if (rawDescriptors != null) {
+ parseDescriptors(rawDescriptors);
+ return true;
}
- return true;
+ return false;
}
- /**
- * @hide
- */
- public boolean parseDevice() {
- byte[] rawDescriptors = getRawDescriptors();
-
- return rawDescriptors != null
- ? parseDescriptors(rawDescriptors) : false;
- }
-
- private byte[] getRawDescriptors() {
- return getRawDescriptors_native(mDeviceAddr);
- }
-
- private native byte[] getRawDescriptors_native(String deviceAddr);
-
- /**
- * @hide
- */
- public String getDescriptorString(int stringId) {
- return getDescriptorString_native(mDeviceAddr, stringId);
- }
-
- private native String getDescriptorString_native(String deviceAddr, int stringId);
+ private native byte[] getRawDescriptors(String deviceAddr);
public int getParsingSpec() {
return mDeviceDescriptor != null ? mDeviceDescriptor.getSpec() : 0;
@@ -267,17 +200,6 @@
/**
* @hide
*/
- public UsbDevice toAndroidUsbDevice() {
- if (mDeviceDescriptor == null) {
- return null;
- }
-
- return mDeviceDescriptor.toAndroid(this);
- }
-
- /**
- * @hide
- */
public ArrayList<UsbDescriptor> getDescriptors(byte type) {
ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
for (UsbDescriptor descriptor : mDescriptors) {
@@ -433,6 +355,8 @@
* to count on the peripheral being a headset.
*/
public boolean isInputHeadset() {
+ // TEMP
+ Log.i(TAG, "---- isInputHeadset() prob:" + (getInputHeadsetProbability() * 100f) + "%");
return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER;
}
@@ -486,6 +410,8 @@
* to count on the peripheral being a headset.
*/
public boolean isOutputHeadset() {
+ // TEMP
+ Log.i(TAG, "---- isOutputHeadset() prob:" + (getOutputHeadsetProbability() * 100f) + "%");
return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index c89d9b6..d5cb89e 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -15,14 +15,9 @@
*/
package com.android.server.usb.descriptors;
-import android.hardware.usb.UsbConfiguration;
-import android.hardware.usb.UsbDevice;
-
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
-import java.util.ArrayList;
-
/**
* @hide
* A USB Device Descriptor.
@@ -49,9 +44,6 @@
private byte mSerialNum; // 16:1 Index of Serial Number String Descriptor
private byte mNumConfigs; // 17:1 Number of Possible Configurations
- private ArrayList<UsbConfigDescriptor> mConfigDescriptors =
- new ArrayList<UsbConfigDescriptor>();
-
UsbDeviceDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 1;
@@ -105,35 +97,6 @@
return mNumConfigs;
}
- void addConfigDescriptor(UsbConfigDescriptor config) {
- mConfigDescriptors.add(config);
- }
-
- /**
- * @hide
- */
- public UsbDevice toAndroid(UsbDescriptorParser parser) {
- String mfgName = parser.getDescriptorString(mMfgIndex);
- String prodName = parser.getDescriptorString(mProductIndex);
-
- // Create version string in "%.%" format
- String versionString =
- Integer.toString(mDeviceRelease >> 8) + "." + (mDeviceRelease & 0xFF);
- String serialStr = parser.getDescriptorString(mSerialNum);
-
- UsbDevice device = new UsbDevice(parser.getDeviceAddr(), mVendorID, mProductID,
- mDevClass, mDevSubClass,
- mProtocol, mfgName, prodName,
- versionString, serialStr);
- UsbConfiguration[] configs = new UsbConfiguration[mConfigDescriptors.size()];
- for (int index = 0; index < mConfigDescriptors.size(); index++) {
- configs[index] = mConfigDescriptors.get(index).toAndroid(parser);
- }
- device.setConfigurations(configs);
-
- return device;
- }
-
@Override
public int parseRawDescriptors(ByteStream stream) {
mSpec = stream.unpackUsbShort();
@@ -171,11 +134,12 @@
+ " Product ID: " + ReportCanvas.getHexString(getProductID())
+ " Product Release: " + ReportCanvas.getBCDString(getDeviceRelease()));
- UsbDescriptorParser parser = canvas.getParser();
byte mfgIndex = getMfgIndex();
- String manufacturer = parser.getDescriptorString(mfgIndex);
+ String manufacturer =
+ UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), mfgIndex);
byte productIndex = getProductIndex();
- String product = parser.getDescriptorString(productIndex);
+ String product =
+ UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), productIndex);
canvas.writeListItem("Manufacturer " + mfgIndex + ": " + manufacturer
+ " Product " + productIndex + ": " + product);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 9cb8f05..6322fbe 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -15,8 +15,6 @@
*/
package com.android.server.usb.descriptors;
-import android.hardware.usb.UsbEndpoint;
-
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
@@ -107,10 +105,6 @@
return mSyncAddress;
}
- /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
- return new UsbEndpoint(mEndpointAddress, mAttributes, mPacketSize, mInterval);
- }
-
@Override
public int parseRawDescriptors(ByteStream stream) {
mEndpointAddress = stream.getByte();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 645807d..4eef6ca 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -15,14 +15,9 @@
*/
package com.android.server.usb.descriptors;
-import android.hardware.usb.UsbEndpoint;
-import android.hardware.usb.UsbInterface;
-
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
-import java.util.ArrayList;
-
/**
* @hide
* A common super-class for all USB Interface Descritor subtypes.
@@ -39,9 +34,6 @@
protected byte mProtocol; // 7:1 Protocol Code
protected byte mDescrIndex; // 8:1 Index of String Descriptor Describing this interface
- private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
- new ArrayList<UsbEndpointDescriptor>();
-
UsbInterfaceDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 3;
@@ -88,22 +80,6 @@
return mDescrIndex;
}
- void addEndpointDescriptor(UsbEndpointDescriptor endpoint) {
- mEndpointDescriptors.add(endpoint);
- }
-
- UsbInterface toAndroid(UsbDescriptorParser parser) {
- String name = parser.getDescriptorString(mDescrIndex);
- UsbInterface ntrface = new UsbInterface(
- mInterfaceNumber, mAlternateSetting, name, mUsbClass, mUsbSubclass, mProtocol);
- UsbEndpoint[] endpoints = new UsbEndpoint[mEndpointDescriptors.size()];
- for (int index = 0; index < mEndpointDescriptors.size(); index++) {
- endpoints[index] = mEndpointDescriptors.get(index).toAndroid(parser);
- }
- ntrface.setEndpoints(endpoints);
- return ntrface;
- }
-
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
index adfc514..99ebcca 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
@@ -15,7 +15,7 @@
*/
package com.android.server.usb.descriptors.report;
-import com.android.server.usb.descriptors.UsbDescriptorParser;
+import android.hardware.usb.UsbDeviceConnection;
/**
* @hide
@@ -32,8 +32,8 @@
* from the USB device.
* @param stringBuilder Generated output gets written into this object.
*/
- public HTMLReportCanvas(UsbDescriptorParser parser, StringBuilder stringBuilder) {
- super(parser);
+ public HTMLReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) {
+ super(connection);
mStringBuilder = stringBuilder;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
index c34dc98..9e0adf5 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
@@ -15,7 +15,7 @@
*/
package com.android.server.usb.descriptors.report;
-import com.android.server.usb.descriptors.UsbDescriptorParser;
+import android.hardware.usb.UsbDeviceConnection;
/**
* @hide
@@ -24,19 +24,22 @@
public abstract class ReportCanvas {
private static final String TAG = "ReportCanvas";
- private final UsbDescriptorParser mParser;
+ private final UsbDeviceConnection mConnection;
/**
* Constructor.
* @param connection The USB connection object used to retrieve strings
* from the USB device.
*/
- public ReportCanvas(UsbDescriptorParser parser) {
- mParser = parser;
+ public ReportCanvas(UsbDeviceConnection connection) {
+ mConnection = connection;
}
- public UsbDescriptorParser getParser() {
- return mParser;
+ /**
+ * @returns the UsbDeviceConnection member (mConnection).
+ */
+ public UsbDeviceConnection getConnection() {
+ return mConnection;
}
/**
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
index 1e19ea1..a43569d 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
@@ -15,7 +15,7 @@
*/
package com.android.server.usb.descriptors.report;
-import com.android.server.usb.descriptors.UsbDescriptorParser;
+import android.hardware.usb.UsbDeviceConnection;
/**
* @hide
@@ -34,8 +34,8 @@
* from the USB device.
* @param stringBuilder Generated output gets written into this object.
*/
- public TextReportCanvas(UsbDescriptorParser parser, StringBuilder stringBuilder) {
- super(parser);
+ public TextReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) {
+ super(connection);
mStringBuilder = stringBuilder;
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index e13bd61..a07f2bb 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -855,6 +855,39 @@
*/
public static abstract class Callback {
/**
+ * @hide
+ */
+ @IntDef({HANDOVER_FAILURE_DEST_APP_REJECTED, HANDOVER_FAILURE_DEST_NOT_SUPPORTED,
+ HANDOVER_FAILURE_DEST_INVALID_PERM, HANDOVER_FAILURE_DEST_USER_REJECTED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HandoverFailureErrors {}
+
+ /**
+ * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when the app
+ * to handover the call rejects handover.
+ */
+ public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1;
+
+ /**
+ * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when there is
+ * an error associated with unsupported handover.
+ */
+ public static final int HANDOVER_FAILURE_DEST_NOT_SUPPORTED = 2;
+
+ /**
+ * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when there
+ * are some permission errors associated with APIs doing handover.
+ */
+ public static final int HANDOVER_FAILURE_DEST_INVALID_PERM = 3;
+
+ /**
+ * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when user
+ * rejects handover.
+ */
+ public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4;
+
+
+ /**
* Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
*
* @param call The {@code Call} invoking this method.
@@ -989,6 +1022,21 @@
* {@link android.telecom.Connection.RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
*/
public void onRttInitiationFailure(Call call, int reason) {}
+
+ /**
+ * Invoked when Call handover from one {@link PhoneAccount} to other {@link PhoneAccount}
+ * has completed successfully.
+ * @param call The call which had initiated handover.
+ */
+ public void onHandoverComplete(Call call) {}
+
+ /**
+ * Invoked when Call handover from one {@link PhoneAccount} to other {@link PhoneAccount}
+ * has failed.
+ * @param call The call which had initiated handover.
+ * @param failureReason Error reason for failure
+ */
+ public void onHandoverFailed(Call call, @HandoverFailureErrors int failureReason) {}
}
/**
@@ -1367,6 +1415,24 @@
}
/**
+ * Initiates a handover of this {@link Call} to the {@link ConnectionService} identified
+ * by {@code toHandle}. The videoState specified indicates the desired video state after the
+ * handover.
+ * <p>
+ * A handover request is initiated by the user from one app to indicate a desire
+ * to handover a call to another.
+ *
+ * @param toHandle {@link PhoneAccountHandle} of the {@link ConnectionService} to handover
+ * this call to.
+ * @param videoState Indicates the video state desired after the handover.
+ * @param extras Bundle containing extra information to be passed to the
+ * {@link ConnectionService}
+ */
+ public void handoverTo(PhoneAccountHandle toHandle, int videoState, Bundle extras) {
+ mInCallAdapter.handoverTo(mTelecomCallId, toHandle, videoState, extras);
+ }
+
+ /**
* Terminate the RTT session on this call. The resulting state change will be notified via
* the {@link Callback#onRttStatusChanged(Call, boolean, RttCall)} callback.
*/
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
index f601d8b..4b827d2 100644
--- a/telecomm/java/android/telecom/CallAudioState.java
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -16,16 +16,35 @@
package android.telecom;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.Locale;
+import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Encapsulates the telecom audio state, including the current audio routing, supported audio
* routing and mute.
*/
public final class CallAudioState implements Parcelable {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER},
+ flag=true)
+ public @interface CallAudioRoute {}
+
/** Direct the audio stream through the device's earpiece. */
public static final int ROUTE_EARPIECE = 0x00000001;
@@ -55,6 +74,8 @@
private final boolean isMuted;
private final int route;
private final int supportedRouteMask;
+ private final BluetoothDevice activeBluetoothDevice;
+ private final Collection<BluetoothDevice> supportedBluetoothDevices;
/**
* Constructor for a {@link CallAudioState} object.
@@ -73,10 +94,21 @@
* {@link #ROUTE_WIRED_HEADSET}
* {@link #ROUTE_SPEAKER}
*/
- public CallAudioState(boolean muted, int route, int supportedRouteMask) {
- this.isMuted = muted;
+ public CallAudioState(boolean muted, @CallAudioRoute int route,
+ @CallAudioRoute int supportedRouteMask) {
+ this(muted, route, supportedRouteMask, null, Collections.emptyList());
+ }
+
+ /** @hide */
+ public CallAudioState(boolean isMuted, @CallAudioRoute int route,
+ @CallAudioRoute int supportedRouteMask,
+ @Nullable BluetoothDevice activeBluetoothDevice,
+ @NonNull Collection<BluetoothDevice> supportedBluetoothDevices) {
+ this.isMuted = isMuted;
this.route = route;
this.supportedRouteMask = supportedRouteMask;
+ this.activeBluetoothDevice = activeBluetoothDevice;
+ this.supportedBluetoothDevices = supportedBluetoothDevices;
}
/** @hide */
@@ -84,6 +116,8 @@
isMuted = state.isMuted();
route = state.getRoute();
supportedRouteMask = state.getSupportedRouteMask();
+ activeBluetoothDevice = state.activeBluetoothDevice;
+ supportedBluetoothDevices = state.getSupportedBluetoothDevices();
}
/** @hide */
@@ -92,6 +126,8 @@
isMuted = state.isMuted();
route = state.getRoute();
supportedRouteMask = state.getSupportedRouteMask();
+ activeBluetoothDevice = null;
+ supportedBluetoothDevices = Collections.emptyList();
}
@Override
@@ -103,17 +139,32 @@
return false;
}
CallAudioState state = (CallAudioState) obj;
- return isMuted() == state.isMuted() && getRoute() == state.getRoute() &&
- getSupportedRouteMask() == state.getSupportedRouteMask();
+ if (supportedBluetoothDevices.size() != state.supportedBluetoothDevices.size()) {
+ return false;
+ }
+ for (BluetoothDevice device : supportedBluetoothDevices) {
+ if (!state.supportedBluetoothDevices.contains(device)) {
+ return false;
+ }
+ }
+ return Objects.equals(activeBluetoothDevice, state.activeBluetoothDevice) && isMuted() ==
+ state.isMuted() && getRoute() == state.getRoute() && getSupportedRouteMask() ==
+ state.getSupportedRouteMask();
}
@Override
public String toString() {
+ String bluetoothDeviceList = supportedBluetoothDevices.stream()
+ .map(BluetoothDevice::getAddress).collect(Collectors.joining(", "));
+
return String.format(Locale.US,
- "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s]",
+ "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s, " +
+ "activeBluetoothDevice: [%s], supportedBluetoothDevices: [%s]]",
isMuted,
audioRouteToString(route),
- audioRouteToString(supportedRouteMask));
+ audioRouteToString(supportedRouteMask),
+ activeBluetoothDevice,
+ bluetoothDeviceList);
}
/**
@@ -126,6 +177,7 @@
/**
* @return The current audio route being used.
*/
+ @CallAudioRoute
public int getRoute() {
return route;
}
@@ -133,11 +185,27 @@
/**
* @return Bit mask of all routes supported by this call.
*/
+ @CallAudioRoute
public int getSupportedRouteMask() {
return supportedRouteMask;
}
/**
+ * @return The {@link BluetoothDevice} through which audio is being routed.
+ * Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
+ */
+ public BluetoothDevice getActiveBluetoothDevice() {
+ return activeBluetoothDevice;
+ }
+
+ /**
+ * @return {@link List} of {@link BluetoothDevice}s that can be used for this call.
+ */
+ public Collection<BluetoothDevice> getSupportedBluetoothDevices() {
+ return supportedBluetoothDevices;
+ }
+
+ /**
* Converts the provided audio route into a human readable string representation.
*
* @param route to convert into a string.
@@ -177,7 +245,13 @@
boolean isMuted = source.readByte() == 0 ? false : true;
int route = source.readInt();
int supportedRouteMask = source.readInt();
- return new CallAudioState(isMuted, route, supportedRouteMask);
+ BluetoothDevice activeBluetoothDevice = source.readParcelable(
+ ClassLoader.getSystemClassLoader());
+ List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>();
+ source.readParcelableList(supportedBluetoothDevices,
+ ClassLoader.getSystemClassLoader());
+ return new CallAudioState(isMuted, route,
+ supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices);
}
@Override
@@ -202,6 +276,8 @@
destination.writeByte((byte) (isMuted ? 1 : 0));
destination.writeInt(route);
destination.writeInt(supportedRouteMask);
+ destination.writeParcelable(activeBluetoothDevice, 0);
+ destination.writeParcelableList(new ArrayList<>(supportedBluetoothDevices), 0);
}
private static void listAppend(StringBuffer buffer, String str) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 8ba934c..2bb1c4e 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -25,6 +25,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Notification;
+import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
@@ -819,7 +820,7 @@
public void onConnectionEvent(Connection c, String event, Bundle extras) {}
/** @hide */
public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
- public void onAudioRouteChanged(Connection c, int audioRoute) {}
+ public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {}
public void onRttInitiationSuccess(Connection c) {}
public void onRttInitiationFailure(Connection c, int reason) {}
public void onRttSessionRemotelyTerminated(Connection c) {}
@@ -1683,6 +1684,8 @@
// The internal telecom call ID associated with this connection.
private String mTelecomCallId;
+ // The PhoneAccountHandle associated with this connection.
+ private PhoneAccountHandle mPhoneAccountHandle;
private int mState = STATE_NEW;
private CallAudioState mCallAudioState;
private Uri mAddress;
@@ -2576,7 +2579,29 @@
*/
public final void setAudioRoute(int route) {
for (Listener l : mListeners) {
- l.onAudioRouteChanged(this, route);
+ l.onAudioRouteChanged(this, route, null);
+ }
+ }
+
+ /**
+ *
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified if the
+ * bluetooth stack is unable to route audio to the requested device.
+ * A list of available devices can be obtained via
+ * {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * <p>
+ * Used by self-managed {@link ConnectionService}s which wish to use bluetooth audio for a
+ * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
+ * <p>
+ * See also {@link InCallService#requestBluetoothAudio(String)}
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}.
+ */
+ public void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+ for (Listener l : mListeners) {
+ l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
}
}
@@ -3076,6 +3101,27 @@
}
/**
+ * Sets the {@link PhoneAccountHandle} associated with this connection.
+ *
+ * @hide
+ */
+ public void setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+ if (mPhoneAccountHandle != phoneAccountHandle) {
+ mPhoneAccountHandle = phoneAccountHandle;
+ notifyPhoneAccountChanged(phoneAccountHandle);
+ }
+ }
+
+ /**
+ * Returns the {@link PhoneAccountHandle} associated with this connection.
+ *
+ * @hide
+ */
+ public PhoneAccountHandle getPhoneAccountHandle() {
+ return mPhoneAccountHandle;
+ }
+
+ /**
* Sends an event associated with this {@code Connection} with associated event extras to the
* {@link InCallService}.
* <p>
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index a81fba9..7e83306 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1294,10 +1294,10 @@
}
@Override
- public void onAudioRouteChanged(Connection c, int audioRoute) {
+ public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {
String id = mIdByConnection.get(c);
if (id != null) {
- mAdapter.setAudioRoute(id, audioRoute);
+ mAdapter.setAudioRoute(id, audioRoute, bluetoothAddress);
}
}
@@ -1382,7 +1382,7 @@
connection.setTelecomCallId(callId);
if (connection.getState() != Connection.STATE_DISCONNECTED) {
- addConnection(callId, connection);
+ addConnection(request.getAccountHandle(), callId, connection);
}
Uri address = connection.getAddress();
@@ -1846,6 +1846,7 @@
mAdapter.setIsConferenced(connectionId, id);
}
}
+ onConferenceAdded(conference);
}
}
@@ -2033,6 +2034,43 @@
}
/**
+ * Called by Telecom on the initiating side of the handover to create an instance of a
+ * handover connection.
+ * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the
+ * ConnectionService which needs to handover the call.
+ * @param request Details about the call which needs to be handover.
+ * @return Connection object corresponding to the handover call.
+ */
+ public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
+ ConnectionRequest request) {
+ return null;
+ }
+
+ /**
+ * Called by Telecom on the receiving side of the handover to request the
+ * {@link ConnectionService} to create an instance of a handover connection.
+ * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the
+ * ConnectionService which needs to handover the call.
+ * @param request Details about the call which needs to be handover.
+ * @return {@link Connection} object corresponding to the handover call.
+ */
+ public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
+ ConnectionRequest request) {
+ return null;
+ }
+
+ /**
+ * Called by Telecom in response to a {@code TelecomManager#acceptHandover()}
+ * invocation which failed.
+ * @param request Details about the call which needs to be handover.
+ * @param error Reason for handover failure as defined in
+ * {@link android.telecom.Call.Callback#HANDOVER_FAILURE_DEST_INVALID_PERM}
+ */
+ public void onHandoverFailed(ConnectionRequest request, int error) {
+ return;
+ }
+
+ /**
* Create a {@code Connection} for a new unknown call. An unknown call is a call originating
* from the ConnectionService that was neither a user-initiated outgoing call, nor an incoming
* call created using
@@ -2056,6 +2094,30 @@
public void onConference(Connection connection1, Connection connection2) {}
/**
+ * Called when a connection is added.
+ * @hide
+ */
+ public void onConnectionAdded(Connection connection) {}
+
+ /**
+ * Called when a connection is removed.
+ * @hide
+ */
+ public void onConnectionRemoved(Connection connection) {}
+
+ /**
+ * Called when a conference is added.
+ * @hide
+ */
+ public void onConferenceAdded(Conference conference) {}
+
+ /**
+ * Called when a conference is removed.
+ * @hide
+ */
+ public void onConferenceRemoved(Conference conference) {}
+
+ /**
* Indicates that a remote conference has been created for existing {@link RemoteConnection}s.
* When this method is invoked, this {@link ConnectionService} should create its own
* representation of the conference call and send it to telecom using {@link #addConference}.
@@ -2122,16 +2184,18 @@
// prefix for a unique incremental call ID.
id = handle.getComponentName().getClassName() + "@" + getNextCallId();
}
- addConnection(id, connection);
+ addConnection(handle, id, connection);
return id;
}
- private void addConnection(String callId, Connection connection) {
+ private void addConnection(PhoneAccountHandle handle, String callId, Connection connection) {
connection.setTelecomCallId(callId);
mConnectionById.put(callId, connection);
mIdByConnection.put(connection, callId);
connection.addConnectionListener(mConnectionListener);
connection.setConnectionService(this);
+ connection.setPhoneAccountHandle(handle);
+ onConnectionAdded(connection);
}
/** {@hide} */
@@ -2143,6 +2207,7 @@
mConnectionById.remove(id);
mIdByConnection.remove(connection);
mAdapter.removeCall(id);
+ onConnectionRemoved(connection);
}
}
@@ -2179,6 +2244,8 @@
mConferenceById.remove(id);
mIdByConference.remove(conference);
mAdapter.removeCall(id);
+
+ onConferenceRemoved(conference);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 111fcc7..92a9dc2 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -520,11 +520,14 @@
* @param callId The unique ID of the call.
* @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}).
*/
- void setAudioRoute(String callId, int audioRoute) {
- Log.v(this, "setAudioRoute: %s %s", callId, CallAudioState.audioRouteToString(audioRoute));
+ void setAudioRoute(String callId, int audioRoute, String bluetoothAddress) {
+ Log.v(this, "setAudioRoute: %s %s %s", callId,
+ CallAudioState.audioRouteToString(audioRoute),
+ bluetoothAddress);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setAudioRoute(callId, audioRoute, Log.getExternalSession());
+ adapter.setAudioRoute(callId, audioRoute,
+ bluetoothAddress, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index b1617f4..3fbdeb1 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -298,8 +298,8 @@
case MSG_SET_AUDIO_ROUTE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.setAudioRoute((String) args.arg1, args.argi1,
- (Session.Info) args.arg2);
+ mDelegate.setAudioRoute((String) args.arg1, args.argi1, (String) args.arg2,
+ (Session.Info) args.arg3);
} finally {
args.recycle();
}
@@ -548,12 +548,12 @@
@Override
public final void setAudioRoute(String connectionId, int audioRoute,
- Session.Info sessionInfo) {
-
+ String bluetoothAddress, Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.argi1 = audioRoute;
- args.arg2 = sessionInfo;
+ args.arg2 = bluetoothAddress;
+ args.arg3 = sessionInfo;
mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, args).sendToTarget();
}
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 9559a28..4bc2a9b 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.RemoteException;
@@ -128,7 +129,22 @@
*/
public void setAudioRoute(int route) {
try {
- mAdapter.setAudioRoute(route);
+ mAdapter.setAudioRoute(route, null);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified. A list of
+ * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred.
+ */
+ public void requestBluetoothAudio(String bluetoothAddress) {
+ try {
+ mAdapter.setAudioRoute(CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
} catch (RemoteException e) {
}
}
@@ -419,4 +435,21 @@
} catch (RemoteException ignored) {
}
}
+
+
+ /**
+ * Initiates a handover of this {@link Call} to the {@link ConnectionService} identified
+ * by destAcct.
+ * @param callId The callId of the Call which calls this function.
+ * @param destAcct ConnectionService to which the call should be handed over.
+ * @param videoState The video state desired after the handover.
+ * @param extras Extra information to be passed to ConnectionService
+ */
+ public void handoverTo(String callId, PhoneAccountHandle destAcct, int videoState,
+ Bundle extras) {
+ try {
+ mAdapter.handoverTo(callId, destAcct, videoState, extras);
+ } catch (RemoteException ignored) {
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index e384d46..d558bba 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -16,9 +16,11 @@
package android.telecom;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
+import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
@@ -377,6 +379,22 @@
}
/**
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified if the
+ * bluetooth stack is unable to route audio to the requested device.
+ * A list of available devices can be obtained via
+ * {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}.
+ */
+ public final void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+ if (mPhone != null) {
+ mPhone.requestBluetoothAudio(bluetoothAddress);
+ }
+ }
+
+ /**
* Invoked when the {@code Phone} has been created. This is a signal to the in-call experience
* to start displaying in-call information to the user. Each instance of {@code InCallService}
* will have only one {@code Phone}, and this method will be called exactly once in the lifetime
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 066f6c2..421b1a4 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -17,7 +17,9 @@
package android.telecom;
import android.annotation.SystemApi;
+import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
+import android.os.RemoteException;
import android.util.ArrayMap;
import java.util.Collections;
@@ -295,6 +297,18 @@
}
/**
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified. A list of
+ * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred.
+ */
+ public void requestBluetoothAudio(String bluetoothAddress) {
+ mInCallAdapter.requestBluetoothAudio(bluetoothAddress);
+ }
+
+ /**
* Turns the proximity sensor on. When this request is made, the proximity sensor will
* become active, and the touch screen and display will be turned off when the user's face
* is detected to be in close proximity to the screen. This operation is a no-op on devices
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 691e7cf..74b9465 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -86,13 +86,11 @@
/**
* Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
* indicates whether this {@link PhoneAccount} is capable of supporting a request to handover a
- * connection (see {@link android.telecom.Call#EVENT_REQUEST_HANDOVER}) to this
- * {@link PhoneAccount} from a {@link PhoneAccount} specifying
- * {@link #EXTRA_SUPPORTS_HANDOVER_FROM}.
+ * connection (see {@code android.telecom.Call#handoverTo()}) to this {@link PhoneAccount} from
+ * a {@link PhoneAccount} specifying {@link #EXTRA_SUPPORTS_HANDOVER_FROM}.
* <p>
* A handover request is initiated by the user from the default dialer app to indicate a desire
* to handover a call from one {@link PhoneAccount}/{@link ConnectionService} to another.
- * @hide
*/
public static final String EXTRA_SUPPORTS_HANDOVER_TO =
"android.telecom.extra.SUPPORTS_HANDOVER_TO";
@@ -113,12 +111,11 @@
* Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
* indicates whether this {@link PhoneAccount} is capable of supporting a request to handover a
* connection from this {@link PhoneAccount} to another {@link PhoneAccount}.
- * (see {@link android.telecom.Call#EVENT_REQUEST_HANDOVER}) which specifies
+ * (see {@code android.telecom.Call#handoverTo()}) which specifies
* {@link #EXTRA_SUPPORTS_HANDOVER_TO}.
* <p>
* A handover request is initiated by the user from the default dialer app to indicate a desire
* to handover a call from one {@link PhoneAccount}/{@link ConnectionService} to another.
- * @hide
*/
public static final String EXTRA_SUPPORTS_HANDOVER_FROM =
"android.telecom.extra.SUPPORTS_HANDOVER_FROM";
@@ -132,7 +129,6 @@
* <p>
* By default, Self-Managed {@link PhoneAccount}s do not log their calls to the call log.
* Setting this extra to {@code true} provides a means for them to log their calls.
- * @hide
*/
public static final String EXTRA_LOG_SELF_MANAGED_CALLS =
"android.telecom.extra.LOG_SELF_MANAGED_CALLS";
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index f30d7bd..05480dc 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -1065,7 +1065,7 @@
*
* @param state The audio state of this {@code RemoteConnection}.
* @hide
- * @deprecated Use {@link #setCallAudioState(CallAudioState) instead.
+ * @deprecated Use {@link #setCallAudioState(CallAudioState)} instead.
*/
@SystemApi
@Deprecated
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 2cc4314..85906ad 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -398,7 +398,8 @@
}
@Override
- public void setAudioRoute(String callId, int audioRoute, Session.Info sessionInfo) {
+ public void setAudioRoute(String callId, int audioRoute, String bluetoothAddress,
+ Session.Info sessionInfo) {
if (hasConnection(callId)) {
// TODO(3pcalls): handle this for remote connections.
// Likely we don't want to do anything since it doesn't make sense for self-managed
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9e52c71..da32e0b 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1750,6 +1750,41 @@
return false;
}
+ /**
+ * Called from the recipient side of a handover to indicate a desire to accept the handover
+ * of an ongoing call to another {@link ConnectionService} identified by
+ * {@link PhoneAccountHandle} destAcct. For managed {@link ConnectionService}s, the specified
+ * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and
+ * the user must have enabled the corresponding {@link PhoneAccount}. This can be checked using
+ * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have
+ * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to handover a call to it.
+ * <p>
+ * Once invoked, this method will cause the system to bind to the {@link ConnectionService}
+ * associated with the {@link PhoneAccountHandle} destAcct and call
+ * (See {@link ConnectionService#onCreateIncomingHandoverConnection}).
+ * <p>
+ * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either
+ * the {@link PhoneAccountHandle} destAcct does not correspond to a registered
+ * {@link PhoneAccount} or the associated {@link PhoneAccount} is not currently enabled by the
+ * user.
+ * <p>
+ * For a self-managed {@link ConnectionService}, a {@link SecurityException} will be thrown if
+ * the calling app does not have {@link android.Manifest.permission#MANAGE_OWN_CALLS}.
+ *
+ * @param srcAddr The {@link android.net.Uri} of the ongoing call to handover to the caller’s
+ * {@link ConnectionService}.
+ * @param videoState Video state after the handover.
+ * @param destAcct The {@link PhoneAccountHandle} registered to the calling package.
+ */
+ public void acceptHandover(Uri srcAddr, int videoState, PhoneAccountHandle destAcct) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().acceptHandover(srcAddr, videoState, destAcct);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException acceptHandover: " + e);
+ }
+ }
private ITelecomService getTelecomService() {
if (mTelecomServiceOverride != null) {
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index d20da18..da2015f 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -103,7 +103,8 @@
void removeExtras(String callId, in List<String> keys, in Session.Info sessionInfo);
- void setAudioRoute(String callId, int audioRoute, in Session.Info sessionInfo);
+ void setAudioRoute(String callId, int audioRoute, String bluetoothAddress,
+ in Session.Info sessionInfo);
void onConnectionEvent(String callId, String event, in Bundle extras,
in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 73fa29a..23ac940 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -39,7 +39,7 @@
void mute(boolean shouldMute);
- void setAudioRoute(int route);
+ void setAudioRoute(int route, String bluetoothAddress);
void playDtmfTone(String callId, char digit);
@@ -77,4 +77,7 @@
void stopRtt(String callId);
void setRttMode(String callId, int mode);
+
+ void handoverTo(String callId, in PhoneAccountHandle destAcct, int videoState,
+ in Bundle extras);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 8ebac2c..3460754 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -274,4 +274,9 @@
* @see TelecomServiceImpl#waitOnHandler
*/
void waitOnHandlers();
+
+ /**
+ * @see TelecomServiceImpl#acceptHandover
+ */
+ void acceptHandover(in Uri srcAddr, int videoState, in PhoneAccountHandle destAcct);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 0030ab6..1db6ef7 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -855,6 +855,14 @@
public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
/**
+ * Default Enhanced 4G LTE mode enabled. When this is {@code true}, Enhanced 4G LTE mode by
+ * default is on, otherwise if {@code false}, Enhanced 4G LTE mode by default is off.
+ * @hide
+ */
+ public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL =
+ "enhanced_4g_lte_on_by_default_bool";
+
+ /**
* Determine whether IMS apn can be shown.
*/
public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
@@ -1812,6 +1820,7 @@
sDefaults.putBoolean(KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL, true);
sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_ENHANCED_4G_LTE_BOOL, false);
+ sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL, false);
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 6276626..c968406 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -101,9 +101,6 @@
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
*
- * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
- * not a 2 or 3-digit code.
- *
* @hide
*/
public CellIdentityGsm (int lac, int cid, int arfcn, int bsic, String mccStr,
@@ -115,22 +112,29 @@
// for inbound parcels
mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
+ // Only allow INT_MAX if unknown string mcc/mnc
if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
mMccStr = mccStr;
- } else if (mccStr.isEmpty()) {
- // If the mccStr parsed from Parcel is empty, set it as null.
+ } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mccStr is empty or unknown, set it as null.
mMccStr = null;
} else {
- throw new IllegalArgumentException("invalid MCC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+ // after the bug got fixed.
+ mMccStr = null;
+ log("invalid MCC format: " + mccStr);
}
if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
mMncStr = mncStr;
- } else if (mncStr.isEmpty()) {
- // If the mncStr parsed from Parcel is empty, set it as null.
+ } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mncStr is empty or unknown, set it as null.
mMncStr = null;
} else {
- throw new IllegalArgumentException("invalid MNC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+ // after the bug got fixed.
+ mMncStr = null;
+ log("invalid MNC format: " + mncStr);
}
mAlphaLong = alphal;
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 74d2966..825dcc3 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -102,9 +102,6 @@
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
*
- * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
- * not a 2 or 3-digit code.
- *
* @hide
*/
public CellIdentityLte (int ci, int pci, int tac, int earfcn, String mccStr,
@@ -114,22 +111,29 @@
mTac = tac;
mEarfcn = earfcn;
+ // Only allow INT_MAX if unknown string mcc/mnc
if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
mMccStr = mccStr;
- } else if (mccStr.isEmpty()) {
- // If the mccStr parsed from Parcel is empty, set it as null.
+ } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mccStr is empty or unknown, set it as null.
mMccStr = null;
} else {
- throw new IllegalArgumentException("invalid MCC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+ // after the bug got fixed.
+ mMccStr = null;
+ log("invalid MCC format: " + mccStr);
}
if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
mMncStr = mncStr;
- } else if (mncStr.isEmpty()) {
- // If the mncStr parsed from Parcel is empty, set it as null.
+ } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mncStr is empty or unknown, set it as null.
mMncStr = null;
} else {
- throw new IllegalArgumentException("invalid MNC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+ // after the bug got fixed.
+ mMncStr = null;
+ log("invalid MNC format: " + mncStr);
}
mAlphaLong = alphal;
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 51b11aa..e74b570 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -102,9 +102,6 @@
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
*
- * @throws IllegalArgumentException if the input MCC is not a 3-digit code or the input MNC is
- * not a 2 or 3-digit code.
- *
* @hide
*/
public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
@@ -114,22 +111,29 @@
mPsc = psc;
mUarfcn = uarfcn;
+ // Only allow INT_MAX if unknown string mcc/mnc
if (mccStr == null || mccStr.matches("^[0-9]{3}$")) {
mMccStr = mccStr;
- } else if (mccStr.isEmpty()) {
- // If the mccStr parsed from Parcel is empty, set it as null.
+ } else if (mccStr.isEmpty() || mccStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mccStr is empty or unknown, set it as null.
mMccStr = null;
} else {
- throw new IllegalArgumentException("invalid MCC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+ // after the bug got fixed.
+ mMccStr = null;
+ log("invalid MCC format: " + mccStr);
}
if (mncStr == null || mncStr.matches("^[0-9]{2,3}$")) {
mMncStr = mncStr;
- } else if (mncStr.isEmpty()) {
- // If the mncStr parsed from Parcel is empty, set it as null.
+ } else if (mncStr.isEmpty() || mncStr.equals(String.valueOf(Integer.MAX_VALUE))) {
+ // If the mncStr is empty or unknown, set it as null.
mMncStr = null;
} else {
- throw new IllegalArgumentException("invalid MNC format");
+ // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+ // after the bug got fixed.
+ mMncStr = null;
+ log("invalid MNC format: " + mncStr);
}
mAlphaLong = alphal;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 88f4880..1e6abf2 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -28,6 +28,7 @@
import android.net.INetworkPolicyManager;
import android.net.Uri;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -360,6 +361,45 @@
public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
/**
+ * TelephonyProvider column name for enable Volte.
+ *
+ * If this setting is not initialized (set to -1) then we use the Carrier Config value
+ * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
+ *@hide
+ */
+ public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+
+ /**
+ * TelephonyProvider column name for enable VT (Video Telephony over IMS)
+ *@hide
+ */
+ public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+
+ /**
+ * TelephonyProvider column name for enable Wifi calling
+ *@hide
+ */
+ public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+
+ /**
+ * TelephonyProvider column name for Wifi calling mode
+ *@hide
+ */
+ public static final String WFC_IMS_MODE = "wfc_ims_mode";
+
+ /**
+ * TelephonyProvider column name for Wifi calling mode in roaming
+ *@hide
+ */
+ public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+
+ /**
+ * TelephonyProvider column name for enable Wifi calling in roaming
+ *@hide
+ */
+ public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+
+ /**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
*
@@ -410,7 +450,15 @@
* for #onSubscriptionsChanged to be invoked.
*/
public static class OnSubscriptionsChangedListener {
- private final Handler mHandler = new Handler() {
+ private class OnSubscriptionsChangedListenerHandler extends Handler {
+ OnSubscriptionsChangedListenerHandler() {
+ super();
+ }
+
+ OnSubscriptionsChangedListenerHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
if (DBG) {
@@ -418,7 +466,22 @@
}
OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
}
- };
+ }
+
+ private final Handler mHandler;
+
+ public OnSubscriptionsChangedListener() {
+ mHandler = new OnSubscriptionsChangedListenerHandler();
+ }
+
+ /**
+ * Allow a listener to be created with a custom looper
+ * @param looper the looper that the underlining handler should run on
+ * @hide
+ */
+ public OnSubscriptionsChangedListener(Looper looper) {
+ mHandler = new OnSubscriptionsChangedListenerHandler(looper);
+ }
/**
* Callback invoked when there is any change to any SubscriptionInfo. Typically
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 92a21b6..7bcdcdc 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -73,8 +73,8 @@
/**
* Informs the user that there is some error about the scan.
*
- * This callback will be called whenever there is any error about the scan, but the scan
- * won't stop unless the onComplete() callback is called.
+ * This callback will be called whenever there is any error about the scan, and the scan
+ * will be terminated. onComplete() will NOT be called.
*/
public void onError(int error) {}
}
diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
index 892fbf0..9f60cc3 100644
--- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
@@ -38,7 +38,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
+ @IntDef(flag = true, value = {ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
public @interface FilterFlag {}
/**
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index 0d737b5..e064adb 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -17,10 +17,13 @@
package android.telephony.mbms;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Describes a single file that is available over MBMS.
*/
@@ -47,6 +50,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public FileInfo(Uri uri, String mimeType) {
this.uri = uri;
this.mimeType = mimeType;
@@ -82,4 +86,23 @@
public String getMimeType() {
return mimeType;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ FileInfo fileInfo = (FileInfo) o;
+ return Objects.equals(uri, fileInfo.uri) &&
+ Objects.equals(mimeType, fileInfo.mimeType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uri, mimeType);
+ }
}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 9af1eb9..9ef188c 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -165,16 +165,16 @@
Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
return false;
}
+ if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
+ Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
+ return false;
+ }
// We do not need to verify below extras if the result is not success.
if (MbmsDownloadSession.RESULT_SUCCESSFUL !=
intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
MbmsDownloadSession.RESULT_CANCELLED)) {
return true;
}
- if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
- Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
- return false;
- }
if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
return false;
@@ -242,10 +242,12 @@
int result = intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
MbmsDownloadSession.RESULT_CANCELLED);
intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT, result);
+ intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, request);
if (result != MbmsDownloadSession.RESULT_SUCCESSFUL) {
Log.i(LOG_TAG, "Download request indicated a failed download. Aborting.");
context.sendBroadcast(intentForApp);
+ setResultCode(RESULT_OK);
return;
}
@@ -273,7 +275,6 @@
intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI,
stagedFileLocation);
intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
- intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, request);
context.sendBroadcast(intentForApp);
setResultCode(RESULT_OK);
diff --git a/telephony/java/android/telephony/mbms/UriPathPair.java b/telephony/java/android/telephony/mbms/UriPathPair.java
index 187e9ee..dd20a69 100644
--- a/telephony/java/android/telephony/mbms/UriPathPair.java
+++ b/telephony/java/android/telephony/mbms/UriPathPair.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Parcel;
@@ -29,6 +30,7 @@
* @hide
*/
@SystemApi
+@TestApi
public final class UriPathPair implements Parcelable {
private final Uri mFilePathUri;
private final Uri mContentUri;
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
index a43f122..f1cac8c 100644
--- a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
+++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
@@ -17,6 +17,7 @@
package android.telephony.mbms.vendor;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -34,6 +35,7 @@
* @hide
*/
@SystemApi
+@TestApi
public class VendorUtils {
/**
diff --git a/test-mock/Android.mk b/test-mock/Android.mk
new file mode 100644
index 0000000..18da8b8
--- /dev/null
+++ b/test-mock/Android.mk
@@ -0,0 +1,144 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+android_test_mock_source_files := $(call all-java-files-under, src/android/test/mock)
+
+# Build the repackaged.android.test.mock library
+# ==============================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test
+
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../legacy-test/jarjar-rules.txt
+
+LOCAL_MODULE:= repackaged.android.test.mock
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Build the android.test.mock library
+# ===================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
+
+LOCAL_MODULE:= android.test.mock
+
+include $(BUILD_JAVA_LIBRARY)
+
+# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
+ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
+
+# Generate the stub source files for android.test.mock.stubs
+# ==========================================================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(android_test_mock_source_files)
+
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src/android/test/mock
+
+ANDROID_TEST_MOCK_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/api.txt
+ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/removed.txt
+
+ANDROID_TEST_MOCK_API_FILE := $(LOCAL_PATH)/api/android-test-mock-current.txt
+ANDROID_TEST_MOCK_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-mock-removed.txt
+
+LOCAL_DROIDDOC_OPTIONS:= \
+ -stubpackages android.test.mock \
+ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/src \
+ -nodocs \
+ -api $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) \
+ -removedApi $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) \
+
+LOCAL_UNINSTALLABLE_MODULE := true
+LOCAL_MODULE := android-test-mock-api-stubs-gen
+
+include $(BUILD_DROIDDOC)
+
+# Remember the target that will trigger the code generation.
+android_test_mock_gen_stamp := $(full_target)
+
+# Add some additional dependencies
+$(ANDROID_TEST_MOCK_OUTPUT_API_FILE): $(full_target)
+$(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE): $(full_target)
+
+# Build the android.test.mock.stubs library
+# =========================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.test.mock.stubs
+
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+
+# Make sure to run droiddoc first to generate the stub source files.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_mock_gen_stamp)
+android_test_mock_gen_stamp :=
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Archive a copy of the classes.jar in SDK build.
+$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.mock.stubs.jar)
+
+# Check that the android.test.mock.stubs library has not changed
+# ==============================================================
+
+# Check that the API we're building hasn't changed from the not-yet-released
+# SDK version.
+$(eval $(call check-api, \
+ check-android-test-mock-api-current, \
+ $(ANDROID_TEST_MOCK_API_FILE), \
+ $(ANDROID_TEST_MOCK_OUTPUT_API_FILE), \
+ $(ANDROID_TEST_MOCK_REMOVED_API_FILE), \
+ $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE), \
+ -error 2 -error 3 -error 4 -error 5 -error 6 \
+ -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
+ -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
+ -error 25 -error 26 -error 27, \
+ cat $(LOCAL_PATH)/api/apicheck_msg_android_test_mock.txt, \
+ check-android-test-mock-api, \
+ $(call doc-timestamp-for,android-test-mock-api-stubs-gen) \
+ ))
+
+.PHONY: check-android-test-mock-api
+checkapi: check-android-test-mock-api
+
+.PHONY: update-android-test-mock-api
+update-api: update-android-test-mock-api
+
+update-android-test-mock-api: $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) | $(ACP)
+ @echo Copying current.txt
+ $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) $(ANDROID_TEST_MOCK_API_FILE)
+ @echo Copying removed.txt
+ $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_MOCK_REMOVED_API_FILE)
+
+# Build the android.test.mock.sdk library
+# =======================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.test.mock.sdk
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := android.test.mock.stubs
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
diff --git a/test-runner/api/android-test-mock-current.txt b/test-mock/api/android-test-mock-current.txt
similarity index 99%
rename from test-runner/api/android-test-mock-current.txt
rename to test-mock/api/android-test-mock-current.txt
index 93bbf6c..07fcee2 100644
--- a/test-runner/api/android-test-mock-current.txt
+++ b/test-mock/api/android-test-mock-current.txt
@@ -344,7 +344,6 @@
method public int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public int installExistingPackageAsUser(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void installPackage(android.net.Uri, android.content.pm.IPackageInstallObserver, int, java.lang.String);
method public void installPackage(android.net.Uri, android.app.PackageInstallObserver, int, java.lang.String);
method public boolean isInstantApp();
method public boolean isInstantApp(java.lang.String);
diff --git a/test-runner/api/android-test-mock-removed.txt b/test-mock/api/android-test-mock-removed.txt
similarity index 100%
rename from test-runner/api/android-test-mock-removed.txt
rename to test-mock/api/android-test-mock-removed.txt
diff --git a/test-runner/api/apicheck_msg_android_test_mock.txt b/test-mock/api/apicheck_msg_android_test_mock.txt
similarity index 100%
rename from test-runner/api/apicheck_msg_android_test_mock.txt
rename to test-mock/api/apicheck_msg_android_test_mock.txt
diff --git a/test-runner/src/android/test/mock/MockApplication.java b/test-mock/src/android/test/mock/MockApplication.java
similarity index 100%
rename from test-runner/src/android/test/mock/MockApplication.java
rename to test-mock/src/android/test/mock/MockApplication.java
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
similarity index 100%
rename from test-runner/src/android/test/mock/MockContentProvider.java
rename to test-mock/src/android/test/mock/MockContentProvider.java
diff --git a/test-runner/src/android/test/mock/MockContentResolver.java b/test-mock/src/android/test/mock/MockContentResolver.java
similarity index 100%
rename from test-runner/src/android/test/mock/MockContentResolver.java
rename to test-mock/src/android/test/mock/MockContentResolver.java
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
similarity index 100%
rename from test-runner/src/android/test/mock/MockContext.java
rename to test-mock/src/android/test/mock/MockContext.java
diff --git a/test-runner/src/android/test/mock/MockCursor.java b/test-mock/src/android/test/mock/MockCursor.java
similarity index 100%
rename from test-runner/src/android/test/mock/MockCursor.java
rename to test-mock/src/android/test/mock/MockCursor.java
diff --git a/test-runner/src/android/test/mock/MockDialogInterface.java b/test-mock/src/android/test/mock/MockDialogInterface.java
similarity index 100%
rename from test-runner/src/android/test/mock/MockDialogInterface.java
rename to test-mock/src/android/test/mock/MockDialogInterface.java
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
similarity index 100%
rename from test-runner/src/android/test/mock/MockIContentProvider.java
rename to test-mock/src/android/test/mock/MockIContentProvider.java
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
similarity index 98%
rename from test-runner/src/android/test/mock/MockPackageManager.java
rename to test-mock/src/android/test/mock/MockPackageManager.java
index 7e08f51..0c562e6 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -26,12 +26,11 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
-import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstantAppInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
@@ -653,15 +652,6 @@
throw new UnsupportedOperationException();
}
- /**
- * @hide - to match hiding in superclass
- */
- @Override
- public void installPackage(Uri packageURI, IPackageInstallObserver observer,
- int flags, String installerPackageName) {
- throw new UnsupportedOperationException();
- }
-
@Override
public void setInstallerPackageName(String targetPackage,
String installerPackageName) {
diff --git a/test-runner/src/android/test/mock/MockResources.java b/test-mock/src/android/test/mock/MockResources.java
similarity index 100%
rename from test-runner/src/android/test/mock/MockResources.java
rename to test-mock/src/android/test/mock/MockResources.java
diff --git a/test-runner/src/android/test/mock/package.html b/test-mock/src/android/test/mock/package.html
similarity index 100%
rename from test-runner/src/android/test/mock/package.html
rename to test-mock/src/android/test/mock/package.html
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 3367aba..d0f5b32 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -16,14 +16,11 @@
LOCAL_PATH:= $(call my-dir)
-android_test_mock_source_files := $(call all-java-files-under, src/android/test/mock)
-
# Build the android.test.runner library
# =====================================
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- $(filter-out $(android_test_mock_source_files), $(call all-java-files-under, src))
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := \
core-oj \
@@ -42,7 +39,12 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test
+LOCAL_JAVA_LIBRARIES := \
+ core-oj \
+ core-libart \
+ framework \
+ legacy-test \
+ android.test.mock \
LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../legacy-test/jarjar-rules.txt
@@ -57,10 +59,7 @@
# ============================================================
include $(CLEAR_VARS)
-# Exclude android.test.mock classes as stubs for them are created in the
-# android.test.mock.stubs target
-LOCAL_SRC_FILES := \
- $(filter-out $(android_test_mock_source_files), $(call all-java-files-under, src))
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := \
core-oj \
@@ -153,117 +152,3 @@
$(hide) $(ACP) $(ANDROID_TEST_RUNNER_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_RUNNER_REMOVED_API_FILE)
endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
-
-# Build the android.test.mock library
-# ===================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(android_test_mock_source_files)
-
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-
-LOCAL_MODULE:= android.test.mock
-
-include $(BUILD_JAVA_LIBRARY)
-
-# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
-ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
-
-# Generate the stub source files for android.test.mock.stubs
-# ==========================================================
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(android_test_mock_source_files)
-
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src/android/test/mock
-
-ANDROID_TEST_MOCK_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/api.txt
-ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/removed.txt
-
-ANDROID_TEST_MOCK_API_FILE := $(LOCAL_PATH)/api/android-test-mock-current.txt
-ANDROID_TEST_MOCK_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-mock-removed.txt
-
-LOCAL_DROIDDOC_OPTIONS:= \
- -stubpackages android.test.mock \
- -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/src \
- -nodocs \
- -api $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) \
- -removedApi $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) \
-
-LOCAL_UNINSTALLABLE_MODULE := true
-LOCAL_MODULE := android-test-mock-api-stubs-gen
-
-include $(BUILD_DROIDDOC)
-
-# Remember the target that will trigger the code generation.
-android_test_mock_gen_stamp := $(full_target)
-
-# Add some additional dependencies
-$(ANDROID_TEST_MOCK_OUTPUT_API_FILE): $(full_target)
-$(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE): $(full_target)
-
-# Build the android.test.mock.stubs library
-# =========================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := android.test.mock.stubs
-
-LOCAL_SOURCE_FILES_ALL_GENERATED := true
-
-# Make sure to run droiddoc first to generate the stub source files.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_mock_gen_stamp)
-android_test_mock_gen_stamp :=
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Archive a copy of the classes.jar in SDK build.
-$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.mock.stubs.jar)
-
-# Check that the android.test.mock.stubs library has not changed
-# ==============================================================
-
-# Check that the API we're building hasn't changed from the not-yet-released
-# SDK version.
-$(eval $(call check-api, \
- check-android-test-mock-api-current, \
- $(ANDROID_TEST_MOCK_API_FILE), \
- $(ANDROID_TEST_MOCK_OUTPUT_API_FILE), \
- $(ANDROID_TEST_MOCK_REMOVED_API_FILE), \
- $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE), \
- -error 2 -error 3 -error 4 -error 5 -error 6 \
- -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
- -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
- -error 25 -error 26 -error 27, \
- cat $(LOCAL_PATH)/api/apicheck_msg_android_test_mock.txt, \
- check-android-test-mock-api, \
- $(call doc-timestamp-for,android-test-mock-api-stubs-gen) \
- ))
-
-.PHONY: check-android-test-mock-api
-checkapi: check-android-test-mock-api
-
-.PHONY: update-android-test-mock-api
-update-api: update-android-test-mock-api
-
-update-android-test-mock-api: $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) | $(ACP)
- @echo Copying current.txt
- $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) $(ANDROID_TEST_MOCK_API_FILE)
- @echo Copying removed.txt
- $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_MOCK_REMOVED_API_FILE)
-
-# Build the android.test.mock.sdk library
-# =======================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := android.test.mock.sdk
-LOCAL_SDK_VERSION := current
-
-LOCAL_STATIC_JAVA_LIBRARIES := android.test.mock.stubs
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# additionally, build unit tests in a separate .apk
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
-endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 9caf9d0..ebf5f68 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -282,6 +282,15 @@
<category android:name="com.android.test.hwui.TEST" />
</intent-filter>
</activity>
+
+ <activity
+ android:name="ColoredShadowsActivity"
+ android:label="View/ColoredShadows">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
<activity
android:name="OpaqueActivity"
diff --git a/tests/HwAccelerationTest/res/layout/colored_shadows_activity.xml b/tests/HwAccelerationTest/res/layout/colored_shadows_activity.xml
new file mode 100644
index 0000000..1863325
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/colored_shadows_activity.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/colored_grid">
+ <include layout="@layout/colored_shadows_row" />
+ <include layout="@layout/colored_shadows_row" />
+ <include layout="@layout/colored_shadows_row" />
+ <include layout="@layout/colored_shadows_row" />
+ <include layout="@layout/colored_shadows_row" />
+ <include layout="@layout/colored_shadows_row" />
+ <include layout="@layout/colored_shadows_row" />
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/res/layout/colored_shadows_row.xml b/tests/HwAccelerationTest/res/layout/colored_shadows_row.xml
new file mode 100644
index 0000000..61b0759
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/colored_shadows_row.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="25dp"
+ android:paddingBottom="25dp"
+ android:clipToPadding="false" >
+ <View android:id="@+id/grey"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:elevation="16dp"
+ android:background="#3C4043"
+ android:layout_marginLeft="20dp" />
+ <View android:id="@+id/blue"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:elevation="16dp"
+ android:background="#185ABC"
+ android:layout_marginLeft="20dp"/>
+ <View android:id="@+id/red"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:elevation="16dp"
+ android:background="#B31412"
+ android:layout_marginLeft="20dp"/>
+ <View android:id="@+id/yellow"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:elevation="16dp"
+ android:background="#EA8600"
+ android:layout_marginLeft="20dp"/>
+ <View android:id="@+id/green"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:elevation="16dp"
+ android:background="#137333"
+ android:layout_marginLeft="20dp"/>
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
new file mode 100644
index 0000000..135c93c
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class ColoredShadowsActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.colored_shadows_activity);
+ ViewGroup grid = findViewById(R.id.colored_grid);
+ for (int i = 0; i < grid.getChildCount(); i++) {
+ setShadowColors((ViewGroup) grid.getChildAt(i), i);
+ }
+ }
+
+ private void setShadowColors(ViewGroup row, int rowIndex) {
+ for (int i = 0; i < row.getChildCount(); i++) {
+ View view = row.getChildAt(i);
+ view.setShadowColor(shadowColorFor(view));
+ view.setElevation(6.0f * (rowIndex + 1));
+ }
+ }
+
+ private int shadowColorFor(View view) {
+ switch (view.getId()) {
+ case R.id.grey: return 0xFF3C4043;
+ case R.id.blue: return 0xFF185ABC;
+ case R.id.red: return 0xFFB31412;
+ case R.id.yellow: return 0xFFEA8600;
+ case R.id.green: return 0xFF137333;
+ default: return 0xFF000000;
+ }
+ }
+
+}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 7b07038..8e579aa 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -23,7 +23,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -46,6 +50,8 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
import org.junit.Before;
import org.junit.Test;
@@ -57,6 +63,8 @@
public class IpSecServiceTest {
private static final int DROID_SPI = 0xD1201D;
+ private static final int MAX_NUM_ENCAP_SOCKETS = 100;
+ private static final int MAX_NUM_SPIS = 100;
private static final int TEST_UDP_ENCAP_INVALID_PORT = 100;
private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000;
@@ -260,4 +268,115 @@
}
}
}
+
+ /**
+ * This function checks if the number of encap UDP socket that one UID can reserve
+ * has a reasonable limit.
+ */
+ @Test
+ public void testSocketResourceTrackerLimitation() throws Exception {
+ List<IpSecUdpEncapResponse> openUdpEncapSockets = new ArrayList<IpSecUdpEncapResponse>();
+ // Reserve sockets until it fails.
+ for (int i = 0; i < MAX_NUM_ENCAP_SOCKETS; i++) {
+ IpSecUdpEncapResponse newUdpEncapSocket =
+ mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+ assertNotNull(newUdpEncapSocket);
+ if (IpSecManager.Status.OK != newUdpEncapSocket.status) {
+ break;
+ }
+ openUdpEncapSockets.add(newUdpEncapSocket);
+ }
+ // Assert that the total sockets quota has a reasonable limit.
+ assertTrue(
+ openUdpEncapSockets.size() > 0
+ && openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
+
+ // Try to reserve one more UDP encapsulation socket, and should fail.
+ IpSecUdpEncapResponse extraUdpEncapSocket =
+ mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+ assertNotNull(extraUdpEncapSocket);
+ assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status);
+
+ // Close one of the open UDP encapsulation scokets.
+ mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId);
+ openUdpEncapSockets.get(0).fileDescriptor.close();
+ openUdpEncapSockets.remove(0);
+
+ // Try to reserve one more UDP encapsulation socket, and should be successful.
+ extraUdpEncapSocket = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+ assertNotNull(extraUdpEncapSocket);
+ assertEquals(IpSecManager.Status.OK, extraUdpEncapSocket.status);
+ openUdpEncapSockets.add(extraUdpEncapSocket);
+
+ // Close open UDP sockets.
+ for (IpSecUdpEncapResponse openSocket : openUdpEncapSockets) {
+ mIpSecService.closeUdpEncapsulationSocket(openSocket.resourceId);
+ openSocket.fileDescriptor.close();
+ }
+ }
+
+ /**
+ * This function checks if the number of SPI that one UID can reserve
+ * has a reasonable limit.
+ * This test does not test for both address families or duplicate SPIs because resource
+ * tracking code does not depend on them.
+ */
+ @Test
+ public void testSpiResourceTrackerLimitation() throws Exception {
+ List<IpSecSpiResponse> reservedSpis = new ArrayList<IpSecSpiResponse>();
+ // Return the same SPI for all SPI allocation since IpSecService only
+ // tracks the resource ID.
+ when(mMockNetd.ipSecAllocateSpi(
+ anyInt(),
+ eq(IpSecTransform.DIRECTION_OUT),
+ anyString(),
+ eq(InetAddress.getLoopbackAddress().getHostAddress()),
+ anyInt()))
+ .thenReturn(DROID_SPI);
+ // Reserve spis until it fails.
+ for (int i = 0; i < MAX_NUM_SPIS; i++) {
+ IpSecSpiResponse newSpi =
+ mIpSecService.reserveSecurityParameterIndex(
+ 0x1,
+ InetAddress.getLoopbackAddress().getHostAddress(),
+ DROID_SPI + i,
+ new Binder());
+ assertNotNull(newSpi);
+ if (IpSecManager.Status.OK != newSpi.status) {
+ break;
+ }
+ reservedSpis.add(newSpi);
+ }
+ // Assert that the SPI quota has a reasonable limit.
+ assertTrue(reservedSpis.size() > 0 && reservedSpis.size() < MAX_NUM_SPIS);
+
+ // Try to reserve one more SPI, and should fail.
+ IpSecSpiResponse extraSpi =
+ mIpSecService.reserveSecurityParameterIndex(
+ 0x1,
+ InetAddress.getLoopbackAddress().getHostAddress(),
+ DROID_SPI + MAX_NUM_SPIS,
+ new Binder());
+ assertNotNull(extraSpi);
+ assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraSpi.status);
+
+ // Release one reserved spi.
+ mIpSecService.releaseSecurityParameterIndex(reservedSpis.get(0).resourceId);
+ reservedSpis.remove(0);
+
+ // Should successfully reserve one more spi.
+ extraSpi =
+ mIpSecService.reserveSecurityParameterIndex(
+ 0x1,
+ InetAddress.getLoopbackAddress().getHostAddress(),
+ DROID_SPI + MAX_NUM_SPIS,
+ new Binder());
+ assertNotNull(extraSpi);
+ assertEquals(IpSecManager.Status.OK, extraSpi.status);
+
+ // Release reserved SPIs.
+ for (IpSecSpiResponse spiResp : reservedSpis) {
+ mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
+ }
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index a65bb24..0656c5f 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -337,7 +337,6 @@
public void testNetworkEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(NetworkEvent.class),
- anInt(100),
anInt(5),
aLong(20410));
@@ -352,9 +351,6 @@
" network_event <",
" event_type: 5",
" latency_ms: 20410",
- " network_id <",
- " network_id: 100",
- " >",
" >",
">",
"version: 2\n");
@@ -508,6 +504,13 @@
stats.rootWakeups = 2;
stats.systemWakeups = 3;
stats.noUidWakeups = 3;
+ stats.l2UnicastCount = 5;
+ stats.l2MulticastCount = 1;
+ stats.l2BroadcastCount = 2;
+ stats.ethertypes.put(0x800, 3);
+ stats.ethertypes.put(0x86dd, 3);
+ stats.ipNextHeaders.put(6, 5);
+
IpConnectivityEvent got = IpConnectivityEventBuilder.toProto(stats);
String want = String.join("\n",
@@ -521,6 +524,21 @@
" wakeup_stats <",
" application_wakeups: 5",
" duration_sec: 0",
+ " ethertype_counts <",
+ " key: 2048",
+ " value: 3",
+ " >",
+ " ethertype_counts <",
+ " key: 34525",
+ " value: 3",
+ " >",
+ " ip_next_header_counts <",
+ " key: 6",
+ " value: 5",
+ " >",
+ " l2_broadcast_count: 2",
+ " l2_multicast_count: 1",
+ " l2_unicast_count: 5",
" no_uid_wakeups: 3",
" non_application_wakeups: 1",
" root_wakeups: 2",
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index b48ff8d..10d6deb 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -78,6 +78,9 @@
private static final String EXAMPLE_IPV4 = "192.0.2.1";
private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
+ private static final byte[] MAC_ADDR =
+ {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b};
+
@Mock Context mCtx;
@Mock IIpConnectivityMetrics mMockService;
@Mock ConnectivityManager mCm;
@@ -351,13 +354,21 @@
dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34);
// iface, uid
- wakeupEvent("wlan0", 1000);
- wakeupEvent("rmnet0", 10123);
- wakeupEvent("wlan0", 1000);
- wakeupEvent("rmnet0", 10008);
- wakeupEvent("wlan0", -1);
- wakeupEvent("wlan0", 10008);
- wakeupEvent("rmnet0", 1000);
+ final byte[] mac = {0x48, 0x7c, 0x2b, 0x6a, 0x3e, 0x4b};
+ final String srcIp = "192.168.2.1";
+ final String dstIp = "192.168.2.23";
+ final int sport = 2356;
+ final int dport = 13489;
+ final long now = 1001L;
+ final int v4 = 0x800;
+ final int tcp = 6;
+ final int udp = 17;
+ wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
+ wakeupEvent("wlan0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
+ wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
+ wakeupEvent("wlan0", 10008, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
+ wakeupEvent("wlan0", -1, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
+ wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
@@ -560,34 +571,33 @@
">",
"events <",
" if_name: \"\"",
- " link_layer: 2",
- " network_id: 0",
- " time_ms: 0",
- " transports: 0",
- " wakeup_stats <",
- " application_wakeups: 2",
- " duration_sec: 0",
- " no_uid_wakeups: 0",
- " non_application_wakeups: 0",
- " root_wakeups: 0",
- " system_wakeups: 1",
- " total_wakeups: 3",
- " >",
- ">",
- "events <",
- " if_name: \"\"",
" link_layer: 4",
" network_id: 0",
" time_ms: 0",
" transports: 0",
" wakeup_stats <",
- " application_wakeups: 1",
+ " application_wakeups: 3",
" duration_sec: 0",
+ " ethertype_counts <",
+ " key: 2048",
+ " value: 6",
+ " >",
+ " ip_next_header_counts <",
+ " key: 6",
+ " value: 3",
+ " >",
+ " ip_next_header_counts <",
+ " key: 17",
+ " value: 3",
+ " >",
+ " l2_broadcast_count: 0",
+ " l2_multicast_count: 0",
+ " l2_unicast_count: 6",
" no_uid_wakeups: 1",
" non_application_wakeups: 0",
" root_wakeups: 0",
" system_wakeups: 2",
- " total_wakeups: 4",
+ " total_wakeups: 6",
" >",
">",
"version: 2\n");
@@ -610,9 +620,10 @@
mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
}
- void wakeupEvent(String iface, int uid) throws Exception {
+ void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp,
+ String dstIp, int sport, int dport, long now) throws Exception {
String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
- mNetdListener.onWakeupEvent(prefix, uid, uid, 0);
+ mNetdListener.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now);
}
NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) {
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 83194d9..67805c9 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -61,7 +61,10 @@
private static final String EXAMPLE_IPV4 = "192.0.2.1";
private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
- NetdEventListenerService mNetdEventListenerService;
+ private static final byte[] MAC_ADDR =
+ {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b};
+
+ NetdEventListenerService mService;
ConnectivityManager mCm;
@Before
@@ -75,28 +78,49 @@
when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
- mNetdEventListenerService = new NetdEventListenerService(mCm);
+ mService = new NetdEventListenerService(mCm);
}
@Test
public void testWakeupEventLogging() throws Exception {
final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
+ final long now = System.currentTimeMillis();
+ final String iface = "wlan0";
+ final byte[] mac = MAC_ADDR;
+ final String srcIp = "192.168.2.1";
+ final String dstIp = "192.168.2.23";
+ final String srcIp6 = "2001:db8:4:fd00:a585:13d1:6a23:4fb4";
+ final String dstIp6 = "2001:db8:4006:807::200a";
+ final int sport = 2356;
+ final int dport = 13489;
+
+ final int v4 = 0x800;
+ final int v6 = 0x86dd;
+ final int tcp = 6;
+ final int udp = 17;
+ final int icmp6 = 58;
// Baseline without any event
String[] baseline = listNetdEvent();
- long now = System.currentTimeMillis();
- String prefix = "iface:wlan0";
- int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
- for (int uid : uids) {
- mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
- }
+ int[] uids = {10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004};
+ wakeupEvent(iface, uids[0], v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent(iface, uids[1], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent(iface, uids[2], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent(iface, uids[3], v4, icmp6, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent(iface, uids[4], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent(iface, uids[5], v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent(iface, uids[6], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent(iface, uids[7], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent(iface, uids[8], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
String[] events2 = remove(listNetdEvent(), baseline);
int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
assertEquals(expectedLength2, events2.length);
assertContains(events2[0], "WakeupStats");
assertContains(events2[0], "wlan0");
+ assertContains(events2[0], "0x800");
+ assertContains(events2[0], "0x86dd");
for (int i = 0; i < uids.length; i++) {
String got = events2[i+1];
assertContains(got, "WakeupEvent");
@@ -107,7 +131,7 @@
int uid = 20000;
for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
long ts = now + 10;
- mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
+ wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, ts);
}
String[] events3 = remove(listNetdEvent(), baseline);
@@ -123,7 +147,7 @@
}
uid = 45678;
- mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
+ wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, now);
String[] events4 = remove(listNetdEvent(), baseline);
String lastEvent = events4[events4.length - 1];
@@ -134,21 +158,36 @@
@Test
public void testWakeupStatsLogging() throws Exception {
- wakeupEvent("wlan0", 1000);
- wakeupEvent("rmnet0", 10123);
- wakeupEvent("wlan0", 1000);
- wakeupEvent("rmnet0", 10008);
- wakeupEvent("wlan0", -1);
- wakeupEvent("wlan0", 10008);
- wakeupEvent("rmnet0", 1000);
- wakeupEvent("wlan0", 10004);
- wakeupEvent("wlan0", 1000);
- wakeupEvent("wlan0", 0);
- wakeupEvent("wlan0", -1);
- wakeupEvent("rmnet0", 10052);
- wakeupEvent("wlan0", 0);
- wakeupEvent("rmnet0", 1000);
- wakeupEvent("wlan0", 1010);
+ final byte[] mac = MAC_ADDR;
+ final String srcIp = "192.168.2.1";
+ final String dstIp = "192.168.2.23";
+ final String srcIp6 = "2401:fa00:4:fd00:a585:13d1:6a23:4fb4";
+ final String dstIp6 = "2404:6800:4006:807::200a";
+ final int sport = 2356;
+ final int dport = 13489;
+ final long now = 1001L;
+
+ final int v4 = 0x800;
+ final int v6 = 0x86dd;
+ final int tcp = 6;
+ final int udp = 17;
+ final int icmp6 = 58;
+
+ wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("rmnet0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("rmnet0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("rmnet0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("wlan0", 10004, v4, udp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("wlan0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("rmnet0", 10052, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("rmnet0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("wlan0", 1010, v4, udp, mac, srcIp, dstIp, sport, dport, now);
String got = flushStatistics();
String want = String.join("\n",
@@ -162,6 +201,21 @@
" wakeup_stats <",
" application_wakeups: 3",
" duration_sec: 0",
+ " ethertype_counts <",
+ " key: 2048",
+ " value: 4",
+ " >",
+ " ethertype_counts <",
+ " key: 34525",
+ " value: 1",
+ " >",
+ " ip_next_header_counts <",
+ " key: 6",
+ " value: 5",
+ " >",
+ " l2_broadcast_count: 0",
+ " l2_multicast_count: 0",
+ " l2_unicast_count: 5",
" no_uid_wakeups: 0",
" non_application_wakeups: 0",
" root_wakeups: 0",
@@ -178,6 +232,29 @@
" wakeup_stats <",
" application_wakeups: 2",
" duration_sec: 0",
+ " ethertype_counts <",
+ " key: 2048",
+ " value: 5",
+ " >",
+ " ethertype_counts <",
+ " key: 34525",
+ " value: 5",
+ " >",
+ " ip_next_header_counts <",
+ " key: 6",
+ " value: 3",
+ " >",
+ " ip_next_header_counts <",
+ " key: 17",
+ " value: 5",
+ " >",
+ " ip_next_header_counts <",
+ " key: 58",
+ " value: 2",
+ " >",
+ " l2_broadcast_count: 0",
+ " l2_multicast_count: 0",
+ " l2_unicast_count: 10",
" no_uid_wakeups: 2",
" non_application_wakeups: 1",
" root_wakeups: 2",
@@ -401,7 +478,7 @@
Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
return new Thread(() -> {
try {
- mNetdEventListenerService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
+ mService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
} catch (Exception e) {
fail(e.toString());
}
@@ -409,12 +486,13 @@
}
void dnsEvent(int netId, int type, int result, int latency) throws Exception {
- mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
+ mService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
}
- void wakeupEvent(String iface, int uid) throws Exception {
+ void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp,
+ String dstIp, int sport, int dport, long now) throws Exception {
String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
- mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, 0);
+ mService.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now);
}
void asyncDump(long durationMs) throws Exception {
@@ -422,7 +500,7 @@
final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
new Thread(() -> {
while (System.currentTimeMillis() < stop) {
- mNetdEventListenerService.list(pw);
+ mService.list(pw);
}
}).start();
}
@@ -431,7 +509,7 @@
String flushStatistics() throws Exception {
IpConnectivityMetrics metricsService =
new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
- metricsService.mNetdListener = mNetdEventListenerService;
+ metricsService.mNetdListener = mService;
StringWriter buffer = new StringWriter();
PrintWriter writer = new PrintWriter(buffer);
@@ -453,7 +531,7 @@
String[] listNetdEvent() throws Exception {
StringWriter buffer = new StringWriter();
PrintWriter writer = new PrintWriter(buffer);
- mNetdEventListenerService.list(writer);
+ mService.list(writer);
return buffer.toString().split("\\n");
}
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 08efc27..0f6fb50 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -218,6 +218,7 @@
if (attr.id) {
printer_->Print(attr.id.value().to_string());
}
+ printer_->Println();
}
}
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 921d853..5981401 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -235,8 +235,7 @@
return false;
}
} else {
- uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
- if (!io::CopyFileToArchive(context, file, path, compression_flags, writer)) {
+ if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
return false;
}
}
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 96a0203..6fcf0f6 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -268,6 +268,11 @@
return out << res_id.to_string();
}
+// For generic code to call 'using std::to_string; to_string(T);'.
+inline std::string to_string(const ResourceId& id) {
+ return id.to_string();
+}
+
//
// ResourceType implementation.
//
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 89ae9e8..2bd2405 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -26,49 +26,52 @@
#include "format/binary/TableFlattener.h"
#include "format/binary/XmlFlattener.h"
#include "format/proto/ProtoDeserialize.h"
+#include "format/proto/ProtoSerialize.h"
#include "io/BigBufferStream.h"
#include "io/Util.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
+#include "util/Util.h"
using ::android::StringPiece;
+using ::android::base::StringPrintf;
using ::std::unique_ptr;
using ::std::vector;
namespace aapt {
-static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml,
- const std::string& entry_path, bool utf16, IArchiveWriter* writer) {
- BigBuffer buffer(4096);
- XmlFlattenerOptions options = {};
- options.use_utf16 = utf16;
- XmlFlattener flattener(&buffer, options);
- if (!flattener.Consume(context, &xml)) {
- return false;
- }
- io::BigBufferInputStream input_stream(&buffer);
- return io::CopyInputStreamToArchive(context, &input_stream, entry_path, ArchiveEntry::kCompress,
- writer);
-}
+class IApkSerializer {
+ public:
+ IApkSerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
-bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr<LoadedApk> apk,
- const TableFlattenerOptions& options, IArchiveWriter* writer) {
- if (!FlattenXml(context, *apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
+ virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
+ IArchiveWriter* writer) = 0;
+ virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0;
+ virtual bool SerializeFile(const FileReference* file, IArchiveWriter* writer) = 0;
+
+ virtual ~IApkSerializer() = default;
+
+ protected:
+ IAaptContext* context_;
+ Source source_;
+};
+
+bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer* serializer,
+ IArchiveWriter* writer) {
+ if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to serialize AndroidManifest.xml");
return false;
}
- BigBuffer buffer(4096);
- TableFlattener table_flattener(options, &buffer);
- if (!table_flattener.Consume(context, apk->GetResourceTable())) {
+ // Resource table
+ if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to serialize the resource table");
return false;
}
- io::BigBufferInputStream input_stream(&buffer);
- if (!io::CopyInputStreamToArchive(context, &input_stream, kApkResourceTablePath,
- ArchiveEntry::kAlign, writer)) {
- return false;
- }
-
+ // Resources
for (const auto& package : apk->GetResourceTable()->packages) {
for (const auto& type : package->types) {
for (const auto& entry : type->entries) {
@@ -76,59 +79,187 @@
const FileReference* file = ValueCast<FileReference>(config_value->value.get());
if (file != nullptr) {
if (file->file == nullptr) {
- context->GetDiagnostics()->Warn(DiagMessage(apk->GetSource())
- << "no file associated with " << *file);
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "no file associated with " << *file);
return false;
}
- if (file->type == ResourceFile::Type::kProtoXml) {
- unique_ptr<io::InputStream> in = file->file->OpenInputStream();
- if (in == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to open file " << *file->path);
- return false;
- }
-
- pb::XmlNode pb_node;
- io::ZeroCopyInputAdaptor adaptor(in.get());
- if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to parse proto XML " << *file->path);
- return false;
- }
-
- std::string error;
- unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
- if (xml == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to deserialize proto XML "
- << *file->path << ": " << error);
- return false;
- }
-
- if (!FlattenXml(context, *xml, *file->path, false /*utf16*/, writer)) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to serialize XML " << *file->path);
- return false;
- }
- } else {
- if (!io::CopyFileToArchive(context, file->file, *file->path,
- file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u,
- writer)) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to copy file " << *file->path);
- return false;
- }
+ if (!serializer->SerializeFile(file, writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to serialize file " << *file->path);
+ return false;
}
-
} // file
} // config_value
} // entry
} // type
} // package
+
+ // Other files
+ std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
+ while (iterator->HasNext()) {
+ io::IFile* file = iterator->Next();
+
+ std::string path = file->GetSource().path;
+ // The name of the path has the format "<zip-file-name>@<path-to-file>".
+ path = path.substr(path.find('@') + 1);
+
+ // Manifest, resource table and resources have already been taken care of.
+ if (path == kAndroidManifestPath ||
+ path == kApkResourceTablePath ||
+ path == kProtoResourceTablePath ||
+ path.find("res/") == 0) {
+ continue;
+ }
+
+ if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to copy file " << path);
+ return false;
+ }
+ }
+
return true;
}
+
+class BinaryApkSerializer : public IApkSerializer {
+ public:
+ BinaryApkSerializer(IAaptContext* context, const Source& source,
+ const TableFlattenerOptions& options)
+ : IApkSerializer(context, source), tableFlattenerOptions_(options) {}
+
+ bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
+ IArchiveWriter* writer) override {
+ BigBuffer buffer(4096);
+ XmlFlattenerOptions options = {};
+ options.use_utf16 = utf16;
+ XmlFlattener flattener(&buffer, options);
+ if (!flattener.Consume(context_, xml)) {
+ return false;
+ }
+
+ io::BigBufferInputStream input_stream(&buffer);
+ return io::CopyInputStreamToArchive(context_, &input_stream, path, ArchiveEntry::kCompress,
+ writer);
+ }
+
+ bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
+ BigBuffer buffer(4096);
+ TableFlattener table_flattener(tableFlattenerOptions_, &buffer);
+ if (!table_flattener.Consume(context_, table)) {
+ return false;
+ }
+
+ io::BigBufferInputStream input_stream(&buffer);
+ return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
+ ArchiveEntry::kAlign, writer);
+ }
+
+ bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override {
+ if (file->type == ResourceFile::Type::kProtoXml) {
+ unique_ptr<io::InputStream> in = file->file->OpenInputStream();
+ if (in == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to open file " << *file->path);
+ return false;
+ }
+
+ pb::XmlNode pb_node;
+ io::ZeroCopyInputAdaptor adaptor(in.get());
+ if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to parse proto XML " << *file->path);
+ return false;
+ }
+
+ std::string error;
+ unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
+ if (xml == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to deserialize proto XML "
+ << *file->path << ": " << error);
+ return false;
+ }
+
+ if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to serialize to binary XML: " << *file->path);
+ return false;
+ }
+ } else {
+ if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to copy file " << *file->path);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ TableFlattenerOptions tableFlattenerOptions_;
+
+ DISALLOW_COPY_AND_ASSIGN(BinaryApkSerializer);
+};
+
+class ProtoApkSerializer : public IApkSerializer {
+ public:
+ ProtoApkSerializer(IAaptContext* context, const Source& source)
+ : IApkSerializer(context, source) {}
+
+ bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
+ IArchiveWriter* writer) override {
+ pb::XmlNode pb_node;
+ SerializeXmlResourceToPb(*xml, &pb_node);
+ return io::CopyProtoToArchive(context_, &pb_node, path, ArchiveEntry::kCompress, writer);
+ }
+
+ bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
+ pb::ResourceTable pb_table;
+ SerializeTableToPb(*table, &pb_table);
+ return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
+ ArchiveEntry::kCompress, writer);
+ }
+
+ bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override {
+ if (file->type == ResourceFile::Type::kBinaryXml) {
+ std::unique_ptr<io::IData> data = file->file->OpenAsData();
+ if (!data) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to open file " << *file->path);
+ return false;
+ }
+
+ std::string error;
+ std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error);
+ if (xml == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: "
+ << error);
+ return false;
+ }
+
+ if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to serialize to proto XML: " << *file->path);
+ return false;
+ }
+ } else {
+ if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to copy file " << *file->path);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProtoApkSerializer);
+};
+
class Context : public IAaptContext {
public:
Context() : mangler_({}), symbols_(&mangler_) {
@@ -181,12 +312,20 @@
};
int Convert(const vector<StringPiece>& args) {
+
+ static const char* kOutputFormatProto = "proto";
+ static const char* kOutputFormatBinary = "binary";
+
Context context;
std::string output_path;
+ Maybe<std::string> output_format;
TableFlattenerOptions options;
Flags flags =
Flags()
.RequiredFlag("-o", "Output path", &output_path)
+ .OptionalFlag("--output-format", StringPrintf("Format of the output. Accepted values are "
+ "'%s' and '%s'. When not set, defaults to '%s'.", kOutputFormatProto,
+ kOutputFormatBinary, kOutputFormatBinary), &output_format)
.OptionalSwitch("--enable-sparse-encoding",
"Enables encoding sparse entries using a binary search tree.\n"
"This decreases APK size at the cost of resource retrieval performance.",
@@ -222,7 +361,21 @@
if (writer == nullptr) {
return 1;
}
- return ConvertProtoApkToBinaryApk(&context, std::move(apk), options, writer.get()) ? 0 : 1;
+
+ unique_ptr<IApkSerializer> serializer;
+ if (!output_format || output_format.value() == kOutputFormatBinary) {
+ serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options));
+ } else if (output_format.value() == kOutputFormatProto) {
+ serializer.reset(new ProtoApkSerializer(&context, apk->GetSource()));
+ } else {
+ context.GetDiagnostics()->Error(DiagMessage(path)
+ << "Invalid value for flag --output-format: "
+ << output_format.value());
+ return 1;
+ }
+
+
+ return ConvertApk(&context, std::move(apk), serializer.get(), writer.get()) ? 0 : 1;
}
} // namespace aapt
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 66b5a7a..e94c0b4 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -16,7 +16,6 @@
#include <sys/stat.h>
-#include <fstream>
#include <queue>
#include <unordered_map>
#include <vector>
@@ -212,6 +211,8 @@
// This delegate will attempt to masquerade any '@id/' references with ID 0xPPTTEEEE,
// where PP > 7f, as 0x7fPPEEEE. Any potential overlapping is verified and an error occurs if such
// an overlap exists.
+//
+// See b/37498913.
class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
public:
FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) {
@@ -652,24 +653,26 @@
static bool WriteStableIdMapToPath(IDiagnostics* diag,
const std::unordered_map<ResourceName, ResourceId>& id_map,
const std::string& id_map_path) {
- std::ofstream fout(id_map_path, std::ofstream::binary);
- if (!fout) {
- diag->Error(DiagMessage(id_map_path) << strerror(errno));
+ io::FileOutputStream fout(id_map_path);
+ if (fout.HadError()) {
+ diag->Error(DiagMessage(id_map_path) << "failed to open: " << fout.GetError());
return false;
}
+ text::Printer printer(&fout);
for (const auto& entry : id_map) {
const ResourceName& name = entry.first;
const ResourceId& id = entry.second;
- fout << name << " = " << id << "\n";
+ printer.Print(name.to_string());
+ printer.Print(" = ");
+ printer.Println(id.to_string());
}
+ fout.Flush();
- if (!fout) {
- diag->Error(DiagMessage(id_map_path) << "failed writing to file: "
- << android::base::SystemErrorCodeToString(errno));
+ if (fout.HadError()) {
+ diag->Error(DiagMessage(id_map_path) << "failed writing to file: " << fout.GetError());
return false;
}
-
return true;
}
@@ -985,36 +988,35 @@
file::AppendPath(&out_path, "R.java");
- std::ofstream fout(out_path, std::ofstream::binary);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ io::FileOutputStream fout(out_path);
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout.GetError());
return false;
}
- std::unique_ptr<std::ofstream> fout_text;
+ std::unique_ptr<io::FileOutputStream> fout_text;
if (out_text_symbols_path) {
- fout_text =
- util::make_unique<std::ofstream>(out_text_symbols_path.value(), std::ofstream::binary);
- if (!*fout_text) {
- context_->GetDiagnostics()->Error(
- DiagMessage() << "failed writing to '" << out_text_symbols_path.value()
- << "': " << android::base::SystemErrorCodeToString(errno));
+ fout_text = util::make_unique<io::FileOutputStream>(out_text_symbols_path.value());
+ if (fout_text->HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed writing to '" << out_text_symbols_path.value()
+ << "': " << fout_text->GetError());
return false;
}
}
JavaClassGenerator generator(context_, table, java_options);
if (!generator.Generate(package_name_to_generate, out_package, &fout, fout_text.get())) {
- context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.getError());
+ context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.GetError());
return false;
}
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ fout.Flush();
+
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout.GetError());
return false;
}
return true;
@@ -1142,18 +1144,19 @@
file::AppendPath(&out_path, "Manifest.java");
- std::ofstream fout(out_path, std::ofstream::binary);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ io::FileOutputStream fout(out_path);
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path
+ << "': " << fout.GetError());
return false;
}
- if (!ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true, &fout)) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true, &fout);
+ fout.Flush();
+
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout.GetError());
return false;
}
return true;
@@ -1165,19 +1168,19 @@
}
const std::string& out_path = out.value();
- std::ofstream fout(out_path, std::ofstream::binary);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed to open '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ io::FileOutputStream fout(out_path);
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path
+ << "': " << fout.GetError());
return false;
}
- proguard::WriteKeepSet(&fout, keep_set);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ proguard::WriteKeepSet(keep_set, &fout);
+ fout.Flush();
+
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout.GetError());
return false;
}
return true;
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 688b6a7..2bf91a5 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -256,10 +256,8 @@
for (auto& entry : config_sorted_files) {
FileReference* file_ref = entry.second;
- uint32_t compression_flags =
- file_ref->file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
- if (!io::CopyFileToArchive(context_, file_ref->file, *file_ref->path, compression_flags,
- writer)) {
+ if (!io::CopyFileToArchivePreserveCompression(context_, file_ref->file, *file_ref->path,
+ writer)) {
return false;
}
}
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index d39f43e..8b3a670 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -140,12 +140,27 @@
return decl;
}
+// Returns a copy of 'name' which conforms to the regex '[a-zA-Z]+[a-zA-Z0-9_]*' by
+// replacing nonconforming characters with underscores.
+//
+// See frameworks/base/core/java/android/content/pm/PackageParser.java which
+// checks this at runtime.
static std::string MakePackageSafeName(const std::string &name) {
std::string result(name);
+ bool first = true;
for (char &c : result) {
- if (c == '-') {
- c = '_';
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ first = false;
+ continue;
}
+ if (!first) {
+ if (c >= '0' && c <= '9') {
+ continue;
+ }
+ }
+
+ c = '_';
+ first = false;
}
return result;
}
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 9c33135..0c527f6 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -24,15 +24,16 @@
TEST(UtilTest, SplitNamesAreSanitized) {
AppInfo app_info{"com.pkg"};
- SplitConstraints split_constraints{{test::ParseConfigOrDie("en-rUS-land")}};
+ SplitConstraints split_constraints{
+ {test::ParseConfigOrDie("en-rUS-land"), test::ParseConfigOrDie("b+sr+Latn")}};
const auto doc = GenerateSplitManifest(app_info, split_constraints);
const auto &root = doc->root;
EXPECT_EQ(root->name, "manifest");
- // split names cannot contain hyphens
- EXPECT_EQ(root->FindAttribute("", "split")->value, "config.en_rUS_land");
+ // split names cannot contain hyphens or plus signs.
+ EXPECT_EQ(root->FindAttribute("", "split")->value, "config.b_sr_Latn_en_rUS_land");
// but we should use resource qualifiers verbatim in 'targetConfig'.
- EXPECT_EQ(root->FindAttribute("", "targetConfig")->value, "en-rUS-land");
+ EXPECT_EQ(root->FindAttribute("", "targetConfig")->value, "b+sr+Latn,en-rUS-land");
}
} // namespace aapt
diff --git a/tools/aapt2/io/FileStream.cpp b/tools/aapt2/io/FileStream.cpp
index 4ff6d78..27529bc 100644
--- a/tools/aapt2/io/FileStream.cpp
+++ b/tools/aapt2/io/FileStream.cpp
@@ -25,6 +25,12 @@
#include "android-base/macros.h"
#include "android-base/utf8.h"
+#if defined(_WIN32)
+// This is only needed for O_CLOEXEC.
+#include <windows.h>
+#define O_CLOEXEC O_NOINHERIT
+#endif
+
using ::android::base::SystemErrorCodeToString;
using ::android::base::unique_fd;
@@ -32,18 +38,20 @@
namespace io {
FileInputStream::FileInputStream(const std::string& path, size_t buffer_capacity)
- : FileInputStream(::android::base::utf8::open(path.c_str(), O_RDONLY | O_BINARY),
- buffer_capacity) {
+ : buffer_capacity_(buffer_capacity) {
+ int mode = O_RDONLY | O_CLOEXEC | O_BINARY;
+ fd_.reset(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), mode)));
+ if (fd_ == -1) {
+ error_ = SystemErrorCodeToString(errno);
+ } else {
+ buffer_.reset(new uint8_t[buffer_capacity_]);
+ }
}
FileInputStream::FileInputStream(int fd, size_t buffer_capacity)
- : fd_(fd),
- buffer_capacity_(buffer_capacity),
- buffer_offset_(0u),
- buffer_size_(0u),
- total_byte_count_(0u) {
- if (fd_ == -1) {
- error_ = SystemErrorCodeToString(errno);
+ : fd_(fd), buffer_capacity_(buffer_capacity) {
+ if (fd_ < 0) {
+ error_ = "Bad File Descriptor";
} else {
buffer_.reset(new uint8_t[buffer_capacity_]);
}
@@ -100,9 +108,16 @@
return error_;
}
-FileOutputStream::FileOutputStream(const std::string& path, int mode, size_t buffer_capacity)
- : FileOutputStream(unique_fd(::android::base::utf8::open(path.c_str(), mode)),
- buffer_capacity) {
+FileOutputStream::FileOutputStream(const std::string& path, size_t buffer_capacity)
+ : buffer_capacity_(buffer_capacity) {
+ int mode = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY;
+ owned_fd_.reset(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), mode, 0666)));
+ fd_ = owned_fd_.get();
+ if (fd_ < 0) {
+ error_ = SystemErrorCodeToString(errno);
+ } else {
+ buffer_.reset(new uint8_t[buffer_capacity_]);
+ }
}
FileOutputStream::FileOutputStream(unique_fd fd, size_t buffer_capacity)
@@ -111,9 +126,9 @@
}
FileOutputStream::FileOutputStream(int fd, size_t buffer_capacity)
- : fd_(fd), buffer_capacity_(buffer_capacity), buffer_offset_(0u), total_byte_count_(0u) {
- if (fd_ == -1) {
- error_ = SystemErrorCodeToString(errno);
+ : fd_(fd), buffer_capacity_(buffer_capacity) {
+ if (fd_ < 0) {
+ error_ = "Bad File Descriptor";
} else {
buffer_.reset(new uint8_t[buffer_capacity_]);
}
diff --git a/tools/aapt2/io/FileStream.h b/tools/aapt2/io/FileStream.h
index 4ed1ad5..62d910f 100644
--- a/tools/aapt2/io/FileStream.h
+++ b/tools/aapt2/io/FileStream.h
@@ -22,7 +22,6 @@
#include <memory>
#include <string>
-#include "android-base/file.h" // for O_BINARY
#include "android-base/macros.h"
#include "android-base/unique_fd.h"
@@ -55,15 +54,15 @@
android::base::unique_fd fd_;
std::string error_;
std::unique_ptr<uint8_t[]> buffer_;
- size_t buffer_capacity_;
- size_t buffer_offset_;
- size_t buffer_size_;
- size_t total_byte_count_;
+ size_t buffer_capacity_ = 0u;
+ size_t buffer_offset_ = 0u;
+ size_t buffer_size_ = 0u;
+ size_t total_byte_count_ = 0u;
};
class FileOutputStream : public OutputStream {
public:
- explicit FileOutputStream(const std::string& path, int mode = O_RDWR | O_CREAT | O_BINARY,
+ explicit FileOutputStream(const std::string& path,
size_t buffer_capacity = kDefaultBufferCapacity);
// Does not take ownership of `fd`.
@@ -97,9 +96,9 @@
int fd_;
std::string error_;
std::unique_ptr<uint8_t[]> buffer_;
- size_t buffer_capacity_;
- size_t buffer_offset_;
- size_t total_byte_count_;
+ size_t buffer_capacity_ = 0u;
+ size_t buffer_offset_ = 0u;
+ size_t total_byte_count_ = 0u;
};
} // namespace io
diff --git a/tools/aapt2/io/FileStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp
index a6d58ca..c0eaa8e 100644
--- a/tools/aapt2/io/FileStream_test.cpp
+++ b/tools/aapt2/io/FileStream_test.cpp
@@ -16,6 +16,7 @@
#include "io/FileStream.h"
+#include "android-base/file.h"
#include "android-base/macros.h"
#include "android-base/test_utils.h"
diff --git a/tools/aapt2/io/Util.cpp b/tools/aapt2/io/Util.cpp
index 7ee1016..9751632 100644
--- a/tools/aapt2/io/Util.cpp
+++ b/tools/aapt2/io/Util.cpp
@@ -48,6 +48,12 @@
return CopyInputStreamToArchive(context, data.get(), out_path, compression_flags, writer);
}
+bool CopyFileToArchivePreserveCompression(IAaptContext* context, io::IFile* file,
+ const std::string& out_path, IArchiveWriter* writer) {
+ uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
+ return CopyFileToArchive(context, file, out_path, compression_flags, writer);
+}
+
bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* proto_msg,
const std::string& out_path, uint32_t compression_flags,
IArchiveWriter* writer) {
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index de2ab39..b07fb53 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -35,6 +35,9 @@
bool CopyFileToArchive(IAaptContext* context, IFile* file, const std::string& out_path,
uint32_t compression_flags, IArchiveWriter* writer);
+bool CopyFileToArchivePreserveCompression(IAaptContext* context, IFile* file,
+ const std::string& out_path, IArchiveWriter* writer);
+
bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* proto_msg,
const std::string& out_path, uint32_t compression_flags,
IArchiveWriter* writer);
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index c93461a..8d91b00 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -23,6 +23,7 @@
#include "text/Utf8Iterator.h"
#include "util/Util.h"
+using ::aapt::text::Printer;
using ::aapt::text::Utf8Iterator;
using ::android::StringPiece;
@@ -109,23 +110,22 @@
}
}
-void AnnotationProcessor::WriteToStream(const StringPiece& prefix, std::ostream* out) const {
+void AnnotationProcessor::Print(Printer* printer) const {
if (has_comments_) {
std::string result = comment_.str();
for (StringPiece line : util::Tokenize(result, '\n')) {
- *out << prefix << line << "\n";
+ printer->Println(line);
}
- *out << prefix << " */"
- << "\n";
+ printer->Println(" */");
}
if (annotation_bit_mask_ & AnnotationRule::kDeprecated) {
- *out << prefix << "@Deprecated\n";
+ printer->Println("@Deprecated");
}
for (const AnnotationRule& rule : sAnnotationRules) {
if (annotation_bit_mask_ & rule.bit_mask) {
- *out << prefix << rule.annotation << "\n";
+ printer->Println(rule.annotation);
}
}
}
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index a7bf73f..ae7bdb0 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -22,6 +22,8 @@
#include "androidfw/StringPiece.h"
+#include "text/Printer.h"
+
namespace aapt {
// Builds a JavaDoc comment from a set of XML comments.
@@ -61,8 +63,8 @@
void AppendNewLine();
- // Writes the comments and annotations to the stream, with the given prefix before each line.
- void WriteToStream(const android::StringPiece& prefix, std::ostream* out) const;
+ // Writes the comments and annotations to the Printer.
+ void Print(text::Printer* printer) const;
private:
std::stringstream comment_;
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index 856f4cc..69f49c8 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -16,8 +16,12 @@
#include "java/AnnotationProcessor.h"
+#include "io/StringStream.h"
#include "test/Test.h"
+#include "text/Printer.h"
+using ::aapt::io::StringOutputStream;
+using ::aapt::text::Printer;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::Not;
@@ -33,9 +37,11 @@
AnnotationProcessor processor;
processor.AppendComment(comment);
- std::stringstream result;
- processor.WriteToStream("", &result);
- std::string annotations = result.str();
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
EXPECT_THAT(annotations, HasSubstr("@Deprecated"));
}
@@ -44,9 +50,11 @@
AnnotationProcessor processor;
processor.AppendComment("@SystemApi This is a system API");
- std::stringstream result;
- processor.WriteToStream("", &result);
- std::string annotations = result.str();
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
EXPECT_THAT(annotations, HasSubstr("@android.annotation.SystemApi"));
EXPECT_THAT(annotations, Not(HasSubstr("@SystemApi")));
@@ -57,9 +65,11 @@
AnnotationProcessor processor;
processor.AppendComment("@TestApi This is a test API");
- std::stringstream result;
- processor.WriteToStream("", &result);
- std::string annotations = result.str();
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
EXPECT_THAT(annotations, HasSubstr("@android.annotation.TestApi"));
EXPECT_THAT(annotations, Not(HasSubstr("@TestApi")));
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index 0c57e7e..b692ccf 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -18,25 +18,27 @@
#include "androidfw/StringPiece.h"
+using ::aapt::text::Printer;
using ::android::StringPiece;
namespace aapt {
-void ClassMember::WriteToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
- processor_.WriteToStream(prefix, out);
+void ClassMember::Print(bool /*final*/, Printer* printer) const {
+ processor_.Print(printer);
}
void MethodDefinition::AppendStatement(const StringPiece& statement) {
statements_.push_back(statement.to_string());
}
-void MethodDefinition::WriteToStream(const StringPiece& prefix, bool final,
- std::ostream* out) const {
- *out << prefix << signature_ << " {\n";
+void MethodDefinition::Print(bool final, Printer* printer) const {
+ printer->Print(signature_).Println(" {");
+ printer->Indent();
for (const auto& statement : statements_) {
- *out << prefix << " " << statement << "\n";
+ printer->Println(statement);
}
- *out << prefix << "}";
+ printer->Undent();
+ printer->Print("}");
}
ClassDefinition::Result ClassDefinition::AddMember(std::unique_ptr<ClassMember> member) {
@@ -62,34 +64,32 @@
return true;
}
-void ClassDefinition::WriteToStream(const StringPiece& prefix, bool final,
- std::ostream* out) const {
+void ClassDefinition::Print(bool final, Printer* printer) const {
if (empty() && !create_if_empty_) {
return;
}
- ClassMember::WriteToStream(prefix, final, out);
+ ClassMember::Print(final, printer);
- *out << prefix << "public ";
+ printer->Print("public ");
if (qualifier_ == ClassQualifier::kStatic) {
- *out << "static ";
+ printer->Print("static ");
}
- *out << "final class " << name_ << " {\n";
-
- std::string new_prefix = prefix.to_string();
- new_prefix.append(kIndent);
+ printer->Print("final class ").Print(name_).Println(" {");
+ printer->Indent();
for (const std::unique_ptr<ClassMember>& member : ordered_members_) {
// There can be nullptr members when a member is added to the ClassDefinition
// and takes precedence over a previous member with the same name. The overridden member is
// set to nullptr.
if (member != nullptr) {
- member->WriteToStream(new_prefix, final, out);
- *out << "\n";
+ member->Print(final, printer);
+ printer->Println();
}
}
- *out << prefix << "}";
+ printer->Undent();
+ printer->Print("}");
}
constexpr static const char* sWarningHeader =
@@ -100,11 +100,12 @@
" * should not be modified by hand.\n"
" */\n\n";
-bool ClassDefinition::WriteJavaFile(const ClassDefinition* def, const StringPiece& package,
- bool final, std::ostream* out) {
- *out << sWarningHeader << "package " << package << ";\n\n";
- def->WriteToStream("", final, out);
- return bool(*out);
+void ClassDefinition::WriteJavaFile(const ClassDefinition* def, const StringPiece& package,
+ bool final, io::OutputStream* out) {
+ Printer printer(out);
+ printer.Print(sWarningHeader).Print("package ").Print(package).Println(";");
+ printer.Println();
+ def->Print(final, &printer);
}
} // namespace aapt
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 28a3489..fb11266 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -17,7 +17,6 @@
#ifndef AAPT_JAVA_CLASSDEFINITION_H
#define AAPT_JAVA_CLASSDEFINITION_H
-#include <ostream>
#include <string>
#include <unordered_map>
#include <vector>
@@ -27,6 +26,7 @@
#include "Resource.h"
#include "java/AnnotationProcessor.h"
+#include "text/Printer.h"
#include "util/Util.h"
namespace aapt {
@@ -47,11 +47,10 @@
virtual const std::string& GetName() const = 0;
- // Writes the class member to the out stream. Subclasses should derive this method
+ // Writes the class member to the Printer. Subclasses should derive this method
// to write their own data. Call this base method from the subclass to write out
// this member's comments/annotations.
- virtual void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const;
+ virtual void Print(bool final, text::Printer* printer) const;
private:
AnnotationProcessor processor_;
@@ -71,11 +70,16 @@
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override {
- ClassMember::WriteToStream(prefix, final, out);
- *out << prefix << "public static " << (final ? "final " : "") << "int " << name_ << "=" << val_
- << ";";
+ void Print(bool final, text::Printer* printer) const override {
+ using std::to_string;
+
+ ClassMember::Print(final, printer);
+
+ printer->Print("public static ");
+ if (final) {
+ printer->Print("final ");
+ }
+ printer->Print("int ").Print(name_).Print("=").Print(to_string(val_)).Print(";");
}
private:
@@ -100,12 +104,14 @@
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override {
- ClassMember::WriteToStream(prefix, final, out);
+ void Print(bool final, text::Printer* printer) const override {
+ ClassMember::Print(final, printer);
- *out << prefix << "public static " << (final ? "final " : "") << "String "
- << name_ << "=\"" << val_ << "\";";
+ printer->Print("public static ");
+ if (final) {
+ printer->Print("final ");
+ }
+ printer->Print("String ").Print(name_).Print("=\"").Print(val_).Print("\";");
}
private:
@@ -136,25 +142,27 @@
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override {
- ClassMember::WriteToStream(prefix, final, out);
+ void Print(bool final, text::Printer* printer) const override {
+ ClassMember::Print(final, printer);
- *out << prefix << "public static final int[] " << name_ << "={";
+ printer->Print("public static final int[] ").Print(name_).Print("={");
+ printer->Indent();
const auto begin = elements_.begin();
const auto end = elements_.end();
for (auto current = begin; current != end; ++current) {
if (std::distance(begin, current) % kAttribsPerLine == 0) {
- *out << "\n" << prefix << kIndent << kIndent;
+ printer->Println();
}
- *out << *current;
+ printer->Print(to_string(*current));
if (std::distance(current, end) > 1) {
- *out << ", ";
+ printer->Print(", ");
}
}
- *out << "\n" << prefix << kIndent << "};";
+ printer->Println();
+ printer->Undent();
+ printer->Print("};");
}
private:
@@ -187,8 +195,7 @@
return false;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override;
+ void Print(bool final, text::Printer* printer) const override;
private:
DISALLOW_COPY_AND_ASSIGN(MethodDefinition);
@@ -201,8 +208,8 @@
class ClassDefinition : public ClassMember {
public:
- static bool WriteJavaFile(const ClassDefinition* def, const android::StringPiece& package,
- bool final, std::ostream* out);
+ static void WriteJavaFile(const ClassDefinition* def, const android::StringPiece& package,
+ bool final, io::OutputStream* out);
ClassDefinition(const android::StringPiece& name, ClassQualifier qualifier, bool createIfEmpty)
: name_(name.to_string()), qualifier_(qualifier), create_if_empty_(createIfEmpty) {}
@@ -220,8 +227,7 @@
return name_;
}
- void WriteToStream(const android::StringPiece& prefix, bool final,
- std::ostream* out) const override;
+ void Print(bool final, text::Printer* printer) const override;
private:
DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 91cef64..9861770 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -37,8 +37,10 @@
#include "java/ClassDefinition.h"
#include "process/SymbolTable.h"
-using android::StringPiece;
-using android::base::StringPrintf;
+using ::aapt::io::OutputStream;
+using ::aapt::text::Printer;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
@@ -230,7 +232,7 @@
const StringPiece& package_name_to_generate,
ClassDefinition* out_class_def,
MethodDefinition* out_rewrite_method,
- std::ostream* out_r_txt) {
+ Printer* r_txt_printer) {
const std::string array_field_name = TransformToFieldName(name.entry);
std::unique_ptr<ResourceArrayMember> array_def =
util::make_unique<ResourceArrayMember>(array_field_name);
@@ -323,8 +325,8 @@
array_def->GetCommentBuilder()->AppendComment(styleable_comment.str());
}
- if (out_r_txt != nullptr) {
- *out_r_txt << "int[] styleable " << array_field_name << " {";
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Print("int[] styleable ").Print(array_field_name).Print(" {");
}
// Add the ResourceIds to the array member.
@@ -332,16 +334,16 @@
const ResourceId id = sorted_attributes[i].attr_ref->id.value_or_default(ResourceId(0));
array_def->AddElement(id);
- if (out_r_txt != nullptr) {
+ if (r_txt_printer != nullptr) {
if (i != 0) {
- *out_r_txt << ",";
+ r_txt_printer->Print(",");
}
- *out_r_txt << " " << id;
+ r_txt_printer->Print(" ").Print(id.to_string());
}
}
- if (out_r_txt != nullptr) {
- *out_r_txt << " }\n";
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Println(" }");
}
// Add the Styleable array to the Styleable class.
@@ -396,9 +398,9 @@
attr_processor->AppendComment(
StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
- if (out_r_txt != nullptr) {
- *out_r_txt << StringPrintf("int styleable %s %d\n", sorted_attributes[i].field_name.data(),
- (int)i);
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Println(
+ StringPrintf("int styleable %s %zd", sorted_attributes[i].field_name.c_str(), i));
}
out_class_def->AddMember(std::move(index_member));
@@ -422,10 +424,12 @@
void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
const ResourceEntry& entry, ClassDefinition* out_class_def,
MethodDefinition* out_rewrite_method,
- std::ostream* out_r_txt) {
+ text::Printer* r_txt_printer) {
ResourceId real_id = id;
if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
id.package_id() > kAppPackageId) {
+ // Workaround for feature splits using package IDs > 0x7F.
+ // See b/37498913.
real_id = ResourceId(kAppPackageId, id.package_id(), id.entry_id());
}
@@ -456,8 +460,13 @@
out_class_def->AddMember(std::move(resource_member));
- if (out_r_txt != nullptr) {
- *out_r_txt << "int " << name.type << " " << field_name << " " << real_id << "\n";
+ if (r_txt_printer != nullptr) {
+ r_txt_printer->Print("int ")
+ .Print(to_string(name.type))
+ .Print(" ")
+ .Print(field_name)
+ .Print(" ")
+ .Println(real_id.to_string());
}
if (out_rewrite_method != nullptr) {
@@ -497,7 +506,7 @@
const ResourceTableType& type,
ClassDefinition* out_type_class_def,
MethodDefinition* out_rewrite_method_def,
- std::ostream* out_r_txt) {
+ Printer* r_txt_printer) {
for (const auto& entry : type.entries) {
const Maybe<std::string> unmangled_name =
UnmangleResource(package.name, package_name_to_generate, *entry);
@@ -532,18 +541,18 @@
static_cast<const Styleable*>(entry->values.front()->value.get());
ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
- out_rewrite_method_def, out_r_txt);
+ out_rewrite_method_def, r_txt_printer);
} else {
ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def,
- out_r_txt);
+ r_txt_printer);
}
}
return true;
}
-bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, std::ostream* out,
- std::ostream* out_r_txt) {
- return Generate(package_name_to_generate, package_name_to_generate, out);
+bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, OutputStream* out,
+ OutputStream* out_r_txt) {
+ return Generate(package_name_to_generate, package_name_to_generate, out, out_r_txt);
}
static void AppendJavaDocAnnotations(const std::vector<std::string>& annotations,
@@ -556,11 +565,16 @@
}
bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
- const StringPiece& out_package_name, std::ostream* out,
- std::ostream* out_r_txt) {
+ const StringPiece& out_package_name, OutputStream* out,
+ OutputStream* out_r_txt) {
ClassDefinition r_class("R", ClassQualifier::kNone, true);
std::unique_ptr<MethodDefinition> rewrite_method;
+ std::unique_ptr<Printer> r_txt_printer;
+ if (out_r_txt != nullptr) {
+ r_txt_printer = util::make_unique<Printer>(out_r_txt);
+ }
+
// Generate an onResourcesLoaded() callback if requested.
if (options_.rewrite_callback_options) {
rewrite_method =
@@ -586,7 +600,7 @@
std::unique_ptr<ClassDefinition> class_def = util::make_unique<ClassDefinition>(
to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty);
if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
- rewrite_method.get(), out_r_txt)) {
+ rewrite_method.get(), r_txt_printer.get())) {
return false;
}
@@ -595,7 +609,7 @@
const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
if (priv_type) {
if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
- rewrite_method.get(), out_r_txt)) {
+ rewrite_method.get(), r_txt_printer.get())) {
return false;
}
}
@@ -619,22 +633,7 @@
}
AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
-
- if (!ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out)) {
- return false;
- }
-
- out->flush();
-
- if (out_r_txt != nullptr) {
- out_r_txt->flush();
-
- if (!*out_r_txt) {
- error_ = android::base::SystemErrorCodeToString(errno);
- return false;
- }
- }
-
+ ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out);
return true;
}
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 2541749..4992f07 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -17,16 +17,16 @@
#ifndef AAPT_JAVA_CLASS_GENERATOR_H
#define AAPT_JAVA_CLASS_GENERATOR_H
-#include <ostream>
#include <string>
#include "androidfw/StringPiece.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "androidfw/StringPiece.h"
+#include "io/Io.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
+#include "text/Printer.h"
namespace aapt {
@@ -70,14 +70,14 @@
// All symbols technically belong to a single package, but linked libraries will
// have their names mangled, denoting that they came from a different package.
// We need to generate these symbols in a separate file. Returns true on success.
- bool Generate(const android::StringPiece& package_name_to_generate, std::ostream* out,
- std::ostream* out_r_txt = nullptr);
+ bool Generate(const android::StringPiece& package_name_to_generate, io::OutputStream* out,
+ io::OutputStream* out_r_txt = nullptr);
bool Generate(const android::StringPiece& package_name_to_generate,
- const android::StringPiece& output_package_name, std::ostream* out,
- std::ostream* out_r_txt = nullptr);
+ const android::StringPiece& output_package_name, io::OutputStream* out,
+ io::OutputStream* out_r_txt = nullptr);
- const std::string& getError() const;
+ const std::string& GetError() const;
static std::string TransformToFieldName(const android::StringPiece& symbol);
@@ -94,13 +94,13 @@
bool ProcessType(const android::StringPiece& package_name_to_generate,
const ResourceTablePackage& package, const ResourceTableType& type,
ClassDefinition* out_type_class_def, MethodDefinition* out_rewrite_method_def,
- std::ostream* out_r_txt);
+ text::Printer* r_txt_printer);
// Writes a resource to the R.java file, optionally writing out a rewrite rule for its package
// ID if `out_rewrite_method` is not nullptr.
void ProcessResource(const ResourceNameRef& name, const ResourceId& id,
const ResourceEntry& entry, ClassDefinition* out_class_def,
- MethodDefinition* out_rewrite_method, std::ostream* out_r_txt);
+ MethodDefinition* out_rewrite_method, text::Printer* r_txt_printer);
// Writes a styleable resource to the R.java file, optionally writing out a rewrite rule for
// its package ID if `out_rewrite_method` is not nullptr.
@@ -109,7 +109,7 @@
const Styleable& styleable,
const android::StringPiece& package_name_to_generate,
ClassDefinition* out_class_def, MethodDefinition* out_rewrite_method,
- std::ostream* out_r_txt);
+ text::Printer* r_txt_printer);
IAaptContext* context_;
ResourceTable* table_;
@@ -117,7 +117,7 @@
std::string error_;
};
-inline const std::string& JavaClassGenerator::getError() const {
+inline const std::string& JavaClassGenerator::GetError() const {
return error_;
}
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 668e434..02f4cb1 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -16,12 +16,13 @@
#include "java/JavaClassGenerator.h"
-#include <sstream>
#include <string>
+#include "io/StringStream.h"
#include "test/Test.h"
#include "util/Util.h"
+using ::aapt::io::StringOutputStream;
using ::android::StringPiece;
using ::testing::HasSubstr;
using ::testing::Lt;
@@ -45,7 +46,8 @@
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+ std::string result;
+ StringOutputStream out(&result);
EXPECT_FALSE(generator.Generate("android", &out));
}
@@ -69,10 +71,10 @@
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
EXPECT_TRUE(generator.Generate("android", &out));
-
- std::string output = out.str();
+ out.Flush();
EXPECT_THAT(output, HasSubstr("public static final int hey_man=0x01020000;"));
EXPECT_THAT(output, HasSubstr("public static final int[] hey_dude={"));
@@ -93,10 +95,12 @@
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
- ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
- std::string output = out.str();
+ std::string output;
+ StringOutputStream out(&output);
+ ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
+ out.Flush();
+
EXPECT_THAT(output, HasSubstr("package com.android.internal;"));
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, Not(HasSubstr("two")));
@@ -117,10 +121,12 @@
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
- ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ std::string output;
+ StringOutputStream out(&output);
+ ASSERT_TRUE(generator.Generate("android", &out));
+ out.Flush();
+
EXPECT_THAT(output, HasSubstr("public static final class attr"));
EXPECT_THAT(output, Not(HasSubstr("public static final class ^attr-private")));
}
@@ -147,9 +153,11 @@
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
{
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
+
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, Not(HasSubstr("two")));
EXPECT_THAT(output, Not(HasSubstr("three")));
@@ -158,9 +166,11 @@
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
{
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
+
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
EXPECT_THAT(output, Not(HasSubstr("three")));
@@ -169,9 +179,11 @@
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
{
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
+
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
EXPECT_THAT(output, HasSubstr("public static final int three=0x01020002;"));
@@ -235,10 +247,11 @@
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
EXPECT_TRUE(generator.Generate("android", &out));
+ out.Flush();
- std::string output = out.str();
EXPECT_THAT(output, HasSubstr("int foo_bar="));
EXPECT_THAT(output, HasSubstr("int foo_com_lib_bar="));
}
@@ -258,9 +271,11 @@
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
const char* expected_text =
R"EOF(/**
@@ -298,9 +313,11 @@
JavaClassGeneratorOptions options;
options.use_final = false;
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
EXPECT_THAT(output, HasSubstr("attr name android:one"));
EXPECT_THAT(output, HasSubstr("attr description"));
@@ -332,9 +349,11 @@
JavaClassGeneratorOptions options;
JavaClassGenerator generator(context.get(), table.get(), {});
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
std::string::size_type actionbar_pos = output.find("int[] ActionBar");
ASSERT_THAT(actionbar_pos, Ne(std::string::npos));
@@ -373,9 +392,11 @@
JavaClassGeneratorOptions options;
options.use_final = false;
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
- std::string output = out.str();
+ out.Flush();
EXPECT_THAT(output, Not(HasSubstr("@attr name android:one")));
EXPECT_THAT(output, Not(HasSubstr("@attr description")));
@@ -409,10 +430,10 @@
options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{{"com.foo", "com.boo"}};
JavaClassGenerator generator(context.get(), table.get(), options);
- std::stringstream out;
+ std::string output;
+ StringOutputStream out(&output);
ASSERT_TRUE(generator.Generate("android", &out));
-
- std::string output = out.str();
+ out.Flush();
EXPECT_THAT(output, HasSubstr("void onResourcesLoaded"));
EXPECT_THAT(output, HasSubstr("com.foo.R.onResourcesLoaded"));
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
index b12202a..3f6645f 100644
--- a/tools/aapt2/java/ManifestClassGenerator.h
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -23,8 +23,7 @@
namespace aapt {
-std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
- xml::XmlResource* res);
+std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag, xml::XmlResource* res);
} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index ada5634..c324238 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -16,8 +16,10 @@
#include "java/ManifestClassGenerator.h"
+#include "io/StringStream.h"
#include "test/Test.h"
+using ::aapt::io::StringOutputStream;
using ::testing::HasSubstr;
using ::testing::Not;
@@ -144,12 +146,9 @@
return ::testing::AssertionFailure() << "manifest_class == nullptr";
}
- std::stringstream out;
- if (!manifest_class->WriteJavaFile(manifest_class.get(), "android", true, &out)) {
- return ::testing::AssertionFailure() << "failed to write java file";
- }
-
- *out_str = out.str();
+ StringOutputStream out(out_str);
+ manifest_class->WriteJavaFile(manifest_class.get(), "android", true, &out);
+ out.Flush();
return ::testing::AssertionSuccess();
}
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index b9ae654..132b234 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -20,14 +20,18 @@
#include <string>
#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
#include "JavaClassGenerator.h"
#include "ResourceUtils.h"
#include "ValueVisitor.h"
-#include "androidfw/StringPiece.h"
+#include "text/Printer.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
+using ::aapt::io::OutputStream;
+using ::aapt::text::Printer;
+
namespace aapt {
namespace proguard {
@@ -326,12 +330,13 @@
return true;
}
-bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set) {
+void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) {
+ Printer printer(out);
for (const auto& entry : keep_set.manifest_class_set_) {
for (const UsageLocation& location : entry.second) {
- *out << "# Referenced at " << location.source << "\n";
+ printer.Print("# Referenced at ").Println(location.source.to_string());
}
- *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+ printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
}
for (const auto& entry : keep_set.conditional_class_set_) {
@@ -342,27 +347,31 @@
}
for (const UsageLocation& location : entry.second) {
- *out << "# Referenced at " << location.source << "\n";
+ printer.Print("# Referenced at ").Println(location.source.to_string());
}
if (keep_set.conditional_keep_rules_ && can_be_conditional) {
- *out << "-keep class " << entry.first << " {\n ifused class **.R$layout {\n";
+ printer.Println("-if class **.R$layout {");
+ printer.Indent();
for (const UsageLocation& location : locations) {
- auto transformed_name = JavaClassGenerator::TransformToFieldName(location.name.entry);
- *out << " int " << transformed_name << ";\n";
+ printer.Print("int ")
+ .Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
+ .Println(";");
}
- *out << " };\n <init>(...);\n}\n" << std::endl;
- } else {
- *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+ printer.Undent();
+ printer.Println("}");
+ printer.Println();
}
+ printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
+ printer.Println();
}
for (const auto& entry : keep_set.method_set_) {
for (const UsageLocation& location : entry.second) {
- *out << "# Referenced at " << location.source << "\n";
+ printer.Print("# Referenced at ").Println(location.source.to_string());
}
- *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
+ printer.Print("-keepclassmembers class * { *** ").Print(entry.first).Println("(...); }");
+ printer.Println();
}
- return true;
}
bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 8dbe3c2..46827ee 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -22,11 +22,13 @@
#include <set>
#include <string>
+#include "androidfw/StringPiece.h"
+
#include "Resource.h"
#include "ResourceTable.h"
#include "Source.h"
#include "ValueVisitor.h"
-#include "androidfw/StringPiece.h"
+#include "io/Io.h"
#include "process/IResourceTableConsumer.h"
#include "xml/XmlDom.h"
@@ -62,7 +64,7 @@
}
private:
- friend bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);
+ friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
std::set<UsageLocation>* locations);
@@ -76,11 +78,12 @@
bool CollectProguardRulesForManifest(xml::XmlResource* res, KeepSet* keep_set,
bool main_dex_only = false);
-bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set);
-bool CollectResourceReferences(aapt::IAaptContext* context, ResourceTable* table,
- KeepSet* keep_set);
-bool WriteKeepSet(std::ostream* out, const KeepSet& keep_set);
+bool CollectProguardRules(xml::XmlResource* res, KeepSet* keep_set);
+
+bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
+
+void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
std::set<UsageLocation>* locations);
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index df3ac8b..37d1a5f 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -17,13 +17,23 @@
#include "java/ProguardRules.h"
#include "link/Linkers.h"
+#include "io/StringStream.h"
#include "test/Test.h"
+using ::aapt::io::StringOutputStream;
using ::testing::HasSubstr;
using ::testing::Not;
namespace aapt {
+std::string GetKeepSetString(const proguard::KeepSet& set) {
+ std::string out;
+ StringOutputStream sout(&out);
+ proguard::WriteKeepSet(set, &sout);
+ sout.Flush();
+ return out;
+}
+
TEST(ProguardRulesTest, FragmentNameRuleIsEmitted) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> layout = test::BuildXmlDom(R"(
@@ -34,10 +44,8 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
}
@@ -50,10 +58,8 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
}
@@ -68,10 +74,8 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
}
@@ -87,10 +91,8 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
}
@@ -126,11 +128,10 @@
ASSERT_TRUE(proguard::CollectProguardRules(bar_layout.get(), &set));
ASSERT_TRUE(proguard::CollectProguardRules(foo_layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
- EXPECT_THAT(actual, HasSubstr("ifused class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
@@ -148,11 +149,10 @@
set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
- EXPECT_THAT(actual, HasSubstr("ifused class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
@@ -170,11 +170,10 @@
set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
- EXPECT_THAT(actual, Not(HasSubstr("ifused")));
+ EXPECT_THAT(actual, Not(HasSubstr("-if")));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
}
TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
@@ -187,10 +186,8 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(layout.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("bar_method"));
}
@@ -208,10 +205,8 @@
proguard::KeepSet set;
ASSERT_TRUE(proguard::CollectProguardRules(menu.get(), &set));
- std::stringstream out;
- ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
+ std::string actual = GetKeepSetString(set);
- std::string actual = out.str();
EXPECT_THAT(actual, HasSubstr("on_click"));
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Baz"));
diff --git a/tools/aapt2/text/Printer.cpp b/tools/aapt2/text/Printer.cpp
index 38b3585..243800c 100644
--- a/tools/aapt2/text/Printer.cpp
+++ b/tools/aapt2/text/Printer.cpp
@@ -26,18 +26,18 @@
namespace aapt {
namespace text {
-void Printer::Println(const StringPiece& str) {
+Printer& Printer::Println(const StringPiece& str) {
Print(str);
- Print("\n");
+ return Print("\n");
}
-void Printer::Println() {
- Print("\n");
+Printer& Printer::Println() {
+ return Print("\n");
}
-void Printer::Print(const StringPiece& str) {
+Printer& Printer::Print(const StringPiece& str) {
if (error_) {
- return;
+ return *this;
}
auto remaining_str_begin = str.begin();
@@ -53,7 +53,7 @@
for (int i = 0; i < indent_level_; i++) {
if (!io::Copy(out_, " ")) {
error_ = true;
- return;
+ return *this;
}
}
needs_indent_ = false;
@@ -61,7 +61,7 @@
if (!io::Copy(out_, str_to_copy)) {
error_ = true;
- return;
+ return *this;
}
}
@@ -69,7 +69,7 @@
if (new_line_iter != remaining_str_end) {
if (!io::Copy(out_, "\n")) {
error_ = true;
- return;
+ return *this;
}
needs_indent_ = true;
// Ok to increment iterator here because we know that the '\n' character is one byte.
@@ -78,6 +78,7 @@
remaining_str_begin = new_line_iter;
}
}
+ return *this;
}
void Printer::Indent() {
diff --git a/tools/aapt2/text/Printer.h b/tools/aapt2/text/Printer.h
index 94b3c0b..f399f8e 100644
--- a/tools/aapt2/text/Printer.h
+++ b/tools/aapt2/text/Printer.h
@@ -31,9 +31,9 @@
explicit Printer(::aapt::io::OutputStream* out) : out_(out) {
}
- void Print(const ::android::StringPiece& str);
- void Println(const ::android::StringPiece& str);
- void Println();
+ Printer& Print(const ::android::StringPiece& str);
+ Printer& Println(const ::android::StringPiece& str);
+ Printer& Println();
void Indent();
void Undent();
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 2eab22e..fd42033 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -72,6 +72,9 @@
self.ident = self.raw.replace(" deprecated ", " ")
+ def __hash__(self):
+ return hash(self.raw)
+
def __repr__(self):
return self.raw
@@ -110,6 +113,9 @@
ident = ident[:ident.index(" throws ")]
self.ident = ident
+ def __hash__(self):
+ return hash(self.raw)
+
def __repr__(self):
return self.raw
@@ -145,6 +151,9 @@
self.name = self.fullname[self.fullname.rindex(".")+1:]
+ def __hash__(self):
+ return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods)))
+
def __repr__(self):
return self.raw
@@ -256,6 +265,14 @@
_fail(clazz, detail, True, rule, msg)
+noticed = {}
+
+def notice(clazz):
+ global noticed
+
+ noticed[clazz.fullname] = hash(clazz)
+
+
def verify_constants(clazz):
"""All static final constants must be FOO_NAME style."""
if re.match("android\.R\.[a-z]+", clazz.fullname): return
@@ -1203,6 +1220,9 @@
def examine_clazz(clazz):
"""Find all style issues in the given class."""
+
+ notice(clazz)
+
if clazz.pkg.name.startswith("java"): return
if clazz.pkg.name.startswith("junit"): return
if clazz.pkg.name.startswith("org.apache"): return
@@ -1258,10 +1278,11 @@
def examine_stream(stream):
"""Find all style issues in the given API stream."""
- global failures
+ global failures, noticed
failures = {}
+ noticed = {}
_parse_stream(stream, examine_clazz)
- return failures
+ return (failures, noticed)
def examine_api(api):
@@ -1338,6 +1359,8 @@
help="Disable terminal colors")
parser.add_argument("--allow-google", action='store_const', const=True,
help="Allow references to Google")
+ parser.add_argument("--show-noticed", action='store_const', const=True,
+ help="Show API changes noticed")
args = vars(parser.parse_args())
if args['no_color']:
@@ -1350,16 +1373,21 @@
previous_file = args['previous.txt']
with current_file as f:
- cur_fail = examine_stream(f)
+ cur_fail, cur_noticed = examine_stream(f)
if not previous_file is None:
with previous_file as f:
- prev_fail = examine_stream(f)
+ prev_fail, prev_noticed = examine_stream(f)
# ignore errors from previous API level
for p in prev_fail:
if p in cur_fail:
del cur_fail[p]
+ # ignore classes unchanged from previous API level
+ for k, v in prev_noticed.iteritems():
+ if k in cur_noticed and v == cur_noticed[k]:
+ del cur_noticed[k]
+
"""
# NOTE: disabled because of memory pressure
# look for compatibility issues
@@ -1371,7 +1399,15 @@
print
"""
- print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
- for f in sorted(cur_fail):
- print cur_fail[f]
+ if args['show_noticed'] and len(cur_noticed) != 0:
+ print "%s API changes noticed %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+ for f in sorted(cur_noticed.keys()):
+ print f
print
+
+ if len(cur_fail) != 0:
+ print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+ for f in sorted(cur_fail):
+ print cur_fail[f]
+ print
+ sys.exit(77)
diff --git a/tools/apilint/apilint_sha.sh b/tools/apilint/apilint_sha.sh
new file mode 100755
index 0000000..2a45b10
--- /dev/null
+++ b/tools/apilint/apilint_sha.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then
+ python tools/apilint/apilint.py <(git show $1:api/current.txt) <(git show $1^:api/current.txt)
+fi
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_linter.py
similarity index 84%
rename from tools/fonts/fontchain_lint.py
rename to tools/fonts/fontchain_linter.py
index dcb90e4..15d39fd 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_linter.py
@@ -163,11 +163,14 @@
'U+%04X was not found in %s' % (char, font))
-def assert_font_supports_none_of_chars(font, chars):
+def assert_font_supports_none_of_chars(font, chars, fallbackName):
best_cmap = get_best_cmap(font)
for char in chars:
- assert char not in best_cmap, (
- 'U+%04X was found in %s' % (char, font))
+ if fallbackName:
+ assert char not in best_cmap, 'U+%04X was found in %s' % (char, font)
+ else:
+ assert char not in best_cmap, (
+ 'U+%04X was found in %s in fallback %s' % (char, font, fallbackName))
def assert_font_supports_all_sequences(font, sequences):
@@ -196,19 +199,21 @@
class FontRecord(object):
- def __init__(self, name, scripts, variant, weight, style, font):
+ def __init__(self, name, scripts, variant, weight, style, fallback_for, font):
self.name = name
self.scripts = scripts
self.variant = variant
self.weight = weight
self.style = style
+ self.fallback_for = fallback_for
self.font = font
def parse_fonts_xml(fonts_xml_path):
- global _script_to_font_map, _fallback_chain
+ global _script_to_font_map, _fallback_chains, _all_fonts
_script_to_font_map = collections.defaultdict(set)
- _fallback_chain = []
+ _fallback_chains = {}
+ _all_fonts = []
tree = ElementTree.parse(fonts_xml_path)
families = tree.findall('family')
# Minikin supports up to 254 but users can place their own font at the first
@@ -225,10 +230,17 @@
'No variant expected for LGC font %s.' % name)
assert langs is None, (
'No language expected for LGC fonts %s.' % name)
+ assert name not in _fallback_chains, 'Duplicated name entry %s' % name
+ _fallback_chains[name] = []
else:
assert variant in {None, 'elegant', 'compact'}, (
'Unexpected value for variant: %s' % variant)
+ for family in families:
+ name = family.get('name')
+ variant = family.get('variant')
+ langs = family.get('lang')
+
if langs:
langs = langs.split()
scripts = {lang_to_script(lang) for lang in langs}
@@ -247,17 +259,36 @@
assert style in {'normal', 'italic'}, (
'Unknown style "%s"' % style)
+ fallback_for = child.get('fallbackFor')
+
+ assert not name or not fallback_for, (
+ 'name and fallbackFor cannot be present at the same time')
+ assert not fallback_for or fallback_for in _fallback_chains, (
+ 'Unknown fallback name: %s' % fallback_for)
+
index = child.get('index')
if index:
index = int(index)
- _fallback_chain.append(FontRecord(
+ record = FontRecord(
name,
frozenset(scripts),
variant,
weight,
style,
- (font_file, index)))
+ fallback_for,
+ (font_file, index))
+
+ _all_fonts.append(record)
+
+ if not fallback_for:
+ if not name or name == 'sans-serif':
+ for _, fallback in _fallback_chains.iteritems():
+ fallback.append(record)
+ else:
+ _fallback_chains[name].append(record)
+ else:
+ _fallback_chains[fallback_for].append(record)
if name: # non-empty names are used for default LGC fonts
map_scripts = {'Latn', 'Grek', 'Cyrl'}
@@ -274,7 +305,7 @@
def get_emoji_font():
emoji_fonts = [
- record.font for record in _fallback_chain
+ record.font for record in _all_fonts
if 'Zsye' in record.scripts]
assert len(emoji_fonts) == 1, 'There are %d emoji fonts.' % len(emoji_fonts)
return emoji_fonts[0]
@@ -318,35 +349,36 @@
def check_emoji_defaults(default_emoji):
missing_text_chars = _emoji_properties['Emoji'] - default_emoji
- emoji_font_seen = False
- for record in _fallback_chain:
- if 'Zsye' in record.scripts:
- emoji_font_seen = True
- # No need to check the emoji font
- continue
- # For later fonts, we only check them if they have a script
- # defined, since the defined script may get them to a higher
- # score even if they appear after the emoji font. However,
- # we should skip checking the text symbols font, since
- # symbol fonts should be able to override the emoji display
- # style when 'Zsym' is explicitly specified by the user.
- if emoji_font_seen and (not record.scripts or 'Zsym' in record.scripts):
- continue
+ for name, fallback_chain in _fallback_chains.iteritems():
+ emoji_font_seen = False
+ for record in fallback_chain:
+ if 'Zsye' in record.scripts:
+ emoji_font_seen = True
+ # No need to check the emoji font
+ continue
+ # For later fonts, we only check them if they have a script
+ # defined, since the defined script may get them to a higher
+ # score even if they appear after the emoji font. However,
+ # we should skip checking the text symbols font, since
+ # symbol fonts should be able to override the emoji display
+ # style when 'Zsym' is explicitly specified by the user.
+ if emoji_font_seen and (not record.scripts or 'Zsym' in record.scripts):
+ continue
- # Check default emoji-style characters
- assert_font_supports_none_of_chars(record.font, sorted(default_emoji))
+ # Check default emoji-style characters
+ assert_font_supports_none_of_chars(record.font, sorted(default_emoji), name)
- # Mark default text-style characters appearing in fonts above the emoji
- # font as seen
- if not emoji_font_seen:
- missing_text_chars -= set(get_best_cmap(record.font))
+ # Mark default text-style characters appearing in fonts above the emoji
+ # font as seen
+ if not emoji_font_seen:
+ missing_text_chars -= set(get_best_cmap(record.font))
- # Noto does not have monochrome glyphs for Unicode 7.0 wingdings and
- # webdings yet.
- missing_text_chars -= _chars_by_age['7.0']
- assert missing_text_chars == set(), (
- 'Text style version of some emoji characters are missing: ' +
- repr(missing_text_chars))
+ # Noto does not have monochrome glyphs for Unicode 7.0 wingdings and
+ # webdings yet.
+ missing_text_chars -= _chars_by_age['7.0']
+ assert missing_text_chars == set(), (
+ 'Text style version of some emoji characters are missing: ' +
+ repr(missing_text_chars))
# Setting reverse to true returns a dictionary that maps the values to sets of
@@ -626,8 +658,19 @@
return all_emoji, default_emoji, equivalent_emoji
+def check_compact_only_fallback():
+ for name, fallback_chain in _fallback_chains.iteritems():
+ for record in fallback_chain:
+ if record.variant == 'compact':
+ same_script_elegants = [x for x in fallback_chain
+ if x.scripts == record.scripts and x.variant == 'elegant']
+ assert same_script_elegants, (
+ '%s must be in elegant of %s as fallback of "%s" too' % (
+ record.font, record.scripts, record.fallback_for),)
+
+
def check_vertical_metrics():
- for record in _fallback_chain:
+ for record in _all_fonts:
if record.name in ['sans-serif', 'sans-serif-condensed']:
font = open_font(record.font)
assert font['head'].yMax == 2163 and font['head'].yMin == -555, (
@@ -646,11 +689,12 @@
def check_cjk_punctuation():
cjk_scripts = {'Hans', 'Hant', 'Jpan', 'Kore'}
cjk_punctuation = range(0x3000, 0x301F + 1)
- for record in _fallback_chain:
- if record.scripts.intersection(cjk_scripts):
- # CJK font seen. Stop checking the rest of the fonts.
- break
- assert_font_supports_none_of_chars(record.font, cjk_punctuation)
+ for name, fallback_chain in _fallback_chains.iteritems():
+ for record in fallback_chain:
+ if record.scripts.intersection(cjk_scripts):
+ # CJK font seen. Stop checking the rest of the fonts.
+ break
+ assert_font_supports_none_of_chars(record.font, cjk_punctuation, name)
def main():
@@ -661,6 +705,8 @@
fonts_xml_path = path.join(target_out, 'etc', 'fonts.xml')
parse_fonts_xml(fonts_xml_path)
+ check_compact_only_fallback()
+
check_vertical_metrics()
hyphens_dir = path.join(target_out, 'usr', 'hyphen-data')
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index a910c62..468864b 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -23,6 +23,10 @@
"Collation.cpp",
"main.cpp",
],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libstats_proto_host",
@@ -90,6 +94,10 @@
name: "libstatslog",
generated_sources: ["statslog.cpp"],
generated_headers: ["statslog.h"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
export_generated_headers: ["statslog.h"],
shared_libs: [
"liblog",
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 6350b72..cca1294 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -2,7 +2,7 @@
#include "Collation.h"
-#include "frameworks/base/cmds/statsd/src/stats_events.pb.h"
+#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
#include <set>
#include <vector>
@@ -18,7 +18,7 @@
namespace android {
namespace stats_log_api_gen {
-using android::os::statsd::StatsEvent;
+using android::os::statsd::Atom;
// TODO: Support WorkSources
@@ -96,8 +96,6 @@
static int
write_stats_log_cpp(FILE* out, const Atoms& atoms)
{
- int errorCount;
-
// Print prelude
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
@@ -109,6 +107,8 @@
fprintf(out, "namespace android {\n");
fprintf(out, "namespace util {\n");
+ fprintf(out, "// the single event tag id for all stats logs\n");
+ fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
// Print write methods
fprintf(out, "\n");
@@ -117,7 +117,7 @@
int argIndex;
fprintf(out, "void\n");
- fprintf(out, "stats_write(int code");
+ fprintf(out, "stats_write(int32_t code");
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -128,7 +128,8 @@
fprintf(out, "{\n");
argIndex = 1;
- fprintf(out, " android_log_event_list event(code);\n");
+ fprintf(out, " android_log_event_list event(kStatsEventTag);\n");
+ fprintf(out, " event << code;\n");
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_STRING) {
@@ -157,8 +158,6 @@
static int
write_stats_log_header(FILE* out, const Atoms& atoms)
{
- int errorCount;
-
// Print prelude
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
@@ -208,8 +207,7 @@
fprintf(out, "//\n");
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
signature != atoms.signatures.end(); signature++) {
-
- fprintf(out, "void stats_write(int code");
+ fprintf(out, "void stats_write(int32_t code ");
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -229,8 +227,6 @@
static int
write_stats_log_java(FILE* out, const Atoms& atoms)
{
- int errorCount;
-
// Print prelude
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
@@ -395,8 +391,6 @@
static int
write_stats_log_jni(FILE* out, const Atoms& atoms)
{
- int errorCount;
-
// Print prelude
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
@@ -582,7 +576,7 @@
// Collate the parameters
Atoms atoms;
- int errorCount = collate_atoms(StatsEvent::descriptor(), &atoms);
+ int errorCount = collate_atoms(Atom::descriptor(), &atoms);
if (errorCount != 0) {
return 1;
}
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index 2311a11..6686158 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -16,7 +16,7 @@
syntax = "proto2";
-import "frameworks/base/cmds/statsd/src/stats_events.proto";
+import "frameworks/base/cmds/statsd/src/atoms.proto";
package android.stats_log_api_gen;
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
index 4816984..4779020 100644
--- a/tools/streaming_proto/cpp/main.cpp
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -18,6 +18,12 @@
return file_descriptor.name() + ".h";
}
+static inline bool
+should_generate_enums_mapping(const EnumDescriptorProto& enu)
+{
+ return enu.options().GetExtension(stream_enum).enable_enums_mapping();
+}
+
static void
write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
{
@@ -25,10 +31,27 @@
text << indent << "// enum " << enu.name() << endl;
for (int i=0; i<N; i++) {
const EnumValueDescriptorProto& value = enu.value(i);
- text << indent << "const uint32_t "
+ text << indent << "const int "
<< make_constant_name(value.name())
<< " = " << value.number() << ";" << endl;
}
+
+ if (should_generate_enums_mapping(enu)) {
+ 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;
+ 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;
+ for (int i=0; i<N; i++) {
+ text << indent << INDENT << make_constant_name(enu.value(i).name()) << "," << endl;
+ }
+ text << indent << "};" << endl;
+ }
+
text << endl;
}
@@ -59,7 +82,7 @@
static inline bool
should_generate_fields_mapping(const DescriptorProto& message)
{
- return message.options().GetExtension(stream).enable_fields_mapping();
+ return message.options().GetExtension(stream_msg).enable_fields_mapping();
}
static void
diff --git a/tools/streaming_proto/stream.proto b/tools/streaming_proto/stream.proto
index 123506c..c081209 100644
--- a/tools/streaming_proto/stream.proto
+++ b/tools/streaming_proto/stream.proto
@@ -21,12 +21,22 @@
package android.stream_proto;
// This option tells streaming proto plugin to compile .proto files with extra features.
-message StreamFlags {
+message MessageOptions {
// creates a mapping of field names of the message to its field ids
optional bool enable_fields_mapping = 1;
}
extend google.protobuf.MessageOptions {
// Flags used by streaming proto plugins
- optional StreamFlags stream = 126856794;
+ optional MessageOptions stream_msg = 126856794;
+}
+
+message EnumOptions {
+ // creates a mapping of enum names to its values, strip its prefix enum type for each value
+ optional bool enable_enums_mapping = 1;
+}
+
+extend google.protobuf.EnumOptions {
+ // Flags used by streaming proto plugins
+ optional EnumOptions stream_enum = 126856794;
}
diff --git a/tools/streaming_proto/string_utils.cpp b/tools/streaming_proto/string_utils.cpp
index bd34ab7..607d820 100644
--- a/tools/streaming_proto/string_utils.cpp
+++ b/tools/streaming_proto/string_utils.cpp
@@ -108,6 +108,17 @@
return result;
}
+string
+stripPrefix(const string& str, const string& prefix)
+{
+ if (str.size() <= prefix.size()) return str;
+ size_t i = 0, len = prefix.size();
+ for (; i<len; i++) {
+ if (str[i] != prefix[i]) return str;
+ }
+ return str.substr(i);
+}
+
} // namespace stream_proto
} // namespace android
diff --git a/tools/streaming_proto/string_utils.h b/tools/streaming_proto/string_utils.h
index d6f195f..315b275 100644
--- a/tools/streaming_proto/string_utils.h
+++ b/tools/streaming_proto/string_utils.h
@@ -26,15 +26,20 @@
string file_base_name(const string& str);
/**
- * Replace all occurances of 'replace' with 'with'.
+ * Replaces all occurances of 'replace' with 'with'.
*/
string replace_string(const string& str, const char replace, const char with);
/**
- * Split a string to parts by delimiter.
+ * Splits a string to parts by delimiter.
*/
vector<string> split(const string& str, const char delimiter);
+/**
+ * Returns the rest of str if it has prefix, otherwise return all.
+ */
+string stripPrefix(const string& str, const string& prefix);
+
} // namespace stream_proto
} // namespace android
diff --git a/vr/Android.mk b/vr/Android.mk
index 5b65d3f..73e9f23 100644
--- a/vr/Android.mk
+++ b/vr/Android.mk
@@ -18,6 +18,7 @@
LOCAL_MODULE := libdvr_loader
LOCAL_MODULE_OWNER := google
LOCAL_SRC_FILES := dvr_library_loader.cpp
+LOCAL_CFLAGS := -Wall -Werror
include $(BUILD_SHARED_LIBRARY)
# Java platform library for vr stuff.
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 551e4df..c2f11d9 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -66,11 +66,11 @@
List<OsuProvider> getMatchingOsuProviders(in ScanResult scanResult);
- int addOrUpdateNetwork(in WifiConfiguration config);
+ int addOrUpdateNetwork(in WifiConfiguration config, String packageName);
- boolean addOrUpdatePasspointConfiguration(in PasspointConfiguration config);
+ boolean addOrUpdatePasspointConfiguration(in PasspointConfiguration config, String packageName);
- boolean removePasspointConfiguration(in String fqdn);
+ boolean removePasspointConfiguration(in String fqdn, String packageName);
List<PasspointConfiguration> getPasspointConfigurations();
@@ -80,21 +80,21 @@
void deauthenticateNetwork(long holdoff, boolean ess);
- boolean removeNetwork(int netId);
+ boolean removeNetwork(int netId, String packageName);
- boolean enableNetwork(int netId, boolean disableOthers);
+ boolean enableNetwork(int netId, boolean disableOthers, String packageName);
- boolean disableNetwork(int netId);
+ boolean disableNetwork(int netId, String packageName);
- void startScan(in ScanSettings requested, in WorkSource ws, in String packageName);
+ void startScan(in ScanSettings requested, in WorkSource ws, String packageName);
List<ScanResult> getScanResults(String callingPackage);
- void disconnect();
+ void disconnect(String packageName);
- void reconnect();
+ void reconnect(String packageName);
- void reassociate();
+ void reassociate(String packageName);
WifiInfo getConnectionInfo(String callingPackage);
@@ -102,13 +102,13 @@
int getWifiEnabledState();
- void setCountryCode(String country, boolean persist);
+ void setCountryCode(String country);
String getCountryCode();
boolean isDualBandSupported();
- boolean saveConfiguration();
+ boolean saveConfiguration(String packageName);
DhcpInfo getDhcpInfo();
@@ -134,9 +134,9 @@
boolean stopSoftAp();
- int startLocalOnlyHotspot(in Messenger messenger, in IBinder binder, in String packageName);
+ int startLocalOnlyHotspot(in Messenger messenger, in IBinder binder, String packageName);
- void stopLocalOnlyHotspot();
+ void stopLocalOnlyHotspot(String packageName);
void startWatchLocalOnlyHotspot(in Messenger messenger, in IBinder binder);
@@ -146,9 +146,9 @@
WifiConfiguration getWifiApConfiguration();
- void setWifiApConfiguration(in WifiConfiguration wifiConfig);
+ void setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName);
- Messenger getWifiServiceMessenger();
+ Messenger getWifiServiceMessenger(String packageName);
void enableTdls(String remoteIPAddress, boolean enable);
@@ -166,16 +166,16 @@
void setAllowScansWithTraffic(int enabled);
int getAllowScansWithTraffic();
- boolean setEnableAutoJoinWhenAssociated(boolean enabled);
+ boolean setEnableAutoJoinWhenAssociated(boolean enabled, String packageName);
boolean getEnableAutoJoinWhenAssociated();
void enableWifiConnectivityManager(boolean enabled);
WifiConnectionStatistics getConnectionStatistics();
- void disableEphemeralNetwork(String SSID);
+ void disableEphemeralNetwork(String SSID, String packageName);
- void factoryReset();
+ void factoryReset(String packageName);
Network getCurrentNetwork();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 04efd56..66fabf3 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -1127,7 +1128,7 @@
*/
private int addOrUpdateNetwork(WifiConfiguration config) {
try {
- return mService.addOrUpdateNetwork(config);
+ return mService.addOrUpdateNetwork(config, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1148,7 +1149,7 @@
*/
public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
try {
- if (!mService.addOrUpdatePasspointConfiguration(config)) {
+ if (!mService.addOrUpdatePasspointConfiguration(config, mContext.getOpPackageName())) {
throw new IllegalArgumentException();
}
} catch (RemoteException e) {
@@ -1165,7 +1166,7 @@
*/
public void removePasspointConfiguration(String fqdn) {
try {
- if (!mService.removePasspointConfiguration(fqdn)) {
+ if (!mService.removePasspointConfiguration(fqdn, mContext.getOpPackageName())) {
throw new IllegalArgumentException();
}
} catch (RemoteException e) {
@@ -1251,7 +1252,7 @@
*/
public boolean removeNetwork(int netId) {
try {
- return mService.removeNetwork(netId);
+ return mService.removeNetwork(netId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1297,7 +1298,7 @@
boolean success;
try {
- success = mService.enableNetwork(netId, attemptConnect);
+ success = mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1323,7 +1324,7 @@
*/
public boolean disableNetwork(int netId) {
try {
- return mService.disableNetwork(netId);
+ return mService.disableNetwork(netId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1336,7 +1337,7 @@
*/
public boolean disconnect() {
try {
- mService.disconnect();
+ mService.disconnect(mContext.getOpPackageName());
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1351,7 +1352,7 @@
*/
public boolean reconnect() {
try {
- mService.reconnect();
+ mService.reconnect(mContext.getOpPackageName());
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1366,7 +1367,7 @@
*/
public boolean reassociate() {
try {
- mService.reassociate();
+ mService.reassociate(mContext.getOpPackageName());
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1739,7 +1740,7 @@
@Deprecated
public boolean saveConfiguration() {
try {
- return mService.saveConfiguration();
+ return mService.saveConfiguration(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1748,13 +1749,12 @@
/**
* Set the country code.
* @param countryCode country code in ISO 3166 format.
- * @param persist {@code true} if this needs to be remembered
*
* @hide
*/
- public void setCountryCode(String country, boolean persist) {
+ public void setCountryCode(@NonNull String country) {
try {
- mService.setCountryCode(country, persist);
+ mService.setCountryCode(country);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2056,7 +2056,7 @@
}
mLOHSCallbackProxy = null;
try {
- mService.stopLocalOnlyHotspot();
+ mService.stopLocalOnlyHotspot(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2175,7 +2175,7 @@
@RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
try {
- mService.setWifiApConfiguration(wifiConfig);
+ mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
return true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2947,7 +2947,7 @@
public void disableEphemeralNetwork(String SSID) {
if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
try {
- mService.disableEphemeralNetwork(SSID);
+ mService.disableEphemeralNetwork(SSID, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2986,7 +2986,7 @@
*/
public Messenger getWifiServiceMessenger() {
try {
- return mService.getWifiServiceMessenger();
+ return mService.getWifiServiceMessenger(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3516,7 +3516,7 @@
*/
public void factoryReset() {
try {
- mService.factoryReset();
+ mService.factoryReset(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3543,7 +3543,7 @@
*/
public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
try {
- return mService.setEnableAutoJoinWhenAssociated(enabled);
+ return mService.setEnableAutoJoinWhenAssociated(enabled, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index 115b86d..aa2c268 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -113,6 +113,32 @@
}
/**
+ * Called when a discovery (publish or subscribe) operation results in a
+ * service discovery. Called when a Subscribe service was configured with a range requirement
+ * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and/or
+ * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)}. A discovery will only be declared
+ * (i.e. this callback called) if the range of the publisher is within the specified distance
+ * constraints.
+ *
+ * @param peerHandle An opaque handle to the peer matching our discovery operation.
+ * @param serviceSpecificInfo The service specific information (arbitrary
+ * byte array) provided by the peer as part of its discovery
+ * configuration.
+ * @param matchFilter The filter which resulted in this service discovery. For
+ * {@link PublishConfig#PUBLISH_TYPE_UNSOLICITED},
+ * {@link SubscribeConfig#SUBSCRIBE_TYPE_PASSIVE} discovery sessions this is the publisher's
+ * match filter. For {@link PublishConfig#PUBLISH_TYPE_SOLICITED},
+ * {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE} discovery sessions this
+ * is the subscriber's match filter.
+ * @param distanceMm The measured distance to the Publisher in mm.
+ * @hide
+ */
+ public void onServiceDiscoveredWithinRange(PeerHandle peerHandle,
+ byte[] serviceSpecificInfo, List<byte[]> matchFilter, int distanceMm) {
+ /* empty */
+ }
+
+ /**
* Called in response to
* {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}
* when a message is transmitted successfully - i.e. when it was received successfully by the
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
index 8ff3842..421a8af 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
@@ -29,6 +29,8 @@
void onSessionTerminated(int reason);
void onMatch(int peerId, in byte[] serviceSpecificInfo, in byte[] matchFilter);
+ void onMatchWithDistance(int peerId, in byte[] serviceSpecificInfo, in byte[] matchFilter,
+ int distanceMm);
void onMessageSendSuccess(int messageId);
void onMessageSendFail(int messageId, int reason);
diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
index d018620..e60f52f 100644
--- a/wifi/java/android/net/wifi/aware/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -29,6 +29,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* Defines the configuration of a Aware publish session. Built using
@@ -81,14 +82,19 @@
public final boolean mEnableTerminateNotification;
/** @hide */
+ public final boolean mEnableRanging;
+
+ /** @hide */
public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
- int publishType, int ttlSec, boolean enableTerminateNotification) {
+ int publishType, int ttlSec, boolean enableTerminateNotification,
+ boolean enableRanging) {
mServiceName = serviceName;
mServiceSpecificInfo = serviceSpecificInfo;
mMatchFilter = matchFilter;
mPublishType = publishType;
mTtlSec = ttlSec;
mEnableTerminateNotification = enableTerminateNotification;
+ mEnableRanging = enableRanging;
}
@Override
@@ -103,7 +109,8 @@
+ (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString()
+ ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length)
+ ", mPublishType=" + mPublishType + ", mTtlSec=" + mTtlSec
- + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
+ + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+ + ", mEnableRanging=" + mEnableRanging + "]";
}
@Override
@@ -119,6 +126,7 @@
dest.writeInt(mPublishType);
dest.writeInt(mTtlSec);
dest.writeInt(mEnableTerminateNotification ? 1 : 0);
+ dest.writeInt(mEnableRanging ? 1 : 0);
}
public static final Creator<PublishConfig> CREATOR = new Creator<PublishConfig>() {
@@ -135,9 +143,10 @@
int publishType = in.readInt();
int ttlSec = in.readInt();
boolean enableTerminateNotification = in.readInt() != 0;
+ boolean enableRanging = in.readInt() != 0;
return new PublishConfig(serviceName, ssi, matchFilter, publishType,
- ttlSec, enableTerminateNotification);
+ ttlSec, enableTerminateNotification, enableRanging);
}
};
@@ -157,21 +166,14 @@
lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
&& mPublishType == lhs.mPublishType
&& mTtlSec == lhs.mTtlSec
- && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
+ && mEnableTerminateNotification == lhs.mEnableTerminateNotification
+ && mEnableRanging == lhs.mEnableRanging;
}
@Override
public int hashCode() {
- int result = 17;
-
- result = 31 * result + Arrays.hashCode(mServiceName);
- result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
- result = 31 * result + Arrays.hashCode(mMatchFilter);
- result = 31 * result + mPublishType;
- result = 31 * result + mTtlSec;
- result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
-
- return result;
+ return Objects.hash(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType, mTtlSec,
+ mEnableTerminateNotification, mEnableRanging);
}
/**
@@ -226,6 +228,7 @@
private int mPublishType = PUBLISH_TYPE_UNSOLICITED;
private int mTtlSec = 0;
private boolean mEnableTerminateNotification = true;
+ private boolean mEnableRanging = false;
/**
* Specify the service name of the publish session. The actual on-air
@@ -352,12 +355,35 @@
}
/**
+ * Configure whether the publish discovery session supports ranging and allows peers to
+ * measure distance to it. This API is used in conjunction with
+ * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and
+ * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} to specify a minimum and/or
+ * maximum distance at which discovery will be triggered.
+ * <p>
+ * Optional. Disabled by default - i.e. any peer which attempts to measure distance to this
+ * device will be refused. If the peer has ranging enabled (using the
+ * {@link SubscribeConfig} APIs listed above, it will never discover this device.
+ *
+ * @param enable If true, ranging is supported on request of the peer.
+ *
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ *
+ * @hide
+ */
+ public Builder setRangingEnabled(boolean enable) {
+ mEnableRanging = enable;
+ return this;
+ }
+
+ /**
* Build {@link PublishConfig} given the current requests made on the
* builder.
*/
public PublishConfig build() {
return new PublishConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType,
- mTtlSec, mEnableTerminateNotification);
+ mTtlSec, mEnableTerminateNotification, mEnableRanging);
}
}
}
diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
index 4bf2fb6..f6552a7 100644
--- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -29,6 +29,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* Defines the configuration of a Aware subscribe session. Built using
@@ -79,15 +80,32 @@
public final boolean mEnableTerminateNotification;
/** @hide */
+ public final boolean mMinDistanceMmSet;
+
+ /** @hide */
+ public final int mMinDistanceMm;
+
+ /** @hide */
+ public final boolean mMaxDistanceMmSet;
+
+ /** @hide */
+ public final int mMaxDistanceMm;
+
+ /** @hide */
public SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
- int subscribeType, int ttlSec,
- boolean enableTerminateNotification) {
+ int subscribeType, int ttlSec, boolean enableTerminateNotification,
+ boolean minDistanceMmSet, int minDistanceMm, boolean maxDistanceMmSet,
+ int maxDistanceMm) {
mServiceName = serviceName;
mServiceSpecificInfo = serviceSpecificInfo;
mMatchFilter = matchFilter;
mSubscribeType = subscribeType;
mTtlSec = ttlSec;
mEnableTerminateNotification = enableTerminateNotification;
+ mMinDistanceMm = minDistanceMm;
+ mMinDistanceMmSet = minDistanceMmSet;
+ mMaxDistanceMm = maxDistanceMm;
+ mMaxDistanceMmSet = maxDistanceMmSet;
}
@Override
@@ -102,7 +120,11 @@
+ (new TlvBufferUtils.TlvIterable(0, 1, mMatchFilter)).toString()
+ ", mMatchFilter.length=" + (mMatchFilter == null ? 0 : mMatchFilter.length)
+ ", mSubscribeType=" + mSubscribeType + ", mTtlSec=" + mTtlSec
- + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
+ + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+ + ", mMinDistanceMm=" + mMinDistanceMm
+ + ", mMinDistanceMmSet=" + mMinDistanceMmSet
+ + ", mMaxDistanceMm=" + mMaxDistanceMm
+ + ", mMaxDistanceMmSet=" + mMaxDistanceMmSet + "]";
}
@Override
@@ -118,6 +140,10 @@
dest.writeInt(mSubscribeType);
dest.writeInt(mTtlSec);
dest.writeInt(mEnableTerminateNotification ? 1 : 0);
+ dest.writeInt(mMinDistanceMm);
+ dest.writeInt(mMinDistanceMmSet ? 1 : 0);
+ dest.writeInt(mMaxDistanceMm);
+ dest.writeInt(mMaxDistanceMmSet ? 1 : 0);
}
public static final Creator<SubscribeConfig> CREATOR = new Creator<SubscribeConfig>() {
@@ -134,9 +160,14 @@
int subscribeType = in.readInt();
int ttlSec = in.readInt();
boolean enableTerminateNotification = in.readInt() != 0;
+ int minDistanceMm = in.readInt();
+ boolean minDistanceMmSet = in.readInt() != 0;
+ int maxDistanceMm = in.readInt();
+ boolean maxDistanceMmSet = in.readInt() != 0;
- return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType,
- ttlSec, enableTerminateNotification);
+ return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType, ttlSec,
+ enableTerminateNotification, minDistanceMmSet, minDistanceMm, maxDistanceMmSet,
+ maxDistanceMm);
}
};
@@ -152,23 +183,37 @@
SubscribeConfig lhs = (SubscribeConfig) o;
- return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
- lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
- && mSubscribeType == lhs.mSubscribeType
- && mTtlSec == lhs.mTtlSec
- && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
+ if (!(Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(
+ mServiceSpecificInfo, lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter,
+ lhs.mMatchFilter) && mSubscribeType == lhs.mSubscribeType && mTtlSec == lhs.mTtlSec
+ && mEnableTerminateNotification == lhs.mEnableTerminateNotification
+ && mMinDistanceMmSet == lhs.mMinDistanceMmSet
+ && mMaxDistanceMmSet == lhs.mMaxDistanceMmSet)) {
+ return false;
+ }
+
+ if (mMinDistanceMmSet && mMinDistanceMm != lhs.mMinDistanceMm) {
+ return false;
+ }
+
+ if (mMaxDistanceMmSet && mMaxDistanceMm != lhs.mMaxDistanceMm) {
+ return false;
+ }
+
+ return true;
}
@Override
public int hashCode() {
- int result = 17;
+ int result = Objects.hash(mServiceName, mServiceSpecificInfo, mMatchFilter, mSubscribeType,
+ mTtlSec, mEnableTerminateNotification, mMinDistanceMmSet, mMaxDistanceMmSet);
- result = 31 * result + Arrays.hashCode(mServiceName);
- result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
- result = 31 * result + Arrays.hashCode(mMatchFilter);
- result = 31 * result + mSubscribeType;
- result = 31 * result + mTtlSec;
- result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
+ if (mMinDistanceMmSet) {
+ result = Objects.hash(result, mMinDistanceMm);
+ }
+ if (mMaxDistanceMmSet) {
+ result = Objects.hash(result, mMaxDistanceMm);
+ }
return result;
}
@@ -213,6 +258,17 @@
"Match filter longer than supported by device characteristics");
}
}
+
+ if (mMinDistanceMmSet && mMinDistanceMm < 0) {
+ throw new IllegalArgumentException("Minimum distance must be non-negative");
+ }
+ if (mMaxDistanceMmSet && mMaxDistanceMm < 0) {
+ throw new IllegalArgumentException("Maximum distance must be non-negative");
+ }
+ if (mMinDistanceMmSet && mMaxDistanceMmSet && mMaxDistanceMm <= mMinDistanceMm) {
+ throw new IllegalArgumentException(
+ "Maximum distance must be greater than minimum distance");
+ }
}
/**
@@ -225,6 +281,10 @@
private int mSubscribeType = SUBSCRIBE_TYPE_PASSIVE;
private int mTtlSec = 0;
private boolean mEnableTerminateNotification = true;
+ private boolean mMinDistanceMmSet = false;
+ private int mMinDistanceMm;
+ private boolean mMaxDistanceMmSet = false;
+ private int mMaxDistanceMm;
/**
* Specify the service name of the subscribe session. The actual on-air
@@ -350,13 +410,69 @@
}
/**
+ * Configure the minimum distance to a discovered publisher at which to trigger a discovery
+ * notification. I.e. discovery will only be triggered if we've found a matching publisher
+ * (based on the other criteria in this configuration) <b>and</b> the distance to the
+ * publisher is > the value specified in this API.
+ * <p>
+ * Can be used in conjunction with {@link #setMaxDistanceMm(int)} to specify a geo-fence,
+ * i.e. discovery with min < distance < max.
+ * <p>
+ * If this API is called, the subscriber requires ranging. In such a case, the publisher
+ * peer must enable ranging using
+ * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
+ * never be triggered.
+ *
+ * @param minDistanceMm Minimum distance, in mm, to the publisher above which to trigger
+ * discovery.
+ *
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ *
+ * @hide
+ */
+ public Builder setMinDistanceMm(int minDistanceMm) {
+ mMinDistanceMm = minDistanceMm;
+ mMinDistanceMmSet = true;
+ return this;
+ }
+
+ /**
+ * Configure the maximum distance to a discovered publisher at which to trigger a discovery
+ * notification. I.e. discovery will only be triggered if we've found a matching publisher
+ * (based on the other criteria in this configuration) <b>and</b> the distance to the
+ * publisher is < the value specified in this API.
+ * <p>
+ * Can be used in conjunction with {@link #setMinDistanceMm(int)} to specify a geo-fence,
+ * i.e. discovery with min < distance < max.
+ * <p>
+ * If this API is called, the subscriber requires ranging. In such a case, the publisher
+ * peer must enable ranging using
+ * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
+ * never be triggered.
+ *
+ * @param maxDistanceMm Maximum distance, in mm, to the publisher below which to trigger
+ * discovery.
+ *
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ *
+ * @hide
+ */
+ public Builder setMaxDistanceMm(int maxDistanceMm) {
+ mMaxDistanceMm = maxDistanceMm;
+ mMaxDistanceMmSet = true;
+ return this;
+ }
+
+ /**
* Build {@link SubscribeConfig} given the current requests made on the
* builder.
*/
public SubscribeConfig build() {
return new SubscribeConfig(mServiceName, mServiceSpecificInfo, mMatchFilter,
- mSubscribeType, mTtlSec,
- mEnableTerminateNotification);
+ mSubscribeType, mTtlSec, mEnableTerminateNotification,
+ mMinDistanceMmSet, mMinDistanceMm, mMaxDistanceMmSet, mMaxDistanceMm);
}
}
}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index ed6804d..166da48 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -20,8 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
-import android.annotation.SystemService;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemService;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkRequest;
@@ -564,6 +564,7 @@
private static final int CALLBACK_MESSAGE_SEND_SUCCESS = 5;
private static final int CALLBACK_MESSAGE_SEND_FAIL = 6;
private static final int CALLBACK_MESSAGE_RECEIVED = 7;
+ private static final int CALLBACK_MATCH_WITH_DISTANCE = 8;
private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
@@ -618,7 +619,9 @@
case CALLBACK_SESSION_TERMINATED:
onProxySessionTerminated(msg.arg1);
break;
- case CALLBACK_MATCH: {
+ case CALLBACK_MATCH:
+ case CALLBACK_MATCH_WITH_DISTANCE:
+ {
List<byte[]> matchFilter = null;
byte[] arg = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2);
try {
@@ -629,9 +632,16 @@
+ new String(HexEncoding.encode(arg))
+ "' - cannot be parsed: e=" + e);
}
- mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1),
- msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
- matchFilter);
+ if (msg.what == CALLBACK_MATCH) {
+ mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1),
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
+ matchFilter);
+ } else {
+ mOriginalCallback.onServiceDiscoveredWithinRange(
+ new PeerHandle(msg.arg1),
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
+ matchFilter, msg.arg2);
+ }
break;
}
case CALLBACK_MESSAGE_SEND_SUCCESS:
@@ -684,21 +694,38 @@
mHandler.sendMessage(msg);
}
- @Override
- public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
- if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
-
+ private void onMatchCommon(int messageType, int peerId, byte[] serviceSpecificInfo,
+ byte[] matchFilter, int distanceMm) {
Bundle data = new Bundle();
data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo);
data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter);
- Message msg = mHandler.obtainMessage(CALLBACK_MATCH);
+ Message msg = mHandler.obtainMessage(messageType);
msg.arg1 = peerId;
+ msg.arg2 = distanceMm;
msg.setData(data);
mHandler.sendMessage(msg);
}
@Override
+ public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
+ if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
+
+ onMatchCommon(CALLBACK_MATCH, peerId, serviceSpecificInfo, matchFilter, 0);
+ }
+
+ @Override
+ public void onMatchWithDistance(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter,
+ int distanceMm) {
+ if (VDBG) {
+ Log.v(TAG, "onMatchWithDistance: peerId=" + peerId + ", distanceMm=" + distanceMm);
+ }
+
+ onMatchCommon(CALLBACK_MATCH_WITH_DISTANCE, peerId, serviceSpecificInfo, matchFilter,
+ distanceMm);
+ }
+
+ @Override
public void onMessageSendSuccess(int messageId) {
if (VDBG) Log.v(TAG, "onMessageSendSuccess");
diff --git a/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl b/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
index e1ad783..3e37af0 100644
--- a/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
+++ b/wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
@@ -16,6 +16,8 @@
package android.net.wifi.rtt;
+import android.os.WorkSource;
+
import android.net.wifi.rtt.IRttCallback;
import android.net.wifi.rtt.RangingRequest;
@@ -25,6 +27,7 @@
interface IWifiRttManager
{
boolean isAvailable();
- void startRanging(in IBinder binder, in String callingPackage, in RangingRequest request,
- in IRttCallback callback);
+ void startRanging(in IBinder binder, in String callingPackage, in WorkSource workSource,
+ in RangingRequest request, in IRttCallback callback);
+ void cancelRanging(in WorkSource workSource);
}
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
index c7c0923..735e872 100644
--- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -3,16 +3,19 @@
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_WIFI_STATE;
import static android.Manifest.permission.CHANGE_WIFI_STATE;
+import static android.Manifest.permission.LOCATION_HARDWARE;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.util.Log;
import java.util.List;
@@ -41,7 +44,7 @@
@SystemService(Context.WIFI_RTT2_SERVICE)
public class WifiRttManager {
private static final String TAG = "WifiRttManager";
- private static final boolean VDBG = true;
+ private static final boolean VDBG = false;
private final Context mContext;
private final IWifiRttManager mService;
@@ -94,21 +97,63 @@
@RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE, ACCESS_WIFI_STATE})
public void startRanging(RangingRequest request, RangingResultCallback callback,
@Nullable Handler handler) {
+ startRanging(null, request, callback, handler);
+ }
+
+ /**
+ * Initiate a request to range to a set of devices specified in the {@link RangingRequest}.
+ * Results will be returned in the {@link RangingResultCallback} set of callbacks.
+ *
+ * @param workSource A mechanism to specify an alternative work-source for the request.
+ * @param request A request specifying a set of devices whose distance measurements are
+ * requested.
+ * @param callback A callback for the result of the ranging request.
+ * @param handler The Handler on whose thread to execute the callbacks of the {@code
+ * callback} object. If a null is provided then the application's main thread
+ * will be used.
+ *
+ * @hide (@SystemApi)
+ */
+ @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE,
+ ACCESS_WIFI_STATE})
+ public void startRanging(@Nullable WorkSource workSource, RangingRequest request,
+ RangingResultCallback callback, @Nullable Handler handler) {
if (VDBG) {
- Log.v(TAG, "startRanging: request=" + request + ", callback=" + callback + ", handler="
- + handler);
+ Log.v(TAG, "startRanging: workSource=" + workSource + ", request=" + request
+ + ", callback=" + callback + ", handler=" + handler);
}
Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
Binder binder = new Binder();
try {
- mService.startRanging(binder, mContext.getOpPackageName(), request,
+ mService.startRanging(binder, mContext.getOpPackageName(), workSource, request,
new RttCallbackProxy(looper, callback));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /**
+ * Cancel all ranging requests for the specified work sources. The requests have been requested
+ * using {@link #startRanging(WorkSource, RangingRequest, RangingResultCallback, Handler)}.
+ *
+ * @param workSource The work-sources of the requesters.
+ *
+ * @hide (@SystemApi)
+ */
+ @RequiresPermission(allOf = {LOCATION_HARDWARE})
+ public void cancelRanging(WorkSource workSource) {
+ if (VDBG) {
+ Log.v(TAG, "cancelRanging: workSource=" + workSource);
+ }
+
+ try {
+ mService.cancelRanging(workSource);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private static class RttCallbackProxy extends IRttCallback.Stub {
private final Handler mHandler;
private final RangingResultCallback mCallback;
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index ee6f12b..3cad590 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -59,6 +59,7 @@
private static final int ERROR_NOT_SET = -1;
private static final int ERROR_TEST_REASON = 5;
private static final String TEST_PACKAGE_NAME = "TestPackage";
+ private static final String TEST_COUNTRY_CODE = "US";
@Mock Context mContext;
@Mock IWifiManager mWifiService;
@@ -136,7 +137,7 @@
assertEquals(mApConfig, callback.mRes.getWifiConfiguration());
callback.mRes.close();
- verify(mWifiService).stopLocalOnlyHotspot();
+ verify(mWifiService).stopLocalOnlyHotspot(TEST_PACKAGE_NAME);
}
/**
@@ -156,7 +157,7 @@
assertEquals(mApConfig, res.getWifiConfiguration());
}
- verify(mWifiService).stopLocalOnlyHotspot();
+ verify(mWifiService).stopLocalOnlyHotspot(TEST_PACKAGE_NAME);
}
/**
@@ -547,7 +548,7 @@
anyString())).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mWifiManager.cancelLocalOnlyHotspotRequest();
- verify(mWifiService).stopLocalOnlyHotspot();
+ verify(mWifiService).stopLocalOnlyHotspot(TEST_PACKAGE_NAME);
}
/**
@@ -569,7 +570,7 @@
anyString())).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mWifiManager.cancelLocalOnlyHotspotRequest();
- verify(mWifiService).stopLocalOnlyHotspot();
+ verify(mWifiService).stopLocalOnlyHotspot(TEST_PACKAGE_NAME);
mLooper.dispatchAll();
assertEquals(ERROR_NOT_SET, callback.mFailureReason);
assertFalse(callback.mOnStartedCalled);
@@ -593,7 +594,7 @@
assertFalse(callback.mOnStoppedCalled);
assertEquals(null, callback.mRes);
mWifiManager.cancelLocalOnlyHotspotRequest();
- verify(mWifiService, never()).stopLocalOnlyHotspot();
+ verify(mWifiService, never()).stopLocalOnlyHotspot(anyString());
}
/**
@@ -777,4 +778,23 @@
mWifiManager.unregisterLocalOnlyHotspotObserver();
verify(mWifiService).stopWatchLocalOnlyHotspot();
}
+
+ /**
+ * Verify that calls WifiServiceImpl to set country code when no exception happens.
+ */
+ @Test
+ public void testSetWifiCountryCode() throws Exception {
+ mWifiManager.setCountryCode(TEST_COUNTRY_CODE);
+ verify(mWifiService).setCountryCode(TEST_COUNTRY_CODE);
+ }
+
+ /**
+ * Verify that WifiManager.setCountryCode() rethrows exceptions if caller does not
+ * have necessary permissions.
+ */
+ @Test(expected = SecurityException.class)
+ public void testSetWifiCountryCodeFailedOnSecurityException() throws Exception {
+ doThrow(new SecurityException()).when(mWifiService).setCountryCode(anyString());
+ mWifiManager.setCountryCode(TEST_COUNTRY_CODE);
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 1aeeee3..653fcff 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -19,20 +19,17 @@
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.wifi.RttManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
-import android.os.RemoteException;
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
@@ -407,6 +404,7 @@
final byte[] matchFilter = { 1, 12, 3, 31, 32 }; // bad data!
final int messageId = 2123;
final int reason = AWARE_STATUS_ERROR;
+ final int distanceMm = 100;
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
mockSubscribeSession);
@@ -442,6 +440,8 @@
// (3) ...
subscribeSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes());
sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), matchFilter);
+ sessionProxyCallback.getValue().onMatchWithDistance(peerHandle.peerId, string1.getBytes(),
+ matchFilter, distanceMm);
sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes());
sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
@@ -450,7 +450,9 @@
inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId),
eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0));
inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(),
- eq(string1.getBytes()), (List<byte[]>) isNull());
+ eq(string1.getBytes()), isNull());
+ inOrder.verify(mockSessionCallback).onServiceDiscoveredWithinRange(peerIdCaptor.capture(),
+ eq(string1.getBytes()), isNull(), eq(distanceMm));
assertEquals((peerIdCaptor.getValue()).peerId, peerHandle.peerId);
inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
eq(string1.getBytes()));
@@ -685,11 +687,18 @@
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
collector.checkThat("mServiceName", subscribeConfig.mServiceName, equalTo(null));
+ collector.checkThat("mServiceSpecificInfo", subscribeConfig.mServiceSpecificInfo,
+ equalTo(null));
+ collector.checkThat("mMatchFilter", subscribeConfig.mMatchFilter, equalTo(null));
collector.checkThat("mSubscribeType", subscribeConfig.mSubscribeType,
equalTo(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE));
collector.checkThat("mTtlSec", subscribeConfig.mTtlSec, equalTo(0));
collector.checkThat("mEnableTerminateNotification",
subscribeConfig.mEnableTerminateNotification, equalTo(true));
+ collector.checkThat("mMinDistanceCmSet", subscribeConfig.mMinDistanceMmSet, equalTo(false));
+ collector.checkThat("mMinDistanceMm", subscribeConfig.mMinDistanceMm, equalTo(0));
+ collector.checkThat("mMaxDistanceMmSet", subscribeConfig.mMaxDistanceMmSet, equalTo(false));
+ collector.checkThat("mMaxDistanceMm", subscribeConfig.mMaxDistanceMm, equalTo(0));
}
@Test
@@ -698,16 +707,19 @@
final String serviceSpecificInfo = "long arbitrary string with some info";
final byte[] matchFilter = { 1, 16, 1, 22 };
final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
- final int subscribeCount = 10;
final int subscribeTtl = 15;
final boolean enableTerminateNotification = false;
+ final int minDistance = 10;
+ final int maxDistance = 50;
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
- new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+ new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setSubscribeType(subscribeType)
.setTtlSec(subscribeTtl)
- .setTerminateNotificationEnabled(enableTerminateNotification).build();
+ .setTerminateNotificationEnabled(enableTerminateNotification)
+ .setMinDistanceMm(minDistance)
+ .setMaxDistanceMm(maxDistance).build();
collector.checkThat("mServiceName", serviceName.getBytes(),
equalTo(subscribeConfig.mServiceName));
@@ -719,6 +731,10 @@
collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeConfig.mTtlSec));
collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
equalTo(subscribeConfig.mEnableTerminateNotification));
+ collector.checkThat("mMinDistanceMmSet", true, equalTo(subscribeConfig.mMinDistanceMmSet));
+ collector.checkThat("mMinDistanceMm", minDistance, equalTo(subscribeConfig.mMinDistanceMm));
+ collector.checkThat("mMaxDistanceMmSet", true, equalTo(subscribeConfig.mMaxDistanceMmSet));
+ collector.checkThat("mMaxDistanceMm", maxDistance, equalTo(subscribeConfig.mMaxDistanceMm));
}
@Test
@@ -729,13 +745,17 @@
final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
final int subscribeTtl = 15;
final boolean enableTerminateNotification = true;
+ final int minDistance = 10;
+ final int maxDistance = 50;
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setSubscribeType(subscribeType)
.setTtlSec(subscribeTtl)
- .setTerminateNotificationEnabled(enableTerminateNotification).build();
+ .setTerminateNotificationEnabled(enableTerminateNotification)
+ .setMinDistanceMm(minDistance)
+ .setMaxDistanceMm(maxDistance).build();
Parcel parcelW = Parcel.obtain();
subscribeConfig.writeToParcel(parcelW, 0);
@@ -769,11 +789,15 @@
PublishConfig publishConfig = new PublishConfig.Builder().build();
collector.checkThat("mServiceName", publishConfig.mServiceName, equalTo(null));
+ collector.checkThat("mServiceSpecificInfo", publishConfig.mServiceSpecificInfo,
+ equalTo(null));
+ collector.checkThat("mMatchFilter", publishConfig.mMatchFilter, equalTo(null));
collector.checkThat("mPublishType", publishConfig.mPublishType,
equalTo(PublishConfig.PUBLISH_TYPE_UNSOLICITED));
collector.checkThat("mTtlSec", publishConfig.mTtlSec, equalTo(0));
collector.checkThat("mEnableTerminateNotification",
publishConfig.mEnableTerminateNotification, equalTo(true));
+ collector.checkThat("mEnableRanging", publishConfig.mEnableRanging, equalTo(false));
}
@Test
@@ -782,16 +806,17 @@
final String serviceSpecificInfo = "long arbitrary string with some info";
final byte[] matchFilter = { 1, 16, 1, 22 };
final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
- final int publishCount = 10;
final int publishTtl = 15;
final boolean enableTerminateNotification = false;
+ final boolean enableRanging = true;
PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setPublishType(publishType)
.setTtlSec(publishTtl)
- .setTerminateNotificationEnabled(enableTerminateNotification).build();
+ .setTerminateNotificationEnabled(enableTerminateNotification)
+ .setRangingEnabled(enableRanging).build();
collector.checkThat("mServiceName", serviceName.getBytes(),
equalTo(publishConfig.mServiceName));
@@ -802,6 +827,7 @@
collector.checkThat("mTtlSec", publishTtl, equalTo(publishConfig.mTtlSec));
collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
equalTo(publishConfig.mEnableTerminateNotification));
+ collector.checkThat("mEnableRanging", enableRanging, equalTo(publishConfig.mEnableRanging));
}
@Test
@@ -810,16 +836,17 @@
final String serviceSpecificInfo = "long arbitrary string with some info";
final byte[] matchFilter = { 1, 16, 1, 22 };
final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
- final int publishCount = 10;
final int publishTtl = 15;
final boolean enableTerminateNotification = false;
+ final boolean enableRanging = true;
PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setPublishType(publishType)
.setTtlSec(publishTtl)
- .setTerminateNotificationEnabled(enableTerminateNotification).build();
+ .setTerminateNotificationEnabled(enableTerminateNotification)
+ .setRangingEnabled(enableRanging).build();
Parcel parcelW = Parcel.obtain();
publishConfig.writeToParcel(parcelW, 0);
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 33bd982..300d425 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -85,8 +85,8 @@
// verify ranging request passed to service
mDut.startRanging(request, callbackMock, mMockLooperHandler);
- verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(request),
- callbackCaptor.capture());
+ verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(null),
+ eq(request), callbackCaptor.capture());
// service calls back with success
callbackCaptor.getValue().onRangingResults(results);
@@ -109,8 +109,8 @@
// verify ranging request passed to service
mDut.startRanging(request, callbackMock, mMockLooperHandler);
- verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(request),
- callbackCaptor.capture());
+ verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(null),
+ eq(request), callbackCaptor.capture());
// service calls back with failure code
callbackCaptor.getValue().onRangingFailure(failureCode);