diff options
Diffstat (limited to 'core/java/android')
133 files changed, 4918 insertions, 2002 deletions
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 6503d89..951fe49 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -16,9 +16,10 @@ package android.animation; +import android.util.ArrayMap; + import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; /** @@ -68,7 +69,7 @@ public final class AnimatorSet extends Animator { * to a single node representing that Animator, not create a new Node * if one already exists. */ - private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>(); + private ArrayMap<Animator, Node> mNodeMap = new ArrayMap<Animator, Node>(); /** * Set of all nodes created for this AnimatorSet. This list is used upon @@ -646,7 +647,7 @@ public final class AnimatorSet extends Animator { anim.mTerminated = false; anim.mStarted = false; anim.mPlayingSet = new ArrayList<Animator>(); - anim.mNodeMap = new HashMap<Animator, Node>(); + anim.mNodeMap = new ArrayMap<Animator, Node>(); anim.mNodes = new ArrayList<Node>(nodeCount); anim.mSortedNodes = new ArrayList<Node>(nodeCount); anim.mReversible = mReversible; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 9968dbb..49f5099 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3841,10 +3841,7 @@ public class Activity extends ContextThemeWrapper mStartedActivity = true; } - final View decor = mWindow != null ? mWindow.peekDecorView() : null; - if (decor != null) { - decor.cancelPendingInputEvents(); - } + cancelInputsAndStartExitTransition(options); // TODO Consider clearing/flushing other event sources and events for child windows. } else { if (options != null) { @@ -3855,6 +3852,18 @@ public class Activity extends ContextThemeWrapper mParent.startActivityFromChild(this, intent, requestCode); } } + } + + /** + * Cancels pending inputs and if an Activity Transition is to be run, starts the transition. + * + * @param options The ActivityOptions bundle used to start an Activity. + */ + private void cancelInputsAndStartExitTransition(Bundle options) { + final View decor = mWindow != null ? mWindow.peekDecorView() : null; + if (decor != null) { + decor.cancelPendingInputEvents(); + } if (options != null && !isTopOfTask()) { mActivityTransitionState.startExitOutTransition(this, options); } @@ -3872,9 +3881,6 @@ public class Activity extends ContextThemeWrapper */ public void startActivityForResultAsUser(Intent intent, int requestCode, @Nullable Bundle options, UserHandle user) { - if (options != null) { - mActivityTransitionState.startExitOutTransition(this, options); - } if (mParent != null) { throw new RuntimeException("Can't be called from a child"); } @@ -3896,10 +3902,7 @@ public class Activity extends ContextThemeWrapper mStartedActivity = true; } - final View decor = mWindow != null ? mWindow.peekDecorView() : null; - if (decor != null) { - decor.cancelPendingInputEvents(); - } + cancelInputsAndStartExitTransition(options); } /** @@ -3925,6 +3928,7 @@ public class Activity extends ContextThemeWrapper mToken, mEmbeddedID, -1, ar.getResultCode(), ar.getResultData()); } + cancelInputsAndStartExitTransition(options); } /** @@ -3948,6 +3952,7 @@ public class Activity extends ContextThemeWrapper mToken, mEmbeddedID, -1, ar.getResultCode(), ar.getResultData()); } + cancelInputsAndStartExitTransition(options); } /** @@ -4380,6 +4385,7 @@ public class Activity extends ContextThemeWrapper mToken, child.mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } + cancelInputsAndStartExitTransition(options); } /** @@ -4431,9 +4437,6 @@ public class Activity extends ContextThemeWrapper @Override public void startActivityForResult( String who, Intent intent, int requestCode, @Nullable Bundle options) { - if (options != null) { - mActivityTransitionState.startExitOutTransition(this, options); - } Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, who, @@ -4443,6 +4446,7 @@ public class Activity extends ContextThemeWrapper mToken, who, requestCode, ar.getResultCode(), ar.getResultData()); } + cancelInputsAndStartExitTransition(options); } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 9bbb4be..edebc28 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2320,7 +2320,9 @@ public class ActivityManager { * in {@link RunningAppProcessInfo}, giving you the highest importance of all the * processes that this package has code running inside of. If there are no processes * running its code, {@link RunningAppProcessInfo#IMPORTANCE_GONE} is returned. + * @hide */ + @SystemApi public int getPackageImportance(String packageName) { try { int procState = ActivityManagerNative.getDefault().getPackageProcessState(packageName); diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index bde8f39..40eb799 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.NonNull; +import android.content.ComponentName; /** * Activity manager local system service interface. @@ -48,4 +49,10 @@ public abstract class ActivityManagerInternal { */ public abstract void release(); } + + /** + * Returns home activity for the specified user. + * @param userId ID of the user or {@link android.os.UserHandle#USER_ALL} + */ + public abstract ComponentName getHomeActivityForUser(int userId); } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index a71a258..1e9bc54 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2069,6 +2069,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case KEYGUARD_GOING_AWAY_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + keyguardGoingAway(data.readInt() != 0, data.readInt() != 0); + reply.writeNoException(); + return true; + } + case SHOULD_UP_RECREATE_TASK_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); @@ -5179,6 +5186,19 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public void keyguardGoingAway(boolean disableWindowAnimations, + boolean keyguardGoingToNotificationShade) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(disableWindowAnimations ? 1 : 0); + data.writeInt(keyguardGoingToNotificationShade ? 1 : 0); + mRemote.transact(KEYGUARD_GOING_AWAY_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + public boolean shouldUpRecreateTask(IBinder token, String destAffinity) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index f16406a..5dcbe37 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -96,7 +96,7 @@ import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.renderscript.RenderScriptCacheDir; -import android.security.AndroidKeyStoreProvider; +import android.security.keystore.AndroidKeyStoreProvider; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; @@ -571,8 +571,6 @@ public final class ActivityThread { private native void dumpGraphicsInfo(FileDescriptor fd); private class ApplicationThread extends ApplicationThreadNative { - private static final String ONE_COUNT_COLUMN = "%21s %8d"; - private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s"; private int mLastProcessState = -1; @@ -972,18 +970,18 @@ public final class ActivityThread { @Override public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin, - boolean dumpFullInfo, boolean dumpDalvik, String[] args) { + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, String[] args) { FileOutputStream fout = new FileOutputStream(fd); PrintWriter pw = new FastPrintWriter(fout); try { - dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik); + dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly); } finally { pw.flush(); } } private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, - boolean dumpFullInfo, boolean dumpDalvik) { + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly) { long nativeMax = Debug.getNativeHeapSize() / 1024; long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; long nativeFree = Debug.getNativeHeapFreeSize() / 1024; @@ -1007,7 +1005,8 @@ public final class ActivityThread { long openSslSocketCount = Debug.countInstancesOfClass(OpenSSLSocketImpl.class); SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo(); - dumpMemInfoTable(pw, memInfo, checkin, dumpFullInfo, dumpDalvik, Process.myPid(), + dumpMemInfoTable(pw, memInfo, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, + Process.myPid(), (mBoundApplication != null) ? mBoundApplication.processName : "unknown", nativeMax, nativeAllocated, nativeFree, dalvikMax, dalvikAllocated, dalvikFree); @@ -1935,6 +1934,9 @@ public final class ActivityThread { = "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s"; private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s %8s"; + private static final String ONE_COUNT_COLUMN = "%21s %8d"; + private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; + private static final String ONE_COUNT_COLUMN_HEADER = "%21s %8s"; // Formatting for checkin service - update version if row format changes private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3; @@ -1944,7 +1946,8 @@ public final class ActivityThread { } public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, - boolean dumpFullInfo, boolean dumpDalvik, int pid, String processName, + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, + int pid, String processName, long nativeMax, long nativeAllocated, long nativeFree, long dalvikMax, long dalvikAllocated, long dalvikFree) { @@ -2025,106 +2028,50 @@ public final class ActivityThread { return; } - // otherwise, show human-readable format - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private", - "Shared", "Private", "Swapped", "Heap", "Heap", "Heap"); - printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", - "Clean", "Clean", "Dirty", "Size", "Alloc", "Free"); - printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------", - "------", "------", "------", "------", "------", "------"); - printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss, - memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, - memInfo.nativePrivateDirty, memInfo.nativeSharedClean, - memInfo.nativePrivateClean, memInfo.nativeSwappedOut, - nativeMax, nativeAllocated, nativeFree); - printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss, - memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, - memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, - memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, - dalvikMax, dalvikAllocated, dalvikFree); - } else { - printRow(pw, HEAP_COLUMN, "", "Pss", "Private", - "Private", "Swapped", "Heap", "Heap", "Heap"); - printRow(pw, HEAP_COLUMN, "", "Total", "Dirty", - "Clean", "Dirty", "Size", "Alloc", "Free"); - printRow(pw, HEAP_COLUMN, "", "------", "------", "------", - "------", "------", "------", "------", "------"); - printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, - memInfo.nativePrivateDirty, - memInfo.nativePrivateClean, memInfo.nativeSwappedOut, - nativeMax, nativeAllocated, nativeFree); - printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, - memInfo.dalvikPrivateDirty, - memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, - dalvikMax, dalvikAllocated, dalvikFree); - } - - int otherPss = memInfo.otherPss; - int otherSwappablePss = memInfo.otherSwappablePss; - int otherSharedDirty = memInfo.otherSharedDirty; - int otherPrivateDirty = memInfo.otherPrivateDirty; - int otherSharedClean = memInfo.otherSharedClean; - int otherPrivateClean = memInfo.otherPrivateClean; - int otherSwappedOut = memInfo.otherSwappedOut; - - for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { - final int myPss = memInfo.getOtherPss(i); - final int mySwappablePss = memInfo.getOtherSwappablePss(i); - final int mySharedDirty = memInfo.getOtherSharedDirty(i); - final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); - final int mySharedClean = memInfo.getOtherSharedClean(i); - final int myPrivateClean = memInfo.getOtherPrivateClean(i); - final int mySwappedOut = memInfo.getOtherSwappedOut(i); - if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 - || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) { - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, mySwappablePss, mySharedDirty, myPrivateDirty, - mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); - } else { - printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, myPrivateDirty, - myPrivateClean, mySwappedOut, "", "", ""); - } - otherPss -= myPss; - otherSwappablePss -= mySwappablePss; - otherSharedDirty -= mySharedDirty; - otherPrivateDirty -= myPrivateDirty; - otherSharedClean -= mySharedClean; - otherPrivateClean -= myPrivateClean; - otherSwappedOut -= mySwappedOut; - } - } - - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss, - otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, - otherSwappedOut, "", "", ""); - printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(), - memInfo.getTotalSwappablePss(), - memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), - memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), - memInfo.getTotalSwappedOut(), nativeMax+dalvikMax, - nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); - } else { - printRow(pw, HEAP_COLUMN, "Unknown", otherPss, - otherPrivateDirty, otherPrivateClean, otherSwappedOut, - "", "", ""); - printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), - memInfo.getTotalPrivateDirty(), - memInfo.getTotalPrivateClean(), - memInfo.getTotalSwappedOut(), - nativeMax+dalvikMax, - nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); - } - - if (dumpDalvik) { - pw.println(" "); - pw.println(" Dalvik Details"); + if (!dumpSummaryOnly) { + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private", + "Shared", "Private", "Swapped", "Heap", "Heap", "Heap"); + printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", + "Clean", "Clean", "Dirty", "Size", "Alloc", "Free"); + printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------", + "------", "------", "------", "------", "------", "------"); + printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss, + memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, + memInfo.nativePrivateDirty, memInfo.nativeSharedClean, + memInfo.nativePrivateClean, memInfo.nativeSwappedOut, + nativeMax, nativeAllocated, nativeFree); + printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss, + memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, + memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, + memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, + dalvikMax, dalvikAllocated, dalvikFree); + } else { + printRow(pw, HEAP_COLUMN, "", "Pss", "Private", + "Private", "Swapped", "Heap", "Heap", "Heap"); + printRow(pw, HEAP_COLUMN, "", "Total", "Dirty", + "Clean", "Dirty", "Size", "Alloc", "Free"); + printRow(pw, HEAP_COLUMN, "", "------", "------", "------", + "------", "------", "------", "------", "------"); + printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, + memInfo.nativePrivateDirty, + memInfo.nativePrivateClean, memInfo.nativeSwappedOut, + nativeMax, nativeAllocated, nativeFree); + printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, + memInfo.dalvikPrivateDirty, + memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, + dalvikMax, dalvikAllocated, dalvikFree); + } + + int otherPss = memInfo.otherPss; + int otherSwappablePss = memInfo.otherSwappablePss; + int otherSharedDirty = memInfo.otherSharedDirty; + int otherPrivateDirty = memInfo.otherPrivateDirty; + int otherSharedClean = memInfo.otherSharedClean; + int otherPrivateClean = memInfo.otherPrivateClean; + int otherSwappedOut = memInfo.otherSwappedOut; - for (int i=Debug.MemoryInfo.NUM_OTHER_STATS; - i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; i++) { + for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { final int myPss = memInfo.getOtherPss(i); final int mySwappablePss = memInfo.getOtherSwappablePss(i); final int mySharedDirty = memInfo.getOtherSharedDirty(i); @@ -2133,7 +2080,7 @@ public final class ActivityThread { final int myPrivateClean = memInfo.getOtherPrivateClean(i); final int mySwappedOut = memInfo.getOtherSwappedOut(i); if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 - || mySharedClean != 0 || myPrivateClean != 0) { + || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) { if (dumpFullInfo) { printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), myPss, mySwappablePss, mySharedDirty, myPrivateDirty, @@ -2143,9 +2090,89 @@ public final class ActivityThread { myPss, myPrivateDirty, myPrivateClean, mySwappedOut, "", "", ""); } + otherPss -= myPss; + otherSwappablePss -= mySwappablePss; + otherSharedDirty -= mySharedDirty; + otherPrivateDirty -= myPrivateDirty; + otherSharedClean -= mySharedClean; + otherPrivateClean -= myPrivateClean; + otherSwappedOut -= mySwappedOut; + } + } + + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss, + otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, + otherSwappedOut, "", "", ""); + printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(), + memInfo.getTotalSwappablePss(), + memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), + memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), + memInfo.getTotalSwappedOut(), nativeMax+dalvikMax, + nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); + } else { + printRow(pw, HEAP_COLUMN, "Unknown", otherPss, + otherPrivateDirty, otherPrivateClean, otherSwappedOut, + "", "", ""); + printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), + memInfo.getTotalPrivateDirty(), + memInfo.getTotalPrivateClean(), + memInfo.getTotalSwappedOut(), + nativeMax+dalvikMax, + nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); + } + + if (dumpDalvik) { + pw.println(" "); + pw.println(" Dalvik Details"); + + for (int i=Debug.MemoryInfo.NUM_OTHER_STATS; + i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; i++) { + final int myPss = memInfo.getOtherPss(i); + final int mySwappablePss = memInfo.getOtherSwappablePss(i); + final int mySharedDirty = memInfo.getOtherSharedDirty(i); + final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); + final int mySharedClean = memInfo.getOtherSharedClean(i); + final int myPrivateClean = memInfo.getOtherPrivateClean(i); + final int mySwappedOut = memInfo.getOtherSwappedOut(i); + if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 + || mySharedClean != 0 || myPrivateClean != 0) { + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, mySwappablePss, mySharedDirty, myPrivateDirty, + mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); + } else { + printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, myPrivateDirty, + myPrivateClean, mySwappedOut, "", "", ""); + } + } } } } + + pw.println(" "); + pw.println(" App Summary"); + printRow(pw, ONE_COUNT_COLUMN_HEADER, "", "Pss(KB)"); + printRow(pw, ONE_COUNT_COLUMN_HEADER, "", "------"); + printRow(pw, ONE_COUNT_COLUMN, + "Java Heap:", memInfo.getSummaryJavaHeap()); + printRow(pw, ONE_COUNT_COLUMN, + "Native Heap:", memInfo.getSummaryNativeHeap()); + printRow(pw, ONE_COUNT_COLUMN, + "Code:", memInfo.getSummaryCode()); + printRow(pw, ONE_COUNT_COLUMN, + "Stack:", memInfo.getSummaryStack()); + printRow(pw, ONE_COUNT_COLUMN, + "Graphics:", memInfo.getSummaryGraphics()); + printRow(pw, ONE_COUNT_COLUMN, + "Private Other:", memInfo.getSummaryPrivateOther()); + printRow(pw, ONE_COUNT_COLUMN, + "System:", memInfo.getSummarySystem()); + pw.println(" "); + printRow(pw, TWO_COUNT_COLUMNS, + "TOTAL:", memInfo.getSummaryTotalPss(), + "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap()); } public void registerOnActivityPausedListener(Activity activity, diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index 968c956..fa81412 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -31,6 +31,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroupOverlay; import android.view.ViewParent; +import android.view.ViewRootImpl; import android.view.ViewTreeObserver; import android.view.Window; import android.widget.ImageView; @@ -187,11 +188,6 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { */ public static final int MSG_SHARED_ELEMENT_DESTINATION = 107; - /** - * Send the shared element positions. - */ - public static final int MSG_SEND_SHARED_ELEMENT_DESTINATION = 108; - private Window mWindow; final protected ArrayList<String> mAllSharedElementNames; final protected ArrayList<View> mSharedElements = new ArrayList<View>(); @@ -207,6 +203,8 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { new ArrayList<GhostViewListeners>(); private ArrayMap<View, Float> mOriginalAlphas = new ArrayMap<View, Float>(); private ArrayList<Matrix> mSharedElementParentMatrices; + private boolean mSharedElementTransitionComplete; + private boolean mViewsTransitionComplete; public ActivityTransitionCoordinator(Window window, ArrayList<String> allSharedElementNames, @@ -219,6 +217,11 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { } protected void viewsReady(ArrayMap<String, View> sharedElements) { + final View decor = getDecor(); + final ViewRootImpl viewRoot = decor == null ? null : decor.getViewRootImpl(); + if (viewRoot != null) { + viewRoot.setPausedForTransition(true); + } sharedElements.retainAll(mAllSharedElementNames); if (mListener != null) { mListener.onMapSharedElements(mAllSharedElementNames, sharedElements); @@ -878,6 +881,32 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { } } + protected boolean isViewsTransitionComplete() { + return mViewsTransitionComplete; + } + + protected void viewsTransitionComplete() { + mViewsTransitionComplete = true; + startInputWhenTransitionsComplete(); + } + + protected void sharedElementTransitionComplete() { + mSharedElementTransitionComplete = true; + startInputWhenTransitionsComplete(); + } + private void startInputWhenTransitionsComplete() { + if (mViewsTransitionComplete && mSharedElementTransitionComplete) { + final View decor = getDecor(); + if (decor != null) { + final ViewRootImpl viewRoot = decor.getViewRootImpl(); + viewRoot.setPausedForTransition(false); + } + onTransitionsComplete(); + } + } + + protected void onTransitionsComplete() {} + protected class ContinueTransitionListener extends Transition.TransitionListenerAdapter { @Override public void onTransitionStart(Transition transition) { diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index b0fda9c..5e7bd0d 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -71,10 +71,7 @@ import java.io.IOException; * {@link android.content.Context#getSystemService * Context.getSystemService(Context.ALARM_SERVICE)}. */ -public class AlarmManager -{ - private static final String TAG = "AlarmManager"; - +public class AlarmManager { /** * Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()} * (wall clock time in UTC), which will wake up the device when @@ -558,7 +555,93 @@ public class AlarmManager long intervalMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null, null); } - + + /** + * Like {@link #set(int, long, PendingIntent)}, but this alarm will be allowed to execute + * even when the system is in low-power idle modes. This type of alarm must <b>only</b> + * be used for situations where it is actually required that the alarm go off while in + * idle -- a reasonable example would be for a calendar notification that should make a + * sound so the user is aware of it. These alarms can significantly impact the power use + * of the device when idle (and thus cause significant battery blame to the app scheduling + * them), so they should be used with care. + * + * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen + * out of order with any other alarms, even those from the same app. This will clearly happen + * when the device is idle (since this alarm can go off while idle, when any other alarms + * from the app will be held until later), but may also happen even when not idle.</p> + * + * <p>Regardless of the app's target SDK version, this call always allows batching of the + * alarm.</p> + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. + * @param triggerAtMillis time in milliseconds that the alarm should go + * off, using the appropriate clock (depending on the alarm type). + * @param operation Action to perform when the alarm goes off; + * typically comes from {@link PendingIntent#getBroadcast + * IntentSender.getBroadcast()}. + * + * @see #set(int, long, PendingIntent) + * @see #setExactAndAllowWhileIdle + * @see #cancel + * @see android.content.Context#sendBroadcast + * @see android.content.Context#registerReceiver + * @see android.content.Intent#filterEquals + * @see #ELAPSED_REALTIME + * @see #ELAPSED_REALTIME_WAKEUP + * @see #RTC + * @see #RTC_WAKEUP + */ + public void setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { + setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, operation, + null, null); + } + + /** + * Like {@link #setExact(int, long, PendingIntent)}, but this alarm will be allowed to execute + * even when the system is in low-power idle modes. If you don't need exact scheduling of + * the alarm but still need to execute while idle, consider using + * {@link #setAndAllowWhileIdle}. This type of alarm must <b>only</b> + * be used for situations where it is actually required that the alarm go off while in + * idle -- a reasonable example would be for a calendar notification that should make a + * sound so the user is aware of it. These alarms can significantly impact the power use + * of the device when idle (and thus cause significant battery blame to the app scheduling + * them), so they should be used with care. + * + * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen + * out of order with any other alarms, even those from the same app. This will clearly happen + * when the device is idle (since this alarm can go off while idle, when any other alarms + * from the app will be held until later), but may also happen even when not idle. + * Note that the OS will allow itself more flexibility for scheduling these alarms than + * regular exact alarms, since the application has opted into this behavior. When the + * device is idle it may take even more liberties with scheduling in order to optimize + * for battery life.</p> + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. + * @param triggerAtMillis time in milliseconds that the alarm should go + * off, using the appropriate clock (depending on the alarm type). + * @param operation Action to perform when the alarm goes off; + * typically comes from {@link PendingIntent#getBroadcast + * IntentSender.getBroadcast()}. + * + * @see #set + * @see #setRepeating + * @see #setWindow + * @see #cancel + * @see android.content.Context#sendBroadcast + * @see android.content.Context#registerReceiver + * @see android.content.Intent#filterEquals + * @see #ELAPSED_REALTIME + * @see #ELAPSED_REALTIME_WAKEUP + * @see #RTC + * @see #RTC_WAKEUP + */ + public void setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { + setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation, + null, null); + } + /** * Remove any alarms with a matching {@link Intent}. * Any alarm, of any type, whose Intent matches this one (as defined by diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 5aa399b..e728971 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -227,8 +227,10 @@ public class AppOpsManager { public static final int OP_BODY_SENSORS = 56; /** @hide Read previously received cell broadcast messages. */ public static final int OP_READ_CELL_BROADCASTS = 57; + /** @hide Inject mock location into the system. */ + public static final int OP_MOCK_LOCATION = 58; /** @hide */ - public static final int _NUM_OP = 58; + public static final int _NUM_OP = 59; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -308,6 +310,9 @@ public class AppOpsManager { /** @hide Read previously received cell broadcast messages. */ public static final String OPSTR_READ_CELL_BROADCASTS = "android:read_cell_broadcasts"; + /** Inject mock location into the system. */ + public static final String OPSTR_MOCK_LOCATION + = "android:mock_location"; /** * This maps each operation to the operation that serves as the @@ -375,7 +380,8 @@ public class AppOpsManager { OP_PROCESS_OUTGOING_CALLS, OP_USE_FINGERPRINT, OP_BODY_SENSORS, - OP_READ_CELL_BROADCASTS + OP_READ_CELL_BROADCASTS, + OP_MOCK_LOCATION }; /** @@ -440,7 +446,8 @@ public class AppOpsManager { null, OPSTR_USE_FINGERPRINT, OPSTR_BODY_SENSORS, - OPSTR_READ_CELL_BROADCASTS + OPSTR_READ_CELL_BROADCASTS, + OPSTR_MOCK_LOCATION }; /** @@ -505,7 +512,8 @@ public class AppOpsManager { "PROCESS_OUTGOING_CALLS", "USE_FINGERPRINT", "BODY_SENSORS", - "READ_CELL_BROADCASTS" + "READ_CELL_BROADCASTS", + "MOCK_LOCATION" }; /** @@ -570,7 +578,8 @@ public class AppOpsManager { Manifest.permission.PROCESS_OUTGOING_CALLS, Manifest.permission.USE_FINGERPRINT, Manifest.permission.BODY_SENSORS, - Manifest.permission.READ_CELL_BROADCASTS + Manifest.permission.READ_CELL_BROADCASTS, + null }; /** @@ -606,7 +615,7 @@ public class AppOpsManager { UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW null, //ACCESS_NOTIFICATIONS null, //CAMERA - null, //RECORD_AUDIO + UserManager.DISALLOW_RECORD_AUDIO, //RECORD_AUDIO null, //PLAY_AUDIO null, //READ_CLIPBOARD null, //WRITE_CLIPBOARD @@ -636,7 +645,8 @@ public class AppOpsManager { null, // PROCESS_OUTGOING_CALLS null, // USE_FINGERPRINT null, // BODY_SENSORS - null // READ_CELL_BROADCASTS + null, // READ_CELL_BROADCASTS + null // MOCK_LOCATION }; /** @@ -701,7 +711,8 @@ public class AppOpsManager { false, // PROCESS_OUTGOING_CALLS false, // USE_FINGERPRINT false, // BODY_SENSORS - false // READ_CELL_BROADCASTS + false, // READ_CELL_BROADCASTS + false // MOCK_LOCATION }; /** @@ -765,7 +776,8 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, - AppOpsManager.MODE_ALLOWED + AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ERRORED // OP_MOCK_LOCATION }; /** @@ -833,6 +845,7 @@ public class AppOpsManager { false, false, false, + false, false }; diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 90293a4..04f6430 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -446,18 +446,40 @@ final class ApplicationPackageManager extends PackageManager { } @Override - public void grantPermission(String packageName, String permissionName, UserHandle user) { + public void grantRuntimePermission(String packageName, String permissionName, + UserHandle user) { + try { + mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier()); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + + @Override + public void revokeRuntimePermission(String packageName, String permissionName, + UserHandle user) { + try { + mPM.revokeRuntimePermission(packageName, permissionName, user.getIdentifier()); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + + @Override + public int getPermissionFlags(String permissionName, String packageName, UserHandle user) { try { - mPM.grantPermission(packageName, permissionName, user.getIdentifier()); + return mPM.getPermissionFlags(permissionName, packageName, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } } @Override - public void revokePermission(String packageName, String permissionName, UserHandle user) { + public void updatePermissionFlags(String permissionName, String packageName, + int flagMask, int flagValues, UserHandle user) { try { - mPM.revokePermission(packageName, permissionName, user.getIdentifier()); + mPM.updatePermissionFlags(permissionName, packageName, flagMask, + flagValues, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } @@ -1571,9 +1593,15 @@ final class ApplicationPackageManager extends PackageManager { final VolumeInfo currentVol = getPrimaryStorageCurrentVolume(); final List<VolumeInfo> vols = storage.getVolumes(); final List<VolumeInfo> candidates = new ArrayList<>(); - for (VolumeInfo vol : vols) { - if (Objects.equals(vol, currentVol) || isPrimaryStorageCandidateVolume(vol)) { - candidates.add(vol); + if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, + storage.getPrimaryStorageUuid()) && currentVol != null) { + // TODO: support moving primary physical to emulated volume + candidates.add(currentVol); + } else { + for (VolumeInfo vol : vols) { + if (Objects.equals(vol, currentVol) || isPrimaryStorageCandidateVolume(vol)) { + candidates.add(vol); + } } } return candidates; @@ -1590,12 +1618,7 @@ final class ApplicationPackageManager extends PackageManager { return false; } - // We can move to public volumes on legacy devices - if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.getDisk().isDefaultPrimary()) { - return true; - } - - // Otherwise we can move to any private volume + // We can move to any private volume return (vol.getType() == VolumeInfo.TYPE_PRIVATE); } diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index b6989ab..1461380 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -529,10 +529,12 @@ public abstract class ApplicationThreadNative extends Binder boolean checkin = data.readInt() != 0; boolean dumpInfo = data.readInt() != 0; boolean dumpDalvik = data.readInt() != 0; + boolean dumpSummaryOnly = data.readInt() != 0; String[] args = data.readStringArray(); if (fd != null) { try { - dumpMemInfo(fd.getFileDescriptor(), mi, checkin, dumpInfo, dumpDalvik, args); + dumpMemInfo(fd.getFileDescriptor(), mi, checkin, dumpInfo, + dumpDalvik, dumpSummaryOnly, args); } finally { try { fd.close(); @@ -1248,7 +1250,7 @@ class ApplicationThreadProxy implements IApplicationThread { } public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin, - boolean dumpInfo, boolean dumpDalvik, String[] args) throws RemoteException { + boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, String[] args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); @@ -1257,6 +1259,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeInt(checkin ? 1 : 0); data.writeInt(dumpInfo ? 1 : 0); data.writeInt(dumpDalvik ? 1 : 0); + data.writeInt(dumpSummaryOnly ? 1 : 0); data.writeStringArray(args); mRemote.transact(DUMP_MEM_INFO_TRANSACTION, data, reply, 0); reply.readException(); diff --git a/core/java/android/app/AssistAction.java b/core/java/android/app/AssistAction.java deleted file mode 100644 index eb33542..0000000 --- a/core/java/android/app/AssistAction.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.Uri; -import android.os.Bundle; -import android.text.TextUtils; - -import com.android.internal.util.Preconditions; - -/** - * Helper class for building a {@link Bundle} representing an action being performed by the user, - * to be included in the Bundle generated by {@link Activity#onProvideAssistData}. - * - * @see Activity#onProvideAssistData - */ -public final class AssistAction { - - /** - * Key name for the Bundle containing the schema.org representation of - * an action performed, and should be stored in the Bundle generated by - * {@link Activity#onProvideAssistData}. - */ - public static final String ASSIST_ACTION_KEY = "android:assist_action"; - - /** Bundle key to specify the schema.org ID of the content. */ - public static final String KEY_ID = "@id"; - - /** Bundle key to specify the schema.org type of the content. */ - public static final String KEY_TYPE = "@type"; - - /** Bundle key to specify the name of the content. */ - public static final String KEY_NAME = "name"; - - /** Bundle key to specify the description of the content. */ - public static final String KEY_DESCRIPTION = "description"; - - /** Bundle key to specify the URL of the content. */ - public static final String KEY_URL = "url"; - - /** Bundle key to specify the object of an action. */ - public static final String KEY_ACTION_OBJECT = "object"; - - /** Bundle key to specify the action's status. */ - public static final String KEY_ACTION_STATUS = "actionStatus"; - - /** The act of editing by adding an object to a collection. */ - public static final String TYPE_ADD_ACTION = "AddAction"; - - /** The act of bookmarking an object. */ - public static final String TYPE_BOOKMARK_ACTION = "BookmarkAction"; - - /** The act of liking an object. */ - public static final String TYPE_LIKE_ACTION = "LikeAction"; - - /** The act of consuming audio content. */ - public static final String TYPE_LISTEN_ACTION = "ListenAction"; - - /** The act of consuming static visual content. */ - public static final String TYPE_VIEW_ACTION = "ViewAction"; - - /** The act of expressing a desire about the object. */ - public static final String TYPE_WANT_ACTION = "WantAction"; - - /** The act of watching an object. */ - public static final String TYPE_WATCH_ACTION = "WatchAction"; - - /** The status of an active action. */ - public static final String STATUS_TYPE_ACTIVE = "ActiveActionStatus"; - - /** The status of a completed action. */ - public static final String STATUS_TYPE_COMPLETED = "CompletedActionStatus"; - - private AssistAction() { - } - - /** - * Update the Bundle passed into {@link Activity#onProvideAssistData} with the action Bundle, - * built with {@link ActionBuilder}. - * - * @param assistDataBundle The Bundle provided to {@link Activity#onProvideAssistData}. - * @param actionBundle The Bundle representing an schema.org action. - */ - public static void updateAssistData(Bundle assistDataBundle, Bundle actionBundle) { - Preconditions.checkNotNull(assistDataBundle); - Preconditions.checkNotNull(actionBundle); - - Preconditions.checkNotNull(actionBundle.getString(KEY_TYPE), - "The '@type' property is required in the provided actionBundle"); - assistDataBundle.putParcelable(ASSIST_ACTION_KEY, actionBundle); - } - - /** - * Builds a {@link Bundle} representing a schema.org entity. - */ - public static final class ThingBuilder { - private final Bundle mBundle; - - public ThingBuilder() { - mBundle = new Bundle(); - } - - /** - * Sets the name of the content. - * - * @param name The name of the content. - */ - public ThingBuilder setName(@Nullable String name) { - set(KEY_NAME, name); - return this; - } - - /** - * Sets the app URI of the content. - * - * @param uri The app URI of the content. - */ - public ThingBuilder setUrl(@Nullable Uri uri) { - if (uri != null) { - set(KEY_URL, uri.toString()); - } - return this; - } - - /** - * Sets the ID of the content. - * - * @param id Set the ID of the content. - */ - public ThingBuilder setId(@Nullable String id) { - set(KEY_ID, id); - return this; - } - - /** - * Sets the schema.org type of the content. - * - * @param type The schema.org type. - */ - public ThingBuilder setType(@Nullable String type) { - set(KEY_TYPE, type); - return this; - } - - /** - * Sets the optional description of the content. - * - * @param description The description of the content. - */ - public ThingBuilder setDescription(@Nullable String description) { - set(KEY_DESCRIPTION, description); - return this; - } - - /** - * Sets a property of the content. - * - * @param key The schema.org property. Must not be null. - * @param value The value of the schema.org property. - * If null, the value will be ignored. - */ - public ThingBuilder set(@NonNull String key, @Nullable String value) { - if (value != null) { - mBundle.putString(key, value); - } - return this; - } - - /** - * Sets a property of the content. - * - * @param key The schema.org property. Must not be null. - * @param value The value of the schema.org property represented as a bundle. - * If null, the value will be ignored. - */ - public ThingBuilder set(@NonNull String key, @Nullable Bundle value) { - if (value != null) { - mBundle.putParcelable(key, value); - } - return this; - } - - /** - * Build the {@link Bundle} object representing the schema.org entity. - */ - public Bundle build() { - return mBundle; - } - } - - /** - * Builds a {@link Bundle} representing a schema.org action. - */ - public static final class ActionBuilder { - private final Bundle mBundle; - - public ActionBuilder() { - mBundle = new Bundle(); - } - - /** - * Sets the schema.org type of the action. - * - * @param type The schema.org type. - */ - public ActionBuilder setType(@Nullable String type) { - set(KEY_TYPE, type); - return this; - } - - /** - * Sets the schema.org object of the action. - * - * @param object The schema.org object of the action. - */ - public ActionBuilder setObject(@Nullable Bundle object) { - set(KEY_ACTION_OBJECT, object); - return this; - } - - /** - * Sets a property of the action. - * - * @param key The schema.org property. Must not be null. - * @param value The value of the schema.org property. - * If null, the value will be ignored. - */ - public ActionBuilder set(@NonNull String key, @Nullable String value) { - if (value != null) { - mBundle.putString(key, value); - } - return this; - } - - /** - * Sets a property of the action. - * - * @param key The schema.org property. Must not be null. - * @param value The value of the schema.org property represented as a bundle. - * If null, the value will be ignored. - */ - public ActionBuilder set(@NonNull String key, @Nullable Bundle value) { - if (value != null) { - mBundle.putParcelable(key, value); - } - return this; - } - - /** - * Build the {@link Bundle} object representing the schema.org action. - */ - public Bundle build() { - if (TextUtils.isEmpty(mBundle.getString(KEY_TYPE, null))) { - // Defaults to the base action type http://schema.org/Action. - setType("Action"); - } - - return mBundle; - } - } -} diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java index 3abbb5b..a06bc31 100644 --- a/core/java/android/app/AssistStructure.java +++ b/core/java/android/app/AssistStructure.java @@ -35,6 +35,7 @@ import android.util.Log; import android.view.View; import android.view.ViewAssistStructure; import android.view.ViewRootImpl; +import android.view.WindowManager; import android.view.WindowManagerGlobal; import java.util.ArrayList; @@ -140,7 +141,14 @@ final public class AssistStructure implements Parcelable { mTitle = root.getTitle(); mRoot = new ViewNode(); ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false); - view.dispatchProvideAssistStructure(builder); + if ((root.getWindowFlags()&WindowManager.LayoutParams.FLAG_SECURE) != 0) { + // This is a secure window, so it doesn't want a screenshot, and that + // means we should also not copy out its view hierarchy. + view.onProvideStructure(builder); + builder.setAssistBlocked(true); + return; + } + view.dispatchProvideStructure(builder); } WindowNode(Parcel in, PooledStringReader preader) { @@ -652,7 +660,7 @@ final public class AssistStructure implements Parcelable { } @Override - public Bundle editExtras() { + public Bundle getExtras() { if (mNode.mExtras != null) { return mNode.mExtras; } @@ -661,8 +669,8 @@ final public class AssistStructure implements Parcelable { } @Override - public void clearExtras() { - mNode.mExtras = null; + public boolean hasExtras() { + return mNode.mExtras != null; } @Override diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 81a78f6..cb20cb8 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -237,8 +237,11 @@ class ContextImpl extends Context { } @Override - public void setTheme(int resid) { - mThemeResource = resid; + public void setTheme(int resId) { + if (mThemeResource != resId) { + mThemeResource = resId; + initializeTheme(); + } } @Override @@ -248,13 +251,22 @@ class ContextImpl extends Context { @Override public Resources.Theme getTheme() { + if (mTheme != null) { + return mTheme; + } + + mThemeResource = Resources.selectDefaultTheme(mThemeResource, + getOuterContext().getApplicationInfo().targetSdkVersion); + initializeTheme(); + + return mTheme; + } + + private void initializeTheme() { if (mTheme == null) { - mThemeResource = Resources.selectDefaultTheme(mThemeResource, - getOuterContext().getApplicationInfo().targetSdkVersion); mTheme = mResources.newTheme(); - mTheme.applyStyle(mThemeResource, true); } - return mTheme; + mTheme.applyStyle(mThemeResource, true); } @Override diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index e84a8da..4db4be0 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -55,8 +55,6 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { private boolean mWasOpaque; private boolean mAreViewsReady; private boolean mIsViewsTransitionStarted; - private boolean mIsViewsTransitionComplete; - private boolean mIsSharedElementTransitionComplete; private Transition mEnterViewsTransition; public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver, @@ -456,7 +454,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } } if (viewsTransition == null) { - viewTransitionComplete(); + viewsTransitionComplete(); } else { viewsTransition.forceVisibility(View.INVISIBLE, true); final ArrayList<View> transitioningViews = mTransitioningViews; @@ -474,7 +472,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { public void onTransitionEnd(Transition transition) { mEnterViewsTransition = null; transition.removeListener(this); - viewTransitionComplete(); + viewsTransitionComplete(); super.onTransitionEnd(transition); } }); @@ -497,18 +495,9 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { return transition; } - private void viewTransitionComplete() { - mIsViewsTransitionComplete = true; - if (mIsSharedElementTransitionComplete) { - moveSharedElementsFromOverlay(); - } - } - - private void sharedElementTransitionComplete() { - mIsSharedElementTransitionComplete = true; - if (mIsViewsTransitionComplete) { - moveSharedElementsFromOverlay(); - } + @Override + protected void onTransitionsComplete() { + moveSharedElementsFromOverlay(); } private void sharedElementTransitionStarted() { diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index 0f286fb..9ddebb0 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -46,8 +46,6 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { private static final String TAG = "ExitTransitionCoordinator"; private static final long MAX_WAIT_MS = 1000; - private boolean mExitComplete; - private Bundle mSharedElementBundle; private boolean mExitNotified; @@ -165,7 +163,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { @Override public void onTransitionEnd(Transition transition) { transition.removeListener(this); - if (mExitComplete) { + if (isViewsTransitionComplete()) { delayCancel(); } } @@ -310,14 +308,14 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { viewsTransition = configureTransition(getViewsTransition(), true); } if (viewsTransition == null) { - exitTransitionComplete(); + viewsTransitionComplete(); } else { final ArrayList<View> transitioningViews = mTransitioningViews; viewsTransition.addListener(new ContinueTransitionListener() { @Override public void onTransitionEnd(Transition transition) { transition.removeListener(this); - exitTransitionComplete(); + viewsTransitionComplete(); if (mIsHidden && transitioningViews != null) { showViews(transitioningViews, true); } @@ -373,19 +371,15 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } } - private void exitTransitionComplete() { - mExitComplete = true; - notifyComplete(); - } - protected boolean isReadyToNotify() { return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady; } - private void sharedElementTransitionComplete() { + @Override + protected void sharedElementTransitionComplete() { mSharedElementBundle = mExitSharedElementBundle == null ? captureSharedElementState() : captureExitSharedElementsState(); - notifyComplete(); + super.sharedElementTransitionComplete(); } private Bundle captureExitSharedElementsState() { @@ -405,6 +399,11 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { return bundle; } + @Override + protected void onTransitionsComplete() { + notifyComplete(); + } + protected void notifyComplete() { if (isReadyToNotify()) { if (!mSharedElementNotified) { @@ -433,7 +432,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } private void notifyExitComplete() { - if (!mExitNotified && mExitComplete) { + if (!mExitNotified && isViewsTransitionComplete()) { mExitNotified = true; mResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null); mResultReceiver = null; // done talking diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 6b5239d..e0a30ad 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -945,7 +945,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate if (f.mView != null) { // Need to save the current view state if not // done already. - if (!mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) { + if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) { saveFragmentViewState(f); } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 5829fbe..05a936c 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -403,6 +403,9 @@ public interface IActivityManager extends IInterface { public void keyguardWaitingForActivityDrawn() throws RemoteException; + public void keyguardGoingAway(boolean disableWindowAnimations, + boolean keyguardGoingToNotificationShade) throws RemoteException; + public boolean shouldUpRecreateTask(IBinder token, String destAffinity) throws RemoteException; @@ -842,4 +845,5 @@ public interface IActivityManager extends IInterface { int SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+294; int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295; int UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296; + int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 3fb82f6..185578f 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -131,7 +131,7 @@ public interface IApplicationThread extends IInterface { void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException; void scheduleTrimMemory(int level) throws RemoteException; void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin, boolean dumpInfo, - boolean dumpDalvik, String[] args) throws RemoteException; + boolean dumpDalvik, boolean dumpSummaryOnly, String[] args) throws RemoteException; void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException; void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException; void unstableProviderDied(IBinder provider) throws RemoteException; diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index ac8d5d8..63ff005 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -75,7 +75,7 @@ interface INotificationManager void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter); int getInterruptionFilterFromListener(in INotificationListener token); void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim); - NotificationManager.Policy.Token getPolicyTokenFromListener(in INotificationListener listener); + void setInterruptionFilter(String pkg, int interruptionFilter); ComponentName getEffectsSuppressor(); boolean matchesCallFilter(in Bundle extras); @@ -87,10 +87,13 @@ interface INotificationManager oneway void setZenMode(int mode, in Uri conditionId, String reason); oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions); oneway void requestZenModeConditions(in IConditionListener callback, int relevance); - oneway void requestNotificationPolicyToken(String pkg, in INotificationManagerCallback callback); - boolean isNotificationPolicyTokenValid(String pkg, in NotificationManager.Policy.Token token); - NotificationManager.Policy getNotificationPolicy(in NotificationManager.Policy.Token token); - void setNotificationPolicy(in NotificationManager.Policy.Token token, in NotificationManager.Policy policy); + oneway void requestNotificationPolicyAccess(String pkg, in INotificationManagerCallback callback); + boolean isNotificationPolicyAccessGranted(String pkg); + NotificationManager.Policy getNotificationPolicy(String pkg); + void setNotificationPolicy(String pkg, in NotificationManager.Policy policy); + String[] getPackagesRequestingNotificationPolicyAccess(); + boolean isNotificationPolicyAccessGrantedForPackage(String pkg); + void setNotificationPolicyAccessGranted(String pkg, boolean granted); byte[] getBackupPayload(int user); void applyRestore(in byte[] payload, int user); diff --git a/core/java/android/app/INotificationManagerCallback.aidl b/core/java/android/app/INotificationManagerCallback.aidl index b9414ca..9929745 100644 --- a/core/java/android/app/INotificationManagerCallback.aidl +++ b/core/java/android/app/INotificationManagerCallback.aidl @@ -20,5 +20,5 @@ import android.app.NotificationManager; /** @hide */ oneway interface INotificationManagerCallback { - void onPolicyToken(in NotificationManager.Policy.Token token); + void onPolicyRequestResult(boolean granted); } diff --git a/core/java/android/app/NotificationManager.aidl b/core/java/android/app/NotificationManager.aidl index 8380b8d..a5d5671 100644 --- a/core/java/android/app/NotificationManager.aidl +++ b/core/java/android/app/NotificationManager.aidl @@ -17,4 +17,3 @@ package android.app; parcelable NotificationManager.Policy; -parcelable NotificationManager.Policy.Token;
\ No newline at end of file diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 0a59026..e4bbe27 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.app.Notification.Builder; -import android.app.NotificationManager.Policy.Token; import android.content.ComponentName; import android.content.Context; import android.content.pm.ParceledListSlice; @@ -38,6 +37,7 @@ import android.provider.Settings.Global; import android.service.notification.IConditionListener; import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; +import android.util.ArraySet; import android.util.Log; import java.util.Objects; @@ -107,6 +107,43 @@ public class NotificationManager public static final String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED"; + /** + * Intent that is broadcast when the state of getCurrentInterruptionFilter() changes. + * This broadcast is only sent to registered receivers. + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_INTERRUPTION_FILTER_CHANGED + = "android.app.action.INTERRUPTION_FILTER_CHANGED"; + + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * Normal interruption filter. + */ + public static final int INTERRUPTION_FILTER_ALL = 1; + + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * Priority interruption filter. + */ + public static final int INTERRUPTION_FILTER_PRIORITY = 2; + + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * No interruptions filter. + */ + public static final int INTERRUPTION_FILTER_NONE = 3; + + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * Alarms only interruption filter. + */ + public static final int INTERRUPTION_FILTER_ALARMS = 4; + + /** {@link #getCurrentInterruptionFilter() Interruption filter} constant - returned when + * the value is unavailable for any reason. + */ + public static final int INTERRUPTION_FILTER_UNKNOWN = 0; + private static INotificationManager sService; /** @hide */ @@ -357,29 +394,29 @@ public class NotificationManager } /** - * Requests a notification policy token for the calling package. + * Requests the ability to read/modify notification policy for the calling package. * - * @param callback required, used to receive the granted token or the deny signal. + * @param callback required, used to receive the granted or the denied signal. * @param handler The handler used when receiving the result. * If null, the current thread is used. */ - public void requestNotificationPolicyToken(@NonNull final Policy.Token.RequestCallback callback, + public void requestPolicyAccess(@NonNull final NotificationPolicyAccessRequestCallback callback, @Nullable Handler handler) { checkRequired("callback", callback); final Handler h = handler != null ? handler : new Handler(); INotificationManager service = getService(); try { - service.requestNotificationPolicyToken(mContext.getOpPackageName(), + service.requestNotificationPolicyAccess(mContext.getOpPackageName(), new INotificationManagerCallback.Stub() { @Override - public void onPolicyToken(final Token token) throws RemoteException { + public void onPolicyRequestResult(final boolean granted) throws RemoteException { h.post(new Runnable() { @Override public void run() { - if (token != null) { - callback.onTokenGranted(token); + if (granted) { + callback.onAccessGranted(); } else { - callback.onTokenDenied(); + callback.onAccessDenied(); } } }); @@ -389,16 +426,38 @@ public class NotificationManager } } + /** Callback for receiving the result of a policy access request. */ + public static abstract class NotificationPolicyAccessRequestCallback { + /** + * Received if the request was granted for this package. + */ + public abstract void onAccessGranted(); + + /** + * Received if the request was denied for this package. + */ + public abstract void onAccessDenied(); + } + /** - * Checks a given notification policy token. + * Checks the ability to read/modify notification policy for the calling package. * - * Returns true if the token is still valid for managing policy. + * Returns true if the calling package can read/modify notification policy. */ - public boolean isNotificationPolicyTokenValid(@NonNull Policy.Token token) { - if (token == null) return false; + public boolean isNotificationPolicyAccessGranted() { INotificationManager service = getService(); try { - return service.isNotificationPolicyTokenValid(mContext.getOpPackageName(), token); + return service.isNotificationPolicyAccessGranted(mContext.getOpPackageName()); + } catch (RemoteException e) { + } + return false; + } + + /** @hide */ + public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) { + INotificationManager service = getService(); + try { + return service.isNotificationPolicyAccessGrantedForPackage(pkg); } catch (RemoteException e) { } return false; @@ -407,13 +466,13 @@ public class NotificationManager /** * Gets the current notification policy. * - * @param token A valid notification policy token is required to access the current policy. + * <p> + * Only available if policy access is granted. */ - public Policy getNotificationPolicy(@NonNull Policy.Token token) { - checkRequired("token", token); + public Policy getNotificationPolicy() { INotificationManager service = getService(); try { - return service.getNotificationPolicy(token); + return service.getNotificationPolicy(mContext.getOpPackageName()); } catch (RemoteException e) { } return null; @@ -422,19 +481,46 @@ public class NotificationManager /** * Sets the current notification policy. * - * @param token A valid notification policy token is required to modify the current policy. + * <p> + * Only available if policy access is granted. + * * @param policy The new desired policy. */ - public void setNotificationPolicy(@NonNull Policy.Token token, @NonNull Policy policy) { - checkRequired("token", token); + public void setNotificationPolicy(@NonNull Policy policy) { checkRequired("policy", policy); INotificationManager service = getService(); try { - service.setNotificationPolicy(token, policy); + service.setNotificationPolicy(mContext.getOpPackageName(), policy); + } catch (RemoteException e) { + } + } + + /** @hide */ + public void setNotificationPolicyAccessGranted(String pkg, boolean granted) { + INotificationManager service = getService(); + try { + service.setNotificationPolicyAccessGranted(pkg, granted); } catch (RemoteException e) { } } + /** @hide */ + public ArraySet<String> getPackagesRequestingNotificationPolicyAccess() { + INotificationManager service = getService(); + try { + final String[] pkgs = service.getPackagesRequestingNotificationPolicyAccess(); + if (pkgs != null && pkgs.length > 0) { + final ArraySet<String> rt = new ArraySet<>(pkgs.length); + for (int i = 0; i < pkgs.length; i++) { + rt.add(pkgs[i]); + } + return rt; + } + } catch (RemoteException e) { + } + return new ArraySet<String>(); + } + private Context mContext; private static void checkRequired(String name, Object value) { @@ -477,24 +563,30 @@ public class NotificationManager /** Notification categories to prioritize. Bitmask of PRIORITY_CATEGORY_* constants. */ public final int priorityCategories; - /** Notification senders to prioritize. One of: + /** Notification senders to prioritize for calls. One of: + * PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */ + public final int priorityCallSenders; + + /** Notification senders to prioritize for messages. One of: * PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */ - public final int prioritySenders; + public final int priorityMessageSenders; - public Policy(int priorityCategories, int prioritySenders) { + public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders) { this.priorityCategories = priorityCategories; - this.prioritySenders = prioritySenders; + this.priorityCallSenders = priorityCallSenders; + this.priorityMessageSenders = priorityMessageSenders; } /** @hide */ public Policy(Parcel source) { - this(source.readInt(), source.readInt()); + this(source.readInt(), source.readInt(), source.readInt()); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(priorityCategories); - dest.writeInt(prioritySenders); + dest.writeInt(priorityCallSenders); + dest.writeInt(priorityMessageSenders); } @Override @@ -504,7 +596,7 @@ public class NotificationManager @Override public int hashCode() { - return Objects.hash(priorityCategories, prioritySenders); + return Objects.hash(priorityCategories, priorityCallSenders, priorityMessageSenders); } @Override @@ -513,14 +605,16 @@ public class NotificationManager if (o == this) return true; final Policy other = (Policy) o; return other.priorityCategories == priorityCategories - && other.prioritySenders == prioritySenders; + && other.priorityCallSenders == priorityCallSenders + && other.priorityMessageSenders == priorityMessageSenders; } @Override public String toString() { return "NotificationManager.Policy[" + "priorityCategories=" + priorityCategoriesToString(priorityCategories) - + ",prioritySenders=" + prioritySendersToString(prioritySenders) + + ",priorityCallSenders=" + prioritySendersToString(priorityCallSenders) + + ",priorityMessageSenders=" + prioritySendersToString(priorityMessageSenders) + "]"; } @@ -574,75 +668,6 @@ public class NotificationManager } }; - /** - * Represents a client-specific token required to manage notification policy. - */ - public static class Token implements Parcelable { - private final IBinder mBinder; - - /** @hide */ - public Token(IBinder binder) { - if (binder == null) throw new IllegalArgumentException("Binder required for token"); - mBinder = binder; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public int hashCode() { - return Objects.hash(mBinder); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Token)) return false; - if (o == this) return true; - final Token other = (Token) o; - return Objects.equals(other.mBinder, mBinder); - } - - @Override - public String toString() { - return String.format("NotificationManager.Token[0x%08x]", - System.identityHashCode(mBinder)); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStrongBinder(mBinder); - } - - public static final Parcelable.Creator<Token> CREATOR - = new Parcelable.Creator<Token>() { - @Override - public Token createFromParcel(Parcel in) { - return new Token(in.readStrongBinder()); - } - - @Override - public Token[] newArray(int size) { - return new Token[size]; - } - }; - - /** Callback for receiving the result of a token request. */ - public static abstract class RequestCallback { - /** - * Received if the request was granted for this package. - * - * @param token can be used to manage notification policy. - */ - public abstract void onTokenGranted(Policy.Token token); - - /** - * Received if the request was denied for this package. - */ - public abstract void onTokenDenied(); - } - } } /** @@ -671,4 +696,69 @@ public class NotificationManager } return new StatusBarNotification[0]; } + + /** + * Gets the current notification interruption filter. + * + * <p> + * The interruption filter defines which notifications are allowed to interrupt the user + * (e.g. via sound & vibration) and is applied globally. + * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when + * unavailable. + * + * <p> + * Only available if policy access is granted. + */ + public final int getCurrentInterruptionFilter() { + final INotificationManager service = getService(); + try { + return zenModeToInterruptionFilter(service.getZenMode()); + } catch (RemoteException e) { + Log.e(TAG, "Unable to talk to notification manager. Woe!", e); + } + return INTERRUPTION_FILTER_UNKNOWN; + } + + /** + * Sets the current notification interruption filter. + * + * <p> + * The interruption filter defines which notifications are allowed to interrupt the user + * (e.g. via sound & vibration) and is applied globally. + * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when + * unavailable. + * + * <p> + * Only available if policy access is granted. + */ + public final void setInterruptionFilter(int interruptionFilter) { + final INotificationManager service = getService(); + try { + service.setInterruptionFilter(mContext.getOpPackageName(), interruptionFilter); + } catch (RemoteException e) { + Log.e(TAG, "Unable to talk to notification manager. Woe!", e); + } + } + + /** @hide */ + public static int zenModeToInterruptionFilter(int zen) { + switch (zen) { + case Global.ZEN_MODE_OFF: return INTERRUPTION_FILTER_ALL; + case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return INTERRUPTION_FILTER_PRIORITY; + case Global.ZEN_MODE_ALARMS: return INTERRUPTION_FILTER_ALARMS; + case Global.ZEN_MODE_NO_INTERRUPTIONS: return INTERRUPTION_FILTER_NONE; + default: return INTERRUPTION_FILTER_UNKNOWN; + } + } + + /** @hide */ + public static int zenModeFromInterruptionFilter(int interruptionFilter, int defValue) { + switch (interruptionFilter) { + case INTERRUPTION_FILTER_ALL: return Global.ZEN_MODE_OFF; + case INTERRUPTION_FILTER_PRIORITY: return Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + case INTERRUPTION_FILTER_ALARMS: return Global.ZEN_MODE_ALARMS; + case INTERRUPTION_FILTER_NONE: return Global.ZEN_MODE_NO_INTERRUPTIONS; + default: return defValue; + } + } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 391131a..0d00908 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -55,7 +55,6 @@ import android.location.CountryDetector; import android.location.ICountryDetector; import android.location.ILocationManager; import android.location.LocationManager; -import android.media.AudioDevicesManager; import android.media.AudioManager; import android.media.MediaRouter; import android.media.midi.IMidiManager; @@ -701,13 +700,6 @@ final class SystemServiceRegistry { public RadioManager createService(ContextImpl ctx) { return new RadioManager(ctx); }}); - - registerService(Context.AUDIO_DEVICES_SERVICE, AudioDevicesManager.class, - new CachedServiceFetcher<AudioDevicesManager>() { - @Override - public AudioDevicesManager createService(ContextImpl ctx) { - return new AudioDevicesManager(ctx); - }}); } /** @@ -726,7 +718,7 @@ final class SystemServiceRegistry { } /** - * Gets the name of the system-level service that is represented by the specified class. + * Gets the name of the system-level service that is represented by the specified class. */ public static String getSystemServiceName(Class<?> serviceClass) { return SYSTEM_SERVICE_NAMES.get(serviceClass); diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 470804d..87e2f9a 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -24,6 +24,7 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.security.KeyChain; @@ -249,13 +250,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { public static final String EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID = "android.app.extra.CHOOSE_PRIVATE_KEY_SENDER_UID"; /** @hide */ - public static final String EXTRA_CHOOSE_PRIVATE_KEY_HOST = "android.app.extra.CHOOSE_PRIVATE_KEY_HOST"; - - /** @hide */ - public static final String EXTRA_CHOOSE_PRIVATE_KEY_PORT = "android.app.extra.CHOOSE_PRIVATE_KEY_PORT"; - - /** @hide */ - public static final String EXTRA_CHOOSE_PRIVATE_KEY_URL = "android.app.extra.CHOOSE_PRIVATE_KEY_URL"; + public static final String EXTRA_CHOOSE_PRIVATE_KEY_URI = "android.app.extra.CHOOSE_PRIVATE_KEY_URI"; /** @hide */ public static final String EXTRA_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.extra.CHOOSE_PRIVATE_KEY_ALIAS"; @@ -487,15 +482,13 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * @param context The running context as per {@link #onReceive}. * @param intent The received intent as per {@link #onReceive}. * @param uid The uid asking for the private key and certificate pair. - * @param host The authentication host, may be null. - * @param port The authentication port, or -1. - * @param url The URL to authenticate, may be null. + * @param uri The URI to authenticate, may be null. * @param alias The alias preselected by the client, or null. * @return The private key alias to return and grant access to. * @see KeyChain#choosePrivateKeyAlias */ - public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, String host, - int port, String url, String alias) { + public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, Uri uri, + String alias) { return null; } @@ -546,12 +539,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { onProfileProvisioningComplete(context, intent); } else if (ACTION_CHOOSE_PRIVATE_KEY_ALIAS.equals(action)) { int uid = intent.getIntExtra(EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID, -1); - String host = intent.getStringExtra(EXTRA_CHOOSE_PRIVATE_KEY_HOST); - int port = intent.getIntExtra(EXTRA_CHOOSE_PRIVATE_KEY_PORT, -1); - String url = intent.getStringExtra(EXTRA_CHOOSE_PRIVATE_KEY_URL); + Uri uri = intent.getParcelableExtra(EXTRA_CHOOSE_PRIVATE_KEY_URI); String alias = intent.getStringExtra(EXTRA_CHOOSE_PRIVATE_KEY_ALIAS); - String chosenAlias = onChoosePrivateKeyAlias(context, intent, uid, host, port, url, - alias); + String chosenAlias = onChoosePrivateKeyAlias(context, intent, uid, uri, alias); setResultData(chosenAlias); } else if (ACTION_LOCK_TASK_ENTERING.equals(action)) { String pkg = intent.getStringExtra(EXTRA_LOCK_TASK_PACKAGE); diff --git a/core/java/android/app/admin/DeviceInitializerStatus.java b/core/java/android/app/admin/DeviceInitializerStatus.java index b58711c..7de518b 100644 --- a/core/java/android/app/admin/DeviceInitializerStatus.java +++ b/core/java/android/app/admin/DeviceInitializerStatus.java @@ -37,8 +37,8 @@ package android.app.admin; * <li>{@link #STATUS_ERROR_SET_DEVICE_POLICY} * <li>{@link #STATUS_ERROR_DELETE_APPS} * <li>{@link #STATUS_ERROR_DOUBLE_BUMP} - * <li>{@link #STATUS_STATE_CONNECT_BLUETOOTH_PROXY} - * <li>{@link #STATUS_STATE_DISCONNECT_BLUETOOTH_PROXY} + * <li>{@link #STATUS_STATE_CONNECTING_BLUETOOTH_PROXY} + * <li>{@link #STATUS_STATE_DISCONNECTING_BLUETOOTH_PROXY} * <li>{@link #STATUS_STATE_DEVICE_PROVISIONED} * </ul> */ @@ -84,13 +84,13 @@ public class DeviceInitializerStatus { * Device provisioning status code that indicates that a device is connecting to establish * a Bluetooth network proxy. */ - public static final int STATUS_STATE_CONNECT_BLUETOOTH_PROXY = FLAG_STATUS_HIGH_PRIORITY | 8; + public static final int STATUS_STATE_CONNECTING_BLUETOOTH_PROXY = FLAG_STATUS_HIGH_PRIORITY | 8; /** * Device provisioning status code that indicates that a connected Bluetooth network proxy * is being shut down. */ - public static final int STATUS_STATE_DISCONNECT_BLUETOOTH_PROXY = FLAG_STATUS_HIGH_PRIORITY | 9; + public static final int STATUS_STATE_DISCONNECTING_BLUETOOTH_PROXY = FLAG_STATUS_HIGH_PRIORITY | 9; /** * Device provisioning status code that indicates that a device has been successfully @@ -141,37 +141,5 @@ public class DeviceInitializerStatus { */ public static final int STATUS_ERROR_DOUBLE_BUMP = FLAG_STATUS_ERROR | 30; - /** - * Determine if the specified status code represents an error status. - * @param statusCode status code to check - * @return {@code true} if the status code is an error status code - */ - public static boolean isErrorStatus(int statusCode) { - return isFlagSet(statusCode, FLAG_STATUS_ERROR); - } - - /** - * Determine if the specified status code is a custom status. Custom status codes are defined - * and sent by device initialization agents. - * @param statusCode status code to check - * @return {@code true} if the status code is a custom status code - */ - public static boolean isCustomStatus(int statusCode) { - return isFlagSet(statusCode, FLAG_STATUS_CUSTOM); - } - - /** - * Determine if the specified status code is a high priority status code. - * @param statusCode status code to check - * @return {@code true} if the status code is a high priority status code - */ - public static boolean isHighPriority(int statusCode) { - return isFlagSet(statusCode, FLAG_STATUS_HIGH_PRIORITY); - } - - private static boolean isFlagSet(int statusCode, int flag) { - return (statusCode & flag) != 0; - } - private DeviceInitializerStatus() {} } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 8009b6c..55ff85a 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -402,7 +402,7 @@ public class DevicePolicyManager { * has completed successfully. * * <p>The broadcast is limited to the primary profile, to the app specified in the provisioning - * intent (@see #ACTION_PROVISION_MANAGED_PROFILE). + * intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE}. * * <p>This intent will contain the extra {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} which * corresponds to the account requested to be migrated at provisioning time, if any. @@ -702,7 +702,7 @@ public class DevicePolicyManager { * reported to the remote setup device over Bluetooth. * * <p>Broadcasts with this action must supply a - * {@linkplain DeviceInitializerStatus#isCustomStatus(int) custom} status code in the + * {@linkplain DeviceInitializerStatus#FLAG_STATUS_CUSTOM custom} status code in the * {@link EXTRA_DEVICE_INITIALIZER_STATUS_CODE} extra. * * <p>Broadcasts may optionally contain a description in the @@ -718,7 +718,7 @@ public class DevicePolicyManager { * sent as part of a broadcast with an action of {@code ACTION_SEND_DEVICE_INITIALIZER_STATUS}. * * <p>The status code sent with this extra must be a custom status code as defined by - * {@link DeviceInitializerStatus#isCustomStatus(int)}. + * {@link DeviceInitializerStatus#FLAG_STATUS_CUSTOM}. * @hide */ @SystemApi @@ -1653,9 +1653,9 @@ public class DevicePolicyManager { } /** - * Queries whether {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT} flag is set. + * Queries whether {@link #RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT} flag is set. * - * @return true if DO_NOT_ASK_CREDENTIALS_ON_BOOT flag is set. + * @return true if RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT flag is set. * @hide */ public boolean getDoNotAskCredentialsOnBoot() { @@ -1753,7 +1753,7 @@ public class DevicePolicyManager { * is ignored. Once the flag is set, it cannot be reverted back without resetting the * device to factory defaults. */ - public static final int DO_NOT_ASK_CREDENTIALS_ON_BOOT = 0x0002; + public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 0x0002; /** * Force a new device unlock password (the password needed to access the @@ -1779,7 +1779,7 @@ public class DevicePolicyManager { * * @param password The new password for the user. Null or empty clears the password. * @param flags May be 0 or combination of {@link #RESET_PASSWORD_REQUIRE_ENTRY} and - * {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT}. + * {@link #RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT}. * @return Returns true if the password was applied, or false if it is * not acceptable for the current constraints. */ @@ -2523,13 +2523,26 @@ public class DevicePolicyManager { * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES} to be able to call * this method; if it has not, a security exception will be thrown. * - * <p>Calling this from a managed profile will throw a security exception. + * <p>Calling this from a managed profile before version + * {@link android.os.Build.VERSION_CODES#MNC} will throw a security exception. + * + * <p>From version {@link android.os.Build.VERSION_CODES#MNC} a profile owner can set: + * <ul> + * <li>{@link #KEYGUARD_DISABLE_TRUST_AGENTS}, {@link #KEYGUARD_DISABLE_FINGERPRINT} + * these will affect the profile's parent user. + * <li>{@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS} this will affect notifications + * generated by applications in the managed profile. + * </ul> + * <p>Requests to disable other features on a managed profile will be ignored. The admin + * can check which features have been disabled by calling + * {@link #getKeyguardDisabledFeatures(ComponentName)} * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param which {@link #KEYGUARD_DISABLE_FEATURES_NONE} (default), * {@link #KEYGUARD_DISABLE_WIDGETS_ALL}, {@link #KEYGUARD_DISABLE_SECURE_CAMERA}, * {@link #KEYGUARD_DISABLE_SECURE_NOTIFICATIONS}, {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, - * {@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS}, {@link #KEYGUARD_DISABLE_FEATURES_ALL} + * {@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS}, {@link #KEYGUARD_DISABLE_FINGERPRINT}, + * {@link #KEYGUARD_DISABLE_FEATURES_ALL} */ public void setKeyguardDisabledFeatures(ComponentName admin, int which) { if (mService != null) { @@ -2793,17 +2806,16 @@ public class DevicePolicyManager { * @param who Which {@link DeviceAdminReceiver} this request is associated with, or null if not * called by the device owner. * @param initializer Which {@link DeviceAdminReceiver} to make device initializer. - * @param initializerName The user-visible name of the device initializer. * @return whether the component was successfully registered as the device initializer. * @throws IllegalArgumentException if the componentname is null or invalid * @throws IllegalStateException if the caller is not device owner or the device has * already been provisioned or a device initializer already exists. */ - public boolean setDeviceInitializer(ComponentName who, ComponentName initializer, - String initializerName) throws IllegalArgumentException, IllegalStateException { + public boolean setDeviceInitializer(ComponentName who, ComponentName initializer) + throws IllegalArgumentException, IllegalStateException { if (mService != null) { try { - return mService.setDeviceInitializer(who, initializer, initializerName); + return mService.setDeviceInitializer(who, initializer); } catch (RemoteException re) { Log.w(TAG, "Failed to set device initializer"); } @@ -4229,7 +4241,7 @@ public class DevicePolicyManager { * Called by device initializer to send a provisioning status update to the remote setup device. * * @param statusCode a custom status code value as defined by - * {@link DeviceInitializerStatus#isCustomStatus(int)}. + * {@link DeviceInitializerStatus#FLAG_STATUS_CUSTOM}. * @param description custom description of the status code sent */ public void sendDeviceInitializerStatus(int statusCode, String description) { @@ -4253,11 +4265,7 @@ public class DevicePolicyManager { public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) { if (mService != null) { try { - if (policy != null) { - mService.setSystemUpdatePolicy(who, policy.getPolicyBundle()); - } else { - mService.setSystemUpdatePolicy(who, null); - } + mService.setSystemUpdatePolicy(who, policy); } catch (RemoteException re) { Log.w(TAG, "Error calling setSystemUpdatePolicy", re); } @@ -4272,12 +4280,7 @@ public class DevicePolicyManager { public SystemUpdatePolicy getSystemUpdatePolicy() { if (mService != null) { try { - PersistableBundle bundle = mService.getSystemUpdatePolicy(); - if (bundle != null) { - return new SystemUpdatePolicy(bundle); - } else { - return null; - } + return mService.getSystemUpdatePolicy(); } catch (RemoteException re) { Log.w(TAG, "Error calling getSystemUpdatePolicy", re); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e81e7c1..24ef604 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -17,11 +17,13 @@ package android.app.admin; +import android.app.admin.SystemUpdatePolicy; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.net.ProxyInfo; +import android.net.Uri; import android.os.Bundle; import android.os.PersistableBundle; import android.os.RemoteCallback; @@ -130,7 +132,7 @@ interface IDevicePolicyManager { void enforceCanManageCaCerts(in ComponentName admin); boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer, String alias); - void choosePrivateKeyAlias(int uid, in String host, int port, in String url, in String alias, IBinder aliasCallback); + void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback); void setCertInstallerPackage(in ComponentName who, String installerPackage); String getCertInstallerPackage(in ComponentName who); @@ -212,7 +214,7 @@ interface IDevicePolicyManager { boolean setUserEnabled(in ComponentName who); boolean isDeviceInitializer(String packageName); void clearDeviceInitializer(in ComponentName who); - boolean setDeviceInitializer(in ComponentName who, in ComponentName initializer, String initializerName); + boolean setDeviceInitializer(in ComponentName who, in ComponentName initializer); String getDeviceInitializer(); ComponentName getDeviceInitializerComponent(); @@ -221,8 +223,8 @@ interface IDevicePolicyManager { void setUserIcon(in ComponentName admin, in Bitmap icon); void sendDeviceInitializerStatus(int statusCode, String description); - void setSystemUpdatePolicy(in ComponentName who, in PersistableBundle policy); - PersistableBundle getSystemUpdatePolicy(); + void setSystemUpdatePolicy(in ComponentName who, in SystemUpdatePolicy policy); + SystemUpdatePolicy getSystemUpdatePolicy(); boolean setKeyguardDisabled(in ComponentName admin, boolean disabled); boolean setStatusBarDisabled(in ComponentName who, boolean disabled); diff --git a/core/java/android/app/admin/SystemUpdatePolicy.aidl b/core/java/android/app/admin/SystemUpdatePolicy.aidl new file mode 100644 index 0000000..58e8d15 --- /dev/null +++ b/core/java/android/app/admin/SystemUpdatePolicy.aidl @@ -0,0 +1,20 @@ +/* +** +** Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.app.admin; + +parcelable SystemUpdatePolicy; diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java index de56cd0..20ddb77 100644 --- a/core/java/android/app/admin/SystemUpdatePolicy.java +++ b/core/java/android/app/admin/SystemUpdatePolicy.java @@ -17,8 +17,15 @@ package android.app.admin; import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; import android.os.PersistableBundle; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,7 +35,7 @@ import java.lang.annotation.RetentionPolicy; * @see DevicePolicyManager#setSystemUpdatePolicy * @see DevicePolicyManager#getSystemUpdatePolicy */ -public class SystemUpdatePolicy { +public class SystemUpdatePolicy implements Parcelable { /** @hide */ @IntDef({ @@ -39,6 +46,10 @@ public class SystemUpdatePolicy { @interface SystemUpdatePolicyType {} /** + * Unknown policy type, used only internally. + */ + private static final int TYPE_UNKNOWN = -1; + /** * Install system update automatically as soon as one is available. */ public static final int TYPE_INSTALL_AUTOMATIC = 1; @@ -63,45 +74,40 @@ public class SystemUpdatePolicy { private static final String KEY_POLICY_TYPE = "policy_type"; private static final String KEY_INSTALL_WINDOW_START = "install_window_start"; private static final String KEY_INSTALL_WINDOW_END = "install_window_end"; + /** + * The upper boundary of the daily maintenance window: 24 * 60 minutes. + */ + private static final int WINDOW_BOUNDARY = 24 * 60; - private PersistableBundle mPolicy; + @SystemUpdatePolicyType + private int mPolicyType; - public SystemUpdatePolicy() { - mPolicy = new PersistableBundle(); - } + private int mMaintenanceWindowStart; + private int mMaintenanceWindowEnd; - /** - * Construct an SystemUpdatePolicy object from a bundle. - * @hide - */ - public SystemUpdatePolicy(PersistableBundle in) { - mPolicy = new PersistableBundle(in); - } - /** - * Retrieve the underlying bundle where the policy is stored. - * @hide - */ - public PersistableBundle getPolicyBundle() { - return new PersistableBundle(mPolicy); + private SystemUpdatePolicy() { + mPolicyType = TYPE_UNKNOWN; } /** - * Set the policy to: install update automatically as soon as one is available. + * Create a policy object and set it to install update automatically as soon as one is + * available. * * @see #TYPE_INSTALL_AUTOMATIC */ - public void setAutomaticInstallPolicy() { - mPolicy.clear(); - mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_AUTOMATIC); + public static SystemUpdatePolicy createAutomaticInstallPolicy() { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = TYPE_INSTALL_AUTOMATIC; + return policy; } /** - * Set the policy to: new system update will only be installed automatically when the system - * clock is inside a daily maintenance window. If the start and end times are the same, the - * window is considered to include the WHOLE 24 hours, that is, updates can install at any time. - * If the given window in invalid, a {@link SystemUpdatePolicy.InvalidWindowException} will be - * thrown. If start time is later than end time, the window is considered spanning midnight, + * Create a policy object and set it to: new system update will only be installed automatically + * when the system clock is inside a daily maintenance window. If the start and end times are + * the same, the window is considered to include the WHOLE 24 hours, that is, updates can + * install at any time. If the given window in invalid, a {@link IllegalArgumentException} will + * be thrown. If start time is later than end time, the window is considered spanning midnight, * i.e. end time donates a time on the next day. The maintenance window will last for 30 days, * after which the system should revert back to its normal behavior as if no policy were set. * @@ -111,25 +117,29 @@ public class SystemUpdatePolicy { * midnight in the device's local time. Must be in the range of [0, 1440). * @see #TYPE_INSTALL_WINDOWED */ - public void setWindowedInstallPolicy(int startTime, int endTime) throws InvalidWindowException{ - if (startTime < 0 || startTime >= 1440 || endTime < 0 || endTime >= 1440) { - throw new InvalidWindowException("startTime and endTime must be inside [0, 1440)"); + public static SystemUpdatePolicy createWindowedInstallPolicy(int startTime, int endTime) { + if (startTime < 0 || startTime >= WINDOW_BOUNDARY + || endTime < 0 || endTime >= WINDOW_BOUNDARY) { + throw new IllegalArgumentException("startTime and endTime must be inside [0, 1440)"); } - mPolicy.clear(); - mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_WINDOWED); - mPolicy.putInt(KEY_INSTALL_WINDOW_START, startTime); - mPolicy.putInt(KEY_INSTALL_WINDOW_END, endTime); + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = TYPE_INSTALL_WINDOWED; + policy.mMaintenanceWindowStart = startTime; + policy.mMaintenanceWindowEnd = endTime; + return policy; } /** - * Set the policy to: block installation for a maximum period of 30 days. After expiration the - * system should revert back to its normal behavior as if no policy were set. + * Create a policy object and set it to block installation for a maximum period of 30 days. + * After expiration the system should revert back to its normal behavior as if no policy were + * set. * * @see #TYPE_POSTPONE */ - public void setPostponeInstallPolicy() { - mPolicy.clear(); - mPolicy.putInt(KEY_POLICY_TYPE, TYPE_POSTPONE); + public static SystemUpdatePolicy createPostponeInstallPolicy() { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = TYPE_POSTPONE; + return policy; } /** @@ -140,7 +150,7 @@ public class SystemUpdatePolicy { */ @SystemUpdatePolicyType public int getPolicyType() { - return mPolicy.getInt(KEY_POLICY_TYPE, -1); + return mPolicyType; } /** @@ -150,8 +160,8 @@ public class SystemUpdatePolicy { * or -1 if the policy does not have a maintenance window. */ public int getInstallWindowStart() { - if (getPolicyType() == TYPE_INSTALL_WINDOWED) { - return mPolicy.getInt(KEY_INSTALL_WINDOW_START, -1); + if (mPolicyType == TYPE_INSTALL_WINDOWED) { + return mMaintenanceWindowStart; } else { return -1; } @@ -164,26 +174,98 @@ public class SystemUpdatePolicy { * or -1 if the policy does not have a maintenance window. */ public int getInstallWindowEnd() { - if (getPolicyType() == TYPE_INSTALL_WINDOWED) { - return mPolicy.getInt(KEY_INSTALL_WINDOW_END, -1); + if (mPolicyType == TYPE_INSTALL_WINDOWED) { + return mMaintenanceWindowEnd; } else { return -1; } } + /** + * Return if this object represents a valid policy. + * @hide + */ + public boolean isValid() { + if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) { + return true; + } else if (mPolicyType == TYPE_INSTALL_WINDOWED) { + return mMaintenanceWindowStart >= 0 && mMaintenanceWindowStart < WINDOW_BOUNDARY + && mMaintenanceWindowEnd >= 0 && mMaintenanceWindowEnd < WINDOW_BOUNDARY; + } else { + return false; + } + } + @Override public String toString() { - return mPolicy.toString(); + return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d)", + mPolicyType, mMaintenanceWindowStart, mMaintenanceWindowEnd); + } + + @Override + public int describeContents() { + return 0; } + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mPolicyType); + dest.writeInt(mMaintenanceWindowStart); + dest.writeInt(mMaintenanceWindowEnd); + } + + public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR = + new Parcelable.Creator<SystemUpdatePolicy>() { + + @Override + public SystemUpdatePolicy createFromParcel(Parcel source) { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = source.readInt(); + policy.mMaintenanceWindowStart = source.readInt(); + policy.mMaintenanceWindowEnd = source.readInt(); + return policy; + } + + @Override + public SystemUpdatePolicy[] newArray(int size) { + return new SystemUpdatePolicy[size]; + } + }; + + /** - * Exception thrown by {@link SystemUpdatePolicy#setWindowedInstallPolicy(int, int)} in case the - * specified window is invalid. + * @hide */ - public static class InvalidWindowException extends Exception { - public InvalidWindowException(String reason) { - super(reason); + public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) { + try { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + String value = parser.getAttributeValue(null, KEY_POLICY_TYPE); + if (value != null) { + policy.mPolicyType = Integer.parseInt(value); + + value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_START); + if (value != null) { + policy.mMaintenanceWindowStart = Integer.parseInt(value); + } + value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_END); + if (value != null) { + policy.mMaintenanceWindowEnd = Integer.parseInt(value); + } + return policy; + } + } catch (NumberFormatException e) { + // Fail through } + return null; + } + + /** + * @hide + */ + public void saveToXml(XmlSerializer out) throws IOException { + out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType)); + out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart)); + out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd)); } } diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index 23659e3..254408a 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -30,6 +30,6 @@ interface IUsageStatsManager { ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime, String callingPackage); UsageEvents queryEvents(long beginTime, long endTime, String callingPackage); - void setAppIdle(String packageName, boolean idle, int userId); - boolean isAppIdle(String packageName, int userId); + void setAppInactive(String packageName, boolean inactive, int userId); + boolean isAppInactive(String packageName, int userId); } diff --git a/core/java/android/app/usage/NetworkUsageStats.java b/core/java/android/app/usage/NetworkStats.java index 990d231..5193563 100644 --- a/core/java/android/app/usage/NetworkUsageStats.java +++ b/core/java/android/app/usage/NetworkStats.java @@ -19,7 +19,6 @@ package android.app.usage; import android.content.Context; import android.net.INetworkStatsService; import android.net.INetworkStatsSession; -import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; @@ -33,7 +32,7 @@ import dalvik.system.CloseGuard; * Class providing enumeration over buckets of network usage statistics. NetworkUsageStats objects * are returned as results to various queries in {@link NetworkStatsManager}. */ -public final class NetworkUsageStats implements AutoCloseable { +public final class NetworkStats implements AutoCloseable { private final static String TAG = "NetworkUsageStats"; private final CloseGuard mCloseGuard = CloseGuard.get(); @@ -70,7 +69,7 @@ public final class NetworkUsageStats implements AutoCloseable { /** * Results of a summary query. */ - private NetworkStats mSummary = null; + private android.net.NetworkStats mSummary = null; /** * Results of detail queries. @@ -85,11 +84,11 @@ public final class NetworkUsageStats implements AutoCloseable { /** * Recycling entry objects to prevent heap fragmentation. */ - private NetworkStats.Entry mRecycledSummaryEntry = null; + private android.net.NetworkStats.Entry mRecycledSummaryEntry = null; private NetworkStatsHistory.Entry mRecycledHistoryEntry = null; /** @hide */ - NetworkUsageStats(Context context, NetworkTemplate template, long startTimestamp, + NetworkStats(Context context, NetworkTemplate template, long startTimestamp, long endTimestamp) throws RemoteException, SecurityException { final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); @@ -136,14 +135,19 @@ public final class NetworkUsageStats implements AutoCloseable { public static final int STATE_FOREGROUND = 0x2; /** + * Special UID value for aggregate/unspecified. + */ + public static final int UID_ALL = android.net.NetworkStats.UID_ALL; + + /** * Special UID value for removed apps. */ - public static final int UID_REMOVED = -4; + public static final int UID_REMOVED = TrafficStats.UID_REMOVED; /** * Special UID value for data usage by tethering. */ - public static final int UID_TETHERING = -5; + public static final int UID_TETHERING = TrafficStats.UID_TETHERING; private int mUid; private int mState; @@ -156,9 +160,9 @@ public final class NetworkUsageStats implements AutoCloseable { private static int convertState(int networkStatsSet) { switch (networkStatsSet) { - case NetworkStats.SET_ALL : return STATE_ALL; - case NetworkStats.SET_DEFAULT : return STATE_DEFAULT; - case NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND; + case android.net.NetworkStats.SET_ALL : return STATE_ALL; + case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT; + case android.net.NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND; } return 0; } @@ -337,8 +341,8 @@ public final class NetworkUsageStats implements AutoCloseable { void startHistoryEnumeration(int uid) { mHistory = null; try { - mHistory = mSession.getHistoryForUid(mTemplate, uid, NetworkStats.SET_ALL, - NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL); + mHistory = mSession.getHistoryForUid(mTemplate, uid, android.net.NetworkStats.SET_ALL, + android.net.NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL); setSingleUid(uid); } catch (RemoteException e) { Log.w(TAG, e); @@ -364,8 +368,9 @@ public final class NetworkUsageStats implements AutoCloseable { stepUid(); mHistory = null; try { - mHistory = mSession.getHistoryForUid(mTemplate, getUid(), NetworkStats.SET_ALL, - NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL); + mHistory = mSession.getHistoryForUid(mTemplate, getUid(), + android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE, + NetworkStatsHistory.FIELD_ALL); } catch (RemoteException e) { Log.w(TAG, e); // Leaving mHistory null @@ -405,7 +410,7 @@ public final class NetworkUsageStats implements AutoCloseable { } Bucket bucket = new Bucket(); if (mRecycledSummaryEntry == null) { - mRecycledSummaryEntry = new NetworkStats.Entry(); + mRecycledSummaryEntry = new android.net.NetworkStats.Entry(); } mSummary.getTotal(mRecycledSummaryEntry); fillBucketFromSummaryEntry(bucket); diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index af7c053..2ae0181 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -16,18 +16,17 @@ package android.app.usage; -import android.app.usage.NetworkUsageStats.Bucket; +import android.app.usage.NetworkStats.Bucket; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkIdentity; import android.net.NetworkTemplate; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; /** * Provides access to network usage history and statistics. Usage data is collected in - * discrete bins of time called 'Buckets'. See {@link NetworkUsageStats.Bucket} for details. + * discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details. * <p /> * Queries can define a time interval in the form of start and end timestamps (Long.MIN_VALUE and * Long.MAX_VALUE can be used to simulate open ended intervals). All queries (except @@ -37,15 +36,20 @@ import android.util.Log; * <h3> * Summary queries * </h3> + * {@link #querySummaryForDevice} <p /> + * {@link #querySummaryForUser} <p /> + * {@link #querySummary} <p /> * These queries aggregate network usage across the whole interval. Therefore there will be only one * bucket for a particular key and state combination. In case of the user-wide and device-wide * summaries a single bucket containing the totalised network usage is returned. * <h3> * History queries * </h3> + * {@link #queryDetailsForUid} <p /> + * {@link #queryDetails} <p /> * These queries do not aggregate over time but do aggregate over state. Therefore there can be * multiple buckets for a particular key but all Bucket's state is going to be - * {@link NetworkUsageStats.Bucket#STATE_ALL}. + * {@link NetworkStats.Bucket#STATE_ALL}. * <p /> * <b>NOTE:</b> This API requires the permission * {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, which is a system-level permission and @@ -68,7 +72,10 @@ public class NetworkStatsManager { } /** * Query network usage statistics summaries. Result is summarised data usage for the whole - * device. Result is a single Bucket aggregated over time, state and uid. + * device. Result is a single Bucket aggregated over time, state and uid. This means the + * bucket's start and end timestamp are going to be the same as the 'startTime' and 'endTime' + * parameters, state is going to be {@link NetworkStats.Bucket#STATE_ALL} and uid + * {@link NetworkStats.Bucket#UID_ALL}. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -89,7 +96,7 @@ public class NetworkStatsManager { } Bucket bucket = null; - NetworkUsageStats stats = new NetworkUsageStats(mContext, template, startTime, endTime); + NetworkStats stats = new NetworkStats(mContext, template, startTime, endTime); bucket = stats.getDeviceSummaryForNetwork(startTime, endTime); stats.close(); @@ -99,6 +106,9 @@ public class NetworkStatsManager { /** * Query network usage statistics summaries. Result is summarised data usage for all uids * belonging to calling user. Result is a single Bucket aggregated over time, state and uid. + * This means the bucket's start and end timestamp are going to be the same as the 'startTime' + * and 'endTime' parameters, state is going to be {@link NetworkStats.Bucket#STATE_ALL} and uid + * {@link NetworkStats.Bucket#UID_ALL}. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -118,8 +128,8 @@ public class NetworkStatsManager { return null; } - NetworkUsageStats stats; - stats = new NetworkUsageStats(mContext, template, startTime, endTime); + NetworkStats stats; + stats = new NetworkStats(mContext, template, startTime, endTime); stats.startSummaryEnumeration(startTime, endTime); stats.close(); @@ -129,7 +139,9 @@ public class NetworkStatsManager { /** * Query network usage statistics summaries. Result filtered to include only uids belonging to * calling user. Result is aggregated over time, hence all buckets will have the same start and - * end timestamps. Not aggregated over state or uid. + * end timestamps. Not aggregated over state or uid. This means buckets' start and end + * timestamps are going to be the same as the 'startTime' and 'endTime' parameters, state and + * uid are going to vary. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -142,15 +154,15 @@ public class NetworkStatsManager { * @return Statistics object or null if permissions are insufficient or error happened during * statistics collection. */ - public NetworkUsageStats querySummary(int networkType, String subscriberId, long startTime, + public NetworkStats querySummary(int networkType, String subscriberId, long startTime, long endTime) throws SecurityException, RemoteException { NetworkTemplate template = createTemplate(networkType, subscriberId); if (template == null) { return null; } - NetworkUsageStats result; - result = new NetworkUsageStats(mContext, template, startTime, endTime); + NetworkStats result; + result = new NetworkStats(mContext, template, startTime, endTime); result.startSummaryEnumeration(startTime, endTime); return result; @@ -158,7 +170,9 @@ public class NetworkStatsManager { /** * Query network usage statistics details. Only usable for uids belonging to calling user. - * Result is aggregated over state but not aggregated over time. + * Result is aggregated over state but not aggregated over time. This means buckets' start and + * end timestamps are going to be between 'startTime' and 'endTime' parameters, state is going + * to be {@link NetworkStats.Bucket#STATE_ALL} and uid the same as the 'uid' parameter. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -172,15 +186,15 @@ public class NetworkStatsManager { * @return Statistics object or null if permissions are insufficient or error happened during * statistics collection. */ - public NetworkUsageStats queryDetailsForUid(int networkType, String subscriberId, + public NetworkStats queryDetailsForUid(int networkType, String subscriberId, long startTime, long endTime, int uid) throws SecurityException, RemoteException { NetworkTemplate template = createTemplate(networkType, subscriberId); if (template == null) { return null; } - NetworkUsageStats result; - result = new NetworkUsageStats(mContext, template, startTime, endTime); + NetworkStats result; + result = new NetworkStats(mContext, template, startTime, endTime); result.startHistoryEnumeration(uid); return result; @@ -188,7 +202,9 @@ public class NetworkStatsManager { /** * Query network usage statistics details. Result filtered to include only uids belonging to - * calling user. Result is aggregated over state but not aggregated over time or uid. + * calling user. Result is aggregated over state but not aggregated over time or uid. This means + * buckets' start and end timestamps are going to be between 'startTime' and 'endTime' + * parameters, state is going to be {@link NetworkStats.Bucket#STATE_ALL} and uid will vary. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -201,14 +217,14 @@ public class NetworkStatsManager { * @return Statistics object or null if permissions are insufficient or error happened during * statistics collection. */ - public NetworkUsageStats queryDetails(int networkType, String subscriberId, long startTime, + public NetworkStats queryDetails(int networkType, String subscriberId, long startTime, long endTime) throws SecurityException, RemoteException { NetworkTemplate template = createTemplate(networkType, subscriberId); if (template == null) { return null; } - NetworkUsageStats result; - result = new NetworkUsageStats(mContext, template, startTime, endTime); + NetworkStats result; + result = new NetworkStats(mContext, template, startTime, endTime); result.startUserUidEnumeration(); return result; } diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index abfc435..81c7422 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -46,6 +46,13 @@ public final class UsageStats implements Parcelable { public long mLastTimeUsed; /** + * Last time the package was used and the beginning of the idle countdown. + * This uses a different timebase that is about how much the device has been in use in general. + * {@hide} + */ + public long mBeginIdleTime; + + /** * {@hide} */ public long mTotalTimeInForeground; @@ -74,6 +81,7 @@ public final class UsageStats implements Parcelable { mTotalTimeInForeground = stats.mTotalTimeInForeground; mLaunchCount = stats.mLaunchCount; mLastEvent = stats.mLastEvent; + mBeginIdleTime = stats.mBeginIdleTime; } public String getPackageName() { @@ -110,6 +118,15 @@ public final class UsageStats implements Parcelable { } /** + * @hide + * Get the last time this package was active, measured in milliseconds. This timestamp + * uses a timebase that represents how much the device was used and not wallclock time. + */ + public long getBeginIdleTime() { + return mBeginIdleTime; + } + + /** * Get the total time this package spent in the foreground, measured in milliseconds. */ public long getTotalTimeInForeground() { @@ -133,6 +150,7 @@ public final class UsageStats implements Parcelable { mLastEvent = right.mLastEvent; mEndTimeStamp = right.mEndTimeStamp; mLastTimeUsed = right.mLastTimeUsed; + mBeginIdleTime = right.mBeginIdleTime; } mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp); mTotalTimeInForeground += right.mTotalTimeInForeground; @@ -153,6 +171,7 @@ public final class UsageStats implements Parcelable { dest.writeLong(mTotalTimeInForeground); dest.writeInt(mLaunchCount); dest.writeInt(mLastEvent); + dest.writeLong(mBeginIdleTime); } public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() { @@ -166,6 +185,7 @@ public final class UsageStats implements Parcelable { stats.mTotalTimeInForeground = in.readLong(); stats.mLaunchCount = in.readInt(); stats.mLastEvent = in.readInt(); + stats.mBeginIdleTime = in.readLong(); return stats; } diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 8a01d66..34699d8 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -220,18 +220,29 @@ public final class UsageStatsManager { } /** - * Returns whether the specified app is currently considered idle. This will be true if the + * Returns whether the specified app is currently considered inactive. This will be true if the * app hasn't been used directly or indirectly for a period of time defined by the system. This * could be of the order of several hours or days. * @param packageName The package name of the app to query - * @return whether the app is currently considered idle + * @return whether the app is currently considered inactive */ - public boolean isAppIdle(String packageName) { + public boolean isAppInactive(String packageName) { try { - return mService.isAppIdle(packageName, UserHandle.myUserId()); + return mService.isAppInactive(packageName, UserHandle.myUserId()); } catch (RemoteException e) { // fall through and return default } return false; } + + /** + * @hide + */ + public void setAppInactive(String packageName, boolean inactive) { + try { + mService.setAppInactive(packageName, inactive, UserHandle.myUserId()); + } catch (RemoteException e) { + // fall through + } + } } diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 8b3fc2e..7bcc038 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -69,14 +69,6 @@ public abstract class UsageStatsManagerInternal { public abstract boolean isAppIdle(String packageName, int userId); /** - * Returns the most recent time that the specified package was active for the given user. - * @param packageName The package to search. - * @param userId The user id of the user of interest. - * @return The timestamp of when the package was last used, or -1 if it hasn't been used. - */ - public abstract long getLastPackageAccessTime(String packageName, int userId); - - /** * Sets up a listener for changes to packages being accessed. * @param listener A listener within the system process. */ diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index a3eceb5..7a894ae 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -101,8 +101,8 @@ interface IBluetooth void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); - // for dumpsys support - String dump(); + // For dumpsys support + void dump(in ParcelFileDescriptor fd); void onLeServiceUp(); void onBrEdrDown(); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 8687c6b..a434c7b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -134,7 +134,15 @@ public abstract class Context { * explicitly set if desired. * * @see #getSharedPreferences + * + * @deprecated MODE_MULTI_PROCESS does not work reliably in + * some versions of Android, and furthermore does not provide any + * mechanism for reconciling concurrent modifications across + * processes. Applications should not attempt to use it. Instead, + * they should use an explicit cross-process data management + * approach such as {@link android.content.ContentProvider ContentProvider}. */ + @Deprecated public static final int MODE_MULTI_PROCESS = 0x0004; /** @@ -604,11 +612,7 @@ public abstract class Context { * editor (SharedPreferences.edit()) and then commit changes (Editor.commit()). * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the * default operation, {@link #MODE_WORLD_READABLE} - * and {@link #MODE_WORLD_WRITEABLE} to control permissions. The bit - * {@link #MODE_MULTI_PROCESS} can also be used if multiple processes - * are mutating the same SharedPreferences file. {@link #MODE_MULTI_PROCESS} - * is always on in apps targeting Gingerbread (Android 2.3) and below, and - * off by default in later versions. + * and {@link #MODE_WORLD_WRITEABLE} to control permissions. * * @return The single {@link SharedPreferences} instance that can be used * to retrieve and modify the preference values. @@ -616,7 +620,6 @@ public abstract class Context { * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE - * @see #MODE_MULTI_PROCESS */ public abstract SharedPreferences getSharedPreferences(String name, int mode); @@ -3133,16 +3136,6 @@ public abstract class Context { public static final String RADIO_SERVICE = "radio"; /** - * Use with {@link #getSystemService} to retrieve a - * {@link android.media.AudioDevicesManager} for handling device enumeration & notification. - * - * @see #getSystemService - * @see android.media.AudioDevicesManager - */ - public static final String AUDIO_DEVICES_SERVICE = "audio_devices_manager"; - - - /** * 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/Intent.java b/core/java/android/content/Intent.java index d0298cd..7d76760 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2589,21 +2589,6 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.GET_RESTRICTION_ENTRIES"; /** - * @hide - * Activity to challenge the user for a PIN that was configured when setting up - * restrictions. Restrictions include blocking of apps and preventing certain user operations, - * controlled by {@link android.os.UserManager#setUserRestrictions(Bundle). - * Launch the activity using - * {@link android.app.Activity#startActivityForResult(Intent, int)} and check if the - * result is {@link android.app.Activity#RESULT_OK} for a successful response to the - * challenge.<p/> - * Before launching this activity, make sure that there is a PIN in effect, by calling - * {@link android.os.UserManager#hasRestrictionsChallenge()}. - */ - public static final String ACTION_RESTRICTIONS_CHALLENGE = - "android.intent.action.RESTRICTIONS_CHALLENGE"; - - /** * Sent the first time a user is starting, to allow system apps to * perform one time initialization. (This will not be seen by third * party applications because a newly initialized user does not have any @@ -3380,14 +3365,6 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS"; /** - * A Parcelable[] of {@link android.service.chooser.ChooserTarget ChooserTarget} objects - * as set with {@link #putExtra(String, Parcelable[])} representing additional app-specific - * targets to place at the front of the list of choices. Shown to the user with - * {@link #ACTION_CHOOSER}. - */ - public static final String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS"; - - /** * A Bundle forming a mapping of potential target package names to different extras Bundles * to add to the default intent extras in {@link #EXTRA_INTENT} when used with * {@link #ACTION_CHOOSER}. Each key should be a package name. The package need not diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 94b0223..ddff782 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -96,9 +96,14 @@ interface IPackageManager { void removePermission(String name); - void grantPermission(String packageName, String permissionName, int userId); + void grantRuntimePermission(String packageName, String permissionName, int userId); - void revokePermission(String packageName, String permissionName, int userId); + void revokeRuntimePermission(String packageName, String permissionName, int userId); + + int getPermissionFlags(String permissionName, String packageName, int userId); + + void updatePermissionFlags(String permissionName, String packageName, int flagMask, + int flagValues, int userId); boolean isProtectedBroadcast(String actionName); diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java index 87b97aa..6827d7a 100644 --- a/core/java/android/content/pm/LauncherActivityInfo.java +++ b/core/java/android/content/pm/LauncherActivityInfo.java @@ -103,20 +103,30 @@ public class LauncherActivityInfo { * density DPI values from {@link DisplayMetrics}. * @see #getBadgedIcon(int) * @see DisplayMetrics - * @return The drawable associated with the activity + * @return The drawable associated with the activity. */ public Drawable getIcon(int density) { - int iconRes = mResolveInfo.getIconResource(); - Resources resources = null; - Drawable icon = null; - // Get the preferred density icon from the app's resources - if (density != 0 && iconRes != 0) { - try { - resources = mPm.getResourcesForApplication(mActivityInfo.applicationInfo); - icon = resources.getDrawableForDensity(iconRes, density); - } catch (NameNotFoundException | Resources.NotFoundException exc) { - } + final int iconRes = mResolveInfo.getIconResource(); + Drawable icon = getDrawableForDensity(iconRes, density); + // Get the default density icon + if (icon == null) { + icon = mResolveInfo.loadIcon(mPm); } + return icon; + } + + /** + * Returns the icon for this activity, without any badging for the profile. + * This function can get the icon no matter the icon needs to be badged or not. + * @param density The preferred density of the icon, zero for default density. Use + * density DPI values from {@link DisplayMetrics}. + * @see #getBadgedIcon(int) + * @see DisplayMetrics + * @return The drawable associated with the activity. + */ + private Drawable getOriginalIcon(int density) { + final int iconRes = mResolveInfo.getIconResourceInternal(); + Drawable icon = getDrawableForDensity(iconRes, density); // Get the default density icon if (icon == null) { icon = mResolveInfo.loadIcon(mPm); @@ -125,6 +135,27 @@ public class LauncherActivityInfo { } /** + * Returns the drawable for this activity, without any badging for the profile. + * @param resource id of the drawable. + * @param density The preferred density of the icon, zero for default density. Use + * density DPI values from {@link DisplayMetrics}. + * @see DisplayMetrics + * @return The drawable associated with the resource id. + */ + private Drawable getDrawableForDensity(int iconRes, int density) { + // Get the preferred density icon from the app's resources + if (density != 0 && iconRes != 0) { + try { + final Resources resources + = mPm.getResourcesForApplication(mActivityInfo.applicationInfo); + return resources.getDrawableForDensity(iconRes, density); + } catch (NameNotFoundException | Resources.NotFoundException exc) { + } + } + return null; + } + + /** * Returns the application flags from the ApplicationInfo of the activity. * * @return Application flags @@ -167,7 +198,7 @@ public class LauncherActivityInfo { * @return A badged icon for the activity. */ public Drawable getBadgedIcon(int density) { - Drawable originalIcon = getIcon(density); + Drawable originalIcon = getOriginalIcon(density); if (originalIcon instanceof BitmapDrawable) { return mPm.getUserBadgedIcon(originalIcon, mUser); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 7ff6ec3..2ca0306 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -653,6 +653,16 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25; /** + * Installation return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if + * the old package has target SDK high enough to support runtime permission and + * the new package has target SDK low enough to not support runtime permissions. + * @hide + */ + @SystemApi + public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26; + + /** * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} * if the parser was given a path that is not a file, or does not end with the expected @@ -1067,6 +1077,13 @@ public abstract class PackageManager { public static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output"; /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device has professional audio level of functionality, performance, and acoustics. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_AUDIO_PRO = "android.hardware.audio.pro"; + + /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device is capable of communicating with * other devices via Bluetooth. @@ -1881,6 +1898,65 @@ public abstract class PackageManager { public static final String EXTRA_FAILURE_EXISTING_PERMISSION = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION"; + /** + * Permission flag: The permission is set in its current state + * by the user and apps can still request it at runtime. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_USER_SET = 1 << 0; + + /** + * Permission flag: The permission is set in its current state + * by the user and it is fixed, i.e. apps can no longer request + * this permission. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_USER_FIXED = 1 << 1; + + /** + * Permission flag: The permission is set in its current state + * by device policy and neither apps nor the user can change + * its state. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_POLICY_FIXED = 1 << 2; + + /** + * Permission flag: The permission is set in a granted state but + * access to resources it guards is restricted by other means to + * enable revoking a permission on legacy apps that do not support + * runtime permissions. If this permission is upgraded to runtime + * because the app was updated to support runtime permissions, the + * the permission will be revoked in the upgrade process. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 1 << 3; + + /** + * Permission flag: The permission is set in its current state + * because the app is a component that is a part of the system. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_SYSTEM_FIXED = 1 << 4; + + /** + * Mask for all permission flags. + * + * @hide + */ + @SystemApi + public static final int MASK_PERMISSION_FLAGS = 0xFF; + /** * Retrieve overall information about an application package that is * installed on the system. @@ -2367,6 +2443,21 @@ public abstract class PackageManager { */ public abstract void removePermission(String name); + + /** + * Permission flags set when granting or revoking a permission. + * + * @hide + */ + @SystemApi + @IntDef({FLAG_PERMISSION_USER_SET, + FLAG_PERMISSION_USER_FIXED, + FLAG_PERMISSION_POLICY_FIXED, + FLAG_PERMISSION_REVOKE_ON_UPGRADE, + FLAG_PERMISSION_SYSTEM_FIXED}) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionFlags {} + /** * Grant a runtime permission to an application which the application does not * already have. The permission must have been requested by the application. @@ -2382,19 +2473,20 @@ public abstract class PackageManager { * @param permissionName The permission name to grant. * @param user The user for which to grant the permission. * - * @see #revokePermission(String, String, android.os.UserHandle) + * @see #revokeRuntimePermission(String, String, android.os.UserHandle) + * @see android.content.pm.PackageManager.PermissionFlags * * @hide */ @SystemApi - public abstract void grantPermission(@NonNull String packageName, + public abstract void grantRuntimePermission(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user); /** * Revoke a runtime permission that was previously granted by {@link - * #grantPermission(String, String, android.os.UserHandle)}. The permission - * must have been requested by and granted to the application. If the - * application is not allowed to hold the permission, a {@link + * #grantRuntimePermission(String, String, android.os.UserHandle)}. The + * permission must have been requested by and granted to the application. + * If the application is not allowed to hold the permission, a {@link * java.lang.SecurityException} is thrown. * <p> * <strong>Note: </strong>Using this API requires holding @@ -2406,15 +2498,47 @@ public abstract class PackageManager { * @param permissionName The permission name to revoke. * @param user The user for which to revoke the permission. * - * @see #grantPermission(String, String, android.os.UserHandle) + * @see #grantRuntimePermission(String, String, android.os.UserHandle) + * @see android.content.pm.PackageManager.PermissionFlags * * @hide */ @SystemApi - public abstract void revokePermission(@NonNull String packageName, + public abstract void revokeRuntimePermission(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user); /** + * Gets the state flags associated with a permission. + * + * @param permissionName The permission for which to get the flags. + * @param packageName The package name for which to get the flags. + * @param user The user for which to get permission flags. + * @return The permission flags. + * + * @hide + */ + @SystemApi + public abstract @PermissionFlags int getPermissionFlags(String permissionName, + String packageName, @NonNull UserHandle user); + + /** + * Updates the flags associated with a permission by replacing the flags in + * the specified mask with the provided flag values. + * + * @param permissionName The permission for which to update the flags. + * @param packageName The package name for which to update the flags. + * @param flagMask The flags which to replace. + * @param flagValues The flags with which to replace. + * @param user The user for which to update the permission flags. + * + * @hide + */ + @SystemApi + public abstract void updatePermissionFlags(String permissionName, + String packageName, @PermissionFlags int flagMask, int flagValues, + @NonNull UserHandle user); + + /** * Returns an {@link android.content.Intent} suitable for passing to * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)} * which prompts the user to grant permissions to this application. @@ -3625,6 +3749,7 @@ public abstract class PackageManager { * * @hide */ + @SystemApi public abstract void verifyIntentFilter(int verificationId, int verificationCode, List<String> outFailedDomains); @@ -4397,6 +4522,7 @@ public abstract class PackageManager { case INSTALL_FAILED_PACKAGE_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_UID_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_VERSION_DOWNGRADE: return PackageInstaller.STATUS_FAILURE_INVALID; + case INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_PARSE_FAILED_NOT_APK: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_PARSE_FAILED_BAD_MANIFEST: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return PackageInstaller.STATUS_FAILURE_INVALID; diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index c2d2f65..0bd2a3b 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -52,6 +52,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -573,7 +574,7 @@ public abstract class RegisteredServicesCache<V> { private void readPersistentServicesLocked(InputStream is) throws XmlPullParserException, IOException { XmlPullParser parser = Xml.newPullParser(); - parser.setInput(is, null); + parser.setInput(is, StandardCharsets.UTF_8.name()); int eventType = parser.getEventType(); while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { @@ -663,7 +664,7 @@ public abstract class RegisteredServicesCache<V> { try { fos = atomicFile.startWrite(); XmlSerializer out = new FastXmlSerializer(); - out.setOutput(fos, "utf-8"); + out.setOutput(fos, StandardCharsets.UTF_8.name()); out.startDocument(null, true); out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "services"); diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java index 05f5e90..649fdb4 100644 --- a/core/java/android/content/pm/ResolveInfo.java +++ b/core/java/android/content/pm/ResolveInfo.java @@ -221,16 +221,16 @@ public class ResolveInfo implements Parcelable { } return ci.loadIcon(pm); } - + /** * Return the icon resource identifier to use for this match. If the * match defines an icon, that is used; else if the activity defines * an icon, that is used; else, the application icon is used. - * + * This function does not check noResourceId flag. + * * @return The icon associated with this match. */ - public final int getIconResource() { - if (noResourceId) return 0; + final int getIconResourceInternal() { if (icon != 0) return icon; final ComponentInfo ci = getComponentInfo(); if (ci != null) { @@ -239,6 +239,18 @@ public class ResolveInfo implements Parcelable { return 0; } + /** + * Return the icon resource identifier to use for this match. If the + * match defines an icon, that is used; else if the activity defines + * an icon, that is used; else, the application icon is used. + * + * @return The icon associated with this match. + */ + public final int getIconResource() { + if (noResourceId) return 0; + return getIconResourceInternal(); + } + public void dump(Printer pw, String prefix) { if (filter != null) { pw.println(prefix + "Filter:"); diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index e65b4ca..a572590 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1804,8 +1804,8 @@ public class Resources { final int N = mKey.mCount; final String[] themes = new String[N * 2]; for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) { - final int resId = mKey.mResId[i]; - final boolean forced = mKey.mForce[i]; + final int resId = mKey.mResId[j]; + final boolean forced = mKey.mForce[j]; try { themes[i] = getResourceName(resId); } catch (NotFoundException e) { @@ -1819,8 +1819,7 @@ public class Resources { /** @hide */ public void encode(@NonNull ViewHierarchyEncoder encoder) { encoder.beginObject(this); - // TODO: revert after getTheme() is fixed - String[] properties = new String[0]; // getTheme(); + final String[] properties = getTheme(); for (int i = 0; i < properties.length; i += 2) { encoder.addProperty(properties[i], properties[i+1]); } diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index ef71c42..c6f622c 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -29,11 +29,11 @@ import java.util.List; * <p>A CameraCaptureSession is created by providing a set of target output surfaces to * {@link CameraDevice#createCaptureSession createCaptureSession}, or by providing an * {@link android.hardware.camera2.params.InputConfiguration} and a set of target output surfaces to - * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} for a - * reprocessible capture session. Once created, the session is active until a new session is + * {@link CameraDevice#createReprocessableCaptureSession createReprocessableCaptureSession} for a + * reprocessable capture session. Once created, the session is active until a new session is * created by the camera device, or the camera device is closed.</p> * - * <p>All capture sessions can be used for capturing images from the camera but only reprocessible + * <p>All capture sessions can be used for capturing images from the camera but only reprocessable * capture sessions can reprocess images captured from the camera in the same session previously. * </p> * @@ -41,7 +41,7 @@ import java.util.List; * it requires configuring the camera device's internal pipelines and allocating memory buffers for * sending images to the desired targets. Therefore the setup is done asynchronously, and * {@link CameraDevice#createCaptureSession createCaptureSession} and - * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} will + * {@link CameraDevice#createReprocessableCaptureSession createReprocessableCaptureSession} will * send the ready-to-use CameraCaptureSession to the provided listener's * {@link CameraCaptureSession.StateCallback#onConfigured onConfigured} callback. If configuration * cannot be completed, then the @@ -156,7 +156,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * * <p>All capture sessions can be used for capturing images from the camera but only capture * sessions created by - * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} + * {@link CameraDevice#createReprocessableCaptureSession createReprocessableCaptureSession} * can submit reprocess capture requests. Submitting a reprocess request to a regular capture * session will result in an {@link IllegalArgumentException}.</p> * @@ -177,19 +177,22 @@ public abstract class CameraCaptureSession implements AutoCloseable { * was explicitly closed, a new session has been created * or the camera device has been closed. * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not - * configured as outputs for this session; or a reprocess - * capture request is submitted in a non-reprocessible capture - * session; or the reprocess capture request was created with - * a {@link TotalCaptureResult} from a different session; or - * the capture targets a Surface in the middle of being - * {@link #prepare prepared}; or the handler is null, the - * listener is not null, and the calling thread has no looper. + * configured as outputs for this session; or the request + * targets a set of Surfaces that cannot be submitted + * simultaneously in a reprocessable capture session; or a + * reprocess capture request is submitted in a + * non-reprocessable capture session; or the reprocess capture + * request was created with a {@link TotalCaptureResult} from + * a different session; or the capture targets a Surface in + * the middle of being {@link #prepare prepared}; or the + * handler is null, the listener is not null, and the calling + * thread has no looper. * * @see #captureBurst * @see #setRepeatingRequest * @see #setRepeatingBurst * @see #abortCaptures - * @see CameraDevice#createReprocessibleCaptureSession + * @see CameraDevice#createReprocessableCaptureSession */ public abstract int capture(CaptureRequest request, CaptureCallback listener, Handler handler) throws CameraAccessException; @@ -211,7 +214,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * * <p>All capture sessions can be used for capturing images from the camera but only capture * sessions created by - * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} + * {@link CameraDevice#createReprocessableCaptureSession createReprocessableCaptureSession} * can submit reprocess capture requests. Submitting a reprocess request to a regular * capture session will result in an {@link IllegalArgumentException}.</p> * @@ -233,14 +236,16 @@ public abstract class CameraCaptureSession implements AutoCloseable { * was explicitly closed, a new session has been created * or the camera device has been closed. * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target - * Surfaces not currently configured as outputs; or a reprocess - * capture request is submitted in a non-reprocessible capture - * session; or one of the reprocess capture requests was - * created with a {@link TotalCaptureResult} from a different - * session; or one of the captures targets a Surface in the - * middle of being {@link #prepare prepared}; or if the handler - * is null, the listener is not null, and the calling thread - * has no looper. + * Surfaces not currently configured as outputs; or one of the + * requests targets a set of Surfaces that cannot be submitted + * simultaneously in a reprocessable capture session; or a + * reprocess capture request is submitted in a + * non-reprocessable capture session; or one of the reprocess + * capture requests was created with a + * {@link TotalCaptureResult} from a different session; or one + * of the captures targets a Surface in the middle of being + * {@link #prepare prepared}; or if the handler is null, the + * listener is not null, and the calling thread has no looper. * * @see #capture * @see #setRepeatingRequest @@ -420,7 +425,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * * <p>This method is the fastest way to switch the camera device to a new session with * {@link CameraDevice#createCaptureSession} or - * {@link CameraDevice#createReprocessibleCaptureSession}, at the cost of discarding in-progress + * {@link CameraDevice#createReprocessableCaptureSession}, at the cost of discarding in-progress * work. It must be called before the new session is created. Once all pending requests are * either completed or thrown away, the {@link StateCallback#onReady} callback will be called, * if the session has not been closed. Otherwise, the {@link StateCallback#onClosed} @@ -443,7 +448,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @see #setRepeatingRequest * @see #setRepeatingBurst * @see CameraDevice#createCaptureSession - * @see CameraDevice#createReprocessibleCaptureSession + * @see CameraDevice#createReprocessableCaptureSession */ public abstract void abortCaptures() throws CameraAccessException; @@ -454,14 +459,14 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @return {@code true} if the application can submit reprocess capture requests with this * camera capture session. {@code false} otherwise. * - * @see CameraDevice#createReprocessibleCaptureSession + * @see CameraDevice#createReprocessableCaptureSession */ - public abstract boolean isReprocessible(); + public abstract boolean isReprocessable(); /** - * Get the input Surface associated with a reprocessible capture session. + * Get the input Surface associated with a reprocessable capture session. * - * <p>Each reprocessible capture session has an input {@link Surface} where the reprocess + * <p>Each reprocessable capture session has an input {@link Surface} where the reprocess * capture requests get the input images from, rather than the camera device. The application * can create a {@link android.media.ImageWriter} with this input {@link Surface} and use it to * provide input images for reprocess capture requests.</p> @@ -469,7 +474,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @return The {@link Surface} where reprocessing capture requests get the input images from. If * this is not a reprocess capture session, {@code null} will be returned. * - * @see CameraDevice#createReprocessibleCaptureSession + * @see CameraDevice#createReprocessableCaptureSession * @see android.media.ImageWriter * @see android.media.ImageReader */ diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 19e821c..d3b63f9 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1239,7 +1239,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * only the input buffer will be used to produce these output stream buffers, and a * new sensor image will not be captured.</p> * <p>For example, for Zero Shutter Lag (ZSL) still capture use case, the input - * stream image format will be OPAQUE, the associated output stream image format + * stream image format will be PRIVATE, the associated output stream image format * should be JPEG.</p> * <p><b>Range of valid values:</b><br></p> * <p>0 or 1.</p> @@ -1326,7 +1326,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR MANUAL_SENSOR}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING MANUAL_POST_PROCESSING}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}</li> - * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING OPAQUE_REPROCESSING}</li> + * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING PRIVATE_REPROCESSING}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS READ_SENSOR_SETTINGS}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}</li> @@ -1339,7 +1339,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING * @see #REQUEST_AVAILABLE_CAPABILITIES_RAW - * @see #REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING + * @see #REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING * @see #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS * @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE * @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING @@ -1536,12 +1536,12 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <tr> * <td align="left">{@link android.graphics.ImageFormat#PRIVATE }</td> * <td align="left">{@link android.graphics.ImageFormat#JPEG }</td> - * <td align="left">OPAQUE_REPROCESSING</td> + * <td align="left">PRIVATE_REPROCESSING</td> * </tr> * <tr> * <td align="left">{@link android.graphics.ImageFormat#PRIVATE }</td> * <td align="left">{@link android.graphics.ImageFormat#YUV_420_888 }</td> - * <td align="left">OPAQUE_REPROCESSING</td> + * <td align="left">PRIVATE_REPROCESSING</td> * </tr> * <tr> * <td align="left">{@link android.graphics.ImageFormat#YUV_420_888 }</td> @@ -1556,8 +1556,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * </tbody> * </table> * <p>PRIVATE refers to a device-internal format that is not directly application-visible. A - * PRIVATE input surface can be acquired by {@link android.media.ImageReader#newOpaqueInstance }.</p> - * <p>For a OPAQUE_REPROCESSING-capable camera device, using the PRIVATE format as either input + * PRIVATE input surface can be acquired by {@link android.media.ImageReader#newInstance } + * with {@link android.graphics.ImageFormat#PRIVATE } as the format.</p> + * <p>For a PRIVATE_REPROCESSING-capable camera device, using the PRIVATE format as either input * or output will never hurt maximum frame rate (i.e. {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration getOutputStallDuration(ImageFormat.PRIVATE, size)} is always 0),</p> * <p>Attempting to configure an input stream with output streams not * listed as available in this map is not valid.</p> @@ -2647,8 +2648,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * formats/sizes combination.</p> * <p>If this key reports 0, it means a reprocess request doesn't introduce any glitch to the * ongoing camera repeating request outputs, as if this reprocess request is never issued.</p> - * <p>This key is supported if the camera device supports OPAQUE or YUV reprocessing ( - * i.e. {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains OPAQUE_REPROCESSING or + * <p>This key is supported if the camera device supports PRIVATE or YUV reprocessing ( + * i.e. {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains PRIVATE_REPROCESSING or * YUV_REPROCESSING).</p> * <p><b>Units</b>: Number of frames.</p> * <p><b>Range of valid values:</b><br> diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index e9564b3..dad4fb6 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -100,7 +100,7 @@ public abstract class CameraDevice implements AutoCloseable { * means maximizing image quality without compromising preview frame rate. * AE/AWB/AF should be on auto mode. * This template is guaranteed to be supported on camera devices that support the - * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING OPAQUE_REPROCESSING} + * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING PRIVATE_REPROCESSING} * capability or the * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING} * capability. @@ -409,31 +409,34 @@ public abstract class CameraDevice implements AutoCloseable { CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException; /** - * Create a new reprocessible camera capture session by providing the desired reprocessing + * Create a new reprocessable camera capture session by providing the desired reprocessing * input Surface configuration and the target output set of Surfaces to the camera device. * * <p>If a camera device supports YUV reprocessing - * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING}) or OPAQUE + * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING}) or PRIVATE * reprocessing - * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING}), besides - * the capture session created via {@link #createCaptureSession}, the application can also - * create a reprocessible capture session to submit reprocess capture requests in addition to - * regular capture requests. A reprocess capture request takes the next available buffer from - * the session's input Surface, and sends it through the camera device's processing pipeline - * again, to produce buffers for the request's target output Surfaces. No new image data is - * captured for a reprocess request. However the input buffer provided by + * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING}), besides + * the capture session created via {@link #createCaptureSession createCaptureSession}, the + * application can also create a reprocessable capture session to submit reprocess capture + * requests in addition to regular capture requests. A reprocess capture request takes the next + * available buffer from the session's input Surface, and sends it through the camera device's + * processing pipeline again, to produce buffers for the request's target output Surfaces. No + * new image data is captured for a reprocess request. However the input buffer provided by * the application must be captured previously by the same camera device in the same session * directly (e.g. for Zero-Shutter-Lag use case) or indirectly (e.g. combining multiple output * images).</p> * - * <p>The active reprocessible capture session determines an input {@link Surface} and the set + * <p>The active reprocessable capture session determines an input {@link Surface} and the set * of potential output Surfaces for the camera devices for each capture request. The application - * can use {@link #createCaptureRequest} to create regular capture requests to capture new - * images from the camera device, and use {@link #createReprocessCaptureRequest} to create - * reprocess capture requests to process buffers from the input {@link Surface}. A request may - * use all or only some of the outputs. All the output Surfaces in one capture request will come - * from the same source, either from a new capture by the camera device, or from the input - * Surface depending on if the request is a reprocess capture request.</p> + * can use {@link #createCaptureRequest createCaptureRequest} to create regular capture requests + * to capture new images from the camera device, and use {@link #createReprocessCaptureRequest + * createReprocessCaptureRequest} to create reprocess capture requests to process buffers from + * the input {@link Surface}. Some combinations of output Surfaces in a session may not be used + * in a request simultaneously. The guaranteed combinations of output Surfaces that can be used + * in a request simultaneously are listed in the tables under {@link #createCaptureSession + * createCaptureSession}. All the output Surfaces in one capture request will come from the + * same source, either from a new capture by the camera device, or from the input Surface + * depending on if the request is a reprocess capture request.</p> * * <p>Input formats and sizes supported by the camera device can be queried via * {@link StreamConfigurationMap#getInputFormats} and @@ -445,11 +448,93 @@ public abstract class CameraDevice implements AutoCloseable { * they cannot be used as targets for a reprocessing request.</p> * * <p>Since the application cannot access {@link android.graphics.ImageFormat#PRIVATE} images - * directly, an output Surface created by {@link android.media.ImageReader#newOpaqueInstance} - * will be considered as intended to be used for reprocessing input and thus the - * {@link android.media.ImageReader} size must match one of the supported input sizes for - * {@link android.graphics.ImageFormat#PRIVATE} format. Otherwise, creating a reprocessible - * capture session will fail.</p> + * directly, an output Surface created by {@link android.media.ImageReader#newInstance} with + * {@link android.graphics.ImageFormat#PRIVATE} as the format will be considered as intended to + * be used for reprocessing input and thus the {@link android.media.ImageReader} size must + * match one of the supported input sizes for {@link android.graphics.ImageFormat#PRIVATE} + * format. Otherwise, creating a reprocessable capture session will fail.</p> + * + * <p>The guaranteed stream configurations listed in + * {@link #createCaptureSession createCaptureSession} are also guaranteed to work for + * {@link #createReprocessableCaptureSession createReprocessableCaptureSession}. In addition, + * the configurations in the tables below are also guaranteed for creating a reprocessable + * capture session if the camera device supports YUV reprocessing or PRIVATE reprocessing. + * However, not all output targets used to create a reprocessable session may be used in a + * {@link CaptureRequest} simultaneously. The guaranteed output targets that can be included + * in a {@link CaptureRequest} simultaneously are listed in the tables under + * {@link #createCaptureSession createCaptureSession}. For example, with a FULL-capability + * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} {@code == } + * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) device that supports PRIVATE + * reprocessing, an application can create a reprocessable capture session with 1 input, + * ({@code PRIV}, {@code MAXIMUM}), and 3 outputs, ({@code PRIV}, {@code MAXIMUM}), + * ({@code PRIV}, {@code PREVIEW}), and ({@code YUV}, {@code MAXIMUM}). However, it's not + * guaranteed that an application can submit a regular or reprocess capture with ({@code PRIV}, + * {@code MAXIMUM}) and ({@code YUV}, {@code MAXIMUM}) outputs based on the table listed under + * {@link #createCaptureSession createCaptureSession}. In other words, use the tables below to + * determine the guaranteed stream configurations for creating a reprocessable capture session, + * and use the tables under {@link #createCaptureSession createCaptureSession} to determine the + * guaranteed output targets that can be submitted in a regular or reprocess + * {@link CaptureRequest} simultaneously.</p> + * + * <style scoped> + * #rb { border-right-width: thick; } + * </style> + * + * <p>Limited-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} + * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices + * support at least the following stream combinations for creating a reprocessable capture + * session in addition to those listed in {@link #createCaptureSession createCaptureSession} for + * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: + * + * <table> + * <tr><th colspan="11">LIMITED-level additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr> + * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>No-viewfinder still image reprocessing.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>ZSL(Zero-Shutter-Lag) still imaging.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>ZSL still and in-app processing imaging.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>ZSL in-app processing with still capture.</td> </tr> + * </table><br> + * </p> + * + * <p>FULL-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} + * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices + * support at least the following stream combinations for creating a reprocessable capture + * session in addition to those for + * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: + * + * <table> + * <tr><th colspan="11">FULL-capability additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr> + * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>Maximum-resolution multi-frame image fusion in-app processing with regular preview.</td> </tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>Maximum-resolution multi-frame image fusion two-input in-app processing.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code RECORD}</td> <td></td><td id="rb"></td> <td>High-resolution ZSL in-app video processing with regular preview.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Maximum-resolution ZSL in-app processing with regular preview.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code RECORD}</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD}</td> <td>High-resolution ZSL in-app video processing and video snapshot with regular preview.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution two-input ZSL in-app processing with regular preview.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>ZSL still capture and in-app processing.</td> </tr> + * </table><br> + * </p> + * + * <p>RAW-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes + * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}) devices additionally support + * at least the following stream combinations for creating a reprocessable capture session + * on both {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} and + * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices + * + * <table> + * <tr><th colspan="11">RAW-capability additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr> + * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Mutually exclusive ZSL in-app processing and DNG capture.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>Mutually exclusive ZSL in-app processing and preview with DNG capture.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>Mutually exclusive ZSL two-input in-app processing and DNG capture.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>Mutually exclusive ZSL still capture and preview with DNG capture.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>Mutually exclusive ZSL in-app processing with still capture and DNG capture.</td> </tr> + * </table><br> + * </p> * * @param inputConfig The configuration for the input {@link Surface} * @param outputs The new set of Surfaces that should be made available as @@ -466,6 +551,7 @@ public abstract class CameraDevice implements AutoCloseable { * encountered a fatal error * @throws IllegalStateException if the camera device has been closed * + * @see #createCaptureSession * @see CameraCaptureSession * @see StreamConfigurationMap#getInputFormats * @see StreamConfigurationMap#getInputSizes @@ -474,7 +560,7 @@ public abstract class CameraDevice implements AutoCloseable { * @see android.media.ImageWriter * @see android.media.ImageReader */ - public abstract void createReprocessibleCaptureSession(InputConfiguration inputConfig, + public abstract void createReprocessableCaptureSession(InputConfiguration inputConfig, List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException; @@ -516,8 +602,7 @@ public abstract class CameraDevice implements AutoCloseable { * {@link CameraCaptureSession}'s input {@link Surface} to all output {@link Surface Surfaces} * included in the reprocess capture request. The reprocess input images must be generated from * one or multiple output images captured from the same camera device. The application can - * provide input images to camera device via - * {{@link android.media.ImageWriter#queueInputImage ImageWriter#queueInputImage}}. + * provide input images to camera device via {@link android.media.ImageWriter#queueInputImage}. * The application must use the capture result of one of those output images to create a * reprocess capture request so that the camera device can use the information to achieve * optimal reprocess image quality. @@ -532,7 +617,7 @@ public abstract class CameraDevice implements AutoCloseable { * * @see CaptureRequest.Builder * @see TotalCaptureResult - * @see CameraDevice#createReprocessibleCaptureSession + * @see CameraDevice#createReprocessableCaptureSession * @see android.media.ImageWriter */ public abstract CaptureRequest.Builder createReprocessCaptureRequest( diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 9327f00..d99cce7 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -556,7 +556,7 @@ public final class CameraManager { * {@link CameraManager#registerTorchCallback} to be notified of such status changes. * </p> * - * @see registerTorchCallback + * @see #registerTorchCallback */ public static abstract class TorchCallback { /** diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index ca9439b..1d7450e 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -472,7 +472,7 @@ public abstract class CameraMetadata<TKey> { * <li>{@link android.graphics.ImageFormat#PRIVATE } will be reprocessable into both * {@link android.graphics.ImageFormat#YUV_420_888 } and * {@link android.graphics.ImageFormat#JPEG } formats.</li> - * <li>The maximum available resolution for OPAQUE streams + * <li>The maximum available resolution for PRIVATE streams * (both input/output) will match the maximum available * resolution of JPEG streams.</li> * <li>Static metadata {@link CameraCharacteristics#REPROCESS_MAX_CAPTURE_STALL android.reprocess.maxCaptureStall}.</li> @@ -492,7 +492,7 @@ public abstract class CameraMetadata<TKey> { * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ - public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4; + public static final int REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING = 4; /** * <p>The camera device supports accurately reporting the sensor settings for many of @@ -565,7 +565,7 @@ public abstract class CameraMetadata<TKey> { /** * <p>The camera device supports the YUV_420_888 reprocessing use case, similar as - * OPAQUE_REPROCESSING, This capability requires the camera device to support the + * PRIVATE_REPROCESSING, This capability requires the camera device to support the * following:</p> * <ul> * <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li> diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index ab6ce91..3ec11b7 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -158,9 +158,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> private final HashSet<Surface> mSurfaceSet; private final CameraMetadataNative mSettings; private boolean mIsReprocess; - // Each reprocess request must be tied to a reprocessible session ID. + // Each reprocess request must be tied to a reprocessable session ID. // Valid only for reprocess requests (mIsReprocess == true). - private int mReprocessibleSessionId; + private int mReprocessableSessionId; private Object mUserTag; @@ -173,7 +173,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mSettings = new CameraMetadataNative(); mSurfaceSet = new HashSet<Surface>(); mIsReprocess = false; - mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE; + mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE; } /** @@ -186,7 +186,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mSettings = new CameraMetadataNative(source.mSettings); mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone(); mIsReprocess = source.mIsReprocess; - mReprocessibleSessionId = source.mReprocessibleSessionId; + mReprocessableSessionId = source.mReprocessableSessionId; mUserTag = source.mUserTag; } @@ -199,30 +199,30 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * @param isReprocess Indicates whether to create a reprocess capture request. {@code true} * to create a reprocess capture request. {@code false} to create a regular * capture request. - * @param reprocessibleSessionId The ID of the camera capture session this capture is created + * @param reprocessableSessionId The ID of the camera capture session this capture is created * for. This is used to validate if the application submits a * reprocess capture request to the same session where * the {@link TotalCaptureResult}, used to create the reprocess * capture, came from. * * @throws IllegalArgumentException If creating a reprocess capture request with an invalid - * reprocessibleSessionId. + * reprocessableSessionId. * * @see CameraDevice#createReprocessCaptureRequest */ private CaptureRequest(CameraMetadataNative settings, boolean isReprocess, - int reprocessibleSessionId) { + int reprocessableSessionId) { mSettings = CameraMetadataNative.move(settings); mSurfaceSet = new HashSet<Surface>(); mIsReprocess = isReprocess; if (isReprocess) { - if (reprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) { + if (reprocessableSessionId == CameraCaptureSession.SESSION_ID_NONE) { throw new IllegalArgumentException("Create a reprocess capture request with an " + - "invalid session ID: " + reprocessibleSessionId); + "invalid session ID: " + reprocessableSessionId); } - mReprocessibleSessionId = reprocessibleSessionId; + mReprocessableSessionId = reprocessableSessionId; } else { - mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE; + mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE; } } @@ -307,20 +307,20 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> } /** - * Get the reprocessible session ID this reprocess capture request is associated with. + * Get the reprocessable session ID this reprocess capture request is associated with. * - * @return the reprocessible session ID this reprocess capture request is associated with + * @return the reprocessable session ID this reprocess capture request is associated with * * @throws IllegalStateException if this capture request is not a reprocess capture request. * @hide */ - public int getReprocessibleSessionId() { + public int getReprocessableSessionId() { if (mIsReprocess == false || - mReprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) { - throw new IllegalStateException("Getting the reprocessible session ID for a "+ + mReprocessableSessionId == CameraCaptureSession.SESSION_ID_NONE) { + throw new IllegalStateException("Getting the reprocessable session ID for a "+ "non-reprocess capture request is illegal."); } - return mReprocessibleSessionId; + return mReprocessableSessionId; } /** @@ -346,7 +346,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> && mSurfaceSet.equals(other.mSurfaceSet) && mSettings.equals(other.mSettings) && mIsReprocess == other.mIsReprocess - && mReprocessibleSessionId == other.mReprocessibleSessionId; + && mReprocessableSessionId == other.mReprocessableSessionId; } @Override @@ -395,7 +395,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> } mIsReprocess = (in.readInt() == 0) ? false : true; - mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE; + mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE; } @Override @@ -450,19 +450,19 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * @param reprocess Indicates whether to create a reprocess capture request. {@code true} * to create a reprocess capture request. {@code false} to create a regular * capture request. - * @param reprocessibleSessionId The ID of the camera capture session this capture is + * @param reprocessableSessionId The ID of the camera capture session this capture is * created for. This is used to validate if the application * submits a reprocess capture request to the same session * where the {@link TotalCaptureResult}, used to create the * reprocess capture, came from. * * @throws IllegalArgumentException If creating a reprocess capture request with an invalid - * reprocessibleSessionId. + * reprocessableSessionId. * @hide */ public Builder(CameraMetadataNative template, boolean reprocess, - int reprocessibleSessionId) { - mRequest = new CaptureRequest(template, reprocess, reprocessibleSessionId); + int reprocessableSessionId) { + mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId); } /** @@ -1275,7 +1275,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>This control (except for MANUAL) is only effective if * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p> * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} - * contains OPAQUE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if + * contains PRIVATE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR. Other intent values are * always supported.</p> * <p><b>Possible values:</b> diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 3dc8970..d931a76 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -1698,7 +1698,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>This control (except for MANUAL) is only effective if * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p> * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} - * contains OPAQUE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if + * contains PRIVATE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR. Other intent values are * always supported.</p> * <p><b>Possible values:</b> diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index dff6227..d08c52b 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -153,10 +153,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { Handler handler) throws CameraAccessException { if (request == null) { throw new IllegalArgumentException("request must not be null"); - } else if (request.isReprocess() && !isReprocessible()) { + } else if (request.isReprocess() && !isReprocessable()) { throw new IllegalArgumentException("this capture session cannot handle reprocess " + "requests"); - } else if (request.isReprocess() && request.getReprocessibleSessionId() != mId) { + } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { throw new IllegalArgumentException("capture request was created for another session"); } @@ -184,10 +184,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { for (CaptureRequest request : requests) { if (request.isReprocess()) { - if (!isReprocessible()) { + if (!isReprocessable()) { throw new IllegalArgumentException("This capture session cannot handle " + "reprocess requests"); - } else if (request.getReprocessibleSessionId() != mId) { + } else if (request.getReprocessableSessionId() != mId) { throw new IllegalArgumentException("Capture request was created for another " + "session"); } @@ -293,7 +293,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } @Override - public boolean isReprocessible() { + public boolean isReprocessable() { return mInput != null; } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index e84b46a..4508dc8 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -480,16 +480,16 @@ public class CameraDeviceImpl extends CameraDevice { } @Override - public void createReprocessibleCaptureSession(InputConfiguration inputConfig, + public void createReprocessableCaptureSession(InputConfiguration inputConfig, List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException { if (DEBUG) { - Log.d(TAG, "createReprocessibleCaptureSession"); + Log.d(TAG, "createReprocessableCaptureSession"); } if (inputConfig == null) { throw new IllegalArgumentException("inputConfig cannot be null when creating a " + - "reprocessible capture session"); + "reprocessable capture session"); } List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); for (Surface surface : outputs) { diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java index 9e9a6fe..32bbc51 100644 --- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java +++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java @@ -61,6 +61,8 @@ public class ParameterUtils { public static final Rect RECTANGLE_EMPTY = new Rect(/*left*/0, /*top*/0, /*right*/0, /*bottom*/0); + private static final double ASPECT_RATIO_TOLERANCE = 0.05f; + /** * Calculate effective/reported zoom data from a user-specified crop region. */ @@ -498,7 +500,10 @@ public class ParameterUtils { float aspectRatioPreview = previewSize.getWidth() * 1.0f / previewSize.getHeight(); float cropH, cropW; - if (aspectRatioPreview < aspectRatioArray) { + if (Math.abs(aspectRatioPreview - aspectRatioArray) < ASPECT_RATIO_TOLERANCE) { + cropH = activeArray.height(); + cropW = activeArray.width(); + } else if (aspectRatioPreview < aspectRatioArray) { // The new width must be smaller than the height, so scale the width by AR cropH = activeArray.height(); cropW = cropH * aspectRatioPreview; diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java index dea1c5c..0c642cf 100644 --- a/core/java/android/hardware/camera2/params/InputConfiguration.java +++ b/core/java/android/hardware/camera2/params/InputConfiguration.java @@ -19,11 +19,11 @@ package android.hardware.camera2.params; import android.hardware.camera2.utils.HashCodeHelpers; /** - * Immutable class to store an input configuration that is used to create a reprocessible capture + * Immutable class to store an input configuration that is used to create a reprocessable capture * session. * - * @see CameraDevice#createReprocessibleCaptureSession - * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP + * @see android.hardware.camera2.CameraDevice#createReprocessableCaptureSession + * @see android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP */ public final class InputConfiguration { diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 02793f1..aa697ea 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -125,11 +125,13 @@ public abstract class DisplayManagerInternal { * mirroring. * @param requestedRefreshRate The preferred refresh rate for the top-most visible window that * has a preference. + * @param requestedModeId The preferred mode id for the top-most visible window that has a + * preference. * @param inTraversal True if called from WindowManagerService during a window traversal * prior to call to performTraversalInTransactionFromWindowManager. */ public abstract void setDisplayProperties(int displayId, boolean hasContent, - float requestedRefreshRate, boolean inTraversal); + float requestedRefreshRate, int requestedModeId, boolean inTraversal); /** * Applies an offset to the contents of a display, for example to avoid burn-in. diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index cf96145..338bd76 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -32,7 +32,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; -import android.security.AndroidKeyStoreProvider; +import android.security.keystore.AndroidKeyStoreProvider; import android.util.Log; import android.util.Slog; @@ -686,7 +686,7 @@ public class FingerprintManager { private void sendAuthenticatedResult(Fingerprint fp) { if (mAuthenticationCallback != null) { - if (fp.getFingerId() == 0 && fp.getGroupId() == 0) { + if (fp.getFingerId() == 0) { // Fingerprint template valid but doesn't match one in database mAuthenticationCallback.onAuthenticationFailed(); } else { diff --git a/core/java/android/hardware/fingerprint/FingerprintUtils.java b/core/java/android/hardware/fingerprint/FingerprintUtils.java deleted file mode 100644 index 525f254..0000000 --- a/core/java/android/hardware/fingerprint/FingerprintUtils.java +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.hardware.fingerprint; - -import android.content.ContentResolver; -import android.content.Context; -import android.os.Vibrator; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.util.ArrayUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Utility class for dealing with fingerprints and fingerprint settings. - * @hide - */ -public -class FingerprintUtils { - private static final boolean DEBUG = true; - private static final String TAG = "FingerprintUtils"; - private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30}; - private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30}; - - private static int[] toIntArray(List<Integer> list) { - if (list == null) { - return null; - } - int[] arr = new int[list.size()]; - int i = 0; - for (int elem : list) { - arr[i] = elem; - i++; - } - return arr; - } - - public static int[] getFingerprintIdsForUser(ContentResolver res, int userId) { - String fingerIdsRaw = Settings.Secure.getStringForUser(res, - Settings.Secure.USER_FINGERPRINT_IDS, userId); - ArrayList<Integer> tmp = new ArrayList<Integer>(); - if (!TextUtils.isEmpty(fingerIdsRaw)) { - String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", "); - int length = fingerStringIds.length; - for (int i = 0; i < length; i++) { - try { - tmp.add(Integer.decode(fingerStringIds[i])); - } catch (NumberFormatException e) { - if (DEBUG) Log.w(TAG, "Error parsing finger id: '" + fingerStringIds[i] + "'"); - } - } - } - return toIntArray(tmp); - } - - public static void addFingerprintIdForUser(ContentResolver res, int fingerId, int userId) { - // FingerId 0 has special meaning. - if (fingerId == 0) { - Log.w(TAG, "Tried to add fingerId 0"); - return; - } - - int[] fingerIds = getFingerprintIdsForUser(res, userId); - - // Don't allow dups - if (ArrayUtils.contains(fingerIds, fingerId)) { - Log.w(TAG, "finger already added " + fingerId); - return; - } - int[] newList = Arrays.copyOf(fingerIds, fingerIds.length + 1); - newList[fingerIds.length] = fingerId; - Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS, - Arrays.toString(newList), userId); - } - - public static boolean removeFingerprintIdForUser(int fingerId, ContentResolver res, int userId) - { - // FingerId 0 has special meaning. The HAL layer is supposed to remove each finger one - // at a time and invoke notify() for each fingerId. If we get called with 0 here, it means - // something bad has happened. - if (fingerId == 0) throw new IllegalArgumentException("fingerId can't be 0"); - - final int[] fingerIds = getFingerprintIdsForUser(res, userId); - if (ArrayUtils.contains(fingerIds, fingerId)) { - final int[] result = ArrayUtils.removeInt(fingerIds, fingerId); - Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS, - Arrays.toString(result), userId); - return true; - } - return false; - } - - public static void vibrateFingerprintError(Context context) { - Vibrator vibrator = context.getSystemService(Vibrator.class); - if (vibrator != null) { - vibrator.vibrate(FP_ERROR_VIBRATE_PATTERN, -1); - } - } - - public static void vibrateFingerprintSuccess(Context context) { - Vibrator vibrator = context.getSystemService(Vibrator.class); - if (vibrator != null) { - vibrator.vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1); - } - } - -}; - diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 9bc967f..881dc0f 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -83,10 +83,7 @@ interface IUsbManager void clearDefaults(String packageName, int userId); /* Sets the current USB function. */ - void setCurrentFunction(String function, boolean makeDefault); - - /* Sets the file path for USB mass storage backing file. */ - void setMassStorageBackingFile(String path); + void setCurrentFunction(String function); /* Allow USB debugging from the attached host. If alwaysAllow is true, add the * the public key to list of host keys that the user has approved. diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index f283051..000d41f 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -228,6 +228,23 @@ public class UsbManager { */ public static final String EXTRA_PERMISSION_GRANTED = "permission"; + /** + * The persistent property which stores whether adb is enabled or not. Other values are ignored. + * Previously this value stored non-adb settings, but not anymore. + * TODO: rename this to something adb specific, rather than using usb. + * + * {@hide} + */ + public static final String ADB_PERSISTENT_PROPERTY = "persist.sys.usb.config"; + + /** + * The non-persistent property which stores the current USB settings. + * + * {@hide} + */ + public static final String USB_SETTINGS_PROPERTY = "sys.usb.config"; + + private final Context mContext; private final IUsbManager mService; @@ -429,24 +446,7 @@ public class UsbManager { * {@hide} */ public boolean isFunctionEnabled(String function) { - return propertyContainsFunction("sys.usb.config", function); - } - - /** - * Returns the current default USB function. - * - * @return name of the default function. - * - * {@hide} - */ - public String getDefaultFunction() { - String functions = SystemProperties.get("persist.sys.usb.config", ""); - int commaIndex = functions.indexOf(','); - if (commaIndex > 0) { - return functions.substring(0, commaIndex); - } else { - return functions; - } + return propertyContainsFunction(USB_SETTINGS_PROPERTY, function); } /** @@ -454,30 +454,14 @@ public class UsbManager { * If function is null, then the current function is set to the default function. * * @param function name of the USB function, or null to restore the default function - * @param makeDefault true if the function should be set as the new default function * * {@hide} */ - public void setCurrentFunction(String function, boolean makeDefault) { + public void setCurrentFunction(String function) { try { - mService.setCurrentFunction(function, makeDefault); + mService.setCurrentFunction(function); } catch (RemoteException e) { Log.e(TAG, "RemoteException in setCurrentFunction", e); } } - - /** - * Sets the file path for USB mass storage backing file. - * - * @param path backing file path - * - * {@hide} - */ - public void setMassStorageBackingFile(String path) { - try { - mService.setMassStorageBackingFile(path); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in setDefaultFunction", e); - } - } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index d8c3361..26878c0 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -496,6 +496,8 @@ public class ConnectivityManager { * Tests if a given integer represents a valid network type. * @param networkType the type to be tested * @return a boolean. {@code true} if the type is valid, else {@code false} + * @deprecated All APIs accepting a network type are deprecated. There should be no need to + * validate a network type. */ public static boolean isNetworkTypeValid(int networkType) { return networkType >= 0 && networkType <= MAX_NETWORK_TYPE; diff --git a/core/java/android/net/IpReachabilityMonitor.java b/core/java/android/net/IpReachabilityMonitor.java new file mode 100644 index 0000000..7e1c05b --- /dev/null +++ b/core/java/android/net/IpReachabilityMonitor.java @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.ProxyInfo; +import android.net.RouteInfo; +import android.net.netlink.NetlinkConstants; +import android.net.netlink.NetlinkErrorMessage; +import android.net.netlink.NetlinkMessage; +import android.net.netlink.NetlinkSocket; +import android.net.netlink.RtNetlinkNeighborMessage; +import android.net.netlink.StructNdaCacheInfo; +import android.net.netlink.StructNdMsg; +import android.net.netlink.StructNlMsgHdr; +import android.os.SystemClock; +import android.system.ErrnoException; +import android.system.NetlinkSocketAddress; +import android.system.OsConstants; +import android.text.TextUtils; +import android.util.Log; + +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +/** + * IpReachabilityMonitor. + * + * Monitors on-link IP reachability and notifies callers whenever any on-link + * addresses of interest appear to have become unresponsive. + * + * @hide + */ +public class IpReachabilityMonitor { + private static final String TAG = "IpReachabilityMonitor"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + public interface Callback { + public void notifyLost(InetAddress ip, String logMsg); + } + + private final Object mLock = new Object(); + private final String mInterfaceName; + private final int mInterfaceIndex; + private final Callback mCallback; + private final Set<InetAddress> mIpWatchList; + private int mIpWatchListVersion; + private boolean mRunning; + final private Thread mObserverThread; + + // TODO: consider passing in a NetworkInterface object from the caller. + public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException { + mInterfaceName = ifName; + int ifIndex = -1; + try { + NetworkInterface netIf = NetworkInterface.getByName(ifName); + mInterfaceIndex = netIf.getIndex(); + } catch (SocketException | NullPointerException e) { + throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e); + } + mCallback = callback; + mIpWatchList = new HashSet<InetAddress>(); + mIpWatchListVersion = 0; + mRunning = false; + mObserverThread = new Thread(new NetlinkSocketObserver()); + mObserverThread.start(); + } + + public void stop() { + synchronized (mLock) { + mRunning = false; + mIpWatchList.clear(); + } + } + + // TODO: add a public dump() method that can be called during a bug report. + + private static Set<InetAddress> getOnLinkNeighbors(LinkProperties lp) { + Set<InetAddress> allIps = new HashSet<InetAddress>(); + + final List<RouteInfo> routes = lp.getRoutes(); + for (RouteInfo route : routes) { + if (route.hasGateway()) { + allIps.add(route.getGateway()); + } + } + + for (InetAddress nameserver : lp.getDnsServers()) { + allIps.add(nameserver); + } + + try { + // Don't block here for DNS lookups. If the proxy happens to be an + // IP literal then we add it the list, but otherwise skip it. + allIps.add(NetworkUtils.numericToInetAddress(lp.getHttpProxy().getHost())); + } catch (NullPointerException|IllegalArgumentException e) { + // No proxy, PAC proxy, or proxy is not a literal IP address. + } + + Set<InetAddress> neighbors = new HashSet<InetAddress>(); + for (InetAddress ip : allIps) { + // TODO: consider using the prefixes of the LinkAddresses instead + // of the routes--it may be more accurate. + for (RouteInfo route : routes) { + if (route.hasGateway()) { + continue; // Not directly connected. + } + if (route.matches(ip)) { + neighbors.add(ip); + break; + } + } + } + return neighbors; + } + + private String describeWatchList() { + synchronized (mLock) { + return "version{" + mIpWatchListVersion + "}, " + + "ips=[" + TextUtils.join(",", mIpWatchList) + "]"; + } + } + + private boolean isWatching(InetAddress ip) { + synchronized (mLock) { + return mRunning && mIpWatchList.contains(ip); + } + } + + public void updateLinkProperties(LinkProperties lp) { + if (!mInterfaceName.equals(lp.getInterfaceName())) { + // TODO: figure out how to cope with interface changes. + Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + + "' does not match: " + mInterfaceName); + return; + } + + // We rely upon the caller to determine when LinkProperties have actually + // changed and call this at the appropriate time. Note that even though + // the LinkProperties may change, the set of on-link neighbors might not. + // + // Nevertheless, just clear and re-add everything. + final Set<InetAddress> neighbors = getOnLinkNeighbors(lp); + if (neighbors.isEmpty()) { + return; + } + + synchronized (mLock) { + mIpWatchList.clear(); + mIpWatchList.addAll(neighbors); + mIpWatchListVersion++; + } + if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } + } + + public void clearLinkProperties() { + synchronized (mLock) { + mIpWatchList.clear(); + mIpWatchListVersion++; + } + if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } + } + + private void notifyLost(InetAddress ip, String msg) { + if (!isWatching(ip)) { + // Ignore stray notifications. This can happen when, for example, + // several neighbors are reported unreachable or deleted + // back-to-back. Because these messages are parsed serially, and + // this method is called for each notification, the caller above us + // may have already processed an earlier lost notification and + // cleared the watch list as it moves to handle the situation. + return; + } + Log.w(TAG, "ALERT: " + ip.getHostAddress() + " -- " + msg); + if (mCallback != null) { + mCallback.notifyLost(ip, msg); + } + } + + + private final class NetlinkSocketObserver implements Runnable { + private static final String TAG = "NetlinkSocketObserver"; + private NetlinkSocket mSocket; + + @Override + public void run() { + if (VDBG) { Log.d(TAG, "Starting observing thread."); } + synchronized (mLock) { mRunning = true; } + + try { + setupNetlinkSocket(); + } catch (ErrnoException | SocketException e) { + Log.e(TAG, "Failed to suitably initialize a netlink socket", e); + synchronized (mLock) { mRunning = false; } + } + + ByteBuffer byteBuffer; + while (stillRunning()) { + try { + byteBuffer = recvKernelReply(); + } catch (ErrnoException e) { + Log.w(TAG, "ErrnoException: ", e); + break; + } + final long whenMs = SystemClock.elapsedRealtime(); + if (byteBuffer == null) { + continue; + } + parseNetlinkMessageBuffer(byteBuffer, whenMs); + } + + clearNetlinkSocket(); + + synchronized (mLock) { mRunning = false; } + if (VDBG) { Log.d(TAG, "Finishing observing thread."); } + } + + private boolean stillRunning() { + synchronized (mLock) { + return mRunning; + } + } + + private void clearNetlinkSocket() { + if (mSocket != null) { + mSocket.close(); + } + mSocket = null; + } + + // TODO: Refactor the main loop to recreate the socket upon recoverable errors. + private void setupNetlinkSocket() throws ErrnoException, SocketException { + clearNetlinkSocket(); + mSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE); + + final NetlinkSocketAddress listenAddr = new NetlinkSocketAddress( + 0, OsConstants.RTMGRP_NEIGH); + mSocket.bind(listenAddr); + + if (VDBG) { + final NetlinkSocketAddress nlAddr = mSocket.getLocalAddress(); + Log.d(TAG, "bound to sockaddr_nl{" + + ((long) (nlAddr.getPortId() & 0xffffffff)) + ", " + + nlAddr.getGroupsMask() + + "}"); + } + } + + private ByteBuffer recvKernelReply() throws ErrnoException { + try { + return mSocket.recvMessage(0); + } catch (InterruptedIOException e) { + // Interruption or other error, e.g. another thread closed our file descriptor. + } catch (ErrnoException e) { + if (e.errno != OsConstants.EAGAIN) { + throw e; + } + } + return null; + } + + private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { + while (byteBuffer.remaining() > 0) { + final int position = byteBuffer.position(); + final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); + if (nlMsg == null || nlMsg.getHeader() == null) { + byteBuffer.position(position); + Log.e(TAG, "unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer)); + break; + } + + final int srcPortId = nlMsg.getHeader().nlmsg_pid; + if (srcPortId != 0) { + Log.e(TAG, "non-kernel source portId: " + ((long) (srcPortId & 0xffffffff))); + break; + } + + if (nlMsg instanceof NetlinkErrorMessage) { + Log.e(TAG, "netlink error: " + nlMsg); + continue; + } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { + if (DBG) { + Log.d(TAG, "non-rtnetlink neighbor msg: " + nlMsg); + } + continue; + } + + evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); + } + } + + private void evaluateRtNetlinkNeighborMessage( + RtNetlinkNeighborMessage neighMsg, long whenMs) { + final StructNdMsg ndMsg = neighMsg.getNdHeader(); + if (ndMsg == null || ndMsg.ndm_ifindex != mInterfaceIndex) { + return; + } + + final InetAddress destination = neighMsg.getDestination(); + if (!isWatching(destination)) { + return; + } + + final short msgType = neighMsg.getHeader().nlmsg_type; + final short nudState = ndMsg.ndm_state; + final String eventMsg = "NeighborEvent{" + + "elapsedMs=" + whenMs + ", " + + destination.getHostAddress() + ", " + + "[" + NetlinkConstants.hexify(neighMsg.getLinkLayerAddress()) + "], " + + NetlinkConstants.stringForNlMsgType(msgType) + ", " + + StructNdMsg.stringForNudState(nudState) + + "}"; + + if (VDBG) { + Log.d(TAG, neighMsg.toString()); + } else if (DBG) { + Log.d(TAG, eventMsg); + } + + if ((msgType == NetlinkConstants.RTM_DELNEIGH) || + (nudState == StructNdMsg.NUD_FAILED)) { + final String logMsg = "FAILURE: " + eventMsg; + notifyLost(destination, logMsg); + } + } + } +} diff --git a/core/java/android/net/netlink/NetlinkConstants.java b/core/java/android/net/netlink/NetlinkConstants.java new file mode 100644 index 0000000..e331701 --- /dev/null +++ b/core/java/android/net/netlink/NetlinkConstants.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.system.OsConstants; +import com.android.internal.util.HexDump; + +import java.nio.ByteBuffer; + + +/** + * Various constants and static helper methods for netlink communications. + * + * Values taken from: + * + * <linux_src>/include/uapi/linux/netlink.h + * <linux_src>/include/uapi/linux/rtnetlink.h + * + * @hide + */ +public class NetlinkConstants { + private NetlinkConstants() {} + + public static final int NLA_ALIGNTO = 4; + + public static final int alignedLengthOf(short length) { + final int intLength = (int) length & 0xffff; + return alignedLengthOf(intLength); + } + + public static final int alignedLengthOf(int length) { + if (length <= 0) { return 0; } + return (((length + NLA_ALIGNTO - 1) / NLA_ALIGNTO) * NLA_ALIGNTO); + } + + public static String stringForAddressFamily(int family) { + if (family == OsConstants.AF_INET) { return "AF_INET"; } + if (family == OsConstants.AF_INET6) { return "AF_INET6"; } + if (family == OsConstants.AF_NETLINK) { return "AF_NETLINK"; } + return String.valueOf(family); + } + + public static String hexify(byte[] bytes) { + if (bytes == null) { return "(null)"; } + return HexDump.toHexString(bytes); + } + + public static String hexify(ByteBuffer buffer) { + if (buffer == null) { return "(null)"; } + return HexDump.toHexString( + buffer.array(), buffer.position(), buffer.remaining()); + } + + // Known values for struct nlmsghdr nlm_type. + public static final short NLMSG_NOOP = 1; // Nothing + public static final short NLMSG_ERROR = 2; // Error + public static final short NLMSG_DONE = 3; // End of a dump + public static final short NLMSG_OVERRUN = 4; // Data lost + public static final short NLMSG_MAX_RESERVED = 15; // Max reserved value + + public static final short RTM_NEWLINK = 16; + public static final short RTM_DELLINK = 17; + public static final short RTM_GETLINK = 18; + public static final short RTM_SETLINK = 19; + public static final short RTM_NEWADDR = 20; + public static final short RTM_DELADDR = 21; + public static final short RTM_GETADDR = 22; + public static final short RTM_NEWROUTE = 24; + public static final short RTM_DELROUTE = 25; + public static final short RTM_GETROUTE = 26; + public static final short RTM_NEWNEIGH = 28; + public static final short RTM_DELNEIGH = 29; + public static final short RTM_GETNEIGH = 30; + public static final short RTM_NEWRULE = 32; + public static final short RTM_DELRULE = 33; + public static final short RTM_GETRULE = 34; + public static final short RTM_NEWNDUSEROPT = 68; + + public static String stringForNlMsgType(short nlm_type) { + switch (nlm_type) { + case NLMSG_NOOP: return "NLMSG_NOOP"; + case NLMSG_ERROR: return "NLMSG_ERROR"; + case NLMSG_DONE: return "NLMSG_DONE"; + case NLMSG_OVERRUN: return "NLMSG_OVERRUN"; + case RTM_NEWLINK: return "RTM_NEWLINK"; + case RTM_DELLINK: return "RTM_DELLINK"; + case RTM_GETLINK: return "RTM_GETLINK"; + case RTM_SETLINK: return "RTM_SETLINK"; + case RTM_NEWADDR: return "RTM_NEWADDR"; + case RTM_DELADDR: return "RTM_DELADDR"; + case RTM_GETADDR: return "RTM_GETADDR"; + case RTM_NEWROUTE: return "RTM_NEWROUTE"; + case RTM_DELROUTE: return "RTM_DELROUTE"; + case RTM_GETROUTE: return "RTM_GETROUTE"; + case RTM_NEWNEIGH: return "RTM_NEWNEIGH"; + case RTM_DELNEIGH: return "RTM_DELNEIGH"; + case RTM_GETNEIGH: return "RTM_GETNEIGH"; + case RTM_NEWRULE: return "RTM_NEWRULE"; + case RTM_DELRULE: return "RTM_DELRULE"; + case RTM_GETRULE: return "RTM_GETRULE"; + case RTM_NEWNDUSEROPT: return "RTM_NEWNDUSEROPT"; + default: + return "unknown RTM type: " + String.valueOf(nlm_type); + } + } +} diff --git a/core/java/android/net/netlink/NetlinkErrorMessage.java b/core/java/android/net/netlink/NetlinkErrorMessage.java new file mode 100644 index 0000000..dbc10d6 --- /dev/null +++ b/core/java/android/net/netlink/NetlinkErrorMessage.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.StructNlMsgHdr; +import android.net.netlink.NetlinkMessage; +import android.util.Log; + +import java.nio.ByteBuffer; + + +/** + * A NetlinkMessage subclass for netlink error messages. + * + * @hide + */ +public class NetlinkErrorMessage extends NetlinkMessage { + + public static NetlinkErrorMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { + final NetlinkErrorMessage errorMsg = new NetlinkErrorMessage(header); + + errorMsg.mNlMsgErr = StructNlMsgErr.parse(byteBuffer); + if (errorMsg.mNlMsgErr == null) { + return null; + } + + return errorMsg; + } + + private StructNlMsgErr mNlMsgErr; + + NetlinkErrorMessage(StructNlMsgHdr header) { + super(header); + mNlMsgErr = null; + } + + public StructNlMsgErr getNlMsgError() { + return mNlMsgErr; + } + + @Override + public String toString() { + return "NetlinkErrorMessage{ " + + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, " + + "nlmsgerr{" + (mNlMsgErr == null ? "" : mNlMsgErr.toString()) + "} " + + "}"; + } +} diff --git a/core/java/android/net/netlink/NetlinkMessage.java b/core/java/android/net/netlink/NetlinkMessage.java new file mode 100644 index 0000000..bc04a16 --- /dev/null +++ b/core/java/android/net/netlink/NetlinkMessage.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.NetlinkConstants; +import android.net.netlink.NetlinkErrorMessage; +import android.net.netlink.RtNetlinkNeighborMessage; +import android.net.netlink.StructNlAttr; +import android.net.netlink.StructNlMsgHdr; +import android.util.Log; + +import java.nio.ByteBuffer; + + +/** + * NetlinkMessage base class for other, more specific netlink message types. + * + * Classes that extend NetlinkMessage should: + * - implement a public static parse(StructNlMsgHdr, ByteBuffer) method + * - returning either null (parse errors) or a new object of the subclass + * type (cast-able to NetlinkMessage) + * + * NetlinkMessage.parse() should be updated to know which nlmsg_type values + * correspond with which message subclasses. + * + * @hide + */ +public class NetlinkMessage { + private final static String TAG = "NetlinkMessage"; + + public static NetlinkMessage parse(ByteBuffer byteBuffer) { + final int startPosition = (byteBuffer != null) ? byteBuffer.position() : -1; + final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(byteBuffer); + if (nlmsghdr == null) { + return null; + } + + int payloadLength = NetlinkConstants.alignedLengthOf(nlmsghdr.nlmsg_len); + payloadLength -= StructNlMsgHdr.STRUCT_SIZE; + if (payloadLength < 0 || payloadLength > byteBuffer.remaining()) { + // Malformed message or runt buffer. Pretend the buffer was consumed. + byteBuffer.position(byteBuffer.limit()); + return null; + } + + switch (nlmsghdr.nlmsg_type) { + //case NetlinkConstants.NLMSG_NOOP: + case NetlinkConstants.NLMSG_ERROR: + return (NetlinkMessage) NetlinkErrorMessage.parse(byteBuffer); + case NetlinkConstants.NLMSG_DONE: + byteBuffer.position(byteBuffer.position() + payloadLength); + return new NetlinkMessage(nlmsghdr); + //case NetlinkConstants.NLMSG_OVERRUN: + case NetlinkConstants.RTM_NEWNEIGH: + case NetlinkConstants.RTM_DELNEIGH: + case NetlinkConstants.RTM_GETNEIGH: + return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer); + default: + if (nlmsghdr.nlmsg_type <= NetlinkConstants.NLMSG_MAX_RESERVED) { + // Netlink control message. Just parse the header for now, + // pretending the whole message was consumed. + byteBuffer.position(byteBuffer.position() + payloadLength); + return new NetlinkMessage(nlmsghdr); + } + return null; + } + } + + protected StructNlMsgHdr mHeader; + + public NetlinkMessage(StructNlMsgHdr nlmsghdr) { + mHeader = nlmsghdr; + } + + public StructNlMsgHdr getHeader() { + return mHeader; + } + + @Override + public String toString() { + return "NetlinkMessage{" + (mHeader == null ? "" : mHeader.toString()) + "}"; + } +} diff --git a/core/java/android/net/netlink/NetlinkSocket.java b/core/java/android/net/netlink/NetlinkSocket.java new file mode 100644 index 0000000..657d48c --- /dev/null +++ b/core/java/android/net/netlink/NetlinkSocket.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.system.ErrnoException; +import android.system.NetlinkSocketAddress; +import android.system.Os; +import android.system.OsConstants; +import android.system.StructTimeval; +import android.util.Log; +import libcore.io.IoUtils; +import libcore.io.Libcore; + +import java.io.Closeable; +import java.io.FileDescriptor; +import java.io.InterruptedIOException; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + + +/** + * NetlinkSocket + * + * A small wrapper class to assist with AF_NETLINK socket operations. + * + * @hide + */ +public class NetlinkSocket implements Closeable { + private static final String TAG = "NetlinkSocket"; + private static final int SOCKET_RECV_BUFSIZE = 64 * 1024; + private static final int DEFAULT_RECV_BUFSIZE = 8 * 1024; + + final private FileDescriptor mDescriptor; + private NetlinkSocketAddress mAddr; + private long mLastRecvTimeoutMs; + private long mLastSendTimeoutMs; + + public NetlinkSocket(int nlProto) throws ErrnoException { + mDescriptor = Os.socket( + OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto); + + Libcore.os.setsockoptInt( + mDescriptor, OsConstants.SOL_SOCKET, + OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE); + } + + public NetlinkSocketAddress getLocalAddress() throws ErrnoException { + return (NetlinkSocketAddress) Os.getsockname(mDescriptor); + } + + public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException { + Os.bind(mDescriptor, (SocketAddress)localAddr); + } + + public void connectTo(NetlinkSocketAddress peerAddr) + throws ErrnoException, SocketException { + Os.connect(mDescriptor, (SocketAddress) peerAddr); + } + + public void connectToKernel() throws ErrnoException, SocketException { + connectTo(new NetlinkSocketAddress(0, 0)); + } + + /** + * Wait indefinitely (or until underlying socket error) for a + * netlink message of at most DEFAULT_RECV_BUFSIZE size. + */ + public ByteBuffer recvMessage() + throws ErrnoException, InterruptedIOException { + return recvMessage(DEFAULT_RECV_BUFSIZE, 0); + } + + /** + * Wait up to |timeoutMs| (or until underlying socket error) for a + * netlink message of at most DEFAULT_RECV_BUFSIZE size. + */ + public ByteBuffer recvMessage(long timeoutMs) throws ErrnoException, InterruptedIOException { + return recvMessage(DEFAULT_RECV_BUFSIZE, timeoutMs); + } + + private void checkTimeout(long timeoutMs) { + if (timeoutMs < 0) { + throw new IllegalArgumentException("Negative timeouts not permitted"); + } + } + + /** + * Wait up to |timeoutMs| (or until underlying socket error) for a + * netlink message of at most |bufsize| size. + * + * Multi-threaded calls with different timeouts will cause unexpected results. + */ + public ByteBuffer recvMessage(int bufsize, long timeoutMs) + throws ErrnoException, IllegalArgumentException, InterruptedIOException { + checkTimeout(timeoutMs); + + synchronized (mDescriptor) { + if (mLastRecvTimeoutMs != timeoutMs) { + Os.setsockoptTimeval(mDescriptor, + OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO, + StructTimeval.fromMillis(timeoutMs)); + mLastRecvTimeoutMs = timeoutMs; + } + } + + ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize); + int length = Os.read(mDescriptor, byteBuffer); + if (length == bufsize) { + Log.w(TAG, "maximum read"); + } + byteBuffer.position(0); + byteBuffer.limit(length); + byteBuffer.order(ByteOrder.nativeOrder()); + return byteBuffer; + } + + /** + * Send a message to a peer to which this socket has previously connected. + * + * This blocks until completion or an error occurs. + */ + public boolean sendMessage(byte[] bytes, int offset, int count) + throws ErrnoException, InterruptedIOException { + return sendMessage(bytes, offset, count, 0); + } + + /** + * Send a message to a peer to which this socket has previously connected, + * waiting at most |timeoutMs| milliseconds for the send to complete. + * + * Multi-threaded calls with different timeouts will cause unexpected results. + */ + public boolean sendMessage(byte[] bytes, int offset, int count, long timeoutMs) + throws ErrnoException, IllegalArgumentException, InterruptedIOException { + checkTimeout(timeoutMs); + + synchronized (mDescriptor) { + if (mLastSendTimeoutMs != timeoutMs) { + Os.setsockoptTimeval(mDescriptor, + OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO, + StructTimeval.fromMillis(timeoutMs)); + mLastSendTimeoutMs = timeoutMs; + } + } + + return (count == Os.write(mDescriptor, bytes, offset, count)); + } + + @Override + public void close() { + IoUtils.closeQuietly(mDescriptor); + } +} diff --git a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java new file mode 100644 index 0000000..d4b572c --- /dev/null +++ b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.StructNdaCacheInfo; +import android.net.netlink.StructNdMsg; +import android.net.netlink.StructNlAttr; +import android.net.netlink.StructNlMsgHdr; +import android.net.netlink.NetlinkMessage; +import android.util.Log; + +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + + +/** + * A NetlinkMessage subclass for netlink error messages. + * + * see also: <linux_src>/include/uapi/linux/neighbour.h + * + * @hide + */ +public class RtNetlinkNeighborMessage extends NetlinkMessage { + public static final short NDA_UNSPEC = 0; + public static final short NDA_DST = 1; + public static final short NDA_LLADDR = 2; + public static final short NDA_CACHEINFO = 3; + public static final short NDA_PROBES = 4; + public static final short NDA_VLAN = 5; + public static final short NDA_PORT = 6; + public static final short NDA_VNI = 7; + public static final short NDA_IFINDEX = 8; + public static final short NDA_MASTER = 9; + + private static StructNlAttr findNextAttrOfType(short attrType, ByteBuffer byteBuffer) { + while (byteBuffer != null && byteBuffer.remaining() > 0) { + final StructNlAttr nlAttr = StructNlAttr.peek(byteBuffer); + if (nlAttr == null) { + break; + } + if (nlAttr.nla_type == attrType) { + return StructNlAttr.parse(byteBuffer); + } + if (byteBuffer.remaining() < nlAttr.getAlignedLength()) { + break; + } + byteBuffer.position(byteBuffer.position() + nlAttr.getAlignedLength()); + } + return null; + } + + public static RtNetlinkNeighborMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { + final RtNetlinkNeighborMessage neighMsg = new RtNetlinkNeighborMessage(header); + + neighMsg.mNdmsg = StructNdMsg.parse(byteBuffer); + if (neighMsg.mNdmsg == null) { + return null; + } + + // Some of these are message-type dependent, and not always present. + final int baseOffset = byteBuffer.position(); + StructNlAttr nlAttr = findNextAttrOfType(NDA_DST, byteBuffer); + if (nlAttr != null) { + neighMsg.mDestination = nlAttr.getValueAsInetAddress(); + } + + byteBuffer.position(baseOffset); + nlAttr = findNextAttrOfType(NDA_LLADDR, byteBuffer); + if (nlAttr != null) { + neighMsg.mLinkLayerAddr = nlAttr.nla_value; + } + + byteBuffer.position(baseOffset); + nlAttr = findNextAttrOfType(NDA_PROBES, byteBuffer); + if (nlAttr != null) { + neighMsg.mNumProbes = nlAttr.getValueAsInt(0); + } + + byteBuffer.position(baseOffset); + nlAttr = findNextAttrOfType(NDA_CACHEINFO, byteBuffer); + if (nlAttr != null) { + neighMsg.mCacheInfo = StructNdaCacheInfo.parse(nlAttr.getValueAsByteBuffer()); + } + + final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE; + final int kAdditionalSpace = NetlinkConstants.alignedLengthOf( + neighMsg.mHeader.nlmsg_len - kMinConsumed); + if (byteBuffer.remaining() < kAdditionalSpace) { + byteBuffer.position(byteBuffer.limit()); + } else { + byteBuffer.position(baseOffset + kAdditionalSpace); + } + + return neighMsg; + } + + /** + * A convenience method to create an RTM_GETNEIGH request message. + */ + public static byte[] newGetNeighborsRequest(int seqNo) { + final int length = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE; + final byte[] bytes = new byte[length]; + final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + byteBuffer.order(ByteOrder.nativeOrder()); + + final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr(); + nlmsghdr.nlmsg_len = length; + nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETNEIGH; + nlmsghdr.nlmsg_flags = StructNlMsgHdr.NLM_F_REQUEST|StructNlMsgHdr.NLM_F_DUMP; + nlmsghdr.nlmsg_seq = seqNo; + nlmsghdr.pack(byteBuffer); + + final StructNdMsg ndmsg = new StructNdMsg(); + ndmsg.pack(byteBuffer); + + return bytes; + } + + private StructNdMsg mNdmsg; + private InetAddress mDestination; + private byte[] mLinkLayerAddr; + private int mNumProbes; + private StructNdaCacheInfo mCacheInfo; + + private RtNetlinkNeighborMessage(StructNlMsgHdr header) { + super(header); + mNdmsg = null; + mDestination = null; + mLinkLayerAddr = null; + mNumProbes = 0; + mCacheInfo = null; + } + + public StructNdMsg getNdHeader() { + return mNdmsg; + } + + public InetAddress getDestination() { + return mDestination; + } + + public byte[] getLinkLayerAddress() { + return mLinkLayerAddr; + } + + public int getProbes() { + return mNumProbes; + } + + public StructNdaCacheInfo getCacheInfo() { + return mCacheInfo; + } + + @Override + public String toString() { + final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress(); + return "RtNetlinkNeighborMessage{ " + + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, " + + "ndmsg{" + (mNdmsg == null ? "" : mNdmsg.toString()) + "}, " + + "destination{" + ipLiteral + "} " + + "linklayeraddr{" + NetlinkConstants.hexify(mLinkLayerAddr) + "} " + + "probes{" + mNumProbes + "} " + + "cacheinfo{" + (mCacheInfo == null ? "" : mCacheInfo.toString()) + "} " + + "}"; + } +} diff --git a/core/java/android/net/netlink/StructNdMsg.java b/core/java/android/net/netlink/StructNdMsg.java new file mode 100644 index 0000000..e66d45d --- /dev/null +++ b/core/java/android/net/netlink/StructNdMsg.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.NetlinkConstants; +import android.system.OsConstants; +import java.nio.ByteBuffer; + + +/** + * struct ndmsg + * + * see: <linux_src>/include/uapi/linux/neighbour.h + * + * @hide + */ +public class StructNdMsg { + // Already aligned. + public static final int STRUCT_SIZE = 12; + + // Neighbor Cache Entry States + public static final short NUD_INCOMPLETE = 0x01; + public static final short NUD_REACHABLE = 0x02; + public static final short NUD_STALE = 0x04; + public static final short NUD_DELAY = 0x08; + public static final short NUD_PROBE = 0x10; + public static final short NUD_FAILED = 0x20; + public static final short NUD_NOARP = 0x40; + public static final short NUD_PERMANENT = 0x80; + + public static String stringForNudState(short nudState) { + switch (nudState) { + case NUD_INCOMPLETE: return "NUD_INCOMPLETE"; + case NUD_REACHABLE: return "NUD_REACHABLE"; + case NUD_STALE: return "NUD_STALE"; + case NUD_DELAY: return "NUD_DELAY"; + case NUD_PROBE: return "NUD_PROBE"; + case NUD_FAILED: return "NUD_FAILED"; + case NUD_NOARP: return "NUD_NOARP"; + case NUD_PERMANENT: return "NUD_PERMANENT"; + default: + return "unknown NUD state: " + String.valueOf(nudState); + } + } + + public static boolean isNudStateConnected(short nudState) { + return ((nudState & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)) != 0); + } + + // Neighbor Cache Entry Flags + public static byte NTF_USE = (byte) 0x01; + public static byte NTF_SELF = (byte) 0x02; + public static byte NTF_MASTER = (byte) 0x04; + public static byte NTF_PROXY = (byte) 0x08; + public static byte NTF_ROUTER = (byte) 0x80; + + public static String stringForNudFlags(byte flags) { + final StringBuilder sb = new StringBuilder(); + if ((flags & NTF_USE) != 0) { + sb.append("NTF_USE"); + } + if ((flags & NTF_SELF) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NTF_SELF"); + } + if ((flags & NTF_MASTER) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NTF_MASTER"); + } + if ((flags & NTF_PROXY) != 0) { + if (sb.length() > 0) { sb.append("|"); + } + sb.append("NTF_PROXY"); } + if ((flags & NTF_ROUTER) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NTF_ROUTER"); + } + return sb.toString(); + } + + private static boolean hasAvailableSpace(ByteBuffer byteBuffer) { + return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE; + } + + public static StructNdMsg parse(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return null; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the possible + // exception of usage within unittests. + final StructNdMsg struct = new StructNdMsg(); + struct.ndm_family = byteBuffer.get(); + final byte pad1 = byteBuffer.get(); + final short pad2 = byteBuffer.getShort(); + struct.ndm_ifindex = byteBuffer.getInt(); + struct.ndm_state = byteBuffer.getShort(); + struct.ndm_flags = byteBuffer.get(); + struct.ndm_type = byteBuffer.get(); + return struct; + } + + public byte ndm_family; + public int ndm_ifindex; + public short ndm_state; + public byte ndm_flags; + public byte ndm_type; + + public StructNdMsg() { + ndm_family = (byte) OsConstants.AF_UNSPEC; + } + + public boolean pack(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return false; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the exception + // of usage within unittests. + byteBuffer.put(ndm_family); + byteBuffer.put((byte) 0); // pad1 + byteBuffer.putShort((short) 0); // pad2 + byteBuffer.putInt(ndm_ifindex); + byteBuffer.putShort(ndm_state); + byteBuffer.put(ndm_flags); + byteBuffer.put(ndm_type); + return true; + } + + public boolean nudConnected() { + return isNudStateConnected(ndm_state); + } + + public boolean nudValid() { + return (nudConnected() || ((ndm_state & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0)); + } + + @Override + public String toString() { + final String stateStr = "" + ndm_state + " (" + stringForNudState(ndm_state) + ")"; + final String flagsStr = "" + ndm_flags + " (" + stringForNudFlags(ndm_flags) + ")"; + return "StructNdMsg{ " + + "family{" + NetlinkConstants.stringForAddressFamily((int) ndm_family) + "}, " + + "ifindex{" + ndm_ifindex + "}, " + + "state{" + stateStr + "}, " + + "flags{" + flagsStr + "}, " + + "type{" + ndm_type + "} " + + "}"; + } +} diff --git a/core/java/android/net/netlink/StructNdaCacheInfo.java b/core/java/android/net/netlink/StructNdaCacheInfo.java new file mode 100644 index 0000000..16cf563 --- /dev/null +++ b/core/java/android/net/netlink/StructNdaCacheInfo.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.system.Os; +import android.system.OsConstants; + +import java.nio.ByteBuffer; + + +/** + * struct nda_cacheinfo + * + * see: <linux_src>/include/uapi/linux/neighbour.h + * + * @hide + */ +public class StructNdaCacheInfo { + // Already aligned. + public static final int STRUCT_SIZE = 16; + + private static boolean hasAvailableSpace(ByteBuffer byteBuffer) { + return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE; + } + + public static StructNdaCacheInfo parse(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return null; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the possible + // exception of usage within unittests. + final StructNdaCacheInfo struct = new StructNdaCacheInfo(); + struct.ndm_used = byteBuffer.getInt(); + struct.ndm_confirmed = byteBuffer.getInt(); + struct.ndm_updated = byteBuffer.getInt(); + struct.ndm_refcnt = byteBuffer.getInt(); + return struct; + } + + // TODO: investigate whether this can change during device runtime and + // decide what (if anything) should be done about that. + private static final long CLOCK_TICKS_PER_SECOND = Os.sysconf(OsConstants._SC_CLK_TCK); + + private static long ticksToMilliSeconds(int intClockTicks) { + final long longClockTicks = (long) intClockTicks & 0xffffffff; + return (longClockTicks * 1000) / CLOCK_TICKS_PER_SECOND; + } + + /** + * Explanatory notes, for reference. + * + * Before being returned to user space, the neighbor entry times are + * converted to clock_t's like so: + * + * ndm_used = jiffies_to_clock_t(now - neigh->used); + * ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed); + * ndm_updated = jiffies_to_clock_t(now - neigh->updated); + * + * meaning that these values are expressed as "clock ticks ago". To + * convert these clock ticks to seconds divide by sysconf(_SC_CLK_TCK). + * When _SC_CLK_TCK is 100, for example, the ndm_* times are expressed + * in centiseconds. + * + * These values are unsigned, but fortunately being expressed as "some + * clock ticks ago", these values are typically very small (and + * 2^31 centiseconds = 248 days). + * + * By observation, it appears that: + * ndm_used: the last time ARP/ND took place for this neighbor + * ndm_confirmed: the last time ARP/ND succeeded for this neighbor OR + * higher layer confirmation (TCP or MSG_CONFIRM) + * was received + * ndm_updated: the time when the current NUD state was entered + */ + public int ndm_used; + public int ndm_confirmed; + public int ndm_updated; + public int ndm_refcnt; + + public StructNdaCacheInfo() {} + + public long lastUsed() { + return ticksToMilliSeconds(ndm_used); + } + + public long lastConfirmed() { + return ticksToMilliSeconds(ndm_confirmed); + } + + public long lastUpdated() { + return ticksToMilliSeconds(ndm_updated); + } + + @Override + public String toString() { + return "NdaCacheInfo{ " + + "ndm_used{" + lastUsed() + "}, " + + "ndm_confirmed{" + lastConfirmed() + "}, " + + "ndm_updated{" + lastUpdated() + "}, " + + "ndm_refcnt{" + ndm_refcnt + "} " + + "}"; + } +} diff --git a/core/java/android/net/netlink/StructNlAttr.java b/core/java/android/net/netlink/StructNlAttr.java new file mode 100644 index 0000000..9aef4c7 --- /dev/null +++ b/core/java/android/net/netlink/StructNlAttr.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.NetlinkConstants; +import libcore.io.SizeOf; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; + + +/** + * struct nlattr + * + * see: <linux_src>/include/uapi/linux/netlink.h + * + * @hide + */ +public class StructNlAttr { + // Already aligned. + public static final int NLA_HEADERLEN = 4; + + // Return a (length, type) object only, without consuming any bytes in + // |byteBuffer| and without copying or interpreting any value bytes. + // This is used for scanning over a packed set of struct nlattr's, + // looking for instances of a particular type. + public static StructNlAttr peek(ByteBuffer byteBuffer) { + if (byteBuffer == null || byteBuffer.remaining() < NLA_HEADERLEN) { + return null; + } + final int baseOffset = byteBuffer.position(); + + final StructNlAttr struct = new StructNlAttr(); + struct.nla_len = byteBuffer.getShort(); + struct.nla_type = byteBuffer.getShort(); + struct.mByteOrder = byteBuffer.order(); + + byteBuffer.position(baseOffset); + if (struct.nla_len < NLA_HEADERLEN) { + // Malformed. + return null; + } + return struct; + } + + public static StructNlAttr parse(ByteBuffer byteBuffer) { + final StructNlAttr struct = peek(byteBuffer); + if (struct == null || byteBuffer.remaining() < struct.getAlignedLength()) { + return null; + } + + final int baseOffset = byteBuffer.position(); + byteBuffer.position(baseOffset + NLA_HEADERLEN); + + int valueLen = ((int) struct.nla_len) & 0xffff; + valueLen -= NLA_HEADERLEN; + if (valueLen > 0) { + struct.nla_value = new byte[valueLen]; + byteBuffer.get(struct.nla_value, 0, valueLen); + byteBuffer.position(baseOffset + struct.getAlignedLength()); + } + return struct; + } + + public short nla_len; + public short nla_type; + public byte[] nla_value; + public ByteOrder mByteOrder; + + public StructNlAttr() { + mByteOrder = ByteOrder.nativeOrder(); + } + + public int getAlignedLength() { + return NetlinkConstants.alignedLengthOf(nla_len); + } + + public ByteBuffer getValueAsByteBuffer() { + if (nla_value == null) { return null; } + final ByteBuffer byteBuffer = ByteBuffer.wrap(nla_value); + byteBuffer.order(mByteOrder); + return byteBuffer; + } + + public int getValueAsInt(int defaultValue) { + final ByteBuffer byteBuffer = getValueAsByteBuffer(); + if (byteBuffer == null || byteBuffer.remaining() != SizeOf.INT) { + return defaultValue; + } + return getValueAsByteBuffer().getInt(); + } + + public InetAddress getValueAsInetAddress() { + if (nla_value == null) { return null; } + + try { + return InetAddress.getByAddress(nla_value); + } catch (UnknownHostException ignored) { + return null; + } + } + + @Override + public String toString() { + return "StructNlAttr{ " + + "nla_len{" + nla_len + "}, " + + "nla_type{" + nla_type + "}, " + + "nla_value{" + NetlinkConstants.hexify(nla_value) + "}, " + + "}"; + } +} diff --git a/core/java/android/net/netlink/StructNlMsgErr.java b/core/java/android/net/netlink/StructNlMsgErr.java new file mode 100644 index 0000000..5da19a2 --- /dev/null +++ b/core/java/android/net/netlink/StructNlMsgErr.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.NetlinkConstants; +import android.net.netlink.StructNlMsgHdr; +import libcore.io.SizeOf; + +import java.nio.ByteBuffer; + + +/** + * struct nlmsgerr + * + * see <linux_src>/include/uapi/linux/netlink.h + * + * @hide + */ +public class StructNlMsgErr { + public static final int STRUCT_SIZE = SizeOf.INT + StructNlMsgHdr.STRUCT_SIZE; + + public static boolean hasAvailableSpace(ByteBuffer byteBuffer) { + return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE; + } + + public static StructNlMsgErr parse(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return null; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the exception + // of usage within unittests. + final StructNlMsgErr struct = new StructNlMsgErr(); + struct.error = byteBuffer.getInt(); + struct.msg = StructNlMsgHdr.parse(byteBuffer); + return struct; + } + + public int error; + public StructNlMsgHdr msg; + + public StructNlMsgErr() { + error = 0; + msg = null; + } + + public boolean pack(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return false; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the possible + // exception of usage within unittests. + byteBuffer.putInt(error); + if (msg != null) { + msg.pack(byteBuffer); + } + return true; + } + + @Override + public String toString() { + return "StructNlMsgErr{ " + + "error{" + error + "}, " + + "msg{" + (msg == null ? "" : msg.toString()) + "} " + + "}"; + } +} diff --git a/core/java/android/net/netlink/StructNlMsgHdr.java b/core/java/android/net/netlink/StructNlMsgHdr.java new file mode 100644 index 0000000..67925ac --- /dev/null +++ b/core/java/android/net/netlink/StructNlMsgHdr.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.NetlinkConstants; +import java.nio.ByteBuffer; + + +/** + * struct nlmsghdr + * + * see <linux_src>/include/uapi/linux/netlink.h + * + * @hide + */ +public class StructNlMsgHdr { + // Already aligned. + public static final int STRUCT_SIZE = 16; + + public static final short NLM_F_REQUEST = 0x0001; + public static final short NLM_F_MULTI = 0x0002; + public static final short NLM_F_ACK = 0x0004; + public static final short NLM_F_ECHO = 0x0008; + // Flags for a GET request. + public static final short NLM_F_ROOT = 0x0100; + public static final short NLM_F_MATCH = 0x0200; + public static final short NLM_F_DUMP = NLM_F_ROOT|NLM_F_MATCH; + + public static String stringForNlMsgFlags(short flags) { + final StringBuilder sb = new StringBuilder(); + if ((flags & NLM_F_REQUEST) != 0) { + sb.append("NLM_F_REQUEST"); + } + if ((flags & NLM_F_MULTI) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NLM_F_MULTI"); + } + if ((flags & NLM_F_ACK) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NLM_F_ACK"); + } + if ((flags & NLM_F_ECHO) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NLM_F_ECHO"); + } + if ((flags & NLM_F_ROOT) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NLM_F_ROOT"); + } + if ((flags & NLM_F_MATCH) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NLM_F_MATCH"); + } + return sb.toString(); + } + + public static boolean hasAvailableSpace(ByteBuffer byteBuffer) { + return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE; + } + + public static StructNlMsgHdr parse(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return null; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the exception + // of usage within unittests. + final StructNlMsgHdr struct = new StructNlMsgHdr(); + struct.nlmsg_len = byteBuffer.getInt(); + struct.nlmsg_type = byteBuffer.getShort(); + struct.nlmsg_flags = byteBuffer.getShort(); + struct.nlmsg_seq = byteBuffer.getInt(); + struct.nlmsg_pid = byteBuffer.getInt(); + + if (struct.nlmsg_len < STRUCT_SIZE) { + // Malformed. + return null; + } + return struct; + } + + public int nlmsg_len; + public short nlmsg_type; + public short nlmsg_flags; + public int nlmsg_seq; + public int nlmsg_pid; + + public StructNlMsgHdr() { + nlmsg_len = 0; + nlmsg_type = 0; + nlmsg_flags = 0; + nlmsg_seq = 0; + nlmsg_pid = 0; + } + + public boolean pack(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return false; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the possible + // exception of usage within unittests. + byteBuffer.putInt(nlmsg_len); + byteBuffer.putShort(nlmsg_type); + byteBuffer.putShort(nlmsg_flags); + byteBuffer.putInt(nlmsg_seq); + byteBuffer.putInt(nlmsg_pid); + return true; + } + + @Override + public String toString() { + final String typeStr = "" + nlmsg_type + + "(" + NetlinkConstants.stringForNlMsgType(nlmsg_type) + ")"; + final String flagsStr = "" + nlmsg_flags + + "(" + stringForNlMsgFlags(nlmsg_flags) + ")"; + return "StructNlMsgHdr{ " + + "nlmsg_len{" + nlmsg_len + "}, " + + "nlmsg_type{" + typeStr + "}, " + + "nlmsg_flags{" + flagsStr + ")}, " + + "nlmsg_seq{" + nlmsg_seq + "}, " + + "nlmsg_pid{" + nlmsg_pid + "} " + + "}"; + } +} diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 4aff7a1..19c8fa9 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -169,6 +169,65 @@ public final class Debug public int otherSwappedOut; /** @hide */ + public static final int HEAP_UNKNOWN = 0; + /** @hide */ + public static final int HEAP_DALVIK = 1; + /** @hide */ + public static final int HEAP_NATIVE = 2; + + /** @hide */ + public static final int OTHER_DALVIK_OTHER = 0; + /** @hide */ + public static final int OTHER_STACK = 1; + /** @hide */ + public static final int OTHER_CURSOR = 2; + /** @hide */ + public static final int OTHER_ASHMEM = 3; + /** @hide */ + public static final int OTHER_GL_DEV = 4; + /** @hide */ + public static final int OTHER_UNKNOWN_DEV = 5; + /** @hide */ + public static final int OTHER_SO = 6; + /** @hide */ + public static final int OTHER_JAR = 7; + /** @hide */ + public static final int OTHER_APK = 8; + /** @hide */ + public static final int OTHER_TTF = 9; + /** @hide */ + public static final int OTHER_DEX = 10; + /** @hide */ + public static final int OTHER_OAT = 11; + /** @hide */ + public static final int OTHER_ART = 12; + /** @hide */ + public static final int OTHER_UNKNOWN_MAP = 13; + /** @hide */ + public static final int OTHER_GRAPHICS = 14; + /** @hide */ + public static final int OTHER_GL = 15; + /** @hide */ + public static final int OTHER_OTHER_MEMTRACK = 16; + + /** @hide */ + public static final int OTHER_DALVIK_NORMAL = 17; + /** @hide */ + public static final int OTHER_DALVIK_LARGE = 18; + /** @hide */ + public static final int OTHER_DALVIK_LINEARALLOC = 19; + /** @hide */ + public static final int OTHER_DALVIK_ACCOUNTING = 20; + /** @hide */ + public static final int OTHER_DALVIK_CODE_CACHE = 21; + /** @hide */ + public static final int OTHER_DALVIK_ZYGOTE = 22; + /** @hide */ + public static final int OTHER_DALVIK_NON_MOVING = 23; + /** @hide */ + public static final int OTHER_DALVIK_INDIRECT_REFERENCE_TABLE = 24; + + /** @hide */ public static final int NUM_OTHER_STATS = 17; /** @hide */ @@ -284,6 +343,11 @@ public final class Debug } /** @hide */ + public int getOtherPrivate(int which) { + return getOtherPrivateClean(which) + getOtherPrivateDirty(which); + } + + /** @hide */ public int getOtherSharedClean(int which) { return otherStats[which*NUM_CATEGORIES + offsetSharedClean]; } @@ -296,35 +360,157 @@ public final class Debug /** @hide */ public static String getOtherLabel(int which) { switch (which) { - case 0: return "Dalvik Other"; - case 1: return "Stack"; - case 2: return "Cursor"; - case 3: return "Ashmem"; - case 4: return "Gfx dev"; - case 5: return "Other dev"; - case 6: return ".so mmap"; - case 7: return ".jar mmap"; - case 8: return ".apk mmap"; - case 9: return ".ttf mmap"; - case 10: return ".dex mmap"; - case 11: return ".oat mmap"; - case 12: return ".art mmap"; - case 13: return "Other mmap"; - case 14: return "EGL mtrack"; - case 15: return "GL mtrack"; - case 16: return "Other mtrack"; - case 17: return ".Heap"; - case 18: return ".LOS"; - case 19: return ".LinearAlloc"; - case 20: return ".GC"; - case 21: return ".JITCache"; - case 22: return ".Zygote"; - case 23: return ".NonMoving"; - case 24: return ".IndirectRef"; + case OTHER_DALVIK_OTHER: return "Dalvik Other"; + case OTHER_STACK: return "Stack"; + case OTHER_CURSOR: return "Cursor"; + case OTHER_ASHMEM: return "Ashmem"; + case OTHER_GL_DEV: return "Gfx dev"; + case OTHER_UNKNOWN_DEV: return "Other dev"; + case OTHER_SO: return ".so mmap"; + case OTHER_JAR: return ".jar mmap"; + case OTHER_APK: return ".apk mmap"; + case OTHER_TTF: return ".ttf mmap"; + case OTHER_DEX: return ".dex mmap"; + case OTHER_OAT: return ".oat mmap"; + case OTHER_ART: return ".art mmap"; + case OTHER_UNKNOWN_MAP: return "Other mmap"; + case OTHER_GRAPHICS: return "EGL mtrack"; + case OTHER_GL: return "GL mtrack"; + case OTHER_OTHER_MEMTRACK: return "Other mtrack"; + case OTHER_DALVIK_NORMAL: return ".Heap"; + case OTHER_DALVIK_LARGE: return ".LOS"; + case OTHER_DALVIK_LINEARALLOC: return ".LinearAlloc"; + case OTHER_DALVIK_ACCOUNTING: return ".GC"; + case OTHER_DALVIK_CODE_CACHE: return ".JITCache"; + case OTHER_DALVIK_ZYGOTE: return ".Zygote"; + case OTHER_DALVIK_NON_MOVING: return ".NonMoving"; + case OTHER_DALVIK_INDIRECT_REFERENCE_TABLE: return ".IndirectRef"; default: return "????"; } } + /** + * Pss of Java Heap bytes in KB due to the application. + * Notes: + * * OTHER_ART is the boot image. Anything private here is blamed on + * the application, not the system. + * * dalvikPrivateDirty includes private zygote, which means the + * application dirtied something allocated by the zygote. We blame + * the application for that memory, not the system. + * * Does not include OTHER_DALVIK_OTHER, which is considered VM + * Overhead and lumped into Private Other. + * * We don't include dalvikPrivateClean, because there should be no + * such thing as private clean for the Java Heap. + * @hide + */ + public int getSummaryJavaHeap() { + return dalvikPrivateDirty + getOtherPrivate(OTHER_ART); + } + + /** + * Pss of Native Heap bytes in KB due to the application. + * Notes: + * * Includes private dirty malloc space. + * * We don't include nativePrivateClean, because there should be no + * such thing as private clean for the Native Heap. + * @hide + */ + public int getSummaryNativeHeap() { + return nativePrivateDirty; + } + + /** + * Pss of code and other static resource bytes in KB due to + * the application. + * @hide + */ + public int getSummaryCode() { + return getOtherPrivate(OTHER_SO) + + getOtherPrivate(OTHER_JAR) + + getOtherPrivate(OTHER_APK) + + getOtherPrivate(OTHER_TTF) + + getOtherPrivate(OTHER_DEX) + + getOtherPrivate(OTHER_OAT); + } + + /** + * Pss in KB of the stack due to the application. + * Notes: + * * Includes private dirty stack, which includes both Java and Native + * stack. + * * Does not include private clean stack, because there should be no + * such thing as private clean for the stack. + * @hide + */ + public int getSummaryStack() { + return getOtherPrivateDirty(OTHER_STACK); + } + + /** + * Pss in KB of graphics due to the application. + * Notes: + * * Includes private Gfx, EGL, and GL. + * * Warning: These numbers can be misreported by the graphics drivers. + * * We don't include shared graphics. It may make sense to, because + * shared graphics are likely buffers due to the application + * anyway, but it's simpler to implement to just group all shared + * memory into the System category. + * @hide + */ + public int getSummaryGraphics() { + return getOtherPrivate(OTHER_GL_DEV) + + getOtherPrivate(OTHER_GRAPHICS) + + getOtherPrivate(OTHER_GL); + } + + /** + * Pss in KB due to the application that haven't otherwise been + * accounted for. + * @hide + */ + public int getSummaryPrivateOther() { + return getTotalPrivateClean() + + getTotalPrivateDirty() + - getSummaryJavaHeap() + - getSummaryNativeHeap() + - getSummaryCode() + - getSummaryStack() + - getSummaryGraphics(); + } + + /** + * Pss in KB due to the system. + * Notes: + * * Includes all shared memory. + * @hide + */ + public int getSummarySystem() { + return getTotalPss() + - getTotalPrivateClean() + - getTotalPrivateDirty(); + } + + /** + * Total Pss in KB. + * @hide + */ + public int getSummaryTotalPss() { + return getTotalPss(); + } + + /** + * Total Swap in KB. + * Notes: + * * Some of this memory belongs in other categories, but we don't + * know if the Swap memory is shared or private, so we don't know + * what to blame on the application and what on the system. + * For now, just lump all the Swap in one place. + * @hide + */ + public int getSummaryTotalSwap() { + return getTotalSwappedOut(); + } + public int describeContents() { return 0; } diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 021e5e4..917271d 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -528,7 +528,7 @@ public class FileUtils { * {@code /after/foo/bar.txt}. */ public static File rewriteAfterRename(File beforeDir, File afterDir, File file) { - if (file == null) return null; + if (file == null || beforeDir == null || afterDir == null) return null; if (contains(beforeDir, file)) { final String splice = file.getAbsolutePath().substring( beforeDir.getAbsolutePath().length()); diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl index 3cb29ff..602bfea 100644 --- a/core/java/android/os/IDeviceIdleController.aidl +++ b/core/java/android/os/IDeviceIdleController.aidl @@ -23,4 +23,5 @@ interface IDeviceIdleController { String[] getSystemPowerWhitelist(); String[] getFullPowerWhitelist(); int[] getAppIdWhitelist(); + boolean isPowerSaveWhitelistApp(String name); } diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index c2fd3c3..f212daf 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -45,13 +45,12 @@ interface IUserManager { Bundle getUserRestrictions(int userHandle); boolean hasUserRestriction(in String restrictionKey, int userHandle); void setUserRestrictions(in Bundle restrictions, int userHandle); + void setUserRestriction(String key, boolean value, int userId); + void setSystemControlledUserRestriction(String key, boolean value, int userId); void setApplicationRestrictions(in String packageName, in Bundle restrictions, int userHandle); Bundle getApplicationRestrictions(in String packageName); Bundle getApplicationRestrictionsForUser(in String packageName, int userHandle); - boolean setRestrictionsChallenge(in String newPin); - int checkRestrictionsChallenge(in String pin); - boolean hasRestrictionsChallenge(); void removeRestrictions(); void setDefaultGuestRestrictions(in Bundle restrictions); Bundle getDefaultGuestRestrictions(); diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 00ab262..9a0d0d0 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -134,4 +134,6 @@ public abstract class PowerManagerInternal { } public abstract void setDeviceIdleMode(boolean enabled); + + public abstract void setDeviceIdleWhitelist(int[] appids); } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 0b55998..1cc2d33 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -476,8 +476,18 @@ public final class StrictMode { } /** - * Enable detection of mismatches between defined resource types + * Enables detection of mismatches between defined resource types * and getter calls. + * <p> + * This helps detect accidental type mismatches and potentially + * expensive type conversions when obtaining typed resources. + * <p> + * For example, a strict mode violation would be thrown when + * calling {@link android.content.res.TypedArray#getInt(int, int)} + * on an index that contains a String-type resource. If the string + * value can be parsed as an integer, this method call will return + * a value without crashing; however, the developer should format + * the resource as an integer to avoid unnecessary type conversion. */ public Builder detectResourceMismatches() { return enable(DETECT_RESOURCE_MISMATCH); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 44eb1ed..cc37d5e 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -412,6 +412,16 @@ public class UserManager { public static final String DISALLOW_SAFE_BOOT = "no_safe_boot"; /** + * Specifies if a user is not allowed to record audio. This restriction is always enabled for + * background users. The default value is <code>false</code>. + * + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + * @hide + */ + public static final String DISALLOW_RECORD_AUDIO = "no_record_audio"; + + /** * Application restriction key that is used to indicate the pending arrival * of real restrictions for the app. * @@ -688,9 +698,11 @@ public class UserManager { */ @Deprecated public void setUserRestriction(String key, boolean value, UserHandle userHandle) { - Bundle bundle = getUserRestrictions(userHandle); - bundle.putBoolean(key, value); - setUserRestrictions(bundle, userHandle); + try { + mService.setUserRestriction(key, value, userHandle.getIdentifier()); + } catch (RemoteException re) { + Log.w(TAG, "Could not set user restriction", re); + } } /** @@ -744,7 +756,7 @@ public class UserManager { * @see #getSerialNumberForUser(UserHandle) */ public UserHandle getUserForSerialNumber(long serialNumber) { - int ident = getUserHandle((int)serialNumber); + int ident = getUserHandle((int) serialNumber); return ident >= 0 ? new UserHandle(ident) : null; } @@ -1252,49 +1264,10 @@ public class UserManager { * apps and requires the MANAGE_USERS permission. * @param newPin the PIN to use for challenge dialogs. * @return Returns true if the challenge PIN was set successfully. + * @deprecated The restrictions PIN functionality is no longer provided by the system. + * This method is preserved for backwards compatibility reasons and always returns false. */ public boolean setRestrictionsChallenge(String newPin) { - try { - return mService.setRestrictionsChallenge(newPin); - } catch (RemoteException re) { - Log.w(TAG, "Could not change restrictions pin"); - } - return false; - } - - /** - * @hide - * @param pin The PIN to verify, or null to get the number of milliseconds to wait for before - * allowing the user to enter the PIN. - * @return Returns a positive number (including zero) for how many milliseconds before - * you can accept another PIN, when the input is null or the input doesn't match the saved PIN. - * Returns {@link #PIN_VERIFICATION_SUCCESS} if the input matches the saved PIN. Returns - * {@link #PIN_VERIFICATION_FAILED_NOT_SET} if there is no PIN set. - */ - public int checkRestrictionsChallenge(String pin) { - try { - return mService.checkRestrictionsChallenge(pin); - } catch (RemoteException re) { - Log.w(TAG, "Could not check restrictions pin"); - } - return PIN_VERIFICATION_FAILED_INCORRECT; - } - - /** - * @hide - * Checks whether the user has restrictions that are PIN-protected. An application that - * participates in restrictions can check if the owner has requested a PIN challenge for - * any restricted operations. If there is a PIN in effect, the application should launch - * the PIN challenge activity {@link android.content.Intent#ACTION_RESTRICTIONS_CHALLENGE}. - * @see android.content.Intent#ACTION_RESTRICTIONS_CHALLENGE - * @return whether a restrictions PIN is in effect. - */ - public boolean hasRestrictionsChallenge() { - try { - return mService.hasRestrictionsChallenge(); - } catch (RemoteException re) { - Log.w(TAG, "Could not change restrictions pin"); - } return false; } diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index fcde3f4..2b058a8 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -1099,6 +1099,20 @@ public interface IMountService extends IInterface { } @Override + public void forgetAllVolumes() throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + mRemote.transact(Stub.TRANSACTION_forgetAllVolumes, _data, _reply, 0); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + + @Override public String getPrimaryStorageUuid() throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); @@ -1238,9 +1252,10 @@ public interface IMountService extends IInterface { static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 53; static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 54; static final int TRANSACTION_forgetVolume = IBinder.FIRST_CALL_TRANSACTION + 55; + static final int TRANSACTION_forgetAllVolumes = IBinder.FIRST_CALL_TRANSACTION + 56; - static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 56; - static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 57; + static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 57; + static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 58; /** * Cast an IBinder object into an IMountService interface, generating a @@ -1757,6 +1772,12 @@ public interface IMountService extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_forgetAllVolumes: { + data.enforceInterface(DESCRIPTOR); + forgetAllVolumes(); + reply.writeNoException(); + return true; + } case TRANSACTION_getPrimaryStorageUuid: { data.enforceInterface(DESCRIPTOR); String volumeUuid = getPrimaryStorageUuid(); @@ -2075,6 +2096,7 @@ public interface IMountService extends IInterface { public void setVolumeNickname(String fsUuid, String nickname) throws RemoteException; public void setVolumeUserFlags(String fsUuid, int flags, int mask) throws RemoteException; public void forgetVolume(String fsUuid) throws RemoteException; + public void forgetAllVolumes() throws RemoteException; public String getPrimaryStorageUuid() throws RemoteException; public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 3fdabee..8c0bbbf 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -73,6 +73,8 @@ public class StorageManager { /** {@hide} */ public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; /** {@hide} */ + public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable"; + /** {@hide} */ public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; /** {@hide} */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7c3c11b..1f4020d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -809,6 +809,18 @@ public final class Settings { = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; /** + * Activity Action: Show Do Not Disturb access settings. + * <p> + * In some cases, a matching Activity may not exist, so ensure you safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_ZEN_ACCESS_SETTINGS = "android.settings.ZEN_ACCESS_SETTINGS"; + + /** * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @@ -4121,7 +4133,10 @@ public final class Settings { * LocationManager service for testing purposes during application development. These * locations and status values override actual location and status information generated * by network, gps, or other location providers. + * + * @deprecated This settings is not used anymore. */ + @Deprecated public static final String ALLOW_MOCK_LOCATION = "mock_location"; /** @@ -4351,12 +4366,6 @@ public final class Settings { "lock_screen_appwidget_ids"; /** - * List of enrolled fingerprint identifiers (comma-delimited). - * @hide - */ - public static final String USER_FINGERPRINT_IDS = "user_fingerprint_ids"; - - /** * Id of the appwidget shown on the lock screen when appwidgets are disabled. * @hide */ @@ -5505,6 +5514,12 @@ public final class Settings { public static final String APP_IDLE_DURATION = "app_idle_duration"; /** + * Controls whether double tap to wake is enabled. + * @hide + */ + public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake"; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear @@ -5559,7 +5574,8 @@ public final class Settings { MOUNT_UMS_PROMPT, MOUNT_UMS_NOTIFY_ENABLED, UI_NIGHT_MODE, - SLEEP_TIMEOUT + SLEEP_TIMEOUT, + DOUBLE_TAP_TO_WAKE, }; /** diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java index 879f26c..89668c1 100644 --- a/core/java/android/provider/VoicemailContract.java +++ b/core/java/android/provider/VoicemailContract.java @@ -306,6 +306,13 @@ public class VoicemailContract { contentValues.put(Voicemails.SOURCE_PACKAGE, voicemail.getSourcePackage()); contentValues.put(Voicemails.SOURCE_DATA, voicemail.getSourceData()); contentValues.put(Voicemails.IS_READ, voicemail.isRead() ? 1 : 0); + + PhoneAccountHandle phoneAccount = voicemail.getPhoneAccount(); + if (voicemail.getPhoneAccount() != null) { + contentValues.put(Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, + phoneAccount.getComponentName().flattenToString()); + contentValues.put(Voicemails.PHONE_ACCOUNT_ID, phoneAccount.getId()); + } return contentValues; } } diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl index 579cdbe..b0779c0 100644 --- a/core/java/android/security/IKeystoreService.aidl +++ b/core/java/android/security/IKeystoreService.aidl @@ -37,9 +37,9 @@ interface IKeystoreService { int exist(String name, int uid); String[] saw(String namePrefix, int uid); int reset(); - int password(String password); + int onUserPasswordChanged(int userId, String newPassword); int lock(); - int unlock(String password); + int unlock(int userId, String userPassword); int zero(); int generate(String name, int uid, int keyType, int keySize, int flags, in KeystoreArguments args); @@ -75,4 +75,6 @@ interface IKeystoreService { int abort(IBinder handle); boolean isOperationAuthorized(IBinder token); int addAuthToken(in byte[] authToken); + int onUserAdded(int userId, int parentId); + int onUserRemoved(int userId); } diff --git a/core/java/android/service/carrier/CarrierConfigService.java b/core/java/android/service/carrier/CarrierConfigService.java index 1880d16..bc42b6b 100644 --- a/core/java/android/service/carrier/CarrierConfigService.java +++ b/core/java/android/service/carrier/CarrierConfigService.java @@ -16,8 +16,8 @@ package android.service.carrier; import android.app.Service; import android.content.Intent; -import android.os.Bundle; import android.os.IBinder; +import android.os.PersistableBundle; /** * A service that sets carrier configuration for telephony services. @@ -68,16 +68,16 @@ public abstract class CarrierConfigService extends Service { * </p> * <p> * Implementations should use the keys defined in {@link android.telephony.CarrierConfigManager - * CarrierConfigManager}. Any configuration values not set in the returned {@link Bundle} may be - * overridden by the system's default configuration service. + * CarrierConfigManager}. Any configuration values not set in the returned {@link + * PersistableBundle} may be overridden by the system's default configuration service. * </p> * * @param id contains details about the current carrier that can be used do decide what * configuration values to return. - * @return a {@link Bundle} object containing the configuration or null if default values should - * be used. + * @return a {@link PersistableBundle} object containing the configuration or null if default + * values should be used. */ - public abstract Bundle onLoadConfig(CarrierIdentifier id); + public abstract PersistableBundle onLoadConfig(CarrierIdentifier id); /** @hide */ @Override @@ -97,7 +97,7 @@ public abstract class CarrierConfigService extends Service { private class ICarrierConfigServiceWrapper extends ICarrierConfigService.Stub { @Override - public Bundle getCarrierConfig(CarrierIdentifier id) { + public PersistableBundle getCarrierConfig(CarrierIdentifier id) { return CarrierConfigService.this.onLoadConfig(id); } } diff --git a/core/java/android/service/carrier/ICarrierConfigService.aidl b/core/java/android/service/carrier/ICarrierConfigService.aidl index d8390b6..abbc000 100644 --- a/core/java/android/service/carrier/ICarrierConfigService.aidl +++ b/core/java/android/service/carrier/ICarrierConfigService.aidl @@ -16,7 +16,7 @@ package android.service.carrier; -import android.os.Bundle; +import android.os.PersistableBundle; import android.service.carrier.CarrierIdentifier; /** @@ -28,5 +28,5 @@ import android.service.carrier.CarrierIdentifier; interface ICarrierConfigService { /** @see android.service.carrier.CarrierConfigService#onLoadConfig */ - Bundle getCarrierConfig(in CarrierIdentifier id); -}
\ No newline at end of file + PersistableBundle getCarrierConfig(in CarrierIdentifier id); +} diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java index f0ca276..50c435a 100644 --- a/core/java/android/service/chooser/ChooserTarget.java +++ b/core/java/android/service/chooser/ChooserTarget.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.graphics.Bitmap; +import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -34,6 +35,16 @@ import android.util.Log; /** * A ChooserTarget represents a deep-link into an application as returned by a * {@link android.service.chooser.ChooserTargetService}. + * + * <p>A chooser target represents a specific deep link target into an application exposed + * for selection by the user. This might be a frequently emailed contact, a recently active + * group messaging conversation, a folder in a cloud storage app, a collection of related + * items published on a social media service or any other contextually relevant grouping + * of target app + relevant metadata.</p> + * + * <p>Creators of chooser targets should consult the relevant design guidelines for the type + * of target they are presenting. For example, targets involving people should be presented + * with a circular icon.</p> */ public final class ChooserTarget implements Parcelable { private static final String TAG = "ChooserTarget"; @@ -48,7 +59,7 @@ public final class ChooserTarget implements Parcelable { * The icon that will be shown to the user to represent this target. * The system may resize this icon as appropriate. */ - private Bitmap mIcon; + private Icon mIcon; /** * The IntentSender that will be used to deliver the intent to the target. @@ -58,12 +69,6 @@ public final class ChooserTarget implements Parcelable { private IntentSender mIntentSender; /** - * A raw intent provided in lieu of an IntentSender. Will be filled in and sent - * by {@link #sendIntent(Context, Intent)}. - */ - private Intent mIntent; - - /** * The score given to this item. It can be normalized. */ private float mScore; @@ -99,7 +104,7 @@ public final class ChooserTarget implements Parcelable { * @param score ranking score for this target between 0.0f and 1.0f, inclusive * @param pendingIntent PendingIntent to fill in and send if the user chooses this target */ - public ChooserTarget(CharSequence title, Bitmap icon, float score, + public ChooserTarget(CharSequence title, Icon icon, float score, PendingIntent pendingIntent) { this(title, icon, score, pendingIntent.getIntentSender()); } @@ -135,7 +140,7 @@ public final class ChooserTarget implements Parcelable { * @param score ranking score for this target between 0.0f and 1.0f, inclusive * @param intentSender IntentSender to fill in and send if the user chooses this target */ - public ChooserTarget(CharSequence title, Bitmap icon, float score, IntentSender intentSender) { + public ChooserTarget(CharSequence title, Icon icon, float score, IntentSender intentSender) { mTitle = title; mIcon = icon; if (score > 1.f || score < 0.f) { @@ -146,55 +151,15 @@ public final class ChooserTarget implements Parcelable { mIntentSender = intentSender; } - /** - * Construct a deep link target for presentation by a chooser UI. - * - * <p>A target is composed of a title and an icon for presentation to the user. - * The UI presenting this target may truncate the title if it is too long to be presented - * in the available space, as well as crop, resize or overlay the supplied icon.</p> - * - * <p>The creator of a target may supply a ranking score. This score is assumed to be relative - * to the other targets supplied by the same - * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}. - * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match). - * Scores for a set of targets do not need to sum to 1.</p> - * - * <p>Before being sent, the Intent supplied will be - * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied - * to the chooser.</p> - * - * <p>Take care not to place custom {@link android.os.Parcelable} types into - * the Intent as extras, as the system will not be able to unparcel it to merge - * additional extras.</p> - * - * @param title title of this target that will be shown to a user - * @param icon icon to represent this target - * @param score ranking score for this target between 0.0f and 1.0f, inclusive - * @param intent Intent to fill in and send if the user chooses this target - */ - public ChooserTarget(CharSequence title, Bitmap icon, float score, Intent intent) { - mTitle = title; - mIcon = icon; - if (score > 1.f || score < 0.f) { - throw new IllegalArgumentException("Score " + score + " out of range; " - + "must be between 0.0f and 1.0f"); - } - mScore = score; - mIntent = intent; - } - ChooserTarget(Parcel in) { mTitle = in.readCharSequence(); if (in.readInt() != 0) { - mIcon = Bitmap.CREATOR.createFromParcel(in); + mIcon = Icon.CREATOR.createFromParcel(in); } else { mIcon = null; } mScore = in.readFloat(); mIntentSender = IntentSender.readIntentSenderOrNullFromParcel(in); - if (in.readInt() != 0) { - mIntent = Intent.CREATOR.createFromParcel(in); - } } /** @@ -213,7 +178,7 @@ public final class ChooserTarget implements Parcelable { * * @return the icon representing this target, intended to be shown to a user */ - public Bitmap getIcon() { + public Icon getIcon() { return mIcon; } @@ -241,18 +206,6 @@ public final class ChooserTarget implements Parcelable { } /** - * Returns the Intent supplied by the ChooserTarget's creator. - * This may be null if the creator specified an IntentSender or PendingIntent instead. - * - * <p>To fill in and send the intent, see {@link #sendIntent(Context, Intent)}.</p> - * - * @return the Intent supplied by the ChooserTarget's creator - */ - public Intent getIntent() { - return mIntent; - } - - /** * Fill in the IntentSender supplied by the ChooserTarget's creator and send it. * * @param context the sending Context; generally the Activity presenting the chooser UI @@ -272,91 +225,8 @@ public final class ChooserTarget implements Parcelable { Log.e(TAG, "sendIntent " + this + " failed", e); return false; } - } else if (mIntent != null) { - try { - final Intent toSend = new Intent(mIntent); - toSend.fillIn(fillInIntent, 0); - context.startActivity(toSend); - return true; - } catch (Exception e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } } else { - Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); - return false; - } - } - - /** - * Same as {@link #sendIntent(Context, Intent)}, but offers a userId field to use - * for launching the {@link #getIntent() intent} using - * {@link Activity#startActivityAsCaller(Intent, Bundle, int)} if the - * {@link #getIntentSender() IntentSender} is not present. If the IntentSender is present, - * it will be invoked as usual with its own calling identity. - * - * @hide internal use only. - */ - public boolean sendIntentAsCaller(Activity context, Intent fillInIntent, int userId) { - if (fillInIntent != null) { - fillInIntent.migrateExtraStreamToClipData(); - fillInIntent.prepareToLeaveProcess(); - } - if (mIntentSender != null) { - try { - mIntentSender.sendIntent(context, 0, fillInIntent, null, null); - return true; - } catch (IntentSender.SendIntentException e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } - } else if (mIntent != null) { - try { - final Intent toSend = new Intent(mIntent); - toSend.fillIn(fillInIntent, 0); - context.startActivityAsCaller(toSend, null, userId); - return true; - } catch (Exception e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } - } else { - Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); - return false; - } - } - - /** - * The UserHandle is only used if we're launching a raw intent. The IntentSender will be - * launched with its associated identity. - * - * @hide Internal use only - */ - public boolean sendIntentAsUser(Activity context, Intent fillInIntent, UserHandle user) { - if (fillInIntent != null) { - fillInIntent.migrateExtraStreamToClipData(); - fillInIntent.prepareToLeaveProcess(); - } - if (mIntentSender != null) { - try { - mIntentSender.sendIntent(context, 0, fillInIntent, null, null); - return true; - } catch (IntentSender.SendIntentException e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } - } else if (mIntent != null) { - try { - final Intent toSend = new Intent(mIntent); - toSend.fillIn(fillInIntent, 0); - context.startActivityAsUser(toSend, user); - return true; - } catch (Exception e) { - Log.e(TAG, "sendIntent " + this + " failed", e); - return false; - } - } else { - Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); + Log.e(TAG, "sendIntent " + this + " failed - no IntentSender to send"); return false; } } @@ -364,7 +234,7 @@ public final class ChooserTarget implements Parcelable { @Override public String toString() { return "ChooserTarget{" - + (mIntentSender != null ? mIntentSender.getCreatorPackage() : mIntent) + + (mIntentSender != null ? mIntentSender.getCreatorPackage() : null) + ", " + "'" + mTitle + "', " + mScore + "}"; @@ -386,10 +256,6 @@ public final class ChooserTarget implements Parcelable { } dest.writeFloat(mScore); IntentSender.writeIntentSenderOrNullToParcel(mIntentSender, dest); - dest.writeInt(mIntent != null ? 1 : 0); - if (mIntent != null) { - mIntent.writeToParcel(dest, 0); - } } public static final Creator<ChooserTarget> CREATOR diff --git a/core/java/android/service/chooser/ChooserTargetService.java b/core/java/android/service/chooser/ChooserTargetService.java index 699bd0a..0d1834a 100644 --- a/core/java/android/service/chooser/ChooserTargetService.java +++ b/core/java/android/service/chooser/ChooserTargetService.java @@ -107,7 +107,7 @@ public abstract class ChooserTargetService extends Service { * <p>The returned list should be sorted such that the most relevant targets appear first. * Any PendingIntents used to construct the resulting ChooserTargets should always be prepared * to have the relevant data fields filled in by the sender. See - * {@link ChooserTarget#ChooserTarget(CharSequence, android.graphics.Bitmap, float, android.app.PendingIntent) ChooserTarget}.</p> + * {@link ChooserTarget#ChooserTarget(CharSequence, android.graphics.drawable.Icon, float, android.app.PendingIntent) ChooserTarget}.</p> * * <p><em>Important:</em> Calls to this method from other applications will occur on * a binder thread, not on your app's main thread. Make sure that access to relevant data diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 35b8819..7956a3f 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -21,7 +21,7 @@ import android.annotation.SdkConstant; import android.app.INotificationManager; import android.app.Notification; import android.app.Notification.Builder; -import android.app.NotificationManager.Policy; +import android.app.NotificationManager; import android.app.Service; import android.content.ComponentName; import android.content.Context; @@ -64,25 +64,29 @@ public abstract class NotificationListenerService extends Service { * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Normal interruption filter. */ - public static final int INTERRUPTION_FILTER_ALL = 1; + public static final int INTERRUPTION_FILTER_ALL + = NotificationManager.INTERRUPTION_FILTER_ALL; /** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Priority interruption filter. */ - public static final int INTERRUPTION_FILTER_PRIORITY = 2; + public static final int INTERRUPTION_FILTER_PRIORITY + = NotificationManager.INTERRUPTION_FILTER_PRIORITY; /** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * No interruptions filter. */ - public static final int INTERRUPTION_FILTER_NONE = 3; + public static final int INTERRUPTION_FILTER_NONE + = NotificationManager.INTERRUPTION_FILTER_NONE; /** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Alarms only interruption filter. */ - public static final int INTERRUPTION_FILTER_ALARMS = 4; + public static final int INTERRUPTION_FILTER_ALARMS + = NotificationManager.INTERRUPTION_FILTER_ALARMS; /** {@link #getCurrentInterruptionFilter() Interruption filter} constant - returned when * the value is unavailable for any reason. For example, before the notification listener @@ -90,7 +94,8 @@ public abstract class NotificationListenerService extends Service { * * {@see #onListenerConnected()} */ - public static final int INTERRUPTION_FILTER_UNKNOWN = 0; + public static final int INTERRUPTION_FILTER_UNKNOWN + = NotificationManager.INTERRUPTION_FILTER_UNKNOWN; /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI * should disable notification sound, vibrating and other visual or aural effects. @@ -516,22 +521,6 @@ public abstract class NotificationListenerService extends Service { } /** - * Gets the notification policy token associated with this listener. - * - * <p> - * Returns null if this listener is not currently active. - */ - public final Policy.Token getNotificationPolicyToken() { - if (!isBound()) return null; - try { - return getNotificationInterface().getPolicyTokenFromListener(mWrapper); - } catch (android.os.RemoteException ex) { - Log.v(TAG, "Unable to contact notification manager", ex); - return null; - } - } - - /** * Sets the desired {@link #getCurrentListenerHints() listener hints}. * * <p> diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 26bd10f..1e42913 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -538,7 +538,7 @@ public class ZenModeConfig implements Parcelable { } priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders); priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders); - return new Policy(priorityCategories, priorityCallSenders); + return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders); } private static int sourceToPrioritySenders(int source, int def) { @@ -567,28 +567,40 @@ public class ZenModeConfig implements Parcelable { allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0; - allowCallsFrom = prioritySendersToSource(policy.prioritySenders, allowCallsFrom); + allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom); + allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders, + allowMessagesFrom); } public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) { + return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/); + } + + public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle, + boolean shortVersion) { final long now = System.currentTimeMillis(); final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; - return toTimeCondition(context, now + millis, minutesFromNow, now, userHandle); + return toTimeCondition(context, now + millis, minutesFromNow, now, userHandle, + shortVersion); } public static Condition toTimeCondition(Context context, long time, int minutes, long now, - int userHandle) { + int userHandle, boolean shortVersion) { final int num, summaryResId, line1ResId; if (minutes < 60) { // display as minutes num = minutes; - summaryResId = R.plurals.zen_mode_duration_minutes_summary; - line1ResId = R.plurals.zen_mode_duration_minutes; + summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short + : R.plurals.zen_mode_duration_minutes_summary; + line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short + : R.plurals.zen_mode_duration_minutes; } else { // display as hours num = Math.round(minutes / 60f); - summaryResId = com.android.internal.R.plurals.zen_mode_duration_hours_summary; - line1ResId = com.android.internal.R.plurals.zen_mode_duration_hours; + summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short + : R.plurals.zen_mode_duration_hours_summary; + line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short + : R.plurals.zen_mode_duration_hours; } final String skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"; final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); @@ -733,17 +745,20 @@ public class ZenModeConfig implements Parcelable { && conditionId.getPathSegments().get(0).equals(EVENT_PATH); if (!isEvent) return null; final EventInfo rt = new EventInfo(); - rt.calendar = tryParseLong(conditionId.getQueryParameter("calendar"), 0L); + rt.calendar = tryParseLong(conditionId.getQueryParameter("calendar"), + EventInfo.ANY_CALENDAR); rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0); return rt; } public static class EventInfo { + public static final long ANY_CALENDAR = 0; + public static final int REPLY_ANY_EXCEPT_NO = 0; public static final int REPLY_YES_OR_MAYBE = 1; public static final int REPLY_YES = 2; - public long calendar; // CalendarContract.Calendars._ID, or 0 for any + public long calendar = ANY_CALENDAR; // CalendarContract.Calendars._ID, or ANY_CALENDAR public int reply; @Override @@ -788,17 +803,17 @@ public class ZenModeConfig implements Parcelable { } public static String getConditionLine1(Context context, ZenModeConfig config, - int userHandle) { - return getConditionLine(context, config, userHandle, true /*useLine1*/); + int userHandle, boolean shortVersion) { + return getConditionLine(context, config, userHandle, true /*useLine1*/, shortVersion); } public static String getConditionSummary(Context context, ZenModeConfig config, - int userHandle) { - return getConditionLine(context, config, userHandle, false /*useLine1*/); + int userHandle, boolean shortVersion) { + return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion); } private static String getConditionLine(Context context, ZenModeConfig config, - int userHandle, boolean useLine1) { + int userHandle, boolean useLine1, boolean shortVersion) { if (config == null) return ""; if (config.manualRule != null) { final Uri id = config.manualRule.conditionId; @@ -811,7 +826,7 @@ public class ZenModeConfig implements Parcelable { final long now = System.currentTimeMillis(); final long span = time - now; c = toTimeCondition(context, - time, Math.round(span / (float) MINUTES_MS), now, userHandle); + time, Math.round(span / (float) MINUTES_MS), now, userHandle, shortVersion); } final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; return TextUtils.isEmpty(rt) ? "" : rt; diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index fc65f63..e99a960 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -79,7 +79,8 @@ public class DynamicLayout extends Layout boolean includepad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, - spacingmult, spacingadd, includepad, StaticLayout.BREAK_STRATEGY_SIMPLE, + spacingmult, spacingadd, includepad, + StaticLayout.BREAK_STRATEGY_SIMPLE, StaticLayout.HYPHENATION_FREQUENCY_NONE, ellipsize, ellipsizedWidth); } @@ -96,7 +97,7 @@ public class DynamicLayout extends Layout TextPaint paint, int width, Alignment align, TextDirectionHeuristic textDir, float spacingmult, float spacingadd, - boolean includepad, int breakStrategy, + boolean includepad, int breakStrategy, int hyphenationFrequency, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { super((ellipsize == null) ? display @@ -122,6 +123,7 @@ public class DynamicLayout extends Layout mIncludePad = includepad; mBreakStrategy = breakStrategy; + mHyphenationFrequency = hyphenationFrequency; /* * This is annoying, but we can't refer to the layout until @@ -293,7 +295,8 @@ public class DynamicLayout extends Layout .setLineSpacing(getSpacingAdd(), getSpacingMultiplier()) .setEllipsizedWidth(mEllipsizedWidth) .setEllipsize(mEllipsizeAt) - .setBreakStrategy(mBreakStrategy); + .setBreakStrategy(mBreakStrategy) + .setHyphenationFrequency(mHyphenationFrequency); reflowed.generate(b, false, true); int n = reflowed.getLineCount(); @@ -719,6 +722,7 @@ public class DynamicLayout extends Layout private int mEllipsizedWidth; private TextUtils.TruncateAt mEllipsizeAt; private int mBreakStrategy; + private int mHyphenationFrequency; private PackedIntVector mInts; private PackedObjectVector<Directions> mObjects; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 60de02a..f176240 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -71,6 +71,35 @@ public abstract class Layout { */ public static final int BREAK_STRATEGY_BALANCED = 2; + /** @hide */ + @IntDef({HYPHENATION_FREQUENCY_NORMAL, HYPHENATION_FREQUENCY_FULL, + HYPHENATION_FREQUENCY_NONE}) + @Retention(RetentionPolicy.SOURCE) + public @interface HyphenationFrequency {} + + /** + * Value for hyphenation frequency indicating no automatic hyphenation. Useful + * for backward compatibility, and for cases where the automatic hyphenation algorithm results + * in incorrect hyphenation. Mid-word breaks may still happen when a word is wider than the + * layout and there is otherwise no valid break. Soft hyphens are ignored and will not be used + * as suggestions for potential line breaks. + */ + public static final int HYPHENATION_FREQUENCY_NONE = 0; + + /** + * Value for hyphenation frequency indicating a light amount of automatic hyphenation, which + * is a conservative default. Useful for informal cases, such as short sentences or chat + * messages. + */ + public static final int HYPHENATION_FREQUENCY_NORMAL = 1; + + /** + * Value for hyphenation frequency indicating the full amount of automatic hyphenation, typical + * in typography. Useful for running text and where it's important to put the maximum amount of + * text in a screen with limited space. + */ + public static final int HYPHENATION_FREQUENCY_FULL = 2; + private static final ParagraphStyle[] NO_PARA_SPANS = ArrayUtils.emptyArray(ParagraphStyle.class); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 59c7c6d..d6d046b 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -92,6 +92,7 @@ public class StaticLayout extends Layout { b.mEllipsize = null; b.mMaxLines = Integer.MAX_VALUE; b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; + b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; b.mMeasuredText = MeasuredText.obtain(); return b; @@ -276,6 +277,19 @@ public class StaticLayout extends Layout { } /** + * Set hyphenation frequency, to control the amount of automatic hyphenation used. The + * default is {@link Layout#HYPHENATION_FREQUENCY_NONE}. + * + * @param hyphenationFrequency hyphenation frequency for the paragraph + * @return this builder, useful for chaining + * @see android.widget.TextView#setHyphenationFrequency + */ + public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) { + mHyphenationFrequency = hyphenationFrequency; + return this; + } + + /** * Set indents. Arguments are arrays holding an indent amount, one per line, measured in * pixels. For lines past the last element in the array, the last element repeats. * @@ -302,7 +316,8 @@ public class StaticLayout extends Layout { * the native code is as follows. * * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab - * stops, break strategy (and possibly other parameters in the future). + * stops, break strategy, and hyphenation frequency (and possibly other parameters in the + * future). * * Then, for each run within the paragraph: * - setLocale (this must be done at least for the first run, optional afterwards) @@ -377,6 +392,7 @@ public class StaticLayout extends Layout { TextUtils.TruncateAt mEllipsize; int mMaxLines; int mBreakStrategy; + int mHyphenationFrequency; Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); @@ -644,7 +660,7 @@ public class StaticLayout extends Layout { nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, - variableTabStops, TAB_INCREMENT, b.mBreakStrategy); + variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency); // measurement has to be done before performing line breaking // but we don't want to recompute fontmetrics or span ranges the @@ -1153,7 +1169,7 @@ public class StaticLayout extends Layout { // Set up paragraph text and settings; done as one big method to minimize jni crossings private static native void nSetupParagraph(long nativePtr, char[] text, int length, float firstWidth, int firstWidthLineCount, float restWidth, - int[] variableTabStops, int defaultTabStop, int breakStrategy); + int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency); private static native float nAddStyleRun(long nativePtr, long nativePaint, long nativeTypeface, int start, int end, boolean isRtl); diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java index c4dc5ed..5dda8a7 100644 --- a/core/java/android/text/method/WordIterator.java +++ b/core/java/android/text/method/WordIterator.java @@ -194,6 +194,87 @@ public class WordIterator implements Selection.PositionIterator { return BreakIterator.DONE; } + /** + * If <code>offset</code> is within a group of punctuation as defined + * by {@link #isPunctuation(int)}, returns the index of the first character + * of that group, otherwise returns BreakIterator.DONE. + * + * @param offset the offset to search from. + */ + public int getPunctuationBeginning(int offset) { + while (offset != BreakIterator.DONE && !isPunctuationStartBoundary(offset)) { + offset = prevBoundary(offset); + } + // No need to shift offset, prevBoundary handles that. + return offset; + } + + /** + * If <code>offset</code> is within a group of punctuation as defined + * by {@link #isPunctuation(int)}, returns the index of the last character + * of that group plus one, otherwise returns BreakIterator.DONE. + * + * @param offset the offset to search from. + */ + public int getPunctuationEnd(int offset) { + while (offset != BreakIterator.DONE && !isPunctuationEndBoundary(offset)) { + offset = nextBoundary(offset); + } + // No need to shift offset, nextBoundary handles that. + return offset; + } + + /** + * Indicates if the provided offset is after a punctuation character + * as defined by {@link #isPunctuation(int)}. + * + * @param offset the offset to check from. + * @return Whether the offset is after a punctuation character. + */ + public boolean isAfterPunctuation(int offset) { + final int shiftedOffset = offset - mOffsetShift; + if (shiftedOffset >= 1 && shiftedOffset <= mString.length()) { + final int codePoint = mString.codePointBefore(shiftedOffset); + return isPunctuation(codePoint); + } + return false; + } + + /** + * Indicates if the provided offset is at a punctuation character + * as defined by {@link #isPunctuation(int)}. + * + * @param offset the offset to check from. + * @return Whether the offset is at a punctuation character. + */ + public boolean isOnPunctuation(int offset) { + final int shiftedOffset = offset - mOffsetShift; + if (shiftedOffset >= 0 && shiftedOffset < mString.length()) { + final int codePoint = mString.codePointAt(shiftedOffset); + return isPunctuation(codePoint); + } + return false; + } + + private boolean isPunctuationStartBoundary(int offset) { + return isOnPunctuation(offset) && !isAfterPunctuation(offset); + } + + private boolean isPunctuationEndBoundary(int offset) { + return !isOnPunctuation(offset) && isAfterPunctuation(offset); + } + + private boolean isPunctuation(int cp) { + int type = Character.getType(cp); + return (type == Character.CONNECTOR_PUNCTUATION || + type == Character.DASH_PUNCTUATION || + type == Character.END_PUNCTUATION || + type == Character.FINAL_QUOTE_PUNCTUATION || + type == Character.INITIAL_QUOTE_PUNCTUATION || + type == Character.OTHER_PUNCTUATION || + type == Character.START_PUNCTUATION); + } + private boolean isAfterLetterOrDigit(int shiftedOffset) { if (shiftedOffset >= 1 && shiftedOffset <= mString.length()) { final int codePoint = mString.codePointBefore(shiftedOffset); diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 68ad782..3781d40 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -586,7 +586,7 @@ final class AccessibilityInteractionController { } } - private void perfromAccessibilityActionUiThread(Message message) { + private void performAccessibilityActionUiThread(Message message) { final int flags = message.arg1; final int accessibilityViewId = message.arg2; @@ -602,7 +602,8 @@ final class AccessibilityInteractionController { boolean succeeded = false; try { - if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { + if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null || + mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; @@ -1146,7 +1147,7 @@ final class AccessibilityInteractionController { findAccessibilityNodeInfoByAccessibilityIdUiThread(message); } break; case MSG_PERFORM_ACCESSIBILITY_ACTION: { - perfromAccessibilityActionUiThread(message); + performAccessibilityActionUiThread(message); } break; case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: { findAccessibilityNodeInfosByViewIdUiThread(message); diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 79a8489..d2b6533 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -209,7 +209,7 @@ public final class Choreographer { private static float getRefreshRate() { DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo( Display.DEFAULT_DISPLAY); - return di.refreshRate; + return di.getMode().getRefreshRate(); } /** diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java index 9047b1d..ce50091 100644 --- a/core/java/android/view/ContextThemeWrapper.java +++ b/core/java/android/view/ContextThemeWrapper.java @@ -87,9 +87,12 @@ public class ContextThemeWrapper extends ContextWrapper { } } - @Override public void setTheme(int resid) { - mThemeResource = resid; - initializeTheme(); + @Override + public void setTheme(int resid) { + if (mThemeResource != resid) { + mThemeResource = resid; + initializeTheme(); + } } /** @hide */ diff --git a/core/java/android/view/Display.aidl b/core/java/android/view/Display.aidl new file mode 100644 index 0000000..42bba44 --- /dev/null +++ b/core/java/android/view/Display.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +parcelable Display.Mode; diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 71e2251..d4b971a 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -21,6 +21,8 @@ import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; +import android.os.Parcel; +import android.os.Parcelable; import android.os.Process; import android.os.SystemClock; import android.util.DisplayMetrics; @@ -619,18 +621,44 @@ public final class Display { public float getRefreshRate() { synchronized (this) { updateDisplayInfoLocked(); - return mDisplayInfo.refreshRate; + return mDisplayInfo.getMode().getRefreshRate(); } } /** * Get the supported refresh rates of this display in frames per second. + * <p> + * This method only returns refresh rates for the display's default modes. For more options, use + * {@link #getSupportedModes()}. + * + * @deprecated use {@link #getSupportedModes()} instead */ + @Deprecated public float[] getSupportedRefreshRates() { synchronized (this) { updateDisplayInfoLocked(); - final float[] refreshRates = mDisplayInfo.supportedRefreshRates; - return Arrays.copyOf(refreshRates, refreshRates.length); + return mDisplayInfo.getDefaultRefreshRates(); + } + } + + /** + * Returns the active mode of the display. + */ + public Mode getMode() { + synchronized (this) { + updateDisplayInfoLocked(); + return mDisplayInfo.getMode(); + } + } + + /** + * Gets the supported modes of this display. + */ + public Mode[] getSupportedModes() { + synchronized (this) { + updateDisplayInfoLocked(); + final Display.Mode[] modes = mDisplayInfo.supportedModes; + return Arrays.copyOf(modes, modes.length); } } @@ -862,4 +890,152 @@ public final class Display { public static boolean isSuspendedState(int state) { return state == STATE_OFF || state == STATE_DOZE_SUSPEND; } + + /** + * A mode supported by a given display. + * + * @see Display#getSupportedModes() + */ + public static final class Mode implements Parcelable { + /** + * @hide + */ + public static final Mode[] EMPTY_ARRAY = new Mode[0]; + + private final int mModeId; + private final int mWidth; + private final int mHeight; + private final float mRefreshRate; + + /** + * @hide + */ + public Mode(int modeId, int width, int height, float refreshRate) { + mModeId = modeId; + mWidth = width; + mHeight = height; + mRefreshRate = refreshRate; + } + + /** + * Returns this mode's id. + */ + public int getModeId() { + return mModeId; + } + + /** + * Returns the physical width of the display in pixels when configured in this mode's + * resolution. + * <p> + * Note that due to application UI scaling, the number of pixels made available to + * applications when the mode is active (as reported by {@link Display#getWidth()} may + * differ from the mode's actual resolution (as reported by this function). + * <p> + * For example, applications running on a 4K display may have their UI laid out and rendered + * in 1080p and then scaled up. Applications can take advantage of the extra resolution by + * rendering content through a {@link android.view.SurfaceView} using full size buffers. + */ + public int getPhysicalWidth() { + return mWidth; + } + + /** + * Returns the physical height of the display in pixels when configured in this mode's + * resolution. + * <p> + * Note that due to application UI scaling, the number of pixels made available to + * applications when the mode is active (as reported by {@link Display#getHeight()} may + * differ from the mode's actual resolution (as reported by this function). + * <p> + * For example, applications running on a 4K display may have their UI laid out and rendered + * in 1080p and then scaled up. Applications can take advantage of the extra resolution by + * rendering content through a {@link android.view.SurfaceView} using full size buffers. + */ + public int getPhysicalHeight() { + return mHeight; + } + + /** + * Returns the refresh rate in frames per second. + */ + public float getRefreshRate() { + return mRefreshRate; + } + + /** + * Returns {@code true} if this mode matches the given parameters. + * + * @hide + */ + public boolean matches(int width, int height, float refreshRate) { + return mWidth == width && + mHeight == height && + Float.floatToIntBits(mRefreshRate) == Float.floatToIntBits(refreshRate); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof Mode)) { + return false; + } + Mode that = (Mode) other; + return mModeId == that.mModeId && matches(that.mWidth, that.mHeight, that.mRefreshRate); + } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 17 + mModeId; + hash = hash * 17 + mWidth; + hash = hash * 17 + mHeight; + hash = hash * 17 + Float.floatToIntBits(mRefreshRate); + return hash; + } + + @Override + public String toString() { + return new StringBuilder("{") + .append("id=").append(mModeId) + .append(", width=").append(mWidth) + .append(", height=").append(mHeight) + .append(", fps=").append(mRefreshRate) + .append("}") + .toString(); + } + + @Override + public int describeContents() { + return 0; + } + + private Mode(Parcel in) { + this(in.readInt(), in.readInt(), in.readInt(), in.readFloat()); + } + + @Override + public void writeToParcel(Parcel out, int parcelableFlags) { + out.writeInt(mModeId); + out.writeInt(mWidth); + out.writeInt(mHeight); + out.writeFloat(mRefreshRate); + } + + @SuppressWarnings("hiding") + public static final Parcelable.Creator<Mode> CREATOR + = new Parcelable.Creator<Mode>() { + @Override + public Mode createFromParcel(Parcel in) { + return new Mode(in); + } + + @Override + public Mode[] newArray(int size) { + return new Mode[size]; + } + }; + } } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 243961c..b9fde8a 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -20,11 +20,11 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.Parcel; import android.os.Parcelable; +import android.util.ArraySet; import android.util.DisplayMetrics; import java.util.Arrays; -import libcore.util.EmptyArray; import libcore.util.Objects; /** @@ -155,18 +155,19 @@ public final class DisplayInfo implements Parcelable { public int rotation; /** - * The refresh rate of this display in frames per second. - * <p> - * The value of this field is indeterminate if the logical display is presented on - * more than one physical display. - * </p> + * The active display mode. + */ + public int modeId; + + /** + * The default display mode. */ - public float refreshRate; + public int defaultModeId; /** - * The supported refresh rates of this display at the current resolution in frames per second. + * The supported modes of this display. */ - public float[] supportedRefreshRates = EmptyArray.FLOAT; + public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY; /** * The logical display density which is the basis for density-independent @@ -276,7 +277,8 @@ public final class DisplayInfo implements Parcelable { && overscanRight == other.overscanRight && overscanBottom == other.overscanBottom && rotation == other.rotation - && refreshRate == other.refreshRate + && modeId == other.modeId + && defaultModeId == other.defaultModeId && logicalDensityDpi == other.logicalDensityDpi && physicalXDpi == other.physicalXDpi && physicalYDpi == other.physicalYDpi @@ -312,9 +314,9 @@ public final class DisplayInfo implements Parcelable { overscanRight = other.overscanRight; overscanBottom = other.overscanBottom; rotation = other.rotation; - refreshRate = other.refreshRate; - supportedRefreshRates = Arrays.copyOf( - other.supportedRefreshRates, other.supportedRefreshRates.length); + modeId = other.modeId; + defaultModeId = other.defaultModeId; + supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length); logicalDensityDpi = other.logicalDensityDpi; physicalXDpi = other.physicalXDpi; physicalYDpi = other.physicalYDpi; @@ -344,8 +346,13 @@ public final class DisplayInfo implements Parcelable { overscanRight = source.readInt(); overscanBottom = source.readInt(); rotation = source.readInt(); - refreshRate = source.readFloat(); - supportedRefreshRates = source.createFloatArray(); + modeId = source.readInt(); + defaultModeId = source.readInt(); + int nModes = source.readInt(); + supportedModes = new Display.Mode[nModes]; + for (int i = 0; i < nModes; i++) { + supportedModes[i] = Display.Mode.CREATOR.createFromParcel(source); + } logicalDensityDpi = source.readInt(); physicalXDpi = source.readFloat(); physicalYDpi = source.readFloat(); @@ -377,8 +384,12 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(overscanRight); dest.writeInt(overscanBottom); dest.writeInt(rotation); - dest.writeFloat(refreshRate); - dest.writeFloatArray(supportedRefreshRates); + dest.writeInt(modeId); + dest.writeInt(defaultModeId); + dest.writeInt(supportedModes.length); + for (int i = 0; i < supportedModes.length; i++) { + supportedModes[i].writeToParcel(dest, flags); + } dest.writeInt(logicalDensityDpi); dest.writeFloat(physicalXDpi); dest.writeFloat(physicalYDpi); @@ -395,6 +406,61 @@ public final class DisplayInfo implements Parcelable { return 0; } + public Display.Mode getMode() { + return findMode(modeId); + } + + public Display.Mode getDefaultMode() { + return findMode(defaultModeId); + } + + private Display.Mode findMode(int id) { + for (int i = 0; i < supportedModes.length; i++) { + if (supportedModes[i].getModeId() == id) { + return supportedModes[i]; + } + } + throw new IllegalStateException("Unable to locate mode " + id); + } + + /** + * Returns the id of the "default" mode with the given refresh rate, or {@code 0} if no suitable + * mode could be found. + */ + public int findDefaultModeByRefreshRate(float refreshRate) { + Display.Mode[] modes = supportedModes; + Display.Mode defaultMode = getDefaultMode(); + for (int i = 0; i < modes.length; i++) { + if (modes[i].matches( + defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), refreshRate)) { + return modes[i].getModeId(); + } + } + return 0; + } + + /** + * Returns the list of supported refresh rates in the default mode. + */ + public float[] getDefaultRefreshRates() { + Display.Mode[] modes = supportedModes; + ArraySet<Float> rates = new ArraySet<>(); + Display.Mode defaultMode = getDefaultMode(); + for (int i = 0; i < modes.length; i++) { + Display.Mode mode = modes[i]; + if (mode.getPhysicalWidth() == defaultMode.getPhysicalWidth() + && mode.getPhysicalHeight() == defaultMode.getPhysicalHeight()) { + rates.add(mode.getRefreshRate()); + } + } + float[] result = new float[rates.size()]; + int i = 0; + for (Float rate : rates) { + result[i++] = rate; + } + return result; + } + public void getAppMetrics(DisplayMetrics outMetrics) { getAppMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); } @@ -490,10 +556,12 @@ public final class DisplayInfo implements Parcelable { sb.append(smallestNominalAppWidth); sb.append(" x "); sb.append(smallestNominalAppHeight); - sb.append(", "); - sb.append(refreshRate); - sb.append(" fps, supportedRefreshRates "); - sb.append(Arrays.toString(supportedRefreshRates)); + sb.append(", mode "); + sb.append(modeId); + sb.append(", defaultMode "); + sb.append(defaultModeId); + sb.append(", modes "); + sb.append(Arrays.toString(supportedModes)); sb.append(", rotation "); sb.append(rotation); sb.append(", density "); diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java index 9f46f45..41502b6 100644 --- a/core/java/android/view/GhostView.java +++ b/core/java/android/view/GhostView.java @@ -82,8 +82,7 @@ public class GhostView extends View { mView.mGhostView = null; final ViewGroup parent = (ViewGroup) mView.getParent(); if (parent != null) { - parent.mRecreateDisplayList = true; - parent.updateDisplayListIfDirty(); + parent.invalidate(); } } } diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 9fc80fc..0fefdc7 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -80,11 +80,17 @@ oneway interface IWindow { int localValue, int localChanges); /** - * If the window manager returned RELAYOUT_RES_ANIMATING - * from relayout(), this method will be called when the animation - * is done. + * The window is beginning to animate. The application should stop drawing frames until the + * window is not animating anymore, indicated by being called {@link #windowEndAnimating}. + * + * @param remainingFrameCount how many frames the app might still draw before stopping drawing */ - void doneAnimating(); + void onAnimationStarted(int remainingFrameCount); + + /** + * The window has ended animating. See {@link #onAnimationStarted}. + */ + void onAnimationStopped(); /** * Called for non-application windows when the enter animation has completed. diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java index c5e4c21..46ef379 100644 --- a/core/java/android/view/InputEventConsistencyVerifier.java +++ b/core/java/android/view/InputEventConsistencyVerifier.java @@ -97,6 +97,9 @@ public final class InputEventConsistencyVerifier { // Set to true if we received hover enter. private boolean mHoverEntered; + // The bitset of buttons which we've received ACTION_BUTTON_PRESS for. + private int mButtonsPressed; + // The current violation message. private StringBuilder mViolationMessage; @@ -148,6 +151,7 @@ public final class InputEventConsistencyVerifier { mTouchEventStreamIsTainted = false; mTouchEventStreamUnhandled = false; mHoverEntered = false; + mButtonsPressed = 0; while (mKeyStateList != null) { final KeyState state = mKeyStateList; @@ -466,6 +470,8 @@ public final class InputEventConsistencyVerifier { final int action = event.getAction(); final int source = event.getSource(); + final int buttonState = event.getButtonState(); + final int actionButton = event.getActionButton(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { switch (action) { case MotionEvent.ACTION_HOVER_ENTER: @@ -486,6 +492,62 @@ public final class InputEventConsistencyVerifier { ensureHistorySizeIsZeroForThisAction(event); ensurePointerCountIsOneForThisAction(event); break; + case MotionEvent.ACTION_BUTTON_PRESS: + ensureActionButtonIsNonZeroForThisAction(event); + if ((mButtonsPressed & actionButton) != 0) { + problem("Action button for ACTION_BUTTON_PRESS event is " + + actionButton + ", but it has already been pressed and " + + "has yet to be released."); + } + + mButtonsPressed |= actionButton; + // The system will automatically mirror the stylus buttons onto the button + // state as the old set of generic buttons for apps targeting pre-M. If + // it looks this has happened, go ahead and set the generic buttons as + // pressed to prevent spurious errors. + if (actionButton == MotionEvent.BUTTON_STYLUS_PRIMARY && + (buttonState & MotionEvent.BUTTON_SECONDARY) != 0) { + mButtonsPressed |= MotionEvent.BUTTON_SECONDARY; + } else if (actionButton == MotionEvent.BUTTON_STYLUS_SECONDARY && + (buttonState & MotionEvent.BUTTON_TERTIARY) != 0) { + mButtonsPressed |= MotionEvent.BUTTON_TERTIARY; + } + + if (mButtonsPressed != buttonState) { + problem(String.format("Reported button state differs from " + + "expected button state based on press and release events. " + + "Is 0x%08x but expected 0x%08x.", + buttonState, mButtonsPressed)); + } + break; + case MotionEvent.ACTION_BUTTON_RELEASE: + ensureActionButtonIsNonZeroForThisAction(event); + if ((mButtonsPressed & actionButton) != actionButton) { + problem("Action button for ACTION_BUTTON_RELEASE event is " + + actionButton + ", but it was either never pressed or has " + + "already been released."); + } + + mButtonsPressed &= ~actionButton; + // The system will automatically mirror the stylus buttons onto the button + // state as the old set of generic buttons for apps targeting pre-M. If + // it looks this has happened, go ahead and set the generic buttons as + // released to prevent spurious errors. + if (actionButton == MotionEvent.BUTTON_STYLUS_PRIMARY && + (buttonState & MotionEvent.BUTTON_SECONDARY) == 0) { + mButtonsPressed &= ~MotionEvent.BUTTON_SECONDARY; + } else if (actionButton == MotionEvent.BUTTON_STYLUS_SECONDARY && + (buttonState & MotionEvent.BUTTON_TERTIARY) == 0) { + mButtonsPressed &= ~MotionEvent.BUTTON_TERTIARY; + } + + if (mButtonsPressed != buttonState) { + problem(String.format("Reported button state differs from " + + "expected button state based on press and release events. " + + "Is 0x%08x but expected 0x%08x.", + buttonState, mButtonsPressed)); + } + break; default: problem("Invalid action for generic pointer event."); break; @@ -563,6 +625,15 @@ public final class InputEventConsistencyVerifier { } } + private void ensureActionButtonIsNonZeroForThisAction(MotionEvent event) { + final int actionButton = event.getActionButton(); + if (actionButton == 0) { + problem("No action button set. Action button should always be non-zero for " + + MotionEvent.actionToString(event.getAction())); + + } + } + private void ensureHistorySizeIsZeroForThisAction(MotionEvent event) { final int historySize = event.getHistorySize(); if (historySize != 0) { diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 1ac3f45..f6ce353 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1817,9 +1817,7 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final boolean isWakeKey(int keyCode) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: - case KeyEvent.KEYCODE_POWER: case KeyEvent.KEYCODE_MENU: - case KeyEvent.KEYCODE_SLEEP: case KeyEvent.KEYCODE_WAKEUP: case KeyEvent.KEYCODE_PAIRING: return true; diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 5df596a..4394cd8 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -303,6 +303,32 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int ACTION_HOVER_EXIT = 10; /** + * Constant for {@link #getActionMasked}: A button has been pressed. + * + * <p> + * Use {@link #getActionButton()} to get which button was pressed. + * </p><p> + * This action is not a touch event so it is delivered to + * {@link View#onGenericMotionEvent(MotionEvent)} rather than + * {@link View#onTouchEvent(MotionEvent)}. + * </p> + */ + public static final int ACTION_BUTTON_PRESS = 11; + + /** + * Constant for {@link #getActionMasked}: A button has been released. + * + * <p> + * Use {@link #getActionButton()} to get which button was released. + * </p><p> + * This action is not a touch event so it is delivered to + * {@link View#onGenericMotionEvent(MotionEvent)} rather than + * {@link View#onTouchEvent(MotionEvent)}. + * </p> + */ + public static final int ACTION_BUTTON_RELEASE = 12; + + /** * Bits in the action code that represent a pointer index, used with * {@link #ACTION_POINTER_DOWN} and {@link #ACTION_POINTER_UP}. Shifting * down by {@link #ACTION_POINTER_INDEX_SHIFT} provides the actual pointer @@ -1174,14 +1200,14 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int BUTTON_PRIMARY = 1 << 0; /** - * Button constant: Secondary button (right mouse button, stylus first button). + * Button constant: Secondary button (right mouse button). * * @see #getButtonState */ public static final int BUTTON_SECONDARY = 1 << 1; /** - * Button constant: Tertiary button (middle mouse button, stylus second button). + * Button constant: Tertiary button (middle mouse button). * * @see #getButtonState */ @@ -1209,6 +1235,20 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ public static final int BUTTON_FORWARD = 1 << 4; + /** + * Button constant: Primary stylus button pressed. + * + * @see #getButtonState + */ + public static final int BUTTON_STYLUS_PRIMARY = 1 << 5; + + /** + * Button constant: Secondary stylus button pressed. + * + * @see #getButtonState + */ + public static final int BUTTON_STYLUS_SECONDARY = 1 << 6; + // NOTE: If you add a new axis here you must also add it to: // native/include/android/input.h @@ -1220,8 +1260,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { "BUTTON_TERTIARY", "BUTTON_BACK", "BUTTON_FORWARD", - "0x00000020", - "0x00000040", + "BUTTON_STYLUS_PRIMARY", + "BUTTON_STYLUS_SECONDARY", "0x00000080", "0x00000100", "0x00000200", @@ -1357,6 +1397,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { private static native void nativeSetEdgeFlags(long nativePtr, int action); private static native int nativeGetMetaState(long nativePtr); private static native int nativeGetButtonState(long nativePtr); + private static native void nativeSetButtonState(long nativePtr, int buttonState); + private static native int nativeGetActionButton(long nativePtr); private static native void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY); private static native float nativeGetXOffset(long nativePtr); private static native float nativeGetYOffset(long nativePtr); @@ -2212,12 +2254,36 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see #BUTTON_TERTIARY * @see #BUTTON_FORWARD * @see #BUTTON_BACK + * @see #BUTTON_STYLUS_PRIMARY + * @see #BUTTON_STYLUS_SECONDARY */ public final int getButtonState() { return nativeGetButtonState(mNativePtr); } /** + * Sets the bitfield indicating which buttons are pressed. + * + * @see #getButtonState() + * @hide + */ + public final void setButtonState(int buttonState) { + nativeSetButtonState(mNativePtr, buttonState); + } + + /** + * Gets which button has been modified during a press or release action. + * + * For actions other than {@link #ACTION_BUTTON_PRESS} and {@link #ACTION_BUTTON_RELEASE} + * the returned value is undefined. + * + * @see #getButtonState() + */ + public final int getActionButton() { + return nativeGetActionButton(mNativePtr); + } + + /** * Returns the original raw X coordinate of this event. For touch * events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window @@ -3013,6 +3079,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { public String toString() { StringBuilder msg = new StringBuilder(); msg.append("MotionEvent { action=").append(actionToString(getAction())); + msg.append(", actionButton=").append(buttonStateToString(getActionButton())); final int pointerCount = getPointerCount(); for (int i = 0; i < pointerCount; i++) { @@ -3066,6 +3133,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { return "ACTION_HOVER_ENTER"; case ACTION_HOVER_EXIT: return "ACTION_HOVER_EXIT"; + case ACTION_BUTTON_PRESS: + return "ACTION_BUTTON_PRESS"; + case ACTION_BUTTON_RELEASE: + return "ACTION_BUTTON_RELEASE"; } int index = (action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT; switch (action & ACTION_MASK) { @@ -3172,6 +3243,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see #BUTTON_TERTIARY * @see #BUTTON_FORWARD * @see #BUTTON_BACK + * @see #BUTTON_STYLUS_PRIMARY + * @see #BUTTON_STYLUS_SECONDARY */ public final boolean isButtonPressed(int button) { if (button == 0) { @@ -3180,18 +3253,6 @@ public final class MotionEvent extends InputEvent implements Parcelable { return (getButtonState() & button) == button; } - /** - * Checks if a stylus is being used and if the first stylus button is - * pressed. - * - * @return True if the tool is a stylus and if the first stylus button is - * pressed. - * @see #BUTTON_SECONDARY - */ - public final boolean isStylusButtonPressed() { - return (isButtonPressed(BUTTON_SECONDARY) && getToolType(0) == TOOL_TYPE_STYLUS); - } - public static final Parcelable.Creator<MotionEvent> CREATOR = new Parcelable.Creator<MotionEvent>() { public MotionEvent createFromParcel(Parcel in) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a3d0b2a..6ca320a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -633,7 +633,6 @@ import java.util.concurrent.atomic.AtomicInteger; * </p> * * @attr ref android.R.styleable#View_alpha - * @attr ref android.R.styleable#View_assistBlocked * @attr ref android.R.styleable#View_background * @attr ref android.R.styleable#View_clickable * @attr ref android.R.styleable#View_contentDescription @@ -2533,7 +2532,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, PFLAG3_SCROLL_INDICATOR_END >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; /** - * <p>Indicates that we are allowing {@link android.view.ViewAssistStructure} to traverse + * <p>Indicates that we are allowing {@link ViewStructure} to traverse * into this view.<p> */ static final int PFLAG3_ASSIST_BLOCKED = 0x100; @@ -4050,11 +4049,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, viewFlagMasks |= SAVE_DISABLED_MASK; } break; - case com.android.internal.R.styleable.View_assistBlocked: - if (a.getBoolean(attr, false)) { - mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED; - } - break; case com.android.internal.R.styleable.View_duplicateParentState: if (a.getBoolean(attr, false)) { viewFlagValues |= DUPLICATE_PARENT_STATE; @@ -4271,12 +4265,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } mForegroundInfo.mInsidePadding = a.getBoolean(attr, mForegroundInfo.mInsidePadding); + break; case R.styleable.View_scrollIndicators: final int scrollIndicators = - a.getInt(attr, SCROLL_INDICATORS_NONE) & SCROLL_INDICATORS_PFLAG3_MASK; + (a.getInt(attr, 0) << SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT) + & SCROLL_INDICATORS_PFLAG3_MASK; if (scrollIndicators != 0) { - viewFlagValues |= scrollIndicators; - viewFlagMasks |= SCROLL_INDICATORS_PFLAG3_MASK; + mPrivateFlags3 |= scrollIndicators; initializeScrollIndicators = true; } break; @@ -4890,7 +4885,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_scrollIndicators */ public void setScrollIndicators(@ScrollIndicators int indicators) { - setScrollIndicators(indicators, SCROLL_INDICATORS_PFLAG3_MASK); + setScrollIndicators(indicators, + SCROLL_INDICATORS_PFLAG3_MASK >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT); } /** @@ -4960,36 +4956,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; } - /** - * Returns whether the specified scroll indicator is enabled. - * <p> - * Multiple indicator types may be queried by passing the logical OR of the - * desired types. If multiple types are specified, the return value - * represents whether they are all enabled. - * - * @param direction the indicator direction, or the logical OR of multiple - * indicator directions. One or more of: - * <ul> - * <li>{@link #SCROLL_INDICATOR_TOP}</li> - * <li>{@link #SCROLL_INDICATOR_BOTTOM}</li> - * <li>{@link #SCROLL_INDICATOR_LEFT}</li> - * <li>{@link #SCROLL_INDICATOR_RIGHT}</li> - * <li>{@link #SCROLL_INDICATOR_START}</li> - * <li>{@link #SCROLL_INDICATOR_END}</li> - * </ul> - * @return {@code true} if the specified indicator(s) are enabled, - * {@code false} otherwise - * @attr ref android.R.styleable#View_scrollIndicators - */ - public boolean isScrollIndicatorEnabled(int direction) { - // Shift and sanitize input. - direction <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; - direction &= SCROLL_INDICATORS_PFLAG3_MASK; - - // All of the flags must be set. - return (mPrivateFlags3 & direction) == direction; - } - ListenerInfo getListenerInfo() { if (mListenerInfo != null) { return mListenerInfo; @@ -5255,8 +5221,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return True if the event was consumed. */ private boolean performStylusActionOnButtonPress(MotionEvent event) { - if (isStylusButtonPressable() && !mInStylusButtonPress - && !mHasPerformedLongPress && event.isStylusButtonPressed()) { + if (isStylusButtonPressable() && !mInStylusButtonPress && !mHasPerformedLongPress + && event.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY)) { if (performStylusButtonPress()) { mInStylusButtonPress = true; setPressed(true, event.getX(), event.getY()); @@ -6163,7 +6129,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param structure Fill in with structured view data. The default implementation * fills in all data that can be inferred from the view itself. */ - public void onProvideAssistStructure(ViewAssistStructure structure) { + public void onProvideStructure(ViewStructure structure) { final int id = mID; if (id > 0 && (id&0xff000000) != 0 && (id&0x00ff0000) != 0 && (id&0x0000ffff) != 0) { @@ -6217,6 +6183,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, structure.setContentDescription(getContentDescription()); } + /** @hide */ + public void onProvideAssistStructure(ViewStructure structure) { + onProvideStructure(structure); + } + /** * Called when assist structure is being retrieved from a view as part of * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to @@ -6225,19 +6196,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * view's virtual accessibility nodes, if any. You can override this for a more * optimal implementation providing this data. */ - public void onProvideVirtualAssistStructure(ViewAssistStructure structure) { + public void onProvideVirtualStructure(ViewStructure structure) { AccessibilityNodeProvider provider = getAccessibilityNodeProvider(); if (provider != null) { AccessibilityNodeInfo info = createAccessibilityNodeInfo(); Log.i("View", "Provider of " + this + ": children=" + info.getChildCount()); structure.setChildCount(1); - ViewAssistStructure root = structure.newChild(0); - populateVirtualAssistStructure(root, provider, info); + ViewStructure root = structure.newChild(0); + populateVirtualStructure(root, provider, info); info.recycle(); } } - private void populateVirtualAssistStructure(ViewAssistStructure structure, + /** @hide */ + public void onProvideVirtualAssistStructure(ViewStructure structure) { + onProvideVirtualStructure(structure); + } + + private void populateVirtualStructure(ViewStructure structure, AccessibilityNodeProvider provider, AccessibilityNodeInfo info) { structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()), null, null, null); @@ -6288,19 +6264,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, for (int i=0; i<NCHILDREN; i++) { AccessibilityNodeInfo cinfo = provider.createAccessibilityNodeInfo( AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i))); - ViewAssistStructure child = structure.newChild(i); - populateVirtualAssistStructure(child, provider, cinfo); + ViewStructure child = structure.newChild(i); + populateVirtualStructure(child, provider, cinfo); cinfo.recycle(); } } } /** - * Dispatch creation of {@link ViewAssistStructure} down the hierarchy. The default - * implementation calls {@link #onProvideAssistStructure} and - * {@link #onProvideVirtualAssistStructure}. + * Dispatch creation of {@link ViewStructure} down the hierarchy. The default + * implementation calls {@link #onProvideStructure} and + * {@link #onProvideVirtualStructure}. */ - public void dispatchProvideAssistStructure(ViewAssistStructure structure) { + public void dispatchProvideStructure(ViewStructure structure) { if (!isAssistBlocked()) { onProvideAssistStructure(structure); onProvideVirtualAssistStructure(structure); @@ -7904,8 +7880,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide * Indicates whether this view will participate in data collection through - * {@link android.view.ViewAssistStructure}. If true, it will not provide any data + * {@link ViewStructure}. If true, it will not provide any data * for itself or its children. If false, the normal data collection will be allowed. * * @return Returns false if assist data collection is not blocked, else true. @@ -7918,17 +7895,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide * Controls whether assist data collection from this view and its children is enabled - * (that is, whether {@link #onProvideAssistStructure} and - * {@link #onProvideVirtualAssistStructure} will be called). The default value is false, + * (that is, whether {@link #onProvideStructure} and + * {@link #onProvideVirtualStructure} will be called). The default value is false, * allowing normal assist collection. Setting this to false will disable assist collection. * * @param enabled Set to true to <em>disable</em> assist data collection, or false * (the default) to allow it. * * @see #isAssistBlocked() - * @see #onProvideAssistStructure - * @see #onProvideVirtualAssistStructure + * @see #onProvideStructure + * @see #onProvideVirtualStructure * @attr ref android.R.styleable#View_assistBlocked */ public void setAssistBlocked(boolean enabled) { diff --git a/core/java/android/view/ViewAssistStructure.java b/core/java/android/view/ViewAssistStructure.java index fccfbb8..a66d93c 100644 --- a/core/java/android/view/ViewAssistStructure.java +++ b/core/java/android/view/ViewAssistStructure.java @@ -16,70 +16,9 @@ package android.view; -import android.graphics.Rect; -import android.os.Bundle; -import android.text.TextPaint; - /** - * Container for storing additional per-view data generated by {@link View#onProvideAssistStructure - * View.onProvideAssistStructure}. + * @deprecated Temporary until old apps can move off this. */ -public abstract class ViewAssistStructure { - public abstract void setId(int id, String packageName, String typeName, String entryName); - - public abstract void setDimens(int left, int top, int scrollX, int scrollY, int width, - int height); - - public abstract void setVisibility(int visibility); - - public abstract void setAssistBlocked(boolean state); - - public abstract void setEnabled(boolean state); - - public abstract void setClickable(boolean state); - - public abstract void setLongClickable(boolean state); - - public abstract void setStylusButtonPressable(boolean state); - - public abstract void setFocusable(boolean state); - - public abstract void setFocused(boolean state); - - public abstract void setAccessibilityFocused(boolean state); - - public abstract void setCheckable(boolean state); - - public abstract void setChecked(boolean state); - - public abstract void setSelected(boolean state); - - public abstract void setActivated(boolean state); - - public abstract void setClassName(String className); - - public abstract void setContentDescription(CharSequence contentDescription); - - public abstract void setText(CharSequence text); - public abstract void setText(CharSequence text, int selectionStart, int selectionEnd); - public abstract void setTextPaint(TextPaint paint); - public abstract void setHint(CharSequence hint); - - public abstract CharSequence getText(); - public abstract int getTextSelectionStart(); - public abstract int getTextSelectionEnd(); - public abstract CharSequence getHint(); - - public abstract Bundle editExtras(); - public abstract void clearExtras(); - - public abstract void setChildCount(int num); - public abstract int getChildCount(); - public abstract ViewAssistStructure newChild(int index); - - public abstract ViewAssistStructure asyncNewChild(int index); - public abstract void asyncCommit(); - - /** @hide */ - public abstract Rect getTempRect(); +@Deprecated +public abstract class ViewAssistStructure extends ViewStructure { } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index d0738b0..b476e9b 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -586,6 +586,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mGroupFlags |= FLAG_CLIP_CHILDREN; mGroupFlags |= FLAG_CLIP_TO_PADDING; mGroupFlags |= FLAG_ANIMATION_DONE; + mGroupFlags |= FLAG_ANIMATION_CACHE; + mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; @@ -2878,12 +2880,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * Dispatch creation of {@link ViewAssistStructure} down the hierarchy. This implementation + * Dispatch creation of {@link ViewStructure} down the hierarchy. This implementation * adds in all child views of the view group, in addition to calling the default View * implementation. */ - public void dispatchProvideAssistStructure(ViewAssistStructure structure) { - super.dispatchProvideAssistStructure(structure); + public void dispatchProvideStructure(ViewStructure structure) { + super.dispatchProvideStructure(structure); if (!isAssistBlocked()) { if (structure.getChildCount() == 0) { final int childrenCount = getChildCount(); @@ -2898,8 +2900,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager ? getChildDrawingOrder(childrenCount, i) : i; final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex); - ViewAssistStructure cstructure = structure.newChild(i); - child.dispatchProvideAssistStructure(cstructure); + ViewStructure cstructure = structure.newChild(i); + child.dispatchProvideStructure(cstructure); } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index c9c2a82..4f2a3fa 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -40,6 +40,7 @@ import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.media.AudioManager; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Handler; @@ -174,6 +175,9 @@ public final class ViewRootImpl implements ViewParent, // so the window should no longer be active. boolean mStopped = false; + // Set to true to stop input during an Activity Transition. + boolean mPausedForTransition = false; + boolean mLastInCompatMode = false; SurfaceHolder.Callback2 mSurfaceHolderCallback; @@ -217,6 +221,9 @@ public final class ViewRootImpl implements ViewParent, boolean mLastWasImTarget; boolean mWindowsAnimating; boolean mDrawDuringWindowsAnimating; + + /** How many frames the app is still allowed to draw when a window animation is happening. */ + private int mRemainingFrameCount; boolean mIsDrawing; int mLastSystemUiVisibility; int mClientWindowLayoutFlags; @@ -654,6 +661,10 @@ public final class ViewRootImpl implements ViewParent, return (mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0; } + public int getWindowFlags() { + return mWindowAttributes.flags; + } + public CharSequence getTitle() { return mWindowAttributes.getTitle(); } @@ -978,15 +989,25 @@ public final class ViewRootImpl implements ViewParent, return null; } - void setStopped(boolean stopped) { + void setWindowStopped(boolean stopped) { if (mStopped != stopped) { mStopped = stopped; - if (!stopped) { + if (!mStopped) { scheduleTraversals(); } } } + /** + * Block the input events during an Activity Transition. The KEYCODE_BACK event is allowed + * through to allow quick reversal of the Activity Transition. + * + * @param paused true to pause, false to resume. + */ + public void setPausedForTransition(boolean paused) { + mPausedForTransition = paused; + } + @Override public ViewParent getParent() { return null; @@ -1552,10 +1573,6 @@ public final class ViewRootImpl implements ViewParent, } final int surfaceGenerationId = mSurface.getGenerationId(); relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); - if (!mDrawDuringWindowsAnimating && - (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0) { - mWindowsAnimating = true; - } if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString() + " overscan=" + mPendingOverscanInsets.toShortString() @@ -1979,14 +1996,11 @@ public final class ViewRootImpl implements ViewParent, + mView.findFocus()); } } - if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0) { - // The first time we relayout the window, if the system is - // doing window animations, we want to hold of on any future - // draws until the animation is done. - mWindowsAnimating = true; - } } else if (mWindowsAnimating) { - skipDraw = true; + if (mRemainingFrameCount <= 0) { + skipDraw = true; + } + mRemainingFrameCount--; } mFirst = false; @@ -2783,7 +2797,7 @@ public final class ViewRootImpl implements ViewParent, public void setDrawDuringWindowsAnimating(boolean value) { mDrawDuringWindowsAnimating = value; if (value) { - handleDispatchDoneAnimating(); + handleDispatchWindowAnimationStopped(); } } @@ -3135,11 +3149,12 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_UPDATE_CONFIGURATION = 18; private final static int MSG_PROCESS_INPUT_EVENTS = 19; private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21; - private final static int MSG_DISPATCH_DONE_ANIMATING = 22; - private final static int MSG_INVALIDATE_WORLD = 23; - private final static int MSG_WINDOW_MOVED = 24; - private final static int MSG_SYNTHESIZE_INPUT_EVENT = 25; - private final static int MSG_DISPATCH_WINDOW_SHOWN = 26; + private final static int MSG_INVALIDATE_WORLD = 22; + private final static int MSG_WINDOW_MOVED = 23; + private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24; + private final static int MSG_DISPATCH_WINDOW_SHOWN = 25; + private final static int MSG_DISPATCH_WINDOW_ANIMATION_STOPPED = 26; + private final static int MSG_DISPATCH_WINDOW_ANIMATION_STARTED = 27; final class ViewRootHandler extends Handler { @Override @@ -3183,8 +3198,10 @@ public final class ViewRootImpl implements ViewParent, return "MSG_PROCESS_INPUT_EVENTS"; case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST"; - case MSG_DISPATCH_DONE_ANIMATING: - return "MSG_DISPATCH_DONE_ANIMATING"; + case MSG_DISPATCH_WINDOW_ANIMATION_STARTED: + return "MSG_DISPATCH_WINDOW_ANIMATION_STARTED"; + case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED: + return "MSG_DISPATCH_WINDOW_ANIMATION_STOPPED"; case MSG_WINDOW_MOVED: return "MSG_WINDOW_MOVED"; case MSG_SYNTHESIZE_INPUT_EVENT: @@ -3404,8 +3421,12 @@ public final class ViewRootImpl implements ViewParent, case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: { setAccessibilityFocus(null, null); } break; - case MSG_DISPATCH_DONE_ANIMATING: { - handleDispatchDoneAnimating(); + case MSG_DISPATCH_WINDOW_ANIMATION_STARTED: { + int remainingFrameCount = msg.arg1; + handleDispatchWindowAnimationStarted(remainingFrameCount); + } break; + case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED: { + handleDispatchWindowAnimationStopped(); } break; case MSG_INVALIDATE_WORLD: { if (mView != null) { @@ -3633,8 +3654,9 @@ public final class ViewRootImpl implements ViewParent, if (mView == null || !mAdded) { Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent); return true; - } else if ((!mAttachInfo.mHasWindowFocus || mStopped) - && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { + } else if ((!mAttachInfo.mHasWindowFocus + && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) || mStopped + || (mPausedForTransition && !isBack(q.mEvent))) { // This is a focus event and the window doesn't currently have input focus or // has stopped. This could be an event that came back from the previous stage // but the window has lost focus or stopped in the meantime. @@ -3657,6 +3679,14 @@ public final class ViewRootImpl implements ViewParent, mNext.dump(prefix, writer); } } + + private boolean isBack(InputEvent event) { + if (event instanceof KeyEvent) { + return ((KeyEvent) event).getKeyCode() == KeyEvent.KEYCODE_BACK; + } else { + return false; + } + } } /** @@ -4012,7 +4042,7 @@ public final class ViewRootImpl implements ViewParent, } else { // If delivering a new non-key event, make sure the window is // now allowed to start updating. - handleDispatchDoneAnimating(); + handleDispatchWindowAnimationStopped(); final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { return processPointerEvent(q); @@ -4042,7 +4072,7 @@ public final class ViewRootImpl implements ViewParent, if (event.getAction() != KeyEvent.ACTION_UP) { // If delivering a new key event, make sure the window is // now allowed to start updating. - handleDispatchDoneAnimating(); + handleDispatchWindowAnimationStopped(); } // Deliver the key to the view hierarchy. @@ -5259,7 +5289,14 @@ public final class ViewRootImpl implements ViewParent, } } - public void handleDispatchDoneAnimating() { + public void handleDispatchWindowAnimationStarted(int remainingFrameCount) { + if (!mDrawDuringWindowsAnimating) { + mRemainingFrameCount = remainingFrameCount; + mWindowsAnimating = true; + } + } + + public void handleDispatchWindowAnimationStopped() { if (mWindowsAnimating) { mWindowsAnimating = false; if (!mDirty.isEmpty() || mIsAnimating || mFullRedrawNeeded) { @@ -5321,7 +5358,7 @@ public final class ViewRootImpl implements ViewParent, //Log.d(TAG, ">>>>>> CALLING relayout"); if (params != null && mOrigWindowType != params.type) { // For compatibility with old apps, don't crash here. - if (mTargetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { Slog.w(TAG, "Window type can not be changed after " + "the window is added; ignoring change of " + mView); params.type = mOrigWindowType; @@ -5746,6 +5783,7 @@ public final class ViewRootImpl implements ViewParent, void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { + adjustInputEventForCompatibility(event); QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); // Always enqueue the input event in order, regardless of its time stamp. @@ -5851,6 +5889,19 @@ public final class ViewRootImpl implements ViewParent, recycleQueuedInputEvent(q); } + private void adjustInputEventForCompatibility(InputEvent e) { + if (mTargetSdkVersion < Build.VERSION_CODES.MNC && e instanceof MotionEvent) { + MotionEvent motion = (MotionEvent) e; + final int mask = + MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY; + final int buttonState = motion.getButtonState(); + final int compatButtonState = (buttonState & mask) >> 4; + if (compatButtonState != 0) { + motion.setButtonState(buttonState | compatButtonState); + } + } + } + static boolean isTerminalInputEvent(InputEvent event) { if (event instanceof KeyEvent) { final KeyEvent keyEvent = (KeyEvent)event; @@ -6163,8 +6214,13 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args)); } - public void dispatchDoneAnimating() { - mHandler.sendEmptyMessage(MSG_DISPATCH_DONE_ANIMATING); + public void dispatchWindowAnimationStarted(int remainingFrameCount) { + mHandler.obtainMessage(MSG_DISPATCH_WINDOW_ANIMATION_STARTED, + remainingFrameCount, 0 /* unused */).sendToTarget(); + } + + public void dispatchWindowAnimationStopped() { + mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_ANIMATION_STOPPED); } public void dispatchCheckFocus() { @@ -6224,7 +6280,7 @@ public final class ViewRootImpl implements ViewParent, @Override public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { - if (mView == null) { + if (mView == null || mStopped || mPausedForTransition) { return false; } // Intercept accessibility focus events fired by virtual nodes to keep @@ -6687,10 +6743,18 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void doneAnimating() { + public void onAnimationStarted(int remainingFrameCount) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchWindowAnimationStarted(remainingFrameCount); + } + } + + @Override + public void onAnimationStopped() { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { - viewAncestor.dispatchDoneAnimating(); + viewAncestor.dispatchWindowAnimationStopped(); } } diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java new file mode 100644 index 0000000..8dc49ac --- /dev/null +++ b/core/java/android/view/ViewStructure.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Rect; +import android.os.Bundle; +import android.text.TextPaint; + +/** + * Container for storing additional per-view data generated by {@link View#onProvideStructure + * View.onProvideStructure}. + */ +public abstract class ViewStructure { + public abstract void setId(int id, String packageName, String typeName, String entryName); + + public abstract void setDimens(int left, int top, int scrollX, int scrollY, int width, + int height); + + public abstract void setVisibility(int visibility); + + /** @hide */ + public abstract void setAssistBlocked(boolean state); + + public abstract void setEnabled(boolean state); + + public abstract void setClickable(boolean state); + + public abstract void setLongClickable(boolean state); + + public abstract void setStylusButtonPressable(boolean state); + + public abstract void setFocusable(boolean state); + + public abstract void setFocused(boolean state); + + public abstract void setAccessibilityFocused(boolean state); + + public abstract void setCheckable(boolean state); + + public abstract void setChecked(boolean state); + + public abstract void setSelected(boolean state); + + public abstract void setActivated(boolean state); + + public abstract void setClassName(String className); + + public abstract void setContentDescription(CharSequence contentDescription); + + public abstract void setText(CharSequence text); + public abstract void setText(CharSequence text, int selectionStart, int selectionEnd); + public abstract void setTextPaint(TextPaint paint); + public abstract void setHint(CharSequence hint); + + public abstract CharSequence getText(); + public abstract int getTextSelectionStart(); + public abstract int getTextSelectionEnd(); + public abstract CharSequence getHint(); + + public abstract Bundle getExtras(); + public abstract boolean hasExtras(); + + public abstract void setChildCount(int num); + public abstract int getChildCount(); + public abstract ViewAssistStructure newChild(int index); + + public abstract ViewAssistStructure asyncNewChild(int index); + public abstract void asyncCommit(); + + /** @hide */ + public abstract Rect getTempRect(); +} diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 2797b6e..7976ca4 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -101,7 +101,7 @@ public interface WindowManager extends ViewManager { * the given view hierarchy's {@link View#onDetachedFromWindow() * View.onDetachedFromWindow()} methods before returning. This is not * for normal applications; using it correctly requires great care. - * + * * @param view The view to be removed. */ public void removeViewImmediate(View view); @@ -115,7 +115,7 @@ public interface WindowManager extends ViewManager { */ @ViewDebug.ExportedProperty public int x; - + /** * Y position for this window. With the default gravity it is ignored. * When using {@link Gravity#TOP} or {@link Gravity#BOTTOM} it provides @@ -228,12 +228,12 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "TYPE_VOICE_INTERACTION_STARTING"), }) public int type; - + /** * Start of window types that represent normal application windows. */ public static final int FIRST_APPLICATION_WINDOW = 1; - + /** * Window type: an application window that serves as the "base" window * of the overall application; all other application windows will @@ -241,14 +241,14 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_BASE_APPLICATION = 1; - + /** * Window type: a normal application window. The {@link #token} must be * an Activity token identifying who the window belongs to. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_APPLICATION = 2; - + /** * Window type: special application window that is displayed while the * application is starting. Not for use by applications themselves; @@ -257,7 +257,7 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows on all users' windows. */ public static final int TYPE_APPLICATION_STARTING = 3; - + /** * End of types of application windows. */ @@ -330,14 +330,14 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows on all users' windows. */ public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW; - + /** * Window type: the search bar. There can be only one search bar * window; it is placed at the top of the screen. * In multiuser systems shows on all users' windows. */ public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1; - + /** * Window type: phone. These are non-application windows providing * user interaction with the phone (in particular incoming calls). @@ -346,7 +346,7 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows on all users' windows. */ public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2; - + /** * Window type: system window, such as low power alert. These windows * are always on top of application windows. @@ -366,7 +366,7 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5; - + /** * Window type: system overlay windows, which need to be displayed * on top of everything else. These windows must not take input @@ -374,7 +374,7 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6; - + /** * Window type: priority phone UI, which needs to be displayed even if * the keyguard is active. These windows must not take input @@ -382,26 +382,26 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows on all users' windows. */ public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7; - + /** * Window type: panel that slides out from the status bar * In multiuser systems shows on all users' windows. */ public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8; - + /** * Window type: dialogs that the keyguard shows * In multiuser systems shows on all users' windows. */ public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9; - + /** * Window type: internal system error windows, appear on top of * everything they can. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10; - + /** * Window type: internal input methods windows, which appear above * the normal UI. Application windows may be resized or panned to keep @@ -581,16 +581,16 @@ public interface WindowManager extends ViewManager { /** @deprecated this is ignored, this value is set automatically when needed. */ @Deprecated public static final int MEMORY_TYPE_PUSH_BUFFERS = 3; - + /** * @deprecated this is ignored */ @Deprecated public int memoryType; - + /** Window flag: as long as this window is visible to the user, allow - * the lock screen to activate while the screen is on. - * This can be used independently, or in combination with + * the lock screen to activate while the screen is on. + * This can be used independently, or in combination with * {@link #FLAG_KEEP_SCREEN_ON} and/or {@link #FLAG_SHOW_WHEN_LOCKED} */ public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001; @@ -608,26 +608,26 @@ public interface WindowManager extends ViewManager { * instead go to whatever focusable window is behind it. This flag * will also enable {@link #FLAG_NOT_TOUCH_MODAL} whether or not that * is explicitly set. - * + * * <p>Setting this flag also implies that the window will not need to * interact with - * a soft input method, so it will be Z-ordered and positioned + * a soft input method, so it will be Z-ordered and positioned * independently of any active input method (typically this means it * gets Z-ordered on top of the input method, so it can use the full * screen for its content and cover the input method if needed. You * can use {@link #FLAG_ALT_FOCUSABLE_IM} to modify this behavior. */ public static final int FLAG_NOT_FOCUSABLE = 0x00000008; - + /** Window flag: this window can never receive touch events. */ public static final int FLAG_NOT_TOUCHABLE = 0x00000010; - + /** Window flag: even when this window is focusable (its * {@link #FLAG_NOT_FOCUSABLE} is not set), allow any pointer events * outside of the window to be sent to the windows behind it. Otherwise * it will consume all pointer events itself, regardless of whether they * are inside of the window. */ public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020; - + /** Window flag: when set, if the device is asleep when the touch * screen is pressed, you will receive this first touch event. Usually * the first touch event is consumed by the system since the user can @@ -637,21 +637,21 @@ public interface WindowManager extends ViewManager { */ @Deprecated public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040; - + /** Window flag: as long as this window is visible to the user, keep * the device's screen turned on and bright. */ public static final int FLAG_KEEP_SCREEN_ON = 0x00000080; - + /** Window flag: place the window within the entire screen, ignoring * decorations around the border (such as the status bar). The * window must correctly position its contents to take the screen * decoration into account. This flag is normally set for you * by Window as described in {@link Window#setFlags}. */ public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100; - + /** Window flag: allow window to extend outside of the screen. */ public static final int FLAG_LAYOUT_NO_LIMITS = 0x00000200; - + /** * Window flag: hide all screen decorations (such as the status bar) while * this window is displayed. This allows the window to use the entire @@ -673,17 +673,17 @@ public interface WindowManager extends ViewManager { * {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_Fullscreen}.</p> */ public static final int FLAG_FULLSCREEN = 0x00000400; - + /** Window flag: override {@link #FLAG_FULLSCREEN} and force the * screen decorations (such as the status bar) to be shown. */ public static final int FLAG_FORCE_NOT_FULLSCREEN = 0x00000800; - + /** Window flag: turn on dithering when compositing this window to * the screen. * @deprecated This flag is no longer used. */ @Deprecated public static final int FLAG_DITHER = 0x00001000; - + /** Window flag: treat the content of the window as secure, preventing * it from appearing in screenshots or from being viewed on non-secure * displays. @@ -692,21 +692,21 @@ public interface WindowManager extends ViewManager { * secure surfaces and secure displays. */ public static final int FLAG_SECURE = 0x00002000; - + /** Window flag: a special mode where the layout parameters are used * to perform scaling of the surface when it is composited to the * screen. */ public static final int FLAG_SCALED = 0x00004000; - + /** Window flag: intended for windows that will often be used when the user is * holding the screen against their face, it will aggressively filter the event * stream to prevent unintended presses in this situation that may not be - * desired for a particular window, when such an event stream is detected, the + * desired for a particular window, when such an event stream is detected, the * application will receive a CANCEL motion event to indicate this so applications - * can handle this accordingly by taking no action on the event + * can handle this accordingly by taking no action on the event * until the finger is released. */ public static final int FLAG_IGNORE_CHEEK_PRESSES = 0x00008000; - + /** Window flag: a special option only for use in combination with * {@link #FLAG_LAYOUT_IN_SCREEN}. When requesting layout in the * screen your window may appear on top of or behind screen decorations @@ -715,7 +715,7 @@ public interface WindowManager extends ViewManager { * content is not covered by screen decorations. This flag is normally * set for you by Window as described in {@link Window#setFlags}.*/ public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000; - + /** Window flag: invert the state of {@link #FLAG_NOT_FOCUSABLE} with * respect to how this window interacts with the current method. That * is, if FLAG_NOT_FOCUSABLE is set and this flag is set, then the @@ -726,7 +726,7 @@ public interface WindowManager extends ViewManager { * to use more space and cover the input method. */ public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000; - + /** Window flag: if you have set {@link #FLAG_NOT_TOUCH_MODAL}, you * can set this flag to receive a single special MotionEvent with * the action @@ -736,7 +736,7 @@ public interface WindowManager extends ViewManager { * first down as an ACTION_OUTSIDE. */ public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000; - + /** Window flag: special flag to let windows be shown when the screen * is locked. This will let application windows take precedence over * key guard or any other lock screens. Can be used with @@ -766,13 +766,13 @@ public interface WindowManager extends ViewManager { * {@link android.R.style#Theme_DeviceDefault_Wallpaper_NoTitleBar}.</p> */ public static final int FLAG_SHOW_WALLPAPER = 0x00100000; - + /** Window flag: when set as a window is being added or made * visible, once the window has been shown then the system will * poke the power manager's user activity (as if the user had woken * up the device) to turn the screen on. */ public static final int FLAG_TURN_SCREEN_ON = 0x00200000; - + /** Window flag: when set the window will cause the keyguard to * be dismissed, only if it is not a secure lock keyguard. Because such * a keyguard is not needed for security, it will never re-appear if @@ -786,7 +786,7 @@ public interface WindowManager extends ViewManager { * also been set. */ public static final int FLAG_DISMISS_KEYGUARD = 0x00400000; - + /** Window flag: when set the window will accept for touch events * outside of its bounds to be sent to other windows that also * support split touch. When this flag is not set, the first pointer @@ -798,7 +798,7 @@ public interface WindowManager extends ViewManager { * to be split across multiple windows. */ public static final int FLAG_SPLIT_TOUCH = 0x00800000; - + /** * <p>Indicates whether this window should be hardware accelerated. * Requesting hardware acceleration does not guarantee it will happen.</p> @@ -940,7 +940,7 @@ public interface WindowManager extends ViewManager { /** * Various behavioral options/flags. Default is none. - * + * * @see #FLAG_ALLOW_LOCK_WHILE_SCREEN_ON * @see #FLAG_DIM_BEHIND * @see #FLAG_NOT_FOCUSABLE @@ -1041,10 +1041,10 @@ public interface WindowManager extends ViewManager { * as if it was. * Like {@link #FLAG_HARDWARE_ACCELERATED} except for trusted system windows * that need hardware acceleration (e.g. LockScreen), where hardware acceleration - * is generally disabled. This flag must be specified in addition to + * is generally disabled. This flag must be specified in addition to * {@link #FLAG_HARDWARE_ACCELERATED} to enable hardware acceleration for system * windows. - * + * * @hide */ public static final int PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED = 0x00000001; @@ -1055,7 +1055,7 @@ public interface WindowManager extends ViewManager { * If certain parts of the UI that really do want to use hardware * acceleration, this flag can be set to force it. This is basically * for the lock screen. Anyone else using it, you are probably wrong. - * + * * @hide */ public static final int PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED = 0x00000002; @@ -1194,63 +1194,63 @@ public interface WindowManager extends ViewManager { } return false; } - + /** * Mask for {@link #softInputMode} of the bits that determine the * desired visibility state of the soft input area for this window. */ public static final int SOFT_INPUT_MASK_STATE = 0x0f; - + /** * Visibility state for {@link #softInputMode}: no state has been specified. */ public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0; - + /** * Visibility state for {@link #softInputMode}: please don't change the state of * the soft input area. */ public static final int SOFT_INPUT_STATE_UNCHANGED = 1; - + /** * Visibility state for {@link #softInputMode}: please hide any soft input * area when normally appropriate (when the user is navigating * forward to your window). */ public static final int SOFT_INPUT_STATE_HIDDEN = 2; - + /** * Visibility state for {@link #softInputMode}: please always hide any * soft input area when this window receives focus. */ public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3; - + /** * Visibility state for {@link #softInputMode}: please show the soft * input area when normally appropriate (when the user is navigating * forward to your window). */ public static final int SOFT_INPUT_STATE_VISIBLE = 4; - + /** * Visibility state for {@link #softInputMode}: please always make the * soft input area visible when this window receives input focus. */ public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5; - + /** * Mask for {@link #softInputMode} of the bits that determine the * way that the window should be adjusted to accommodate the soft * input window. */ public static final int SOFT_INPUT_MASK_ADJUST = 0xf0; - + /** Adjustment option for {@link #softInputMode}: nothing specified. * The system will try to pick one or * the other depending on the contents of the window. */ public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00; - + /** Adjustment option for {@link #softInputMode}: set to allow the * window to be resized when an input * method is shown, so that its contents are not covered by the input @@ -1263,7 +1263,7 @@ public interface WindowManager extends ViewManager { * not resize, but will stay fullscreen. */ public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10; - + /** Adjustment option for {@link #softInputMode}: set to have a window * pan when an input method is * shown, so it doesn't need to deal with resizing but just panned @@ -1273,7 +1273,7 @@ public interface WindowManager extends ViewManager { * the other depending on the contents of the window. */ public static final int SOFT_INPUT_ADJUST_PAN = 0x20; - + /** Adjustment option for {@link #softInputMode}: set to have a window * not adjust for a shown input method. The window will not be resized, * and it will not be panned to make its focus visible. @@ -1292,7 +1292,7 @@ public interface WindowManager extends ViewManager { /** * Desired operating mode for any soft input area. May be any combination * of: - * + * * <ul> * <li> One of the visibility states * {@link #SOFT_INPUT_STATE_UNSPECIFIED}, {@link #SOFT_INPUT_STATE_UNCHANGED}, @@ -1309,7 +1309,7 @@ public interface WindowManager extends ViewManager { * {@link android.R.attr#windowSoftInputMode} attribute.</p> */ public int softInputMode; - + /** * Placement of window within the screen as per {@link Gravity}. Both * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int, @@ -1326,7 +1326,7 @@ public interface WindowManager extends ViewManager { * @see Gravity */ public int gravity; - + /** * The horizontal margin, as a percentage of the container's width, * between the container and the widget. See @@ -1335,7 +1335,7 @@ public interface WindowManager extends ViewManager { * field is added with {@link #x} to supply the <var>xAdj</var> parameter. */ public float horizontalMargin; - + /** * The vertical margin, as a percentage of the container's height, * between the container and the widget. See @@ -1361,26 +1361,26 @@ public interface WindowManager extends ViewManager { * @hide */ public boolean hasManualSurfaceInsets; - + /** * The desired bitmap format. May be one of the constants in * {@link android.graphics.PixelFormat}. Default is OPAQUE. */ public int format; - + /** * A style resource defining the animations to use for this window. * This must be a system resource; it can not be an application resource * because the window manager does not have access to applications. */ public int windowAnimations; - + /** * An alpha value to apply to this entire window. * An alpha of 1.0 means fully opaque and 0.0 means fully transparent */ public float alpha = 1.0f; - + /** * When {@link #FLAG_DIM_BEHIND} is set, this is the amount of dimming * to apply. Range is from 1.0 for completely opaque to 0.0 for no @@ -1408,7 +1408,7 @@ public interface WindowManager extends ViewManager { * to the hightest value when this window is in front. */ public static final float BRIGHTNESS_OVERRIDE_FULL = 1.0f; - + /** * This can be used to override the user's preferred brightness of * the screen. A value of less than 0, the default, means to use the @@ -1416,7 +1416,7 @@ public interface WindowManager extends ViewManager { * dark to full bright. */ public float screenBrightness = BRIGHTNESS_OVERRIDE_NONE; - + /** * This can be used to override the standard behavior of the button and * keyboard backlights. A value of less than 0, the default, means to @@ -1450,7 +1450,7 @@ public interface WindowManager extends ViewManager { * opaque windows have the #FLAG_FULLSCREEN bit set and are not covered * by other windows. All other situations default to the * {@link #ROTATION_ANIMATION_ROTATE} behavior. - * + * * @see #ROTATION_ANIMATION_ROTATE * @see #ROTATION_ANIMATION_CROSSFADE * @see #ROTATION_ANIMATION_JUMPCUT @@ -1462,18 +1462,18 @@ public interface WindowManager extends ViewManager { * you. */ public IBinder token = null; - + /** * Name of the package owning this window. */ public String packageName = null; - + /** * Specific orientation value for a window. * May be any of the same values allowed - * for {@link android.content.pm.ActivityInfo#screenOrientation}. - * If not set, a default value of - * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} + * for {@link android.content.pm.ActivityInfo#screenOrientation}. + * If not set, a default value of + * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} * will be used. */ public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -1482,13 +1482,28 @@ public interface WindowManager extends ViewManager { * The preferred refresh rate for the window. * * This must be one of the supported refresh rates obtained for the display(s) the window - * is on. + * is on. The selected refresh rate will be applied to the display's default mode. + * + * This value is ignored if {@link #preferredDisplayModeId} is set. * * @see Display#getSupportedRefreshRates() + * @deprecated use {@link #preferredDisplayModeId} instead */ + @Deprecated public float preferredRefreshRate; /** + * Id of the preferred display mode for the window. + * <p> + * This must be one of the supported modes obtained for the display(s) the window is on. + * A value of {@code 0} means no preference. + * + * @see Display#getSupportedModes() + * @see Display.Mode#getModeId() + */ + public int preferredDisplayModeId; + + /** * Control the visibility of the status bar. * * @see View#STATUS_BAR_VISIBLE @@ -1505,7 +1520,7 @@ public interface WindowManager extends ViewManager { /** * Get callbacks about the system ui visibility changing. - * + * * TODO: Maybe there should be a bitfield of optional callbacks that we need. * * @hide @@ -1571,34 +1586,34 @@ public interface WindowManager extends ViewManager { type = TYPE_APPLICATION; format = PixelFormat.OPAQUE; } - + public LayoutParams(int _type) { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = _type; format = PixelFormat.OPAQUE; } - + public LayoutParams(int _type, int _flags) { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = _type; flags = _flags; format = PixelFormat.OPAQUE; } - + public LayoutParams(int _type, int _flags, int _format) { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = _type; flags = _flags; format = _format; } - + public LayoutParams(int w, int h, int _type, int _flags, int _format) { super(w, h); type = _type; flags = _flags; format = _format; } - + public LayoutParams(int w, int h, int xpos, int ypos, int _type, int _flags, int _format) { super(w, h); @@ -1608,14 +1623,14 @@ public interface WindowManager extends ViewManager { flags = _flags; format = _format; } - + public final void setTitle(CharSequence title) { if (null == title) title = ""; - + mTitle = TextUtils.stringOrSpannedString(title); } - + public final CharSequence getTitle() { return mTitle; } @@ -1660,6 +1675,7 @@ public interface WindowManager extends ViewManager { TextUtils.writeToParcel(mTitle, out, parcelableFlags); out.writeInt(screenOrientation); out.writeFloat(preferredRefreshRate); + out.writeInt(preferredDisplayModeId); out.writeInt(systemUiVisibility); out.writeInt(subtreeSystemUiVisibility); out.writeInt(hasSystemUiListeners ? 1 : 0); @@ -1683,8 +1699,8 @@ public interface WindowManager extends ViewManager { return new LayoutParams[size]; } }; - - + + public LayoutParams(Parcel in) { width = in.readInt(); height = in.readInt(); @@ -1709,6 +1725,7 @@ public interface WindowManager extends ViewManager { mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); screenOrientation = in.readInt(); preferredRefreshRate = in.readFloat(); + preferredDisplayModeId = in.readInt(); systemUiVisibility = in.readInt(); subtreeSystemUiVisibility = in.readInt(); hasSystemUiListeners = in.readInt() != 0; @@ -1757,6 +1774,8 @@ public interface WindowManager extends ViewManager { /** {@hide} */ public static final int NEEDS_MENU_KEY_CHANGED = 1 << 22; /** {@hide} */ + public static final int PREFERRED_DISPLAY_MODE_ID = 1 << 23; + /** {@hide} */ public static final int EVERYTHING_CHANGED = 0xffffffff; // internal buffer to backup/restore parameters under compatibility mode. @@ -1863,7 +1882,7 @@ public interface WindowManager extends ViewManager { rotationAnimation = o.rotationAnimation; changes |= ROTATION_ANIMATION_CHANGED; } - + if (screenOrientation != o.screenOrientation) { screenOrientation = o.screenOrientation; changes |= SCREEN_ORIENTATION_CHANGED; @@ -1874,6 +1893,11 @@ public interface WindowManager extends ViewManager { changes |= PREFERRED_REFRESH_RATE_CHANGED; } + if (preferredDisplayModeId != o.preferredDisplayModeId) { + preferredDisplayModeId = o.preferredDisplayModeId; + changes |= PREFERRED_DISPLAY_MODE_ID; + } + if (systemUiVisibility != o.systemUiVisibility || subtreeSystemUiVisibility != o.subtreeSystemUiVisibility) { systemUiVisibility = o.systemUiVisibility; @@ -1924,7 +1948,7 @@ public interface WindowManager extends ViewManager { Log.d("Debug", "WindowManager.LayoutParams={title=" + mTitle + "}"); return ""; } - + @Override public String toString() { StringBuilder sb = new StringBuilder(256); @@ -1996,6 +2020,10 @@ public interface WindowManager extends ViewManager { sb.append(" preferredRefreshRate="); sb.append(preferredRefreshRate); } + if (preferredDisplayModeId != 0) { + sb.append(" preferredDisplayMode="); + sb.append(preferredDisplayModeId); + } if (systemUiVisibility != 0) { sb.append(" sysui=0x"); sb.append(Integer.toHexString(systemUiVisibility)); diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 57558ff..c16578e 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -21,7 +21,6 @@ import android.app.ActivityManager; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.res.Configuration; -import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -70,12 +69,6 @@ public final class WindowManagerGlobal { public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4; /** - * The window manager is currently animating. It will call - * IWindow.doneAnimating() when done. - */ - public static final int RELAYOUT_RES_ANIMATING = 0x8; - - /** * Flag for relayout: the client will be later giving * internal insets; as a result, the window will not impact other window * layouts until the insets are given. @@ -552,7 +545,7 @@ public final class WindowManagerGlobal { for (int i = 0; i < count; i++) { if (token == null || mParams.get(i).token == token) { ViewRootImpl root = mRoots.get(i); - root.setStopped(stopped); + root.setWindowStopped(stopped); } } } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index c785149..42e6766 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -1011,6 +1011,10 @@ public class AccessibilityNodeInfo implements Parcelable { public void addAction(AccessibilityAction action) { enforceNotSealed(); + addActionUnchecked(action); + } + + private void addActionUnchecked(AccessibilityAction action) { if (action == null) { return; } @@ -2846,9 +2850,9 @@ public class AccessibilityNodeInfo implements Parcelable { addLegacyStandardActions(legacyStandardActions); final int nonLegacyActionCount = actionCount - Integer.bitCount(legacyStandardActions); for (int i = 0; i < nonLegacyActionCount; i++) { - AccessibilityAction action = new AccessibilityAction( + final AccessibilityAction action = new AccessibilityAction( parcel.readInt(), parcel.readCharSequence()); - addAction(action); + addActionUnchecked(action); } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 040fd37..568e160 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -350,16 +350,6 @@ public final class InputMethodManager { */ private CursorAnchorInfo mCursorAnchorInfo = null; - /** - * The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}. - */ - private final int[] mViewTopLeft = new int[2]; - - /** - * The matrix to convert the view location into screen coordinates in {@link #updateCursor}. - */ - private final Matrix mViewToScreenMatrix = new Matrix(); - // ----------------------------------------------------------- /** diff --git a/core/java/android/webkit/ViewAssistStructure.java b/core/java/android/webkit/ViewAssistStructure.java new file mode 100644 index 0000000..6f7a645 --- /dev/null +++ b/core/java/android/webkit/ViewAssistStructure.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.graphics.Rect; +import android.os.Bundle; +import android.text.TextPaint; +import android.view.ViewStructure; + + +/** + * TODO This class is temporary. It will be deleted once we update Webview APK to use the + * new ViewStructure method. + * @hide + */ +public class ViewAssistStructure extends android.view.ViewAssistStructure { + + private ViewStructure mV; + + public ViewAssistStructure(ViewStructure v) { + mV = v; + } + + @Override + public void setId(int id, String packageName, String typeName, String entryName) { + mV.setId(id, packageName, typeName, entryName); + } + + @Override + public void setDimens(int left, int top, int scrollX, int scrollY, int width, + int height) { + mV.setDimens(left, top, scrollX, scrollY, width, height); + } + + @Override + public void setVisibility(int visibility) { + mV.setVisibility(visibility); + } + + @Override + public void setAssistBlocked(boolean state) { + mV.setAssistBlocked(state); + } + + @Override + public void setEnabled(boolean state) { + mV.setEnabled(state); + } + + @Override + public void setClickable(boolean state) { + mV.setClickable(state); + } + + @Override + public void setLongClickable(boolean state) { + mV.setLongClickable(state); + } + + @Override + public void setStylusButtonPressable(boolean state) { + mV.setStylusButtonPressable(state); + } + + @Override + public void setFocusable(boolean state) { + mV.setFocusable(state); + } + + @Override + public void setFocused(boolean state) { + mV.setFocused(state); + } + + @Override + public void setAccessibilityFocused(boolean state) { + mV.setAccessibilityFocused(state); + } + + @Override + public void setCheckable(boolean state) { + mV.setCheckable(state); + } + + @Override + public void setChecked(boolean state) { + mV.setChecked(state); + } + + @Override + public void setSelected(boolean state) { + mV.setSelected(state); + } + + @Override + public void setActivated(boolean state) { + mV.setActivated(state); + } + + @Override + public void setClassName(String className) { + mV.setClassName(className); + } + + @Override + public void setContentDescription(CharSequence contentDescription) { + mV.setContentDescription(contentDescription); + } + + @Override + public void setText(CharSequence text) { + mV.setText(text); + } + + @Override + public void setText(CharSequence text, int selectionStart, int selectionEnd) { + mV.setText(text, selectionStart, selectionEnd); + } + + @Override + public void setTextPaint(TextPaint paint) { + mV.setTextPaint(paint); + } + + @Override + public void setHint(CharSequence hint) { + mV.setHint(hint); + } + + @Override + public CharSequence getText() { + return mV.getText(); + } + + @Override + public int getTextSelectionStart() { + return mV.getTextSelectionStart(); + } + + @Override + public int getTextSelectionEnd() { + return mV.getTextSelectionEnd(); + } + + @Override + public CharSequence getHint() { + return mV.getHint(); + } + + @Override + public Bundle getExtras() { + return mV.getExtras(); + } + + @Override + public boolean hasExtras() { + return mV.hasExtras(); + } + + @Override + public void setChildCount(int num) { + mV.setChildCount(num); + } + + @Override + public int getChildCount() { + return mV.getChildCount(); + } + + @Override + public android.view.ViewAssistStructure newChild(int index) { + return mV.newChild(index); + } + + @Override + public android.view.ViewAssistStructure asyncNewChild(int index) { + return mV.asyncNewChild(index); + } + + @Override + public void asyncCommit() { + mV.asyncCommit(); + } + + @Override + public Rect getTempRect() { + return mV.getTempRect(); + } +} diff --git a/core/java/android/webkit/WebMessagePort.java b/core/java/android/webkit/WebMessagePort.java index eab27bd..5f33c7b 100644 --- a/core/java/android/webkit/WebMessagePort.java +++ b/core/java/android/webkit/WebMessagePort.java @@ -16,12 +16,13 @@ package android.webkit; +import android.annotation.SystemApi; import android.os.Handler; /** - * The Java representation of the HTML5 Message Port. See - * https://html.spec.whatwg.org/multipage/comms.html#messageport - * for definition of MessagePort in HTML5. + * The Java representation of the + * <a href="https://html.spec.whatwg.org/multipage/comms.html#messageport"> + * HTML5 message ports.</a> * * A Message port represents one endpoint of a Message Channel. In Android * webview, there is no separate Message Channel object. When a message channel @@ -32,6 +33,19 @@ import android.os.Handler; * When a message port is first created or received via transfer, it does not * have a WebMessageCallback to receive web messages. The messages are queued until * a WebMessageCallback is set. + * + * A message port should be closed when it is not used by the embedder application + * anymore. A closed port cannot be transferred or cannot be reopened to send + * messages. Close can be called multiple times. + * + * When a port is transferred to JS, it cannot be used to send or receive messages + * at the Java side anymore. Different from HTML5 Spec, a port cannot be transferred + * if one of these has ever happened: i. a message callback was set, ii. a message was + * posted on it. A transferred port cannot be closed by the application, since + * the ownership is also transferred. + * + * It is possible to transfer both ports of a channel to JS, for example for + * communication between subframes. */ public abstract class WebMessagePort { @@ -55,6 +69,13 @@ public abstract class WebMessagePort { } /** + * Constructor. + * @hide + */ + @SystemApi + public WebMessagePort() { } + + /** * Post a WebMessage to the entangled port. * * @param message the message from Java to JS. diff --git a/core/java/android/webkit/WebResourceError.java b/core/java/android/webkit/WebResourceError.java index 080d174..90693f3 100644 --- a/core/java/android/webkit/WebResourceError.java +++ b/core/java/android/webkit/WebResourceError.java @@ -16,6 +16,8 @@ package android.webkit; +import android.annotation.SystemApi; + /** * Encapsulates information about errors occured during loading of web resources. See * {@link WebViewClient#onReceivedError(WebView, WebResourceRequest, WebResourceError) WebViewClient.onReceivedError(WebView, WebResourceRequest, WebResourceError)} @@ -34,6 +36,16 @@ public abstract class WebResourceError { * and thus can be used for communicating the problem to the user. * * @return The description of the error + * + * Will become abstract after updated WebView.apk will be submitted + * into the Android tree. + */ + public CharSequence getDescription() { return ""; } + + /** + * This class can not be subclassed by applications. + * @hide */ - public abstract String getDescription(); + @SystemApi + public WebResourceError() {} } diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java index a42aaa7..3a925c8 100644 --- a/core/java/android/webkit/WebResourceResponse.java +++ b/core/java/android/webkit/WebResourceResponse.java @@ -20,12 +20,15 @@ import java.io.InputStream; import java.io.StringBufferInputStream; import java.util.Map; +import android.annotation.SystemApi; + /** * Encapsulates a resource response. Applications can return an instance of this * class from {@link WebViewClient#shouldInterceptRequest} to provide a custom * response when the WebView requests a particular resource. */ public class WebResourceResponse extends WebResourceResponseBase { + private boolean mImmutable; private String mMimeType; private String mEncoding; private int mStatusCode; @@ -80,13 +83,15 @@ public class WebResourceResponse extends WebResourceResponseBase { * @param mimeType The resource response's MIME type */ public void setMimeType(String mimeType) { + checkImmutable(); mMimeType = mimeType; } /** - * {@inheritDoc} + * Gets the resource response's MIME type. + * + * @return The resource response's MIME type */ - @Override public String getMimeType() { return mMimeType; } @@ -98,13 +103,15 @@ public class WebResourceResponse extends WebResourceResponseBase { * @param encoding The resource response's encoding */ public void setEncoding(String encoding) { + checkImmutable(); mEncoding = encoding; } /** - * {@inheritDoc} + * Gets the resource response's encoding. + * + * @return The resource response's encoding */ - @Override public String getEncoding() { return mEncoding; } @@ -118,6 +125,7 @@ public class WebResourceResponse extends WebResourceResponseBase { * and not empty. */ public void setStatusCodeAndReasonPhrase(int statusCode, String reasonPhrase) { + checkImmutable(); if (statusCode < 100) throw new IllegalArgumentException("statusCode can't be less than 100."); if (statusCode > 599) @@ -140,17 +148,19 @@ public class WebResourceResponse extends WebResourceResponseBase { } /** - * {@inheritDoc} + * Gets the resource response's status code. + * + * @return The resource response's status code. */ - @Override public int getStatusCode() { return mStatusCode; } /** - * {@inheritDoc} + * Gets the description of the resource response's status code. + * + * @return The description of the resource response's status code. */ - @Override public String getReasonPhrase() { return mReasonPhrase; } @@ -161,13 +171,15 @@ public class WebResourceResponse extends WebResourceResponseBase { * @param headers Mapping of header name -> header value. */ public void setResponseHeaders(Map<String, String> headers) { + checkImmutable(); mResponseHeaders = headers; } /** - * {@inheritDoc} + * Gets the headers for the resource response. + * + * @return The headers for the resource response. */ - @Override public Map<String, String> getResponseHeaders() { return mResponseHeaders; } @@ -180,6 +192,7 @@ public class WebResourceResponse extends WebResourceResponseBase { * StringBufferInputStream. */ public void setData(InputStream data) { + checkImmutable(); // If data is (or is a subclass of) StringBufferInputStream if (data != null && StringBufferInputStream.class.isAssignableFrom(data.getClass())) { throw new IllegalArgumentException("StringBufferInputStream is deprecated and must " + @@ -189,10 +202,32 @@ public class WebResourceResponse extends WebResourceResponseBase { } /** - * {@inheritDoc} + * Gets the input stream that provides the resource response's data. + * + * @return The input stream that provides the resource response's data */ - @Override public InputStream getData() { return mInputStream; } + + /** + * The internal version of the constructor that doesn't perform arguments checks. + * @hide + */ + @SystemApi + public WebResourceResponse(boolean immutable, String mimeType, String encoding, int statusCode, + String reasonPhrase, Map<String, String> responseHeaders, InputStream data) { + mImmutable = immutable; + mMimeType = mimeType; + mEncoding = encoding; + mStatusCode = statusCode; + mReasonPhrase = reasonPhrase; + mResponseHeaders = responseHeaders; + mInputStream = data; + } + + private void checkImmutable() { + if (mImmutable) + throw new IllegalStateException("This WebResourceResponse instance is immutable"); + } } diff --git a/core/java/android/webkit/WebResourceResponseBase.java b/core/java/android/webkit/WebResourceResponseBase.java index cffde82..69eb397 100644 --- a/core/java/android/webkit/WebResourceResponseBase.java +++ b/core/java/android/webkit/WebResourceResponseBase.java @@ -16,53 +16,9 @@ package android.webkit; -import java.io.InputStream; -import java.util.Map; - /** - * Encapsulates a resource response received from the server. - * This is an abstract class used by WebView callbacks. + * This class will be deleted after updated WebView.apk will be submitted + * into the Android tree. */ public abstract class WebResourceResponseBase { - /** - * Gets the resource response's MIME type. - * - * @return The resource response's MIME type - */ - public abstract String getMimeType(); - - /** - * Gets the resource response's encoding. - * - * @return The resource response's encoding - */ - public abstract String getEncoding(); - - /** - * Gets the resource response's status code. - * - * @return The resource response's status code. - */ - public abstract int getStatusCode(); - - /** - * Gets the description of the resource response's status code. - * - * @return The description of the resource response's status code. - */ - public abstract String getReasonPhrase(); - - /** - * Gets the headers for the resource response. - * - * @return The headers for the resource response. - */ - public abstract Map<String, String> getResponseHeaders(); - - /** - * Gets the input stream that provides the resource response's data. - * - * @return The input stream that provides the resource response's data - */ - public abstract InputStream getData(); } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 453e4f5..cfa347f 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -1340,11 +1340,13 @@ public abstract class WebSettings { * offscreen but attached to a window. Turning this on can avoid * rendering artifacts when animating an offscreen WebView on-screen. * Offscreen WebViews in this mode use more memory. The default value is - * false. + * false.<br> * Please follow these guidelines to limit memory usage: - * - WebView size should be not be larger than the device screen size. - * - Limit use of this mode to a small number of WebViews. Use it for + * <ul> + * <li> WebView size should be not be larger than the device screen size. + * <li> Limit use of this mode to a small number of WebViews. Use it for * visible WebViews and WebViews about to be animated to visible. + * </ul> */ public abstract void setOffscreenPreRaster(boolean enabled); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index a261aaf..92615f3 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -41,7 +41,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAssistStructure; +import android.view.ViewStructure; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewHierarchyEncoder; @@ -366,15 +366,15 @@ public class WebView extends AbsoluteLayout } /** - * Callback interface supplied to {@link #insertVisualStateCallback} for receiving + * Callback interface supplied to {@link #postVisualStateCallback} for receiving * notifications about the visual state. */ public static abstract class VisualStateCallback { /** * Invoked when the visual state is ready to be drawn in the next {@link #onDraw}. * - * @param requestId the id supplied to the corresponding {@link #insertVisualStateCallback} - * request + * @param requestId The identifier passed to {@link #postVisualStateCallback} when this + * callback was posted. */ public abstract void onComplete(long requestId); } @@ -1125,15 +1125,18 @@ public class WebView extends AbsoluteLayout } /** - * Inserts a {@link VisualStateCallback}. + * Posts a {@link VisualStateCallback}, which will be called when + * the current state of the WebView is ready to be drawn. * - * <p>Updates to the the DOM are reflected asynchronously such that when the DOM is updated the - * subsequent {@link WebView#onDraw} invocation might not reflect those updates. The + * <p>Because updates to the the DOM are processed asynchronously, updates to the DOM may not + * immediately be reflected visually by subsequent {@link WebView#onDraw} invocations. The * {@link VisualStateCallback} provides a mechanism to notify the caller when the contents of - * the DOM at the current time are ready to be drawn the next time the {@link WebView} draws. - * By current time we mean the time at which this API was called. The next draw after the - * callback completes is guaranteed to reflect all the updates to the DOM applied before the - * current time, but it may also contain updates applied after the current time.</p> + * the DOM at the current time are ready to be drawn the next time the {@link WebView} + * draws.</p> + * + * <p>The next draw after the callback completes is guaranteed to reflect all the updates to the + * DOM up to the the point at which the {@link VisualStateCallback} was posted, but it may also + * contain updates applied after the callback was posted.</p> * * <p>The state of the DOM covered by this API includes the following: * <ul> @@ -1164,15 +1167,15 @@ public class WebView extends AbsoluteLayout * {@link VisualStateCallback#onComplete} method.</li> * </ul></p> * - * <p>When using this API it is also recommended to enable pre-rasterization if the - * {@link WebView} is offscreen to avoid flickering. See WebSettings#setOffscreenPreRaster for + * <p>When using this API it is also recommended to enable pre-rasterization if the {@link + * WebView} is offscreen to avoid flickering. See {@link WebSettings#setOffscreenPreRaster} for * more details and do consider its caveats.</p> * - * @param requestId an id that will be returned in the callback to allow callers to match - * requests with callbacks. - * @param callback the callback to be invoked. + * @param requestId An id that will be returned in the callback to allow callers to match + * requests with callbacks. + * @param callback The callback to be invoked. */ - public void insertVisualStateCallback(long requestId, VisualStateCallback callback) { + public void postVisualStateCallback(long requestId, VisualStateCallback callback) { checkThread(); mProvider.insertVisualStateCallback(requestId, callback); } @@ -1835,8 +1838,9 @@ public class WebView extends AbsoluteLayout /** * Creates a message channel to communicate with JS and returns the message * ports that represent the endpoints of this message channel. The HTML5 message - * channel functionality is described here: - * https://html.spec.whatwg.org/multipage/comms.html#messagechannel + * channel functionality is described + * <a href="https://html.spec.whatwg.org/multipage/comms.html#messagechannel">here + * </a> * * The returned message channels are entangled and already in started state. * @@ -1850,11 +1854,14 @@ public class WebView extends AbsoluteLayout /** * Post a message to main frame. The embedded application can restrict the * messages to a certain target origin. See - * https://html.spec.whatwg.org/multipage/comms.html#posting-messages - * for how target origin can be used. + * <a href="https://html.spec.whatwg.org/multipage/comms.html#posting-messages"> + * HTML5 spec</a> for how target origin can be used. * * @param message the WebMessage - * @param targetOrigin the target origin. + * @param targetOrigin the target origin. This is the origin of the page + * that is intended to receive the message. For best security + * practices, the user should not specify a wildcard (*) when + * specifying the origin. */ public void postMessageToMainFrame(WebMessage message, Uri targetOrigin) { checkThread(); @@ -2428,8 +2435,9 @@ public class WebView extends AbsoluteLayout } @Override - public void onProvideVirtualAssistStructure(ViewAssistStructure structure) { - mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure); + public void onProvideVirtualStructure(ViewStructure structure) { + ViewAssistStructure s = new ViewAssistStructure(structure); + mProvider.getViewDelegate().onProvideVirtualAssistStructure(s); } /** @hide */ diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 8a2b3fa..feed2b8 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -83,27 +83,31 @@ public class WebViewClient { } /** - * Notify the host application that the page commit is visible. + * Notify the host application that {@link android.webkit.WebView} content left over from + * previous page navigations will no longer be drawn. * - * <p>This is the earliest point at which we can guarantee that the contents of the previously - * loaded page will not longer be drawn in the next {@link WebView#onDraw}. The next draw will - * render the {@link WebView#setBackgroundColor background color} of the WebView or some of the - * contents from the committed page already. This callback may be useful when reusing - * {@link WebView}s to ensure that no stale content is shown. This method is only called for - * the main frame.</p> + * <p>This callback can be used to determine the point at which it is safe to make a recycled + * {@link android.webkit.WebView} visible, ensuring that no stale content is shown. It is called + * at the earliest point at which it can be guaranteed that {@link WebView#onDraw} will no + * longer draw any content from previous navigations. The next draw will display either the + * {@link WebView#setBackgroundColor background color} of the {@link WebView}, or some of the + * contents of the newly loaded page. * - * <p>This method is called when the state of the DOM at the point at which the - * body of the HTTP response (commonly the string of html) had started loading will be visible. - * If you set a background color for the page in the HTTP response body this will most likely - * be visible and perhaps some other elements. At that point no other resources had usually - * been loaded, so you can expect images for example to not be visible. If you want - * a finer level of granularity consider calling {@link WebView#insertVisualStateCallback} - * directly.</p> + * <p>This method is called when the body of the HTTP response has started loading, is reflected + * in the DOM, and will be visible in subsequent draws. This callback occurs early in the + * document loading process, and as such you should expect that linked resources (for example, + * css and images) may not be available.</p> * - * <p>Please note that all the conditions and recommendations presented in - * {@link WebView#insertVisualStateCallback} also apply to this API.<p> + * <p>For more fine-grained notification of visual state updates, see {@link + * WebView#postVisualStateCallback}.</p> * - * @param url the url of the committed page + * <p>Please note that all the conditions and recommendations applicable to + * {@link WebView#postVisualStateCallback} also apply to this API.<p> + * + * <p>This callback is only called for main frame navigations.</p> + * + * @param view The {@link android.webkit.WebView} for which the navigation occurred. + * @param url The URL corresponding to the page navigation that triggered this callback. */ public void onPageCommitVisible(WebView view, String url) { } @@ -229,11 +233,20 @@ public class WebViewClient { public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { if (request.isForMainFrame()) { onReceivedError(view, - error.getErrorCode(), error.getDescription(), request.getUrl().toString()); + error.getErrorCode(), error.getDescription().toString(), + request.getUrl().toString()); } } /** + * This method will be deleted after updated WebView.apk will be submitted + * into the Android tree. + */ + public void onReceivedHttpError( + WebView view, WebResourceRequest request, WebResourceResponseBase errorResponse) { + } + + /** * Notify the host application that an HTTP error has been received from the server while * loading a resource. HTTP errors have status codes >= 400. This callback will be called * for any resource (iframe, image, etc), not just for the main page. Thus, it is recommended to @@ -244,7 +257,7 @@ public class WebViewClient { * @param errorResponse Information about the error occured. */ public void onReceivedHttpError( - WebView view, WebResourceRequest request, WebResourceResponseBase errorResponse) { + WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { } /** diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index d5787de..00aba2a 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -32,7 +32,6 @@ import android.print.PrintDocumentAdapter; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAssistStructure; import android.view.ViewGroup.LayoutParams; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -299,7 +298,7 @@ public interface WebViewProvider { interface ViewDelegate { public boolean shouldDelayChildPressedState(); - public void onProvideVirtualAssistStructure(ViewAssistStructure structure); + public void onProvideVirtualAssistStructure(android.view.ViewAssistStructure structure); public AccessibilityNodeProvider getAccessibilityNodeProvider(); diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java index 51174c3..75c857c 100644 --- a/core/java/android/widget/ActivityChooserModel.java +++ b/core/java/android/widget/ActivityChooserModel.java @@ -40,6 +40,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -991,7 +992,7 @@ public class ActivityChooserModel extends DataSetObservable { } try { XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, null); + parser.setInput(fis, StandardCharsets.UTF_8.name()); int type = XmlPullParser.START_DOCUMENT; while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { @@ -1074,7 +1075,7 @@ public class ActivityChooserModel extends DataSetObservable { try { serializer.setOutput(fos, null); - serializer.startDocument("UTF-8", true); + serializer.startDocument(StandardCharsets.UTF_8.name(), true); serializer.startTag(null, TAG_HISTORICAL_RECORDS); final int recordCount = historicalRecords.size(); diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index 6feb94b..bb4a948 100644 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -244,7 +244,7 @@ public class AppSecurityPermissions { @Override public void onClick(DialogInterface dialog, int which) { PackageManager pm = getContext().getPackageManager(); - pm.revokePermission(mPackageName, mPerm.name, + pm.revokeRuntimePermission(mPackageName, mPerm.name, new UserHandle(mContext.getUserId())); PermissionItemView.this.setVisibility(View.GONE); } diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java index 6ab3828..442fb33 100644 --- a/core/java/android/widget/CalendarViewLegacyDelegate.java +++ b/core/java/android/widget/CalendarViewLegacyDelegate.java @@ -713,7 +713,7 @@ class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelega for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) { label = (TextView) mDayNamesHeader.getChildAt(i); if (mWeekDayTextAppearanceResId > -1) { - label.setTextAppearance(mContext, mWeekDayTextAppearanceResId); + label.setTextAppearance(mWeekDayTextAppearanceResId); } if (i < mDaysPerWeek + 1) { label.setText(mDayNamesShort[i - 1]); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 86a100f..a1194f7 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -209,6 +209,10 @@ public class Editor { // Set when this TextView gained focus with some text selected. Will start selection mode. boolean mCreatedWithASelection; + boolean mDoubleTap = false; + + private Runnable mSelectionModeWithoutSelectionRunnable; + // The span controller helps monitoring the changes to which the Editor needs to react: // - EasyEditSpans, for which we have some UI to display on attach and on hide // - SelectionSpans, for which we need to call updateSelection if an IME is attached @@ -286,6 +290,13 @@ public class Editor { mUndoManager.redo(owners, 1); // Redo 1 action. } + void replace() { + int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2; + stopSelectionActionMode(); + Selection.setSelection((Spannable) mTextView.getText(), middle); + showSuggestions(); + } + void onAttachedToWindow() { if (mShowErrorAfterAttach) { showError(); @@ -342,6 +353,11 @@ public class Editor { mTextView.removeCallbacks(mShowSuggestionRunnable); } + // Cancel the single tap delayed runnable. + if (mSelectionModeWithoutSelectionRunnable != null) { + mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); + } + destroyDisplayListsData(); if (mSpellChecker != null) { @@ -689,14 +705,12 @@ public class Editor { // FIXME - For this and similar methods we're not doing anything to check if there's // a LocaleSpan in the text, this may be something we should try handling or checking for. int retOffset = getWordIteratorWithText().prevBoundary(offset); - if (isPunctBoundaryBehind(retOffset, true /* isStart */)) { - // If we're on a punctuation boundary we should continue to get the - // previous offset until we're not longer on a punctuation boundary. - retOffset = getWordIteratorWithText().prevBoundary(retOffset); - while (!isPunctBoundaryBehind(retOffset, false /* isStart */) - && retOffset != BreakIterator.DONE) { - retOffset = getWordIteratorWithText().prevBoundary(retOffset); - } + if (getWordIteratorWithText().isOnPunctuation(retOffset)) { + // On punctuation boundary or within group of punctuation, find punctuation start. + retOffset = getWordIteratorWithText().getPunctuationBeginning(offset); + } else { + // Not on a punctuation boundary, find the word start. + retOffset = getWordIteratorWithText().getBeginning(offset); } if (retOffset == BreakIterator.DONE) { return offset; @@ -706,14 +720,12 @@ public class Editor { private int getWordEnd(int offset) { int retOffset = getWordIteratorWithText().nextBoundary(offset); - if (isPunctBoundaryForward(retOffset, true /* isStart */)) { - // If we're on a punctuation boundary we should continue to get the - // next offset until we're no longer on a punctuation boundary. - retOffset = getWordIteratorWithText().nextBoundary(retOffset); - while (!isPunctBoundaryForward(retOffset, false /* isStart */) - && retOffset != BreakIterator.DONE) { - retOffset = getWordIteratorWithText().nextBoundary(retOffset); - } + if (getWordIteratorWithText().isAfterPunctuation(retOffset)) { + // On punctuation boundary or within group of punctuation, find punctuation end. + retOffset = getWordIteratorWithText().getPunctuationEnd(offset); + } else { + // Not on a punctuation boundary, find the word end. + retOffset = getWordIteratorWithText().getEnd(offset); } if (retOffset == BreakIterator.DONE) { return offset; @@ -722,70 +734,6 @@ public class Editor { } /** - * Checks for punctuation boundaries for the provided offset and the - * previous character. - * - * @param offset The offset to check from. - * @param isStart Whether the boundary being checked for is at the start or - * end of a punctuation sequence. - * @return Whether this is a punctuation boundary. - */ - private boolean isPunctBoundaryBehind(int offset, boolean isStart) { - CharSequence text = mTextView.getText(); - if (offset == BreakIterator.DONE || offset > text.length() || offset == 0) { - return false; - } - int cp = Character.codePointAt(text, offset); - int prevCp = Character.codePointBefore(text, offset); - - if (isPunctuation(cp)) { - // If it's the start, the current cp and the prev cp are - // punctuation. If it's at the end of a punctuation sequence the - // current is punctuation and the prev is not. - return isStart ? isPunctuation(prevCp) : !isPunctuation(prevCp); - } - return false; - } - - /** - * Checks for punctuation boundaries for the provided offset and the next - * character. - * - * @param offset The offset to check from. - * @param isStart Whether the boundary being checked for is at the start or - * end of a punctuation sequence. - * @return Whether this is a punctuation boundary. - */ - private boolean isPunctBoundaryForward(int offset, boolean isStart) { - CharSequence text = mTextView.getText(); - if (offset == BreakIterator.DONE || offset > text.length() || offset == 0) { - return false; - } - int cp = Character.codePointBefore(text, offset); - int nextCpOffset = Math.min(offset + Character.charCount(cp), text.length() - 1); - int nextCp = Character.codePointBefore(text, nextCpOffset); - - if (isPunctuation(cp)) { - // If it's the start, the current cp and the next cp are - // punctuation. If it's at the end of a punctuation sequence the - // current is punctuation and the next is not. - return isStart ? isPunctuation(nextCp) : !isPunctuation(nextCp); - } - return false; - } - - private boolean isPunctuation(int cp) { - int type = Character.getType(cp); - return (type == Character.CONNECTOR_PUNCTUATION || - type == Character.DASH_PUNCTUATION || - type == Character.END_PUNCTUATION || - type == Character.FINAL_QUOTE_PUNCTUATION || - type == Character.INITIAL_QUOTE_PUNCTUATION || - type == Character.OTHER_PUNCTUATION || - type == Character.START_PUNCTUATION); - } - - /** * Adjusts selection to the word under last touch offset. Return true if the operation was * successfully performed. */ @@ -1867,6 +1815,7 @@ public class Editor { // When the cursor moves, the word that was typed may need spell check mSpellChecker.onSelectionChanged(); } + if (!extractedTextModeWillBeStarted()) { if (isCursorInsideEasyCorrectionSpan()) { mShowSuggestionRunnable = new Runnable() { @@ -2055,7 +2004,7 @@ public class Editor { shadowView.setText(text); shadowView.setTextColor(mTextView.getTextColors()); - shadowView.setTextAppearance(mTextView.getContext(), R.styleable.Theme_textAppearanceLarge); + shadowView.setTextAppearance(R.styleable.Theme_textAppearanceLarge); shadowView.setGravity(Gravity.CENTER); shadowView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, @@ -3185,10 +3134,6 @@ public class Editor { mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) { return true; } - if (item.getItemId() == TextView.ID_REPLACE) { - onReplace(); - return true; - } return mTextView.onTextContextMenuItem(item.getItemId()); } @@ -3262,13 +3207,6 @@ public class Editor { } } - private void onReplace() { - int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2; - stopSelectionActionMode(); - Selection.setSelection((Spannable) mTextView.getText(), middle); - showSuggestions(); - } - /** * A listener to call {@link InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)} * while the input method is requesting the cursor/anchor position. Does nothing as long as @@ -3794,9 +3732,27 @@ public class Editor { super.show(); final long durationSinceCutOrCopy = - SystemClock.uptimeMillis() - TextView.LAST_CUT_OR_COPY_TIME; - if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) { - startSelectionActionModeWithoutSelection(); + SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime; + + // Cancel the single tap delayed runnable. + if (mDoubleTap && mSelectionModeWithoutSelectionRunnable != null) { + mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); + } + + // Prepare and schedule the single tap runnable to run exactly after the double tap + // timeout has passed. + if (!mDoubleTap && (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) { + if (mSelectionModeWithoutSelectionRunnable == null) { + mSelectionModeWithoutSelectionRunnable = new Runnable() { + public void run() { + startSelectionActionModeWithoutSelection(); + } + }; + } + + mTextView.postDelayed( + mSelectionModeWithoutSelectionRunnable, + ViewConfiguration.getDoubleTapTimeout() + 1); } hideAfterDelay(); @@ -3902,6 +3858,9 @@ public class Editor { @Override public void updatePosition(float x, float y) { positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false); + if (mSelectionActionMode != null) { + mSelectionActionMode.invalidate(); + } } @Override @@ -3920,8 +3879,8 @@ public class Editor { private class SelectionStartHandleView extends HandleView { // Indicates whether the cursor is making adjustments within a word. private boolean mInWord = false; - // Offset to track difference between touch and word boundary. - protected int mTouchWordOffset; + // Difference between touch position and word boundary position. + private float mTouchWordDelta; public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) { super(drawableLtr, drawableRtl); @@ -3973,18 +3932,36 @@ public class Editor { offset = mPreviousOffset; } } - mTouchWordOffset = Math.max(trueOffset - offset, 0); - mInWord = !getWordIteratorWithText().isBoundary(offset); - positionCursor = true; - } else if (offset - mTouchWordOffset > mPreviousOffset) { - // User is shrinking the selection. - if (currLine > mPrevLine) { - // We're on a different line, so we'll snap to word boundaries. - offset = end; + final Layout layout = mTextView.getLayout(); + if (layout != null && offset < trueOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX; + } else { + mTouchWordDelta = 0.0f; } - offset -= mTouchWordOffset; - mInWord = !getWordIteratorWithText().isBoundary(offset); positionCursor = true; + } else { + final int adjustedOffset = + mTextView.getOffsetAtCoordinate(currLine, x - mTouchWordDelta); + if (adjustedOffset > mPreviousOffset || currLine > mPrevLine) { + // User is shrinking the selection. + if (currLine > mPrevLine) { + // We're on a different line, so we'll snap to word boundaries. + offset = start; + final Layout layout = mTextView.getLayout(); + if (layout != null && offset < trueOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX; + } else { + mTouchWordDelta = 0.0f; + } + } else { + offset = adjustedOffset; + } + positionCursor = true; + } } // Handles can not cross and selection is at least one character. @@ -3999,7 +3976,9 @@ public class Editor { } else { offset = alteredOffset; } + mTouchWordDelta = 0.0f; } + mInWord = !getWordIteratorWithText().isBoundary(offset); positionAtCursorOffset(offset, false); } } @@ -4009,7 +3988,7 @@ public class Editor { boolean superResult = super.onTouchEvent(event); if (event.getActionMasked() == MotionEvent.ACTION_UP) { // Reset the touch word offset when the user has lifted their finger. - mTouchWordOffset = 0; + mTouchWordDelta = 0.0f; } return superResult; } @@ -4018,8 +3997,8 @@ public class Editor { private class SelectionEndHandleView extends HandleView { // Indicates whether the cursor is making adjustments within a word. private boolean mInWord = false; - // Offset to track difference between touch and word boundary. - protected int mTouchWordOffset; + // Difference between touch position and word boundary position. + private float mTouchWordDelta; public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) { super(drawableLtr, drawableRtl); @@ -4071,18 +4050,36 @@ public class Editor { offset = mPreviousOffset; } } - mTouchWordOffset = Math.max(offset - trueOffset, 0); - mInWord = !getWordIteratorWithText().isBoundary(offset); - positionCursor = true; - } else if (offset + mTouchWordOffset < mPreviousOffset) { - // User is shrinking the selection. - if (currLine < mPrevLine) { - // We're on a different line, so we'll snap to word boundaries. - offset = start; + final Layout layout = mTextView.getLayout(); + if (layout != null && offset > trueOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + adjustedX - mTextView.convertToLocalHorizontalCoordinate(x); + } else { + mTouchWordDelta = 0.0f; } - offset += mTouchWordOffset; positionCursor = true; - mInWord = !getWordIteratorWithText().isBoundary(offset); + } else { + final int adjustedOffset = + mTextView.getOffsetAtCoordinate(currLine, x + mTouchWordDelta); + if (adjustedOffset < mPreviousOffset || currLine < mPrevLine) { + // User is shrinking the selection. + if (currLine < mPrevLine) { + // We're on a different line, so we'll snap to word boundaries. + offset = end; + final Layout layout = mTextView.getLayout(); + if (layout != null && offset > trueOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + adjustedX - mTextView.convertToLocalHorizontalCoordinate(x); + } else { + mTouchWordDelta = 0.0f; + } + } else { + offset = adjustedOffset; + } + positionCursor = true; + } } if (positionCursor) { @@ -4097,7 +4094,9 @@ public class Editor { } else { offset = Math.min(alteredOffset, length); } + mTouchWordDelta = 0.0f; } + mInWord = !getWordIteratorWithText().isBoundary(offset); positionAtCursorOffset(offset, false); } } @@ -4107,7 +4106,7 @@ public class Editor { boolean superResult = super.onTouchEvent(event); if (event.getActionMasked() == MotionEvent.ACTION_UP) { // Reset the touch word offset when the user has lifted their finger. - mTouchWordOffset = 0; + mTouchWordDelta = 0.0f; } return superResult; } @@ -4142,6 +4141,10 @@ public class Editor { public void show() { getHandle().show(); + + if (mSelectionModifierCursorController != null) { + mSelectionModifierCursorController.hide(); + } } public void hide() { @@ -4183,8 +4186,6 @@ public class Editor { // The offsets of that last touch down event. Remembered to start selection there. private int mMinTouchOffset, mMaxTouchOffset; - // Double tap detection - private long mPreviousTapUpTime = 0; private float mDownPositionX, mDownPositionY; private boolean mGestureStayedInTapRegion; @@ -4266,8 +4267,7 @@ public class Editor { // Double tap detection if (mGestureStayedInTapRegion) { - long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime; - if (duration <= ViewConfiguration.getDoubleTapTimeout()) { + if (mDoubleTap) { final float deltaX = x - mDownPositionX; final float deltaY = y - mDownPositionY; final float distanceSquared = deltaX * deltaX + deltaY * deltaY; @@ -4376,7 +4376,6 @@ public class Editor { break; case MotionEvent.ACTION_UP: - mPreviousTapUpTime = SystemClock.uptimeMillis(); if (mDragAcceleratorActive) { // No longer dragging to select text, let the parent intercept events. mTextView.getParent().requestDisallowInterceptTouchEvent(false); @@ -4468,7 +4467,7 @@ public class Editor { private class CorrectionHighlighter { private final Path mPath = new Path(); - private final Paint mPaint = new Paint(); + private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private int mStart, mEnd; private long mFadingStartTime; private RectF mTempRectF; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 05059bc..73a873a 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -36,8 +36,10 @@ import android.graphics.RectF; import android.graphics.Xfermode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; +import android.os.Handler; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -462,6 +464,21 @@ public class ImageView extends View { } /** + * Sets the content of this ImageView to the specified Icon. + * + * <p class="note">Depending on the Icon type, this may do Bitmap reading and decoding + * on the UI thread, which can cause UI jank. If that's a concern, consider using + * {@link Icon#loadDrawableAsync(Context, Icon.OnDrawableLoadedListener, Handler)} + * and then {@link #setImageDrawable(android.graphics.drawable.Drawable)} instead.</p> + * + * @param icon an Icon holding the desired image + */ + @android.view.RemotableViewMethod + public void setImageIcon(Icon icon) { + setImageDrawable(icon.loadDrawable(mContext)); + } + + /** * Applies a tint to the image drawable. Does not modify the current tint * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. * <p> diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index a10be11..dc75fd0 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -35,6 +35,7 @@ import android.graphics.Bitmap; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -1072,6 +1073,7 @@ public class RemoteViews implements Parcelable, Filter { static final int BUNDLE = 13; static final int INTENT = 14; static final int COLOR_STATE_LIST = 15; + static final int ICON = 16; String methodName; int type; @@ -1150,6 +1152,10 @@ public class RemoteViews implements Parcelable, Filter { this.value = ColorStateList.CREATOR.createFromParcel(in); } break; + case ICON: + if (in.readInt() != 0) { + this.value = Icon.CREATOR.createFromParcel(in); + } default: break; } @@ -1225,6 +1231,13 @@ public class RemoteViews implements Parcelable, Filter { if (this.value != null) { ((ColorStateList)this.value).writeToParcel(out, flags); } + break; + case ICON: + out.writeInt(this.value != null ? 1 : 0); + if (this.value != null) { + ((Icon)this.value).writeToParcel(out, flags); + } + break; default: break; } @@ -1262,6 +1275,8 @@ public class RemoteViews implements Parcelable, Filter { return Intent.class; case COLOR_STATE_LIST: return ColorStateList.class; + case ICON: + return Icon.class; default: return null; } @@ -2082,6 +2097,16 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling ImageView.setImageIcon + * + * @param viewId The id of the view whose bitmap should change + * @param icon The new Icon for the ImageView + */ + public void setImageViewIcon(int viewId, Icon icon) { + setIcon(viewId, "setImageIcon", icon); + } + + /** * Equivalent to calling AdapterView.setEmptyView * * @param viewId The id of the view on which to set the empty view @@ -2519,6 +2544,17 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Call a method taking one Icon on a view in the layout for this RemoteViews. + * + * @param viewId The id of the view on which to call the method. + * @param methodName The name of the method to call. + * @param value The {@link android.graphics.drawable.Icon} to pass the method. + */ + public void setIcon(int viewId, String methodName, Icon value) { + addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value)); + } + + /** * Equivalent to calling View.setContentDescription(CharSequence). * * @param viewId The id of the view whose content description should change. diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index f94f97c..f42959f 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -32,7 +32,6 @@ import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; -import android.os.Bundle; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; @@ -46,7 +45,7 @@ import android.view.Gravity; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.VelocityTracker; -import android.view.ViewAssistStructure; +import android.view.ViewStructure; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -216,7 +215,7 @@ public class Switch extends CompoundButton { public Switch(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mTextPaint = new TextPaint(); + mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); final Resources res = getResources(); mTextPaint.density = res.getDisplayMetrics().density; @@ -1363,8 +1362,8 @@ public class Switch extends CompoundButton { } @Override - public void onProvideAssistStructure(ViewAssistStructure structure) { - super.onProvideAssistStructure(structure); + public void onProvideStructure(ViewStructure structure) { + super.onProvideStructure(structure); CharSequence switchText = isChecked() ? mTextOn : mTextOff; if (!TextUtils.isEmpty(switchText)) { CharSequence oldText = structure.getText(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index b9a08f5..5acd79f 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -114,7 +114,7 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAssistStructure; +import android.view.ViewStructure; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup.LayoutParams; @@ -238,6 +238,7 @@ import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; * @attr ref android.R.styleable#TextView_letterSpacing * @attr ref android.R.styleable#TextView_fontFeatureSettings * @attr ref android.R.styleable#TextView_breakStrategy + * @attr ref android.R.styleable#TextView_hyphenationFrequency * @attr ref android.R.styleable#TextView_leftIndents * @attr ref android.R.styleable#TextView_rightIndents */ @@ -291,8 +292,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // New state used to change background based on whether this TextView is multiline. private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline }; - // System wide time for last cut or copy action. - static long LAST_CUT_OR_COPY_TIME; + // System wide time for last cut, copy or text changed action. + static long sLastCutCopyOrTextChangedTime; /** * @hide @@ -555,6 +556,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private float mSpacingAdd = 0.0f; private int mBreakStrategy; + private int mHyphenationFrequency; private int[] mLeftIndents; private int[] mRightIndents; @@ -597,6 +599,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private final Paint mHighlightPaint; private boolean mHighlightPathBogus = true; + private boolean mFirstTouch = false; + private long mLastTouchUpTime = 0; + // Although these fields are specific to editable text, they are not added to Editor because // they are defined by the TextView's style and are theme-dependent. int mCursorDrawableRes; @@ -669,11 +674,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final Resources res = getResources(); final CompatibilityInfo compat = res.getCompatibilityInfo(); - mTextPaint = new TextPaint(); + mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); mTextPaint.density = res.getDisplayMetrics().density; mTextPaint.setCompatibilityScaling(compat.applicationScale); - mHighlightPaint = new Paint(); + mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mHighlightPaint.setCompatibilityScaling(compat.applicationScale); mMovement = getDefaultMovementMethod(); @@ -696,6 +701,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener float letterSpacing = 0; String fontFeatureSettings = null; mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; + mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; final Resources.Theme theme = context.getTheme(); @@ -1154,6 +1160,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE); break; + case com.android.internal.R.styleable.TextView_hyphenationFrequency: + mHyphenationFrequency = a.getInt(attr, Layout.HYPHENATION_FREQUENCY_NONE); + break; + case com.android.internal.R.styleable.TextView_leftIndents: TypedArray margins = res.obtainTypedArray(a.getResourceId(attr, View.NO_ID)); mLeftIndents = parseDimensionArray(margins); @@ -3050,6 +3060,33 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Sets the hyphenation frequency. The default value for both TextView and EditText, which is set + * from the theme, is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}. + * + * @attr ref android.R.styleable#TextView_hyphenationFrequency + * @see #getHyphenationFrequency() + */ + public void setHyphenationFrequency(@Layout.HyphenationFrequency int hyphenationFrequency) { + mHyphenationFrequency = hyphenationFrequency; + if (mLayout != null) { + nullLayouts(); + requestLayout(); + invalidate(); + } + } + + /** + * @return the currently set hyphenation frequency. + * + * @attr ref android.R.styleable#TextView_hyphenationFrequency + * @see #setHyphenationFrequency(int) + */ + @Layout.HyphenationFrequency + public int getHyphenationFrequency() { + return mHyphenationFrequency; + } + + /** * Set indents. Arguments are arrays holding an indent amount, one per line, measured in * pixels. For lines past the last element in the array, the last element repeats. * @@ -6637,7 +6674,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setTextDir(mTextDir) .setLineSpacing(mSpacingAdd, mSpacingMult) .setIncludePad(mIncludePad) - .setBreakStrategy(mBreakStrategy); + .setBreakStrategy(mBreakStrategy) + .setHyphenationFrequency(mHyphenationFrequency); if (mLeftIndents != null || mRightIndents != null) { builder.setIndents(mLeftIndents, mRightIndents); } @@ -6678,7 +6716,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Layout result = null; if (mText instanceof Spannable) { result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth, - alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mBreakStrategy, + alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, + mBreakStrategy, mHyphenationFrequency, getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth); } else { if (boring == UNKNOWN_BORING) { @@ -6726,7 +6765,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setTextDir(mTextDir) .setLineSpacing(mSpacingAdd, mSpacingMult) .setIncludePad(mIncludePad) - .setBreakStrategy(mBreakStrategy); + .setBreakStrategy(mBreakStrategy) + .setHyphenationFrequency(mHyphenationFrequency); if (mLeftIndents != null || mRightIndents != null) { builder.setIndents(mLeftIndents, mRightIndents); } @@ -7968,6 +8008,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * through a thunk. */ void sendAfterTextChanged(Editable text) { + sLastCutCopyOrTextChangedTime = 0; + if (mListeners != null) { final ArrayList<TextWatcher> list = mListeners; final int count = list.size(); @@ -8240,6 +8282,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); + if (mEditor != null && action == MotionEvent.ACTION_DOWN) { + // Detect double tap and inform the Editor. + if (mFirstTouch && (SystemClock.uptimeMillis() - mLastTouchUpTime) <= + ViewConfiguration.getDoubleTapTimeout()) { + mEditor.mDoubleTap = true; + mFirstTouch = false; + } else { + mEditor.mDoubleTap = false; + mFirstTouch = true; + } + } + + if (action == MotionEvent.ACTION_UP) { + mLastTouchUpTime = SystemClock.uptimeMillis(); + } + if (mEditor != null) { mEditor.onTouchEvent(event); @@ -8715,8 +8773,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void onProvideAssistStructure(ViewAssistStructure structure) { - super.onProvideAssistStructure(structure); + public void onProvideStructure(ViewStructure structure) { + super.onProvideStructure(structure); final boolean isPassword = hasPasswordTransformationMethod(); if (!isPassword) { structure.setText(getText(), getSelectionStart(), getSelectionEnd()); @@ -9014,6 +9072,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener stopSelectionActionMode(); return true; + case ID_REPLACE: + if (mEditor != null) { + mEditor.replace(); + } + return true; + case ID_SHARE: shareSelectedText(); return true; @@ -9248,7 +9312,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } stopSelectionActionMode(); - LAST_CUT_OR_COPY_TIME = 0; + sLastCutCopyOrTextChangedTime = 0; } } @@ -9268,7 +9332,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ClipboardManager clipboard = (ClipboardManager) getContext(). getSystemService(Context.CLIPBOARD_SERVICE); clipboard.setPrimaryClip(clip); - LAST_CUT_OR_COPY_TIME = SystemClock.uptimeMillis(); + sLastCutCopyOrTextChangedTime = SystemClock.uptimeMillis(); } /** diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index 087406a..62d948d 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -590,7 +590,7 @@ public class Toolbar extends ViewGroup { mTitleTextView.setSingleLine(); mTitleTextView.setEllipsize(TextUtils.TruncateAt.END); if (mTitleTextAppearance != 0) { - mTitleTextView.setTextAppearance(context, mTitleTextAppearance); + mTitleTextView.setTextAppearance(mTitleTextAppearance); } if (mTitleTextColor != 0) { mTitleTextView.setTextColor(mTitleTextColor); @@ -644,7 +644,7 @@ public class Toolbar extends ViewGroup { mSubtitleTextView.setSingleLine(); mSubtitleTextView.setEllipsize(TextUtils.TruncateAt.END); if (mSubtitleTextAppearance != 0) { - mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance); + mSubtitleTextView.setTextAppearance(mSubtitleTextAppearance); } if (mSubtitleTextColor != 0) { mSubtitleTextView.setTextColor(mSubtitleTextColor); @@ -670,7 +670,7 @@ public class Toolbar extends ViewGroup { public void setTitleTextAppearance(Context context, @StyleRes int resId) { mTitleTextAppearance = resId; if (mTitleTextView != null) { - mTitleTextView.setTextAppearance(context, resId); + mTitleTextView.setTextAppearance(resId); } } @@ -681,7 +681,7 @@ public class Toolbar extends ViewGroup { public void setSubtitleTextAppearance(Context context, @StyleRes int resId) { mSubtitleTextAppearance = resId; if (mSubtitleTextView != null) { - mSubtitleTextView.setTextAppearance(context, resId); + mSubtitleTextView.setTextAppearance(resId); } } |
