Merge "Support insets on secondary displays"
diff --git a/Android.bp b/Android.bp
index 5c1ccb7..2c4963c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,6 +58,7 @@
// runtime, as well as the only protos that are actually
// needed by the device.
srcs: [
+ "core/proto/android/os/cpuinfo.proto",
"core/proto/android/os/kernelwake.proto",
"core/proto/android/os/pagetypeinfo.proto",
"core/proto/android/os/procrank.proto",
@@ -81,6 +82,7 @@
],
srcs: [
+ "core/proto/android/os/cpuinfo.proto",
"core/proto/android/os/kernelwake.proto",
"core/proto/android/os/pagetypeinfo.proto",
"core/proto/android/os/procrank.proto",
diff --git a/Android.mk b/Android.mk
index 817aa80..b847425 100644
--- a/Android.mk
+++ b/Android.mk
@@ -159,6 +159,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 \
@@ -1557,6 +1558,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/api/current.txt b/api/current.txt
index 633205e..9994927 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);
@@ -5049,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();
@@ -5063,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[]);
@@ -5073,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>);
@@ -8533,7 +8533,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();
@@ -9106,6 +9106,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";
@@ -9272,7 +9273,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);
@@ -9878,7 +9879,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();
@@ -9911,15 +9912,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);
}
@@ -11227,6 +11228,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 {
@@ -31841,6 +31851,14 @@
method public static void setVmPolicy(android.os.StrictMode.VmPolicy);
}
+ public static abstract interface StrictMode.OnThreadViolationListener {
+ method public abstract void onThreadViolation(android.os.strictmode.Violation);
+ }
+
+ public static abstract interface StrictMode.OnVmViolationListener {
+ method public abstract void onVmViolation(android.os.strictmode.Violation);
+ }
+
public static final class StrictMode.ThreadPolicy {
field public static final android.os.StrictMode.ThreadPolicy LAX;
}
@@ -31861,6 +31879,7 @@
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
+ method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -31892,6 +31911,7 @@
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
+ method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
}
@@ -32256,6 +32276,62 @@
}
+package android.os.strictmode {
+
+ public final class CleartextNetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class CustomViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskReadViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskWriteViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class FileUriExposedViolation extends android.os.strictmode.Violation {
+ }
+
+ public class InstanceCountViolation extends android.os.strictmode.Violation {
+ method public long getNumberOfInstances();
+ }
+
+ public final class IntentReceiverLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class LeakedClosableViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class NetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ResourceMismatchViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ServiceConnectionLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class SqliteObjectLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
+ }
+
+ public abstract class Violation extends java.lang.Throwable {
+ }
+
+ public final class WebViewMethodCalledOnWrongThreadViolation extends android.os.strictmode.Violation {
+ }
+
+}
+
package android.preference {
public class CheckBoxPreference extends android.preference.TwoStatePreference {
@@ -32542,7 +32618,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);
@@ -32553,7 +32629,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);
}
@@ -39260,7 +39336,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);
@@ -39392,6 +39470,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);
@@ -39582,6 +39661,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";
@@ -49904,7 +49984,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 348fe64..9d2fb10 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);
@@ -5235,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();
@@ -5249,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[]);
@@ -5259,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>);
@@ -9045,7 +9045,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();
@@ -9627,6 +9627,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";
@@ -9806,7 +9807,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);
@@ -10441,7 +10442,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();
@@ -10474,15 +10475,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);
}
@@ -11960,6 +11961,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 {
@@ -34630,6 +34640,14 @@
method public static void setVmPolicy(android.os.StrictMode.VmPolicy);
}
+ public static abstract interface StrictMode.OnThreadViolationListener {
+ method public abstract void onThreadViolation(android.os.strictmode.Violation);
+ }
+
+ public static abstract interface StrictMode.OnVmViolationListener {
+ method public abstract void onVmViolation(android.os.strictmode.Violation);
+ }
+
public static final class StrictMode.ThreadPolicy {
field public static final android.os.StrictMode.ThreadPolicy LAX;
}
@@ -34650,6 +34668,7 @@
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
+ method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -34681,6 +34700,7 @@
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
+ method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
}
@@ -34809,11 +34829,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);
@@ -35128,6 +35150,62 @@
}
+package android.os.strictmode {
+
+ public final class CleartextNetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class CustomViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskReadViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskWriteViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class FileUriExposedViolation extends android.os.strictmode.Violation {
+ }
+
+ public class InstanceCountViolation extends android.os.strictmode.Violation {
+ method public long getNumberOfInstances();
+ }
+
+ public final class IntentReceiverLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class LeakedClosableViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class NetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ResourceMismatchViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ServiceConnectionLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class SqliteObjectLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
+ }
+
+ public abstract class Violation extends java.lang.Throwable {
+ }
+
+ public final class WebViewMethodCalledOnWrongThreadViolation extends android.os.strictmode.Violation {
+ }
+
+}
+
package android.permissionpresenterservice {
public abstract class RuntimePermissionPresenterService extends android.app.Service {
@@ -35426,7 +35504,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);
@@ -35437,7 +35515,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);
}
@@ -42586,7 +42664,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);
@@ -42725,6 +42805,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);
@@ -42918,6 +42999,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";
@@ -43056,6 +43138,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);
}
@@ -53829,7 +53912,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 a06068d7..a583090 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);
@@ -5078,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();
@@ -5092,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[]);
@@ -5102,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>);
@@ -8608,7 +8608,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();
@@ -9183,6 +9183,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";
@@ -9350,7 +9351,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);
@@ -9956,7 +9957,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();
@@ -9989,15 +9990,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);
}
@@ -11316,6 +11317,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 {
@@ -32083,6 +32093,14 @@
field public static final int DETECT_VM_UNTAGGED_SOCKET = -2147483648; // 0x80000000
}
+ public static abstract interface StrictMode.OnThreadViolationListener {
+ method public abstract void onThreadViolation(android.os.strictmode.Violation);
+ }
+
+ public static abstract interface StrictMode.OnVmViolationListener {
+ method public abstract void onVmViolation(android.os.strictmode.Violation);
+ }
+
public static final class StrictMode.ThreadPolicy {
field public static final android.os.StrictMode.ThreadPolicy LAX;
}
@@ -32103,6 +32121,7 @@
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
+ method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -32158,6 +32177,7 @@
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
+ method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
}
@@ -32525,6 +32545,62 @@
}
+package android.os.strictmode {
+
+ public final class CleartextNetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class CustomViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskReadViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskWriteViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class FileUriExposedViolation extends android.os.strictmode.Violation {
+ }
+
+ public class InstanceCountViolation extends android.os.strictmode.Violation {
+ method public long getNumberOfInstances();
+ }
+
+ public final class IntentReceiverLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class LeakedClosableViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class NetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ResourceMismatchViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ServiceConnectionLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class SqliteObjectLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
+ }
+
+ public abstract class Violation extends java.lang.Throwable {
+ }
+
+ public final class WebViewMethodCalledOnWrongThreadViolation extends android.os.strictmode.Violation {
+ }
+
+}
+
package android.preference {
public class CheckBoxPreference extends android.preference.TwoStatePreference {
@@ -32811,7 +32887,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);
@@ -32822,7 +32898,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);
}
@@ -39651,7 +39727,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);
@@ -39786,6 +39864,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);
@@ -39989,6 +40068,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";
@@ -50552,7 +50632,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..0b51e66 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -22,19 +22,28 @@
#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);
+static std::string trim(const std::string& s, const std::string& chars) {
+ const auto head = s.find_first_not_of(chars);
if (head == std::string::npos) return "";
- const auto tail = s.find_last_not_of(DEFAULT_WHITESPACE);
+ const auto tail = s.find_last_not_of(chars);
return s.substr(head, tail - head + 1);
}
+static std::string trimDefault(const std::string& s) {
+ return trim(s, DEFAULT_WHITESPACE);
+}
+
static std::string trimHeader(const std::string& s) {
- std::string res = trim(s);
+ std::string res = trimDefault(s);
std::transform(res.begin(), res.end(), res.begin(), ::tolower);
return res;
}
@@ -68,22 +77,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 +150,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) {
@@ -162,10 +188,41 @@
}
// ==============================================================================
+static int
+lookupName(const char** names, const int size, const char* name)
+{
+ for (int i=0; i<size; i++) {
+ if (strcmp(name, names[i]) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+EnumTypeMap::EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount)
+ :mEnumNames(enumNames),
+ mEnumValues(enumValues),
+ mEnumCount(enumCount)
+{
+}
+
+EnumTypeMap::~EnumTypeMap()
+{
+}
+
+int
+EnumTypeMap::parseValue(const std::string& value)
+{
+ int index = lookupName(mEnumNames, mEnumCount, value.c_str());
+ if (index < 0) return mEnumValues[0]; // Assume value 0 is default
+ return mEnumValues[index];
+}
+
Table::Table(const char* names[], const uint64_t ids[], const int count)
:mFieldNames(names),
mFieldIds(ids),
- mFieldCount(count)
+ mFieldCount(count),
+ mEnums()
{
}
@@ -173,41 +230,54 @@
{
}
-bool
-Table::insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value)
+void
+Table::addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t 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;
- }
- }
+ int index = lookupName(mFieldNames, mFieldCount, field);
+ if (index < 0) return;
+ EnumTypeMap enu(enumNames, enumValues, enumSize);
+ mEnums[index] = enu;
+}
+
+bool
+Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
+{
+ int index = lookupName(mFieldNames, mFieldCount, name.c_str());
+ if (index < 0) return false;
+
+ uint64_t found = mFieldIds[index];
switch (found & FIELD_TYPE_MASK) {
case FIELD_TYPE_DOUBLE:
case FIELD_TYPE_FLOAT:
- // TODO: support parse string to float/double
- return false;
+ proto->write(found, toDouble(value));
+ break;
case FIELD_TYPE_STRING:
case FIELD_TYPE_BYTES:
- proto.write(found, value);
+ proto->write(found, value);
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));
+ proto->write(found, toLongLong(value));
break;
case FIELD_TYPE_BOOL:
+ return false;
case FIELD_TYPE_ENUM:
+ if (mEnums.find(index) == mEnums.end()) {
+ // forget to add enum type mapping
+ return false;
+ }
+ proto->write(found, mEnums[index].parseValue(value));
+ break;
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));
+ proto->write(found, toInt(value));
break;
default:
return false;
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index 86761e9..e8366fa 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,13 @@
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);
/**
* When a text has a table format like this
@@ -47,19 +52,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 +88,29 @@
{
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; };
+};
+
+class EnumTypeMap
+{
+public:
+ EnumTypeMap() {};
+ EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount);
+ ~EnumTypeMap();
+
+ int parseValue(const std::string& value);
+
+private:
+ const char** mEnumNames;
+ const uint32_t* mEnumValues;
+ int mEnumCount;
};
/**
@@ -98,12 +123,15 @@
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 uint32_t enumValues[], const int enumSize);
+ bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
private:
const char** mFieldNames;
const uint64_t* mFieldIds;
const int mFieldCount;
+ map<int, EnumTypeMap> mEnums;
};
#endif // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 3da87b9c..5ebe9bd 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "incident_helper"
+#include "parsers/CpuInfoParser.h"
#include "parsers/KernelWakesParser.h"
#include "parsers/PageTypeInfoParser.h"
#include "parsers/ProcrankParser.h"
@@ -54,6 +55,8 @@
return new PageTypeInfoParser();
case 2002:
return new KernelWakesParser();
+ case 2003:
+ return new CpuInfoParser();
default:
return NULL;
}
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/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/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
new file mode 100644
index 0000000..57ad15c
--- /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, HasSwapInfo) {
+ 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/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/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/statsd/Android.mk b/cmds/statsd/Android.mk
index d99136f..0f6d868 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -31,7 +31,9 @@
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 \
@@ -151,18 +153,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
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 7ff42b6..8c70bb5 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,7 +47,7 @@
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)
@@ -119,8 +120,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.
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 1d7e5a61..fa92f65 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -157,7 +157,7 @@
/**
* Fetches external metrics.
*/
- StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+ StatsPullerManager mStatsPullerManager;
/**
* Tracks the configurations that have been passed to statsd.
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..a694dbf 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,166 @@
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;
+ 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;
+ if (matchedState < 0) {
+ conditionChangedCache[mIndex] = false;
conditionCache[mIndex] = mNonSlicedConditionState;
- return false;
+ return;
}
- 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;
- }
- VLOG("key: %s %d", hashableKey.c_str(), newCondition);
- // dump all dimensions for debugging
- if (DEBUG) {
- print(mSlicedConditionState, mName);
- }
- }
-
- // 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/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 94566ff..d86ab57 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -200,7 +200,7 @@
durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
keyMatcher = durationMetric->add_dimension();
keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
- durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+ durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
@@ -214,7 +214,7 @@
durationMetric->set_type(DurationMetric_AggregationType_DURATION_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_what("WL_HELD_PER_APP_PER_NAME");
durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
@@ -226,7 +226,7 @@
durationMetric->set_metric_id(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_what("WL_HELD_PER_APP_PER_NAME");
durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
@@ -334,12 +334,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 +350,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 +360,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 +368,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/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
new file mode 100644
index 0000000..e004d21
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.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 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);
+ auto elemList = ptr->GetAndroidLogEventList();
+ *elemList << uid;
+ *elemList << idx;
+ *elemList << 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..b84b877
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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);
+ auto elemList = ptr->GetAndroidLogEventList();
+ *elemList << uid;
+ *elemList << userTimeMs;
+ *elemList << 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..319feef4 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,8 +91,8 @@
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 statePtr = make_shared<LogEvent>(
+ android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, timestamp);
auto elemList = statePtr->GetAndroidLogEventList();
*elemList << state.name;
*elemList << state.residencyInMsecSinceBoot;
@@ -107,12 +104,14 @@
(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 voterPtr =
+ make_shared<LogEvent>(android::util::POWER_STATE_VOTER_PULLED, timestamp);
auto elemList = voterPtr->GetAndroidLogEventList();
*elemList << state.name;
*elemList << voter.name;
*elemList << voter.totalTimeInMsecVotedForSinceBoot;
*elemList << 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,7 +140,7 @@
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);
+ android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, timestamp);
auto elemList = subsystemStatePtr->GetAndroidLogEventList();
*elemList << subsystem.name;
*elemList << state.name;
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/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 0c9e522..2252201 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -24,6 +24,7 @@
#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;
@@ -134,11 +135,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 +157,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,
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 2783bcea..74ce9cd 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -23,6 +23,7 @@
#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;
@@ -176,11 +177,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 +199,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,
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index f516cc7..a65092f 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -23,6 +23,7 @@
#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;
@@ -94,6 +95,7 @@
std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
+ mByteSize = 0;
return buffer;
}
@@ -111,16 +113,18 @@
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);
event.ToProto(*mProto);
mProto->end(eventToken);
mProto->end(wrapperToken);
+ // TODO: Find a proper way to derive the size of incoming LogEvent.
}
size_t EventMetricProducer::byteSize() {
- return mProto->size();
+ return mByteSize;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 0dccdf4..7740621 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -65,6 +65,8 @@
private:
const EventMetric mMetric;
+
+ size_t mByteSize;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 285d29b..fafeee4 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -24,6 +24,7 @@
#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;
@@ -123,11 +124,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 +146,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,
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index d80672d..f9e4deb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -81,7 +81,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;
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/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 19317ee..e8a862f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -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);
}
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 2a63073..5cffec1 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -23,6 +23,7 @@
#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;
@@ -30,6 +31,7 @@
using android::util::FIELD_TYPE_MESSAGE;
using android::util::ProtoOutputStream;
using std::list;
+using std::make_pair;
using std::make_shared;
using std::map;
using std::shared_ptr;
@@ -61,13 +63,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,8 +90,9 @@
}
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);
@@ -88,8 +101,19 @@
(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) {
@@ -119,11 +143,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 +165,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,
@@ -168,22 +195,22 @@
}
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);
}
@@ -194,12 +221,16 @@
}
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) {
+ flush_if_needed(eventTime);
+ }
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, true);
}
@@ -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) {
+ } else {
flush_if_needed(eventTimeNs);
+ interval.raw.push_back(make_pair(value, 0));
}
}
@@ -249,7 +292,7 @@
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;
}
}
@@ -267,13 +310,21 @@
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ 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);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index ef9868b..c6c87f5 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"
@@ -71,7 +72,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 +88,7 @@
// internal state of a bucket.
typedef struct {
std::vector<std::pair<long, long>> raw;
+ bool tainted;
} Interval;
std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
@@ -97,6 +105,10 @@
void flush_if_needed(const uint64_t eventTimeNs);
size_t mByteSize;
+
+ FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
+ FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index ca9cdfb..226e4d1 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -195,7 +195,7 @@
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.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index e089d065..edf3af0 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 {
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 7b27642..2398814 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -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/stats_events.proto b/cmds/statsd/src/stats_events.proto
index 8816795..9ab07de 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/stats_events.proto
@@ -86,6 +86,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;
}
}
@@ -849,6 +852,7 @@
}
/**
+<<<<<<< HEAD
* Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky
* behavior in its own uid. However, the metrics of these isolated uid's almost always should be
* attributed back to the parent (host) uid. One example is Chrome.
@@ -866,3 +870,42 @@
// be removed before if it's used for another parent uid.
optional int32 is_create = 3;
}
+
+/*
+<<<<<<< HEAD
+ * 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/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 3b8eeaf..a07f76a 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -83,7 +83,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 {
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 2935ac71..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..05aad29
--- /dev/null
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -0,0 +1,469 @@
+// 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) {
+ auto list = event->GetAndroidLogEventList();
+ *list << uid; // uid
+ *list << wl;
+ *list << 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]);
+
+ // 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/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
new file mode 100644
index 0000000..2a26388
--- /dev/null
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -0,0 +1,299 @@
+// 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_metric_id(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);
+ auto list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 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);
+ list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 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);
+ list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 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_metric_id(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);
+ auto list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 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);
+ auto list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 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);
+ auto list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 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_metric_id(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);
+ auto list = event1->GetAndroidLogEventList();
+ *list << 1;
+ *list << 10;
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ auto list2 = event2->GetAndroidLogEventList();
+ *list2 << 1;
+ *list2 << 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.flush_if_needed(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/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/app/Activity.java b/core/java/android/app/Activity.java
index a8863bf..99f3dee 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) {
}
@@ -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/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/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/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 4efc2c7..d813d66 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;
@@ -922,6 +924,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/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 4035ee1..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);
@@ -417,7 +417,7 @@
return -1;
}
try {
- mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName,
+ mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
mUuid, mPort, getSecurityFlags());
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
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/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index f8c139f..2d490a0 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -22,6 +22,7 @@
import android.database.CrossProcessCursorWrapper;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
@@ -102,8 +103,16 @@
if (sAnrHandler == null) {
sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
}
+
+ // If the remote process hangs, we're going to kill it, so we're
+ // technically okay doing blocking calls.
+ Binder.allowBlocking(mContentProvider.asBinder());
} else {
mAnrRunnable = null;
+
+ // If we're no longer watching for hangs, revert back to default
+ // blocking behavior.
+ Binder.defaultBlocking(mContentProvider.asBinder());
}
}
}
@@ -511,6 +520,10 @@
private boolean closeInternal() {
mCloseGuard.close();
if (mClosed.compareAndSet(false, true)) {
+ // We can't do ANR checks after we cease to exist! Reset any
+ // blocking behavior changes we might have made.
+ setDetectNotResponding(0);
+
if (mStable) {
return mContentResolver.releaseProvider(mContentProvider);
} else {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 01ad3ad..72f75112 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4073,6 +4073,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/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/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/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/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/os/Binder.java b/core/java/android/os/Binder.java
index 2bfb013..b5bcd02 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -193,6 +193,19 @@
}
/**
+ * Reset the given interface back to the default blocking behavior,
+ * reverting any changes made by {@link #allowBlocking(IBinder)}.
+ *
+ * @hide
+ */
+ public static IBinder defaultBlocking(IBinder binder) {
+ if (binder instanceof BinderProxy) {
+ ((BinderProxy) binder).mWarnOnBlocking = sWarnOnBlocking;
+ }
+ return binder;
+ }
+
+ /**
* Inherit the current {@link #allowBlocking(IBinder)} value from one given
* interface to another.
*
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e426356..5d96fd3 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -94,4 +94,5 @@
boolean isUserUnlocked(int userId);
boolean isUserRunning(int userId);
boolean isUserNameSet(int userHandle);
+ boolean hasRestrictedProfiles();
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 02c82d7..f90604a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -29,8 +29,6 @@
import android.content.pm.ApplicationInfo;
import android.net.TrafficStats;
import android.net.Uri;
-import android.os.StrictMode.ThreadPolicy;
-import android.os.StrictMode.VmPolicy;
import android.os.strictmode.CleartextNetworkViolation;
import android.os.strictmode.ContentUriWithoutPermissionViolation;
import android.os.strictmode.CustomViolation;
@@ -44,7 +42,7 @@
import android.os.strictmode.ResourceMismatchViolation;
import android.os.strictmode.ServiceConnectionLeakedViolation;
import android.os.strictmode.SqliteObjectLeakedViolation;
-import android.os.strictmode.UnbufferedIOViolation;
+import android.os.strictmode.UnbufferedIoViolation;
import android.os.strictmode.UntaggedSocketViolation;
import android.os.strictmode.Violation;
import android.os.strictmode.WebViewMethodCalledOnWrongThreadViolation;
@@ -385,8 +383,6 @@
/**
* When #{@link ThreadPolicy.Builder#penaltyListener} is enabled, the listener is called on the
* provided executor when a Thread violation occurs.
- *
- * @hide
*/
public interface OnThreadViolationListener {
/** Called on a thread policy violation. */
@@ -396,8 +392,6 @@
/**
* When #{@link VmPolicy.Builder#penaltyListener} is enabled, the listener is called on the
* provided executor when a VM violation occurs.
- *
- * @hide
*/
public interface OnVmViolationListener {
/** Called on a VM policy violation. */
@@ -640,8 +634,6 @@
/**
* Call #{@link OnThreadViolationListener#onThreadViolation(Violation)} on specified
* executor every violation.
- *
- * @hide
*/
public Builder penaltyListener(
@NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
@@ -977,8 +969,6 @@
/**
* Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
- *
- * @hide
*/
public Builder penaltyListener(
@NonNull OnVmViolationListener listener, @NonNull Executor executor) {
@@ -1449,7 +1439,7 @@
if (tooManyViolationsThisLoop()) {
return;
}
- startHandlingViolationException(new UnbufferedIOViolation());
+ startHandlingViolationException(new UnbufferedIoViolation());
}
// Part of BlockGuard.Policy interface:
@@ -2552,7 +2542,7 @@
return DETECT_CUSTOM;
} else if (mViolation instanceof ResourceMismatchViolation) {
return DETECT_RESOURCE_MISMATCH;
- } else if (mViolation instanceof UnbufferedIOViolation) {
+ } else if (mViolation instanceof UnbufferedIoViolation) {
return DETECT_UNBUFFERED_IO;
} else if (mViolation instanceof SqliteObjectLeakedViolation) {
return DETECT_VM_CURSOR_LEAKS;
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/strictmode/CleartextNetworkViolation.java b/core/java/android/os/strictmode/CleartextNetworkViolation.java
index 893780d..6a0d381 100644
--- a/core/java/android/os/strictmode/CleartextNetworkViolation.java
+++ b/core/java/android/os/strictmode/CleartextNetworkViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class CleartextNetworkViolation extends Violation {
+ /** @hide */
public CleartextNetworkViolation(String msg) {
super(msg);
}
diff --git a/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java b/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java
index 017c4f9..e78dc79 100644
--- a/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java
+++ b/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java
@@ -17,8 +17,8 @@
import android.net.Uri;
-/** @hide */
public final class ContentUriWithoutPermissionViolation extends Violation {
+ /** @hide */
public ContentUriWithoutPermissionViolation(Uri uri, String location) {
super(
uri
diff --git a/core/java/android/os/strictmode/CustomViolation.java b/core/java/android/os/strictmode/CustomViolation.java
index bc1656d..d4ad067 100644
--- a/core/java/android/os/strictmode/CustomViolation.java
+++ b/core/java/android/os/strictmode/CustomViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class CustomViolation extends Violation {
+ /** @hide */
public CustomViolation(String name) {
super(name);
}
diff --git a/core/java/android/os/strictmode/DiskReadViolation.java b/core/java/android/os/strictmode/DiskReadViolation.java
index 2edd33e..fad32db 100644
--- a/core/java/android/os/strictmode/DiskReadViolation.java
+++ b/core/java/android/os/strictmode/DiskReadViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class DiskReadViolation extends Violation {
+ /** @hide */
public DiskReadViolation() {
super(null);
}
diff --git a/core/java/android/os/strictmode/DiskWriteViolation.java b/core/java/android/os/strictmode/DiskWriteViolation.java
index 6465718..cb9ca38 100644
--- a/core/java/android/os/strictmode/DiskWriteViolation.java
+++ b/core/java/android/os/strictmode/DiskWriteViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class DiskWriteViolation extends Violation {
+ /** @hide */
public DiskWriteViolation() {
super(null);
}
diff --git a/core/java/android/os/strictmode/FileUriExposedViolation.java b/core/java/android/os/strictmode/FileUriExposedViolation.java
index 5f71ee5..e3e6f83 100644
--- a/core/java/android/os/strictmode/FileUriExposedViolation.java
+++ b/core/java/android/os/strictmode/FileUriExposedViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class FileUriExposedViolation extends Violation {
+ /** @hide */
public FileUriExposedViolation(String msg) {
super(msg);
}
diff --git a/core/java/android/os/strictmode/InstanceCountViolation.java b/core/java/android/os/strictmode/InstanceCountViolation.java
index d3da800..9ee2c8e 100644
--- a/core/java/android/os/strictmode/InstanceCountViolation.java
+++ b/core/java/android/os/strictmode/InstanceCountViolation.java
@@ -15,7 +15,6 @@
*/
package android.os.strictmode;
-/** @hide */
public class InstanceCountViolation extends Violation {
private final long mInstances;
@@ -24,6 +23,7 @@
"android.os.StrictMode", "setClassInstanceLimit", "StrictMode.java", 1)
};
+ /** @hide */
public InstanceCountViolation(Class klass, long instances, int limit) {
super(klass.toString() + "; instances=" + instances + "; limit=" + limit);
setStackTrace(FAKE_STACK);
diff --git a/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java b/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java
index 1d1dfc0..f416c94 100644
--- a/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java
+++ b/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class IntentReceiverLeakedViolation extends Violation {
+ /** @hide */
public IntentReceiverLeakedViolation(Throwable originStack) {
super(null);
setStackTrace(originStack.getStackTrace());
diff --git a/core/java/android/os/strictmode/LeakedClosableViolation.java b/core/java/android/os/strictmode/LeakedClosableViolation.java
index de12533..c795a6b 100644
--- a/core/java/android/os/strictmode/LeakedClosableViolation.java
+++ b/core/java/android/os/strictmode/LeakedClosableViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class LeakedClosableViolation extends Violation {
+ /** @hide */
public LeakedClosableViolation(String message, Throwable allocationSite) {
super(message);
initCause(allocationSite);
diff --git a/core/java/android/os/strictmode/NetworkViolation.java b/core/java/android/os/strictmode/NetworkViolation.java
index 637d0b6..abcf009 100644
--- a/core/java/android/os/strictmode/NetworkViolation.java
+++ b/core/java/android/os/strictmode/NetworkViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class NetworkViolation extends Violation {
+ /** @hide */
public NetworkViolation() {
super(null);
}
diff --git a/core/java/android/os/strictmode/ResourceMismatchViolation.java b/core/java/android/os/strictmode/ResourceMismatchViolation.java
index 01ae7f8..97c4499 100644
--- a/core/java/android/os/strictmode/ResourceMismatchViolation.java
+++ b/core/java/android/os/strictmode/ResourceMismatchViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class ResourceMismatchViolation extends Violation {
+ /** @hide */
public ResourceMismatchViolation(Object tag) {
super(tag.toString());
}
diff --git a/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java b/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java
index 5bab3bd..2d6b58f 100644
--- a/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java
+++ b/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class ServiceConnectionLeakedViolation extends Violation {
+ /** @hide */
public ServiceConnectionLeakedViolation(Throwable originStack) {
super(null);
setStackTrace(originStack.getStackTrace());
diff --git a/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java b/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java
index 8360088..0200220 100644
--- a/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java
+++ b/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java
@@ -15,9 +15,9 @@
*/
package android.os.strictmode;
-/** @hide */
public final class SqliteObjectLeakedViolation extends Violation {
+ /** @hide */
public SqliteObjectLeakedViolation(String message, Throwable originStack) {
super(message);
initCause(originStack);
diff --git a/core/java/android/os/strictmode/UnbufferedIOViolation.java b/core/java/android/os/strictmode/UnbufferedIoViolation.java
similarity index 69%
rename from core/java/android/os/strictmode/UnbufferedIOViolation.java
rename to core/java/android/os/strictmode/UnbufferedIoViolation.java
index 571ba50..a5c326d 100644
--- a/core/java/android/os/strictmode/UnbufferedIOViolation.java
+++ b/core/java/android/os/strictmode/UnbufferedIoViolation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -15,9 +15,14 @@
*/
package android.os.strictmode;
-/** @hide */
-public final class UnbufferedIOViolation extends Violation {
- public UnbufferedIOViolation() {
+import android.os.StrictMode.ThreadPolicy.Builder;
+
+/**
+ * See #{@link Builder#detectUnbufferedIo()}
+ */
+public final class UnbufferedIoViolation extends Violation {
+ /** @hide */
+ public UnbufferedIoViolation() {
super(null);
}
}
diff --git a/core/java/android/os/strictmode/UntaggedSocketViolation.java b/core/java/android/os/strictmode/UntaggedSocketViolation.java
index 9752753..836a8b9 100644
--- a/core/java/android/os/strictmode/UntaggedSocketViolation.java
+++ b/core/java/android/os/strictmode/UntaggedSocketViolation.java
@@ -15,13 +15,13 @@
*/
package android.os.strictmode;
-/** @hide */
public final class UntaggedSocketViolation extends Violation {
/** @hide */
public static final String MESSAGE =
"Untagged socket detected; use"
+ " TrafficStats.setThreadSocketTag() to track all network usage";
+ /** @hide */
public UntaggedSocketViolation() {
super(MESSAGE);
}
diff --git a/core/java/android/os/strictmode/Violation.java b/core/java/android/os/strictmode/Violation.java
index ebae7fc..31c7d58 100644
--- a/core/java/android/os/strictmode/Violation.java
+++ b/core/java/android/os/strictmode/Violation.java
@@ -16,13 +16,9 @@
package android.os.strictmode;
-/**
- * Root class for all StrictMode violations.
- *
- * @hide
- */
+/** Root class for all StrictMode violations. */
public abstract class Violation extends Throwable {
- protected Violation(String message) {
+ Violation(String message) {
super(message);
}
}
diff --git a/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java b/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
index d4c557a..c328d14 100644
--- a/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
+++ b/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class WebViewMethodCalledOnWrongThreadViolation extends Violation {
+ /** @hide */
public WebViewMethodCalledOnWrongThreadViolation(Throwable originStack) {
super(null);
setStackTrace(originStack.getStackTrace());
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/Settings.java b/core/java/android/provider/Settings.java
index 1bef2b3..d4d24de 100755
--- 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.
*/
@@ -10148,8 +10158,12 @@
* <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 +10171,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 +10182,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 +10193,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 +10204,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 +10216,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 +10902,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/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 37829f0..e30496f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -513,7 +513,7 @@
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
if (!sCompatibilityDone) {
- sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
+ sAlwaysAssignFocus = true;
sCompatibilityDone = true;
}
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/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..fbf6535 100644
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -48,9 +48,13 @@
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 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";
@@ -83,7 +87,7 @@
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;
@@ -115,7 +119,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 +141,18 @@
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(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 +177,7 @@
mSessionStartTime = 0;
mLastEventTime = 0;
mSmartSelectionTriggered = false;
- mVersionTag = getVersionTag(null);
+ mModelName = getModelName(null);
mSessionId = null;
}
@@ -251,113 +259,64 @@
}
}
- 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;
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 widget = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
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 +328,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/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/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 18afe6e..ac79249 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -137,15 +137,18 @@
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) {}
+ StyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
+ std::shared_ptr<minikin::FontCollection>&& collection, bool isRtl)
+ : Run(start, end), mPaint(std::move(paint)), mCollection(std::move(collection)),
+ mIsRtl(isRtl) {}
void addTo(minikin::LineBreaker* lineBreaker) override {
- lineBreaker->addStyleRun(&mPaint, mStart, mEnd, mIsRtl);
+ lineBreaker->addStyleRun(&mPaint, mCollection, mStart, mEnd, mIsRtl);
}
private:
minikin::MinikinPaint mPaint;
+ std::shared_ptr<minikin::FontCollection> mCollection;
const bool mIsRtl;
};
@@ -173,8 +176,10 @@
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 addStyleRun(int32_t start, int32_t end, minikin::MinikinPaint&& paint,
+ std::shared_ptr<minikin::FontCollection> collection, bool isRtl) {
+ mRuns.emplace_back(std::make_unique<StyleRun>(
+ start, end, std::move(paint), std::move(collection), isRtl));
}
void addReplacementRun(int32_t start, int32_t end, float width, uint32_t localeListId) {
@@ -329,7 +334,7 @@
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
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..4c3e937 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,11 +20,12 @@
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/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/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";
@@ -68,6 +69,11 @@
(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"
+ ];
+
// System Services
optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
@@ -80,7 +86,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,
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/providers/settings.proto b/core/proto/android/providers/settings.proto
index 3411c6a..764288c 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;
@@ -287,6 +323,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 +348,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 +375,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 = 342;
}
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 +399,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 +410,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 +424,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 +438,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 +513,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 +536,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 +549,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 +570,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 +580,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 +599,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 +616,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 +657,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 +671,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/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/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/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/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/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/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/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/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 2a1a1f3..90fe0b7 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -30,43 +30,46 @@
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.fontStyle = minikin::FontStyle(minikinVariant, resolved.weight, resolved.slant);
+ 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/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/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 574bb02..3e2eeee 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -141,10 +141,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/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/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/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 67fb4d9..41b205b 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,
@@ -764,6 +869,7 @@
dumpSetting(s, p,
Settings.Global.WAIT_FOR_DEBUGGER,
GlobalSettingsProto.WAIT_FOR_DEBUGGER);
+ // Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Global.LOW_POWER_MODE,
GlobalSettingsProto.LOW_POWER_MODE);
@@ -819,6 +925,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 +1000,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 +1009,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 +1030,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 +1071,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 +1082,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 +1129,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 +1160,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 +1182,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 +1193,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 +1229,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 +1243,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 +1282,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 +1356,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 +1366,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 +1424,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 +1531,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 +1600,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 +1639,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 +1699,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 +1717,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 +1802,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 +1838,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 +1889,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 +1901,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 +1934,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 +1947,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 +1974,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 +1985,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/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/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/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 43f4fb9..fc1c84a 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -16,15 +16,21 @@
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.net.Uri;
import android.os.Binder;
+import android.os.Build;
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;
@@ -49,7 +55,8 @@
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<>();
@@ -70,6 +77,14 @@
}
};
+ 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 +117,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() {
- mOverviewProxy = null;
- }
- };
+ 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 +165,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()));
@@ -167,6 +194,7 @@
private void disconnectFromLauncherService() {
if (mOverviewProxy != null) {
+ mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
mContext.unbindService(mOverviewServiceConnection);
mOverviewProxy = null;
notifyConnectionChanged();
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/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index a436e17..5eb315e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -208,6 +208,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 +224,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;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 54d622b..c4024a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -18,46 +18,21 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.app.INotificationManager;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-import android.widget.Switch;
-import android.widget.TextView;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.statusbar.stack.StackStateAnimator;
-import java.util.Set;
-
/**
* The guts of a notification revealed when performing a long press.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
new file mode 100644
index 0000000..b585bdf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -0,0 +1,355 @@
+/*
+ * 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.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.INotificationManager;
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
+ * closing guts, and keeping track of the currently exposed notification guts.
+ */
+public class NotificationGutsManager implements Dumpable {
+ private static final String TAG = "NotificationGutsManager";
+
+ // Must match constant in Settings. Used to highlight preferences when linking to Settings.
+ private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+
+ private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ private final Set<String> mNonBlockablePkgs;
+ private final NotificationPresenter mPresenter;
+ // TODO: Create NotificationListContainer interface and use it instead of
+ // NotificationStackScrollLayout here
+ private final NotificationStackScrollLayout mStackScroller;
+ private final Context mContext;
+ private final AccessibilityManager mAccessibilityManager;
+ // which notification is currently being longpress-examined by the user
+ private NotificationGuts mNotificationGutsExposed;
+ private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
+ private final NotificationInfo.CheckSaveListener mCheckSaveListener;
+ private String mKeyToRemoveOnGutsClosed;
+
+ public NotificationGutsManager(
+ NotificationPresenter presenter,
+ NotificationStackScrollLayout stackScroller,
+ NotificationInfo.CheckSaveListener checkSaveListener,
+ Context context) {
+ mPresenter = presenter;
+ mStackScroller = stackScroller;
+ mCheckSaveListener = checkSaveListener;
+ mContext = context;
+ Resources res = context.getResources();
+
+ mNonBlockablePkgs = new HashSet<>();
+ Collections.addAll(mNonBlockablePkgs, res.getStringArray(
+ com.android.internal.R.array.config_nonBlockableNotificationPackages));
+
+ mAccessibilityManager = (AccessibilityManager)
+ mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ }
+
+ public String getKeyToRemoveOnGutsClosed() {
+ return mKeyToRemoveOnGutsClosed;
+ }
+
+ public void setKeyToRemoveOnGutsClosed(String keyToRemoveOnGutsClosed) {
+ mKeyToRemoveOnGutsClosed = keyToRemoveOnGutsClosed;
+ }
+
+ private void saveAndCloseNotificationMenu(
+ ExpandableNotificationRow row, NotificationGuts guts, View done) {
+ guts.resetFalsingCheck();
+ int[] rowLocation = new int[2];
+ int[] doneLocation = new int[2];
+ row.getLocationOnScreen(rowLocation);
+ done.getLocationOnScreen(doneLocation);
+
+ final int centerX = done.getWidth() / 2;
+ final int centerY = done.getHeight() / 2;
+ final int x = doneLocation[0] - rowLocation[0] + centerX;
+ final int y = doneLocation[1] - rowLocation[1] + centerY;
+ closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
+ true /* removeControls */, x, y, true /* resetMenu */);
+ }
+
+ /**
+ * Sends an intent to open the notification settings for a particular package and optional
+ * channel.
+ */
+ private void startAppNotificationSettingsActivity(String packageName, final int appUid,
+ final NotificationChannel channel) {
+ final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
+ intent.putExtra(Settings.EXTRA_APP_UID, appUid);
+ if (channel != null) {
+ intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
+ }
+ mPresenter.startNotificationGutsIntent(intent, appUid);
+ }
+
+ public void bindGuts(final ExpandableNotificationRow row) {
+ bindGuts(row, mGutsMenuItem);
+ }
+
+ private void bindGuts(final ExpandableNotificationRow row,
+ NotificationMenuRowPlugin.MenuItem item) {
+ row.inflateGuts();
+ row.setGutsView(item);
+ final StatusBarNotification sbn = row.getStatusBarNotification();
+ row.setTag(sbn.getPackageName());
+ final NotificationGuts guts = row.getGuts();
+ guts.setClosedListener((NotificationGuts g) -> {
+ if (!g.willBeRemoved() && !row.isRemoved()) {
+ mStackScroller.onHeightChanged(
+ row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
+ }
+ if (mNotificationGutsExposed == g) {
+ mNotificationGutsExposed = null;
+ mGutsMenuItem = null;
+ }
+ String key = sbn.getKey();
+ if (key.equals(mKeyToRemoveOnGutsClosed)) {
+ mKeyToRemoveOnGutsClosed = null;
+ mPresenter.removeNotification(key, mPresenter.getLatestRankingMap());
+ }
+ });
+
+ View gutsView = item.getGutsView();
+ if (gutsView instanceof NotificationSnooze) {
+ NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
+ snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
+ snoozeGuts.setStatusBarNotification(sbn);
+ snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
+ guts.setHeightChangedListener((NotificationGuts g) -> {
+ mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */);
+ });
+ }
+
+ if (gutsView instanceof NotificationInfo) {
+ final UserHandle userHandle = sbn.getUser();
+ PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+ userHandle.getIdentifier());
+ final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ final String pkg = sbn.getPackageName();
+ NotificationInfo info = (NotificationInfo) gutsView;
+ // Settings link is only valid for notifications that specify a user, unless this is the
+ // system user.
+ NotificationInfo.OnSettingsClickListener onSettingsClick = null;
+ if (!userHandle.equals(UserHandle.ALL)
+ || mPresenter.getCurrentUserId() == UserHandle.USER_SYSTEM) {
+ onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
+ mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
+ guts.resetFalsingCheck();
+ startAppNotificationSettingsActivity(pkg, appUid, channel);
+ };
+ }
+ final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
+ Intent intent) -> {
+ mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
+ guts.resetFalsingCheck();
+ mPresenter.startNotificationGutsIntent(intent, sbn.getUid());
+ };
+ final View.OnClickListener onDoneClick = (View v) -> {
+ saveAndCloseNotificationMenu(row, guts, v);
+ };
+
+ ArraySet<NotificationChannel> channels = new ArraySet<>();
+ channels.add(row.getEntry().channel);
+ if (row.isSummaryWithChildren()) {
+ // If this is a summary, then add in the children notification channels for the
+ // same user and pkg.
+ final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
+ final int numChildren = childrenRows.size();
+ for (int i = 0; i < numChildren; i++) {
+ final ExpandableNotificationRow childRow = childrenRows.get(i);
+ final NotificationChannel childChannel = childRow.getEntry().channel;
+ final StatusBarNotification childSbn = childRow.getStatusBarNotification();
+ if (childSbn.getUser().equals(userHandle) &&
+ childSbn.getPackageName().equals(pkg)) {
+ channels.add(childChannel);
+ }
+ }
+ }
+ try {
+ info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels),
+ row.getEntry().channel.getImportance(), sbn, onSettingsClick,
+ onAppSettingsClick, onDoneClick, mCheckSaveListener,
+ mNonBlockablePkgs);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }
+
+ /**
+ * Closes guts or notification menus that might be visible and saves any changes.
+ *
+ * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
+ * @param force true if guts should be closed regardless of state (used for snooze only).
+ * @param removeControls true if controls (e.g. info) should be closed.
+ * @param x if closed based on touch location, this is the x touch location.
+ * @param y if closed based on touch location, this is the y touch location.
+ * @param resetMenu if any notification menus that might be revealed should be closed.
+ */
+ public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
+ int x, int y, boolean resetMenu) {
+ if (mNotificationGutsExposed != null) {
+ mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
+ }
+ if (resetMenu) {
+ mStackScroller.resetExposedMenuView(false /* animate */, true /* force */);
+ }
+ }
+
+ /**
+ * Returns the exposed NotificationGuts or null if none are exposed.
+ */
+ public NotificationGuts getExposedGuts() {
+ return mNotificationGutsExposed;
+ }
+
+ public void setExposedGuts(NotificationGuts guts) {
+ mNotificationGutsExposed = guts;
+ }
+
+ /**
+ * Opens guts on the given ExpandableNotificationRow |v|.
+ *
+ * @param v ExpandableNotificationRow to open guts on
+ * @param x x coordinate of origin of circular reveal
+ * @param y y coordinate of origin of circular reveal
+ * @param item MenuItem the guts should display
+ * @return true if guts was opened
+ */
+ public boolean openGuts(View v, int x, int y,
+ NotificationMenuRowPlugin.MenuItem item) {
+ if (!(v instanceof ExpandableNotificationRow)) {
+ return false;
+ }
+
+ if (v.getWindowToken() == null) {
+ Log.e(TAG, "Trying to show notification guts, but not attached to window");
+ return false;
+ }
+
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (row.isDark()) {
+ return false;
+ }
+ v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ if (row.areGutsExposed()) {
+ closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */,
+ true /* resetMenu */);
+ return false;
+ }
+ bindGuts(row, item);
+ NotificationGuts guts = row.getGuts();
+
+ // Assume we are a status_bar_notification_row
+ if (guts == null) {
+ // This view has no guts. Examples are the more card or the dismiss all view
+ return false;
+ }
+
+ mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS);
+
+ // ensure that it's laid but not visible until actually laid out
+ guts.setVisibility(View.INVISIBLE);
+ // Post to ensure the the guts are properly laid out.
+ guts.post(new Runnable() {
+ @Override
+ public void run() {
+ if (row.getWindowToken() == null) {
+ Log.e(TAG, "Trying to show notification guts, but not attached to "
+ + "window");
+ return;
+ }
+ closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+ guts.setVisibility(View.VISIBLE);
+ final double horz = Math.max(guts.getWidth() - x, x);
+ final double vert = Math.max(guts.getHeight() - y, y);
+ final float r = (float) Math.hypot(horz, vert);
+ final Animator a
+ = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
+ a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ // Move the notification view back over the menu
+ row.resetTranslation();
+ }
+ });
+ a.start();
+ final boolean needsFalsingProtection =
+ (mPresenter.isPresenterLocked() &&
+ !mAccessibilityManager.isTouchExplorationEnabled());
+ guts.setExposed(true /* exposed */, needsFalsingProtection);
+ row.closeRemoteInput();
+ mStackScroller.onHeightChanged(row, true /* needsAnimation */);
+ mNotificationGutsExposed = guts;
+ mGutsMenuItem = item;
+ }
+ });
+ return true;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print("mKeyToRemoveOnGutsClosed: ");
+ pw.println(mKeyToRemoveOnGutsClosed);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 3b23a0c..8d1bb5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -84,7 +84,7 @@
public interface CheckSaveListener {
// Invoked when importance has changed and the NotificationInfo wants to try to save it.
// Listener should run saveImportance unless the change should be canceled.
- void checkSave(Runnable saveImportance);
+ void checkSave(Runnable saveImportance, StatusBarNotification sbn);
}
public interface OnSettingsClickListener {
@@ -409,7 +409,7 @@
public boolean handleCloseControls(boolean save, boolean force) {
if (save && hasImportanceChanged()) {
if (mCheckSaveListener != null) {
- mCheckSaveListener.checkSave(() -> { saveImportance(); });
+ mCheckSaveListener.checkSave(this::saveImportance, mSbn);
} else {
saveImportance();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
new file mode 100644
index 0000000..e65bab2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -0,0 +1,245 @@
+package com.android.systemui.statusbar;
+
+import android.app.Notification;
+import android.content.Context;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.systemui.Dumpable;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles tasks and state related to media notifications. For example, there is a 'current' media
+ * notification, which this class keeps track of.
+ */
+public class NotificationMediaManager implements Dumpable {
+ private static final String TAG = "NotificationMediaManager";
+ public static final boolean DEBUG_MEDIA = false;
+
+ private final NotificationPresenter mPresenter;
+ private final Context mContext;
+ private final MediaSessionManager mMediaSessionManager;
+ private MediaController mMediaController;
+ private String mMediaNotificationKey;
+ private MediaMetadata mMediaMetadata;
+
+ private final MediaController.Callback mMediaListener = new MediaController.Callback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ super.onPlaybackStateChanged(state);
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
+ }
+ if (state != null) {
+ if (!isPlaybackActive(state.getState())) {
+ clearCurrentMediaNotification();
+ mPresenter.updateMediaMetaData(true, true);
+ }
+ }
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ super.onMetadataChanged(metadata);
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
+ }
+ mMediaMetadata = metadata;
+ mPresenter.updateMediaMetaData(true, true);
+ }
+ };
+
+ public NotificationMediaManager(NotificationPresenter presenter, Context context) {
+ mPresenter = presenter;
+ mContext = context;
+ mMediaSessionManager
+ = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
+ // in session state
+ }
+
+ public void onNotificationRemoved(String key) {
+ if (key.equals(mMediaNotificationKey)) {
+ clearCurrentMediaNotification();
+ mPresenter.updateMediaMetaData(true, true);
+ }
+ }
+
+ public String getMediaNotificationKey() {
+ return mMediaNotificationKey;
+ }
+
+ public MediaMetadata getMediaMetadata() {
+ return mMediaMetadata;
+ }
+
+ public void findAndUpdateMediaNotifications() {
+ boolean metaDataChanged = false;
+
+ synchronized (mPresenter.getNotificationData()) {
+ ArrayList<NotificationData.Entry> activeNotifications = mPresenter
+ .getNotificationData().getActiveNotifications();
+ final int N = activeNotifications.size();
+
+ // Promote the media notification with a controller in 'playing' state, if any.
+ NotificationData.Entry mediaNotification = null;
+ MediaController controller = null;
+ for (int i = 0; i < N; i++) {
+ final NotificationData.Entry entry = activeNotifications.get(i);
+
+ if (isMediaNotification(entry)) {
+ final MediaSession.Token token =
+ entry.notification.getNotification().extras.getParcelable(
+ Notification.EXTRA_MEDIA_SESSION);
+ if (token != null) {
+ MediaController aController = new MediaController(mContext, token);
+ if (PlaybackState.STATE_PLAYING ==
+ getMediaControllerPlaybackState(aController)) {
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
+ + entry.notification.getKey());
+ }
+ mediaNotification = entry;
+ controller = aController;
+ break;
+ }
+ }
+ }
+ }
+ if (mediaNotification == null) {
+ // Still nothing? OK, let's just look for live media sessions and see if they match
+ // one of our notifications. This will catch apps that aren't (yet!) using media
+ // notifications.
+
+ if (mMediaSessionManager != null) {
+ // TODO: Should this really be for all users?
+ final List<MediaController> sessions
+ = mMediaSessionManager.getActiveSessionsForUser(
+ null,
+ UserHandle.USER_ALL);
+
+ for (MediaController aController : sessions) {
+ if (PlaybackState.STATE_PLAYING ==
+ getMediaControllerPlaybackState(aController)) {
+ // now to see if we have one like this
+ final String pkg = aController.getPackageName();
+
+ for (int i = 0; i < N; i++) {
+ final NotificationData.Entry entry = activeNotifications.get(i);
+ if (entry.notification.getPackageName().equals(pkg)) {
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: found controller matching "
+ + entry.notification.getKey());
+ }
+ controller = aController;
+ mediaNotification = entry;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (controller != null && !sameSessions(mMediaController, controller)) {
+ // We have a new media session
+ clearCurrentMediaNotification();
+ mMediaController = controller;
+ mMediaController.registerCallback(mMediaListener);
+ mMediaMetadata = mMediaController.getMetadata();
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
+ + mMediaMetadata);
+ }
+
+ if (mediaNotification != null) {
+ mMediaNotificationKey = mediaNotification.notification.getKey();
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
+ + mMediaNotificationKey + " controller=" + mMediaController);
+ }
+ }
+ metaDataChanged = true;
+ }
+ }
+
+ if (metaDataChanged) {
+ mPresenter.updateNotifications();
+ }
+ mPresenter.updateMediaMetaData(metaDataChanged, true);
+ }
+
+ public void clearCurrentMediaNotification() {
+ mMediaNotificationKey = null;
+ mMediaMetadata = null;
+ if (mMediaController != null) {
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
+ + mMediaController.getPackageName());
+ }
+ mMediaController.unregisterCallback(mMediaListener);
+ }
+ mMediaController = null;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print(" mMediaSessionManager=");
+ pw.println(mMediaSessionManager);
+ pw.print(" mMediaNotificationKey=");
+ pw.println(mMediaNotificationKey);
+ pw.print(" mMediaController=");
+ pw.print(mMediaController);
+ if (mMediaController != null) {
+ pw.print(" state=" + mMediaController.getPlaybackState());
+ }
+ pw.println();
+ pw.print(" mMediaMetadata=");
+ pw.print(mMediaMetadata);
+ if (mMediaMetadata != null) {
+ pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
+ }
+ pw.println();
+ }
+
+ private boolean isPlaybackActive(int state) {
+ return state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR
+ && state != PlaybackState.STATE_NONE;
+ }
+
+ private boolean sameSessions(MediaController a, MediaController b) {
+ if (a == b) {
+ return true;
+ }
+ if (a == null) {
+ return false;
+ }
+ return a.controlsSameSession(b);
+ }
+
+ private int getMediaControllerPlaybackState(MediaController controller) {
+ if (controller != null) {
+ final PlaybackState playbackState = controller.getPlaybackState();
+ if (playbackState != null) {
+ return playbackState.getState();
+ }
+ }
+ return PlaybackState.STATE_NONE;
+ }
+
+ private boolean isMediaNotification(NotificationData.Entry entry) {
+ // TODO: confirm that there's a valid media key
+ return entry.getExpandedContentView() != null &&
+ entry.getExpandedContentView()
+ .findViewById(com.android.internal.R.id.media_actions) != null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
new file mode 100644
index 0000000..1aca60c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -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
+ */
+package com.android.systemui.statusbar;
+
+import android.content.Intent;
+import android.service.notification.NotificationListenerService;
+
+/**
+ * An abstraction of something that presents notifications, e.g. StatusBar. Contains methods
+ * for both querying the state of the system (some modularised piece of functionality may
+ * want to act differently based on e.g. whether the presenter is visible to the user or not) and
+ * for affecting the state of the system (e.g. starting an intent, given that the presenter may
+ * want to perform some action before doing so).
+ */
+public interface NotificationPresenter {
+
+ /**
+ * Returns true if the presenter is not visible. For example, it may not be necessary to do
+ * animations if this returns true.
+ */
+ boolean isPresenterFullyCollapsed();
+
+ /**
+ * Returns true if the presenter is locked. For example, if the keyguard is active.
+ */
+ boolean isPresenterLocked();
+
+ /**
+ * Returns the current user id. This can change if the user is switched.
+ */
+ int getCurrentUserId();
+
+ /**
+ * Runs the given intent. The presenter may want to run some animations or close itself when
+ * this happens.
+ */
+ void startNotificationGutsIntent(Intent intent, int appUid);
+
+ /**
+ * Returns NotificationData.
+ */
+ NotificationData getNotificationData();
+
+ // TODO: Create NotificationEntryManager and move this method to there.
+ /**
+ * Signals that some notifications have changed, and NotificationPresenter should update itself.
+ */
+ void updateNotifications();
+
+ /**
+ * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
+ */
+ void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation);
+
+ // TODO: Create NotificationUpdateHandler and move this method to there.
+ /**
+ * Removes a notification.
+ */
+ void removeNotification(String key, NotificationListenerService.RankingMap ranking);
+
+ // TODO: Create NotificationEntryManager and move this method to there.
+ /**
+ * Gets the latest ranking map.
+ */
+ NotificationListenerService.RankingMap getLatestRankingMap();
+}
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/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index af03440..86a8f41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -596,7 +596,7 @@
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
closeQs();
- mStatusBar.closeAndSaveGuts(true /* leavebehind */, true /* force */,
+ mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
true /* cancelAnimators */);
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 46e3aa1..e3ed581 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -20,11 +20,12 @@
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
-
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA;
import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -40,10 +41,8 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.INotificationManager;
import android.app.KeyguardManager;
import android.app.Notification;
-import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.RemoteInput;
@@ -80,10 +79,7 @@
import android.graphics.drawable.Drawable;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
-import android.media.session.PlaybackState;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
@@ -115,14 +111,12 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.Display;
-import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
-import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
@@ -181,7 +175,6 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
@@ -207,10 +200,11 @@
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.NotificationGuts;
+import com.android.systemui.statusbar.NotificationGutsManager;
import com.android.systemui.statusbar.NotificationInfo;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.NotificationSnooze;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
@@ -243,7 +237,6 @@
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout
.OnChildLocationsChangedListener;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
import com.android.systemui.util.NotificationChannels;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.volume.VolumeComponent;
@@ -255,10 +248,8 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.Stack;
public class StatusBar extends SystemUI implements DemoMode,
@@ -267,7 +258,7 @@
ActivatableNotificationView.OnActivatedListener,
ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
ExpandableNotificationRow.OnExpandClickListener, InflationCallback,
- ColorExtractor.OnColorsChangedListener, ConfigurationListener {
+ ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
public static final boolean MULTIUSER_DEBUG = false;
public static final boolean ENABLE_REMOTE_INPUT =
@@ -287,9 +278,6 @@
protected static final boolean ENABLE_HEADS_UP = true;
protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
- // Must match constant in Settings. Used to highlight preferences when linking to Settings.
- private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
-
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
// Should match the values in PhoneWindowManager
@@ -308,7 +296,6 @@
public static final boolean SPEW = false;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
public static final boolean DEBUG_GESTURES = false;
- public static final boolean DEBUG_MEDIA = false;
public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
public static final boolean DEBUG_CAMERA_LIFT = false;
@@ -458,6 +445,8 @@
private final int[] mAbsPos = new int[2];
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
+ private NotificationGutsManager mGutsManager;
+
// for disabling the status bar
private int mDisabled1 = 0;
private int mDisabled2 = 0;
@@ -553,31 +542,7 @@
protected final PorterDuffXfermode mSrcOverXferMode =
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
- private MediaSessionManager mMediaSessionManager;
- private MediaController mMediaController;
- private String mMediaNotificationKey;
- private MediaMetadata mMediaMetadata;
- private final MediaController.Callback mMediaListener = new MediaController.Callback() {
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- super.onPlaybackStateChanged(state);
- if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
- if (state != null) {
- if (!isPlaybackActive(state.getState())) {
- clearCurrentMediaNotification();
- updateMediaMetaData(true, true);
- }
- }
- }
-
- @Override
- public void onMetadataChanged(MediaMetadata metadata) {
- super.onMetadataChanged(metadata);
- if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
- mMediaMetadata = metadata;
- updateMediaMetaData(true, true);
- }
- };
+ private NotificationMediaManager mMediaManager;
/** Keys of notifications currently visible to the user. */
private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
@@ -700,7 +665,6 @@
private final HashMap<String, Entry> mPendingNotifications = new HashMap<>();
private boolean mClearAllEnabled;
@Nullable private View mAmbientIndicationContainer;
- private String mKeyToRemoveOnGutsClosed;
private SysuiColorExtractor mColorExtractor;
private ForegroundServiceController mForegroundServiceController;
private ScreenLifecycle mScreenLifecycle;
@@ -817,6 +781,8 @@
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mLockPatternUtils = new LockPatternUtils(mContext);
+ mMediaManager = new NotificationMediaManager(this, mContext);
+
// Connect in to the status bar manager service
mCommandQueue = getComponent(CommandQueue.class);
mCommandQueue.addCallbacks(this);
@@ -903,16 +869,8 @@
Slog.e(TAG, "Failed to register VR mode state listener: " + e);
}
- mNonBlockablePkgs = new HashSet<>();
- Collections.addAll(mNonBlockablePkgs, res.getStringArray(
- com.android.internal.R.array.config_nonBlockableNotificationPackages));
// end old BaseStatusBar.start().
- mMediaSessionManager
- = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
- // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
- // in session state
-
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
mSettingsObserver.onChange(false); // set up
@@ -960,6 +918,8 @@
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
+ mGutsManager = new NotificationGutsManager(this, mStackScroller,
+ mCheckSaveListener, mContext);
mNotificationPanel.setStatusBar(this);
mNotificationPanel.setGroupManager(mGroupManager);
mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
@@ -1302,12 +1262,12 @@
ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
for (int i = 0; i < activeNotifications.size(); i++) {
Entry entry = activeNotifications.get(i);
- boolean exposedGuts = mNotificationGutsExposed != null
- && entry.row.getGuts() == mNotificationGutsExposed;
+ boolean exposedGuts = mGutsManager.getExposedGuts() != null
+ && entry.row.getGuts() == mGutsManager.getExposedGuts();
entry.row.onDensityOrFontScaleChanged();
if (exposedGuts) {
- mNotificationGutsExposed = entry.row.getGuts();
- bindGuts(entry.row, mGutsMenuItem);
+ mGutsManager.setExposedGuts(entry.row.getGuts());
+ mGutsManager.bindGuts(entry.row);
}
}
}
@@ -1672,6 +1632,7 @@
updateNotifications();
}
+ @Override
public void removeNotification(String key, RankingMap ranking) {
boolean deferRemoval = false;
abortExistingInflation(key);
@@ -1685,10 +1646,8 @@
|| !mVisualStabilityManager.isReorderingAllowed();
deferRemoval = !mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
}
- if (key.equals(mMediaNotificationKey)) {
- clearCurrentMediaNotification();
- updateMediaMetaData(true, true);
- }
+ mMediaManager.onNotificationRemoved(key);
+
Entry entry = mNotificationData.get(key);
if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)
&& entry.row != null && !entry.row.isDismissed()) {
@@ -1744,12 +1703,12 @@
mRemoteInputEntriesToRemoveOnCollapse.add(entry);
return;
}
- if (entry != null && mNotificationGutsExposed != null
- && mNotificationGutsExposed == entry.row.getGuts() && entry.row.getGuts() != null
- && !entry.row.getGuts().isLeavebehind()) {
+ if (entry != null && mGutsManager.getExposedGuts() != null
+ && mGutsManager.getExposedGuts() == entry.row.getGuts()
+ && entry.row.getGuts() != null && !entry.row.getGuts().isLeavebehind()) {
Log.w(TAG, "Keeping notification because it's showing guts. " + key);
mLatestRankingMap = ranking;
- mKeyToRemoveOnGutsClosed = key;
+ mGutsManager.setKeyToRemoveOnGutsClosed(key);
return;
}
@@ -2142,7 +2101,8 @@
return entry.row.getParent() instanceof NotificationStackScrollLayout;
}
- protected void updateNotifications() {
+ @Override
+ public void updateNotifications() {
mNotificationData.filterAndSort();
updateNotificationShade();
@@ -2184,136 +2144,9 @@
}
}
- findAndUpdateMediaNotifications();
+ mMediaManager.findAndUpdateMediaNotifications();
}
- public void findAndUpdateMediaNotifications() {
- boolean metaDataChanged = false;
-
- synchronized (mNotificationData) {
- ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
- final int N = activeNotifications.size();
-
- // Promote the media notification with a controller in 'playing' state, if any.
- Entry mediaNotification = null;
- MediaController controller = null;
- for (int i = 0; i < N; i++) {
- final Entry entry = activeNotifications.get(i);
-
- if (isMediaNotification(entry)) {
- final MediaSession.Token token =
- entry.notification.getNotification().extras.getParcelable(
- Notification.EXTRA_MEDIA_SESSION);
- if (token != null) {
- MediaController aController = new MediaController(mContext, token);
- if (PlaybackState.STATE_PLAYING ==
- getMediaControllerPlaybackState(aController)) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
- + entry.notification.getKey());
- }
- mediaNotification = entry;
- controller = aController;
- break;
- }
- }
- }
- }
- if (mediaNotification == null) {
- // Still nothing? OK, let's just look for live media sessions and see if they match
- // one of our notifications. This will catch apps that aren't (yet!) using media
- // notifications.
-
- if (mMediaSessionManager != null) {
- final List<MediaController> sessions
- = mMediaSessionManager.getActiveSessionsForUser(
- null,
- UserHandle.USER_ALL);
-
- for (MediaController aController : sessions) {
- if (PlaybackState.STATE_PLAYING ==
- getMediaControllerPlaybackState(aController)) {
- // now to see if we have one like this
- final String pkg = aController.getPackageName();
-
- for (int i = 0; i < N; i++) {
- final Entry entry = activeNotifications.get(i);
- if (entry.notification.getPackageName().equals(pkg)) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: found controller matching "
- + entry.notification.getKey());
- }
- controller = aController;
- mediaNotification = entry;
- break;
- }
- }
- }
- }
- }
- }
-
- if (controller != null && !sameSessions(mMediaController, controller)) {
- // We have a new media session
- clearCurrentMediaNotification();
- mMediaController = controller;
- mMediaController.registerCallback(mMediaListener);
- mMediaMetadata = mMediaController.getMetadata();
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
- + mMediaMetadata);
- }
-
- if (mediaNotification != null) {
- mMediaNotificationKey = mediaNotification.notification.getKey();
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
- + mMediaNotificationKey + " controller=" + mMediaController);
- }
- }
- metaDataChanged = true;
- }
- }
-
- if (metaDataChanged) {
- updateNotifications();
- }
- updateMediaMetaData(metaDataChanged, true);
- }
-
- private int getMediaControllerPlaybackState(MediaController controller) {
- if (controller != null) {
- final PlaybackState playbackState = controller.getPlaybackState();
- if (playbackState != null) {
- return playbackState.getState();
- }
- }
- return PlaybackState.STATE_NONE;
- }
-
- private boolean isPlaybackActive(int state) {
- return state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR
- && state != PlaybackState.STATE_NONE;
- }
-
- private void clearCurrentMediaNotification() {
- mMediaNotificationKey = null;
- mMediaMetadata = null;
- if (mMediaController != null) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
- + mMediaController.getPackageName());
- }
- mMediaController.unregisterCallback(mMediaListener);
- }
- mMediaController = null;
- }
-
- private boolean sameSessions(MediaController a, MediaController b) {
- if (a == b) return true;
- if (a == null) return false;
- return a.controlsSameSession(b);
- }
/**
* Hide the album artwork that is fading out and release its bitmap.
@@ -2330,9 +2163,11 @@
}
};
+ // TODO: Move this to NotificationMediaManager.
/**
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
+ @Override
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
Trace.beginSection("StatusBar#updateMediaMetaData");
if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
@@ -2351,19 +2186,22 @@
return;
}
+ MediaMetadata mediaMetadata = mMediaManager.getMediaMetadata();
+
if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
- + " metadata=" + mMediaMetadata
+ Log.v(TAG, "DEBUG_MEDIA: updating album art for notification "
+ + mMediaManager.getMediaNotificationKey()
+ + " metadata=" + mediaMetadata
+ " metaDataChanged=" + metaDataChanged
+ " state=" + mState);
}
Drawable artworkDrawable = null;
- if (mMediaMetadata != null) {
+ if (mediaMetadata != null) {
Bitmap artworkBitmap = null;
- artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+ artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
if (artworkBitmap == null) {
- artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+ artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
// might still be null
}
if (artworkBitmap != null) {
@@ -2636,7 +2474,7 @@
@Override // NotificationData.Environment
public String getCurrentMediaNotificationKey() {
- return mMediaNotificationKey;
+ return mMediaManager.getMediaNotificationKey();
}
public boolean isScrimSrcModeEnabled() {
@@ -3101,8 +2939,8 @@
mStatusBarWindowManager.setForceStatusBarVisible(false);
// Close any guts that might be visible
- closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, true /* removeControls */,
- -1 /* x */, -1 /* y */, true /* resetMenu */);
+ mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
runPostCollapseRunnables();
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
@@ -3449,28 +3287,18 @@
pw.println(Settings.Global.zenModeToString(mZenMode));
pw.print(" mUseHeadsUp=");
pw.println(mUseHeadsUp);
- pw.print(" mKeyToRemoveOnGutsClosed=");
- pw.println(mKeyToRemoveOnGutsClosed);
+ pw.print(" mGutsManager: ");
+ if (mGutsManager != null) {
+ mGutsManager.dump(fd, pw, args);
+ }
if (mStatusBarView != null) {
dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
}
- pw.print(" mMediaSessionManager=");
- pw.println(mMediaSessionManager);
- pw.print(" mMediaNotificationKey=");
- pw.println(mMediaNotificationKey);
- pw.print(" mMediaController=");
- pw.print(mMediaController);
- if (mMediaController != null) {
- pw.print(" state=" + mMediaController.getPlaybackState());
+ pw.println(" mMediaManager: ");
+ if (mMediaManager != null) {
+ mMediaManager.dump(fd, pw, args);
}
- pw.println();
- pw.print(" mMediaMetadata=");
- pw.print(mMediaMetadata);
- if (mMediaMetadata != null) {
- pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
- }
- pw.println();
pw.println(" Panels: ");
if (mNotificationPanel != null) {
@@ -3792,7 +3620,7 @@
mReinflateNotificationsOnUserSwitched = false;
}
updateNotificationShade();
- clearCurrentMediaNotification();
+ mMediaManager.clearCurrentMediaNotification();
setLockscreenUser(newUserId);
}
@@ -3872,10 +3700,10 @@
if (visibleToUser) {
boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
boolean clearNotificationEffects =
- !isPanelFullyCollapsed() &&
+ !isPresenterFullyCollapsed() &&
(mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
int notificationLoad = mNotificationData.getActiveNotifications().size();
- if (pinnedHeadsUp && isPanelFullyCollapsed()) {
+ if (pinnedHeadsUp && isPresenterFullyCollapsed()) {
notificationLoad = 1;
}
mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
@@ -4152,7 +3980,8 @@
return mState;
}
- public boolean isPanelFullyCollapsed() {
+ @Override
+ public boolean isPresenterFullyCollapsed() {
return mNotificationPanel.isFullyCollapsed();
}
@@ -4735,7 +4564,7 @@
public void onClosingFinished() {
runPostCollapseRunnables();
- if (!isPanelFullyCollapsed()) {
+ if (!isPresenterFullyCollapsed()) {
// if we set it not to be focusable when collapsing, we have to undo it when we aborted
// the closing
mStatusBarWindowManager.setStatusBarFocusable(true);
@@ -5600,10 +5429,6 @@
protected int mZenMode;
- // which notification is currently being longpress-examined by the user
- private NotificationGuts mNotificationGutsExposed;
- private MenuItem mGutsMenuItem;
-
protected NotificationShelf mNotificationShelf;
protected DismissView mDismissView;
protected EmptyShadeView mEmptyShadeView;
@@ -5614,8 +5439,6 @@
protected boolean mVrMode;
- private Set<String> mNonBlockablePkgs;
-
public boolean isDeviceInteractive() {
return mDeviceInteractive;
}
@@ -6147,26 +5970,8 @@
return mGroupManager;
}
- public boolean isMediaNotification(NotificationData.Entry entry) {
- // TODO: confirm that there's a valid media key
- return entry.getExpandedContentView() != null &&
- entry.getExpandedContentView()
- .findViewById(com.android.internal.R.id.media_actions) != null;
- }
-
- // The button in the guts that links to the system notification settings for that app
- private void startAppNotificationSettingsActivity(String packageName, final int appUid,
- final NotificationChannel channel) {
- final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
- intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
- intent.putExtra(Settings.EXTRA_APP_UID, appUid);
- if (channel != null) {
- intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
- }
- startNotificationGutsIntent(intent, appUid);
- }
-
- private void startNotificationGutsIntent(final Intent intent, final int appUid) {
+ @Override
+ public void startNotificationGutsIntent(final Intent intent, final int appUid) {
dismissKeyguardThenExecute(() -> {
AsyncTask.execute(() -> {
TaskStackBuilder.create(mContext)
@@ -6189,231 +5994,8 @@
}
}
- private void bindGuts(final ExpandableNotificationRow row, MenuItem item) {
- row.inflateGuts();
- row.setGutsView(item);
- final StatusBarNotification sbn = row.getStatusBarNotification();
- row.setTag(sbn.getPackageName());
- final NotificationGuts guts = row.getGuts();
- guts.setClosedListener((NotificationGuts g) -> {
- if (!g.willBeRemoved() && !row.isRemoved()) {
- mStackScroller.onHeightChanged(row, !isPanelFullyCollapsed() /* needsAnimation */);
- }
- if (mNotificationGutsExposed == g) {
- mNotificationGutsExposed = null;
- mGutsMenuItem = null;
- }
- String key = sbn.getKey();
- if (key.equals(mKeyToRemoveOnGutsClosed)) {
- mKeyToRemoveOnGutsClosed = null;
- removeNotification(key, mLatestRankingMap);
- }
- });
-
- View gutsView = item.getGutsView();
- if (gutsView instanceof NotificationSnooze) {
- NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
- snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
- snoozeGuts.setStatusBarNotification(sbn);
- snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
- guts.setHeightChangedListener((NotificationGuts g) -> {
- mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */);
- });
- }
-
- if (gutsView instanceof NotificationInfo) {
- final UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = getPackageManagerForUser(mContext,
- userHandle.getIdentifier());
- final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
- final String pkg = sbn.getPackageName();
- NotificationInfo info = (NotificationInfo) gutsView;
- // Settings link is only valid for notifications that specify a user, unless this is the
- // system user.
- NotificationInfo.OnSettingsClickListener onSettingsClick = null;
- if (!userHandle.equals(UserHandle.ALL) || mCurrentUserId == UserHandle.USER_SYSTEM) {
- onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
- mMetricsLogger.action(MetricsEvent.ACTION_NOTE_INFO);
- guts.resetFalsingCheck();
- startAppNotificationSettingsActivity(pkg, appUid, channel);
- };
- }
- final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
- Intent intent) -> {
- mMetricsLogger.action(MetricsEvent.ACTION_APP_NOTE_SETTINGS);
- guts.resetFalsingCheck();
- startNotificationGutsIntent(intent, sbn.getUid());
- };
- final View.OnClickListener onDoneClick = (View v) -> {
- saveAndCloseNotificationMenu(row, guts, v);
- };
- final NotificationInfo.CheckSaveListener checkSaveListener =
- (Runnable saveImportance) -> {
- // If the user has security enabled, show challenge if the setting is changed.
- if (isLockscreenPublicMode(userHandle.getIdentifier())
- && (mState == StatusBarState.KEYGUARD
- || mState == StatusBarState.SHADE_LOCKED)) {
- onLockedNotificationImportanceChange(() -> {
- saveImportance.run();
- return true;
- });
- } else {
- saveImportance.run();
- }
- };
-
- ArraySet<NotificationChannel> channels = new ArraySet<>();
- channels.add(row.getEntry().channel);
- if (row.isSummaryWithChildren()) {
- // If this is a summary, then add in the children notification channels for the
- // same user and pkg.
- final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
- final int numChildren = childrenRows.size();
- for (int i = 0; i < numChildren; i++) {
- final ExpandableNotificationRow childRow = childrenRows.get(i);
- final NotificationChannel childChannel = childRow.getEntry().channel;
- final StatusBarNotification childSbn = childRow.getStatusBarNotification();
- if (childSbn.getUser().equals(userHandle) &&
- childSbn.getPackageName().equals(pkg)) {
- channels.add(childChannel);
- }
- }
- }
- try {
- info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels),
- row.getEntry().channel.getImportance(), sbn, onSettingsClick,
- onAppSettingsClick, onDoneClick, checkSaveListener,
- mNonBlockablePkgs);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- }
- }
-
- private void saveAndCloseNotificationMenu(
- ExpandableNotificationRow row, NotificationGuts guts, View done) {
- guts.resetFalsingCheck();
- int[] rowLocation = new int[2];
- int[] doneLocation = new int[2];
- row.getLocationOnScreen(rowLocation);
- done.getLocationOnScreen(doneLocation);
-
- final int centerX = done.getWidth() / 2;
- final int centerY = done.getHeight() / 2;
- final int x = doneLocation[0] - rowLocation[0] + centerX;
- final int y = doneLocation[1] - rowLocation[1] + centerY;
- closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
- true /* removeControls */, x, y, true /* resetMenu */);
- }
-
protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
- return new ExpandableNotificationRow.LongPressListener() {
- @Override
- public boolean onLongPress(View v, final int x, final int y,
- MenuItem item) {
- if (!(v instanceof ExpandableNotificationRow)) {
- return false;
- }
-
- if (v.getWindowToken() == null) {
- Log.e(TAG, "Trying to show notification guts, but not attached to window");
- return false;
- }
-
- final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- if (row.isDark()) {
- return false;
- }
- v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- if (row.areGutsExposed()) {
- closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */,
- true /* resetMenu */);
- return true;
- }
- bindGuts(row, item);
- NotificationGuts guts = row.getGuts();
-
- // Assume we are a status_bar_notification_row
- if (guts == null) {
- // This view has no guts. Examples are the more card or the dismiss all view
- return false;
- }
-
- mMetricsLogger.action(MetricsEvent.ACTION_NOTE_CONTROLS);
-
- // ensure that it's laid but not visible until actually laid out
- guts.setVisibility(View.INVISIBLE);
- // Post to ensure the the guts are properly laid out.
- guts.post(new Runnable() {
- @Override
- public void run() {
- if (row.getWindowToken() == null) {
- Log.e(TAG, "Trying to show notification guts, but not attached to "
- + "window");
- return;
- }
- closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
- guts.setVisibility(View.VISIBLE);
- final double horz = Math.max(guts.getWidth() - x, x);
- final double vert = Math.max(guts.getHeight() - y, y);
- final float r = (float) Math.hypot(horz, vert);
- final Animator a
- = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
- a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- a.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- // Move the notification view back over the menu
- row.resetTranslation();
- }
- });
- a.start();
- final boolean needsFalsingProtection =
- (mState == StatusBarState.KEYGUARD &&
- !mAccessibilityManager.isTouchExplorationEnabled());
- guts.setExposed(true /* exposed */, needsFalsingProtection);
- row.closeRemoteInput();
- mStackScroller.onHeightChanged(row, true /* needsAnimation */);
- mNotificationGutsExposed = guts;
- mGutsMenuItem = item;
- }
- });
- return true;
- }
- };
- }
-
- /**
- * Returns the exposed NotificationGuts or null if none are exposed.
- */
- public NotificationGuts getExposedGuts() {
- return mNotificationGutsExposed;
- }
-
- /**
- * Closes guts or notification menus that might be visible and saves any changes.
- *
- * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
- * @param force true if guts should be closed regardless of state (used for snooze only).
- * @param removeControls true if controls (e.g. info) should be closed.
- * @param x if closed based on touch location, this is the x touch location.
- * @param y if closed based on touch location, this is the y touch location.
- * @param resetMenu if any notification menus that might be revealed should be closed.
- */
- public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
- int x, int y, boolean resetMenu) {
- if (mNotificationGutsExposed != null) {
- mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
- }
- if (resetMenu) {
- mStackScroller.resetExposedMenuView(false /* animate */, true /* force */);
- }
+ return (v, x, y, item) -> mGutsManager.openGuts(v, x, y, item);
}
@Override
@@ -6802,7 +6384,7 @@
if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
// Release the HUN notification to the shade.
- if (isPanelFullyCollapsed()) {
+ if (isPresenterFullyCollapsed()) {
HeadsUpManager.setIsClickedNotification(row, true);
}
//
@@ -6934,7 +6516,7 @@
if (mVisible != visible) {
mVisible = visible;
if (!visible) {
- closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+ mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
}
}
@@ -7156,8 +6738,8 @@
}
mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
- if (key.equals(mKeyToRemoveOnGutsClosed)) {
- mKeyToRemoveOnGutsClosed = null;
+ if (key.equals(mGutsManager.getKeyToRemoveOnGutsClosed())) {
+ mGutsManager.setKeyToRemoveOnGutsClosed(null);
Log.w(TAG, "Notification that was kept for guts was updated. " + key);
}
@@ -7355,4 +6937,43 @@
mNavigationBar.getBarTransitions().setAutoDim(true);
}
};
+
+ public NotificationGutsManager getGutsManager() {
+ return mGutsManager;
+ }
+
+ @Override
+ public boolean isPresenterLocked() {
+ return mState == StatusBarState.KEYGUARD;
+ }
+
+ @Override
+ public int getCurrentUserId() {
+ return mCurrentUserId;
+ }
+
+ @Override
+ public NotificationData getNotificationData() {
+ return mNotificationData;
+ }
+
+ @Override
+ public RankingMap getLatestRankingMap() {
+ return mLatestRankingMap;
+ }
+
+ final NotificationInfo.CheckSaveListener mCheckSaveListener =
+ (Runnable saveImportance, StatusBarNotification sbn) -> {
+ // If the user has security enabled, show challenge if the setting is changed.
+ if (isLockscreenPublicMode(sbn.getUser().getIdentifier()) && (
+ mState == StatusBarState.KEYGUARD
+ || mState == StatusBarState.SHADE_LOCKED)) {
+ onLockedNotificationImportanceChange(() -> {
+ saveImportance.run();
+ return true;
+ });
+ } else {
+ saveImportance.run();
+ }
+ };
}
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 1e14626..efe049a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1343,7 +1343,7 @@
}
// Check if we need to clear any snooze leavebehinds
- NotificationGuts guts = mStatusBar.getExposedGuts();
+ NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
if (guts != null && !isTouchInView(ev, guts)
&& guts.getGutsContent() instanceof NotificationSnooze) {
NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
@@ -2508,12 +2508,13 @@
}
// Check if we need to clear any snooze leavebehinds
boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
- NotificationGuts guts = mStatusBar.getExposedGuts();
+ NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
&& !scrollWantsIt) {
mCheckForLeavebehind = false;
- mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
- false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+ false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
}
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mCheckForLeavebehind = true;
@@ -3065,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
@@ -3355,8 +3356,9 @@
public void checkSnoozeLeavebehind() {
if (mCheckForLeavebehind) {
- mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
- false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+ false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
mCheckForLeavebehind = false;
}
}
@@ -4438,8 +4440,9 @@
// of the panel early.
handleChildDismissed(view);
}
- mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
- false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+ false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
handleMenuCoveredOrDismissed();
}
@@ -4524,7 +4527,7 @@
}
public void closeControlsIfOutsideTouch(MotionEvent ev) {
- NotificationGuts guts = mStatusBar.getExposedGuts();
+ NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
View view = null;
if (guts != null && !guts.getGutsContent().isLeavebehind()) {
// Only close visible guts if they're not a leavebehind.
@@ -4536,8 +4539,9 @@
}
if (view != null && !isTouchInView(ev, view)) {
// Touch was outside visible guts / menu notification, close what's visible
- mStatusBar.closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
+ false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
resetExposedMenuView(true /* animate */, true /* force */);
}
}
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..87d11b2 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);
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..4b8f581 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,12 @@
} else {
addExistingRows();
}
- mExpandButtonAnimationDuration = res.getInteger(R.integer.volume_expand_animation_duration);
}
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 +256,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 +283,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 +305,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 +388,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 +427,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 +459,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 +505,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 +574,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 +790,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 +828,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 +836,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 +851,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 +867,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 +1018,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/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 88fa659..6721938 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -745,7 +745,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
mNotificationChannel.getImportance(), mSbn, null, null, null,
- (Runnable saveImportance) -> {
+ (Runnable saveImportance, StatusBarNotification sbn) -> {
},
Collections.singleton(TEST_PACKAGE_NAME));
@@ -762,7 +762,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
mNotificationChannel.getImportance(), mSbn, null, null, null,
- (Runnable saveImportance) -> {
+ (Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
},
Collections.singleton(TEST_PACKAGE_NAME));
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 8e93d19..73a6d7c 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,68 @@
// 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;
+
// 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..8f58a38 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -364,6 +364,12 @@
// (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;
}
// Information that gets logged for every WiFi connection.
@@ -1071,3 +1077,29 @@
// 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;
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 842a5b8..02ba6a0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3889,7 +3889,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;
}
@@ -7075,10 +7075,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
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/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c5514fb..40b1cac 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1139,6 +1139,9 @@
mActivities.remove(newTop);
mActivities.add(newTop);
+
+ // Make sure window manager is aware of the position change.
+ mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
updateEffectiveIntent();
setFrontOfTask();
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/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/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 50ac409..29f48ee 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;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6a0ea4ee..83cffe5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -551,6 +551,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 {
@@ -9598,7 +9600,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 +19902,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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1e5245c..1152310 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1010,6 +1010,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/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/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 2488723..f41ff2c 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;
@@ -71,6 +73,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 +108,16 @@
}
};
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);
+ }
}
private final static int[] toIntArray(List<Integer> list) {
@@ -286,9 +301,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 +458,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/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 5ea0e1d..e693e5a 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;
@@ -116,6 +117,7 @@
private final DimLayer mDimLayer;
private boolean mMinimizedDock;
+ private int mOriginalDockedSide = DOCKED_INVALID;
private boolean mAnimatingForMinimizedDockedStack;
private boolean mAnimationStarted;
private long mAnimationStartTime;
@@ -408,6 +410,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,8 +457,11 @@
inputMethodManagerInternal.hideCurrentInputMethod();
mImeHideRequested = true;
}
+ final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ mOriginalDockedSide = stack.getDockSide();
return;
}
+ mOriginalDockedSide = DOCKED_INVALID;
setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 170feac..dde7946 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -442,7 +442,7 @@
mTmpRect2.set(mBounds);
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
if (inSplitScreenPrimaryWindowingMode()) {
- repositionDockedStackAfterRotation(mTmpRect2);
+ repositionPrimarySplitScreenStackAfterRotation(mTmpRect2);
snapDockedStackAfterRotation(mTmpRect2);
final int newDockSide = getDockSide(mTmpRect2);
@@ -466,14 +466,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);
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/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 24f2014..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,52 +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 numBytes = usb_device_get_descriptors_length(device);
- if (numBytes > 0) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jobject thiz = (jobject)clientData;
- jstring deviceAddress = env->NewStringUTF(devAddress);
+ 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);
- jbyteArray descriptorsArray = env->NewByteArray(numBytes);
- const jbyte* rawDescriptors = (const jbyte*)usb_device_get_raw_descriptors(device);
- env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawDescriptors);
+ jstring deviceName = env->NewStringUTF(devname);
+ jstring manufacturerName = AndroidRuntime::NewStringLatin1(env, manufacturer);
+ jstring productName = AndroidRuntime::NewStringLatin1(env, product);
+ jstring serialNumber = AndroidRuntime::NewStringLatin1(env, serial);
- env->CallBooleanMethod(thiz, method_usbDeviceAdded,
- deviceAddress, classID, subClassID, descriptorsArray);
+ 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(descriptorsArray);
- env->DeleteLocalRef(deviceAddress);
+ env->DeleteLocalRef(serialNumber);
+ env->DeleteLocalRef(productName);
+ env->DeleteLocalRef(manufacturerName);
+ env->DeleteLocalRef(deviceName);
+ free(manufacturer);
+ free(product);
+ free(serial);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- } else {
- // TODO return an error code here?
- ALOGE("error reading descriptors\n");
+ 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);
+ }
}
+ env->CallVoidMethod(thiz, method_endUsbDeviceAdded);
+
+fail:
usb_device_close(device);
+ 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;
}
@@ -115,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;
@@ -153,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_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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 90df82a..74a7bd4a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -95,6 +95,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;
@@ -1489,6 +1490,10 @@
traceBeginAndSlog("StartLauncherAppsService");
mSystemServiceManager.startService(LauncherAppsService.class);
traceEnd();
+
+ traceBeginAndSlog("StartCrossProfileAppsService");
+ mSystemServiceManager.startService(CrossProfileAppsService.class);
+ traceEnd();
}
if (!disableMediaProjection) {
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 322b891..59d2a24 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -26,13 +26,8 @@
LOCAL_PRIVILEGED_MODULE := true
LOCAL_STATIC_JAVA_LIBRARIES := \
- frameworks-base-testutils \
services.backup \
- services.core \
- android-support-test \
- mockito-target-minus-junit4 \
- platform-test-annotations \
- truth-prebuilt
+ services.core
include $(BUILD_PACKAGE)
@@ -45,6 +40,9 @@
# Include the testing libraries (JUnit4 + Robolectric libs).
LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ mockito-robolectric-prebuilt \
+ platform-test-annotations \
truth-prebuilt
LOCAL_JAVA_LIBRARIES := \
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/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/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/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..ffb5e93 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) {}
@@ -2576,7 +2577,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);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index a81fba9..35804f6 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);
}
}
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..9bf0467 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) {
}
}
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/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/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..bce3392 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);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 88f4880..2f39ddb 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -360,6 +360,42 @@
public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
/**
+ * TelephonyProvider column name for enable Volte.
+ *@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>
*
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/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 89ae9e8..027ac8f 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -37,38 +37,39 @@
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 ISerializer {
+ public:
+ ISerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
+
+ 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 ~ISerializer() = default;
+
+ protected:
+ IAaptContext* 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)) {
+ ISerializer* serializer, IArchiveWriter* writer) {
+ // AndroidManifest.xml
+ 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 +77,131 @@
const FileReference* file = ValueCast<FileReference>(config_value->value.get());
if (file != nullptr) {
if (file->file == nullptr) {
- context->GetDiagnostics()->Warn(DiagMessage(apk->GetSource())
+ 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 BinarySerializer : public ISerializer {
+ public:
+ BinarySerializer(IAaptContext* context, const Source& source,
+ const TableFlattenerOptions& options)
+ : ISerializer(context, source), tableFlattenerOptions_(options) {}
+
+ bool SerializeXml(const xml::XmlResource* xml, const std::string& 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, path, ArchiveEntry::kCompress,
+ writer);
+ }
+
+ bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) {
+ 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) {
+ 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 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:
+ TableFlattenerOptions tableFlattenerOptions_;
+
+ DISALLOW_COPY_AND_ASSIGN(BinarySerializer);
+};
+
class Context : public IAaptContext {
public:
Context() : mangler_({}), symbols_(&mangler_) {
@@ -222,7 +295,8 @@
if (writer == nullptr) {
return 1;
}
- return ConvertProtoApkToBinaryApk(&context, std::move(apk), options, writer.get()) ? 0 : 1;
+ BinarySerializer serializer(&context, apk->GetSource(), options);
+ return ConvertProtoApkToBinaryApk(&context, std::move(apk), &serializer, writer.get()) ? 0 : 1;
}
} // namespace aapt
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 d39f43e8..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/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/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index b9ae654..b214d21 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -345,15 +345,14 @@
*out << "# Referenced at " << location.source << "\n";
}
if (keep_set.conditional_keep_rules_ && can_be_conditional) {
- *out << "-keep class " << entry.first << " {\n ifused class **.R$layout {\n";
+ *out << "-if class **.R$layout {\n";
for (const UsageLocation& location : locations) {
auto transformed_name = JavaClassGenerator::TransformToFieldName(location.name.entry);
- *out << " int " << transformed_name << ";\n";
+ *out << " int " << transformed_name << ";\n";
}
- *out << " };\n <init>(...);\n}\n" << std::endl;
- } else {
- *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+ *out << "}\n";
}
+ *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
}
for (const auto& entry : keep_set.method_set_) {
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index df3ac8b..802c56a 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -130,7 +130,7 @@
ASSERT_TRUE(proguard::WriteKeepSet(&out, 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("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
@@ -152,7 +152,7 @@
ASSERT_TRUE(proguard::WriteKeepSet(&out, 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("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
@@ -174,7 +174,7 @@
ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
std::string actual = out.str();
- EXPECT_THAT(actual, Not(HasSubstr("ifused")));
+ EXPECT_THAT(actual, Not(HasSubstr("-if")));
}
TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 2eab22e..c9f3199 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1371,7 +1371,9 @@
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]
- 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/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
index 4816984..9aef562 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)
{
@@ -29,6 +35,23 @@
<< 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 uint32_t _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