diff options
Diffstat (limited to 'core/java')
137 files changed, 7705 insertions, 1073 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 10ef535..e8b3bb9 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -645,7 +645,8 @@ import java.util.HashMap; public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, - OnCreateContextMenuListener, ComponentCallbacks2 { + OnCreateContextMenuListener, ComponentCallbacks2, + Window.OnWindowDismissedCallback { private static final String TAG = "Activity"; private static final boolean DEBUG_LIFECYCLE = false; @@ -2402,6 +2403,15 @@ public class Activity extends ContextThemeWrapper } return false; } + + /** + * Called when the main window associated with the activity has been dismissed. + * @hide + */ + @Override + public void onWindowDismissed() { + finish(); + } /** * Called to process key events. You can override this to intercept all @@ -5189,6 +5199,7 @@ public class Activity extends ContextThemeWrapper mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); + mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 7ca3459..c877cd3 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1289,106 +1289,15 @@ public class ActivityManager { } /** - * Information you can retrieve about the WindowManager StackBox hierarchy. - * @hide - */ - public static class StackBoxInfo implements Parcelable { - public int stackBoxId; - public float weight; - public boolean vertical; - public Rect bounds; - public StackBoxInfo[] children; - public int stackId; - public StackInfo stack; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(stackBoxId); - dest.writeFloat(weight); - dest.writeInt(vertical ? 1 : 0); - bounds.writeToParcel(dest, flags); - dest.writeInt(stackId); - if (children != null) { - children[0].writeToParcel(dest, flags); - children[1].writeToParcel(dest, flags); - } else { - stack.writeToParcel(dest, flags); - } - } - - public void readFromParcel(Parcel source) { - stackBoxId = source.readInt(); - weight = source.readFloat(); - vertical = source.readInt() == 1; - bounds = Rect.CREATOR.createFromParcel(source); - stackId = source.readInt(); - if (stackId == -1) { - children = new StackBoxInfo[2]; - children[0] = StackBoxInfo.CREATOR.createFromParcel(source); - children[1] = StackBoxInfo.CREATOR.createFromParcel(source); - } else { - stack = StackInfo.CREATOR.createFromParcel(source); - } - } - - public static final Creator<StackBoxInfo> CREATOR = - new Creator<ActivityManager.StackBoxInfo>() { - - @Override - public StackBoxInfo createFromParcel(Parcel source) { - return new StackBoxInfo(source); - } - - @Override - public StackBoxInfo[] newArray(int size) { - return new StackBoxInfo[size]; - } - }; - - public StackBoxInfo() { - } - - public StackBoxInfo(Parcel source) { - readFromParcel(source); - } - - public String toString(String prefix) { - StringBuilder sb = new StringBuilder(256); - sb.append(prefix); sb.append("Box id=" + stackBoxId); sb.append(" weight=" + weight); - sb.append(" vertical=" + vertical); sb.append(" bounds=" + bounds.toShortString()); - sb.append("\n"); - if (children != null) { - sb.append(prefix); sb.append("First child=\n"); - sb.append(children[0].toString(prefix + " ")); - sb.append(prefix); sb.append("Second child=\n"); - sb.append(children[1].toString(prefix + " ")); - } else { - sb.append(prefix); sb.append("Stack=\n"); - sb.append(stack.toString(prefix + " ")); - } - return sb.toString(); - } - - @Override - public String toString() { - return toString(""); - } - } - - /** * Information you can retrieve about an ActivityStack in the system. * @hide */ public static class StackInfo implements Parcelable { public int stackId; - public Rect bounds; + public Rect bounds = new Rect(); public int[] taskIds; public String[] taskNames; + public int displayId; @Override public int describeContents() { @@ -1404,6 +1313,7 @@ public class ActivityManager { dest.writeInt(bounds.bottom); dest.writeIntArray(taskIds); dest.writeStringArray(taskNames); + dest.writeInt(displayId); } public void readFromParcel(Parcel source) { @@ -1412,6 +1322,7 @@ public class ActivityManager { source.readInt(), source.readInt(), source.readInt(), source.readInt()); taskIds = source.createIntArray(); taskNames = source.createStringArray(); + displayId = source.readInt(); } public static final Creator<StackInfo> CREATOR = new Creator<StackInfo>() { @@ -1435,7 +1346,9 @@ public class ActivityManager { public String toString(String prefix) { StringBuilder sb = new StringBuilder(256); sb.append(prefix); sb.append("Stack id="); sb.append(stackId); - sb.append(" bounds="); sb.append(bounds.toShortString()); sb.append("\n"); + sb.append(" bounds="); sb.append(bounds.toShortString()); + sb.append(" displayId="); sb.append(displayId); + sb.append("\n"); prefix = prefix + " "; for (int i = 0; i < taskIds.length; ++i) { sb.append(prefix); sb.append("taskId="); sb.append(taskIds[i]); diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java new file mode 100644 index 0000000..5262a5f --- /dev/null +++ b/core/java/android/app/ActivityManagerInternal.java @@ -0,0 +1,28 @@ +/* + * 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.app; + +/** + * Activity manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class ActivityManagerInternal { + // Called by the power manager. + public abstract void goingToSleep(); + public abstract void wakingUp(); +} diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 001ce57..e5ee1fa 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -16,7 +16,7 @@ package android.app; -import android.app.ActivityManager.StackBoxInfo; +import android.app.ActivityManager.StackInfo; import android.content.ComponentName; import android.content.IIntentReceiver; import android.content.IIntentSender; @@ -31,6 +31,7 @@ import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.Rect; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -611,18 +612,6 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case CREATE_STACK_TRANSACTION: { - data.enforceInterface(IActivityManager.descriptor); - int taskId = data.readInt(); - int relativeStackId = data.readInt(); - int position = data.readInt(); - float weight = data.readFloat(); - int res = createStack(taskId, relativeStackId, position, weight); - reply.writeNoException(); - reply.writeInt(res); - return true; - } - case MOVE_TASK_TO_STACK_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int taskId = data.readInt(); @@ -635,25 +624,26 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case RESIZE_STACK_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - int stackBoxId = data.readInt(); + int stackId = data.readInt(); float weight = data.readFloat(); - resizeStackBox(stackBoxId, weight); + Rect r = Rect.CREATOR.createFromParcel(data); + resizeStack(stackId, r); reply.writeNoException(); return true; } - case GET_STACK_BOXES_TRANSACTION: { + case GET_ALL_STACK_INFOS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - List<StackBoxInfo> list = getStackBoxes(); + List<StackInfo> list = getAllStackInfos(); reply.writeNoException(); reply.writeTypedList(list); return true; } - case GET_STACK_BOX_INFO_TRANSACTION: { + case GET_STACK_INFO_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - int stackBoxId = data.readInt(); - StackBoxInfo info = getStackBoxInfo(stackBoxId); + int stackId = data.readInt(); + StackInfo info = getStackInfo(stackId); reply.writeNoException(); if (info != null) { reply.writeInt(1); @@ -1211,20 +1201,6 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case GOING_TO_SLEEP_TRANSACTION: { - data.enforceInterface(IActivityManager.descriptor); - goingToSleep(); - reply.writeNoException(); - return true; - } - - case WAKING_UP_TRANSACTION: { - data.enforceInterface(IActivityManager.descriptor); - wakingUp(); - reply.writeNoException(); - return true; - } - case SET_LOCK_SCREEN_SHOWN_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); setLockScreenShown(data.readInt() != 0); @@ -1295,17 +1271,6 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case START_RUNNING_TRANSACTION: { - data.enforceInterface(IActivityManager.descriptor); - String pkg = data.readString(); - String cls = data.readString(); - String action = data.readString(); - String indata = data.readString(); - startRunning(pkg, cls, action, indata); - reply.writeNoException(); - return true; - } - case HANDLE_APPLICATION_CRASH_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder app = data.readStrongBinder(); @@ -2030,6 +1995,54 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeNoException(); return true; } + + case CREATE_ACTIVITY_CONTAINER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder parentActivityToken = data.readStrongBinder(); + IActivityContainerCallback callback = + IActivityContainerCallback.Stub.asInterface(data.readStrongBinder()); + IActivityContainer activityContainer = + createActivityContainer(parentActivityToken, callback); + reply.writeNoException(); + if (activityContainer != null) { + reply.writeInt(1); + reply.writeStrongBinder(activityContainer.asBinder()); + } else { + reply.writeInt(0); + } + return true; + } + + case DELETE_ACTIVITY_CONTAINER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IActivityContainer activityContainer = + IActivityContainer.Stub.asInterface(data.readStrongBinder()); + deleteActivityContainer(activityContainer); + reply.writeNoException(); + return true; + } + + case GET_ACTIVITY_CONTAINER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder activityToken = data.readStrongBinder(); + IActivityContainer activityContainer = getEnclosingActivityContainer(activityToken); + reply.writeNoException(); + if (activityContainer != null) { + reply.writeInt(1); + reply.writeStrongBinder(activityContainer.asBinder()); + } else { + reply.writeInt(0); + } + return true; + } + + case GET_HOME_ACTIVITY_TOKEN_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder homeActivityToken = getHomeActivityToken(); + reply.writeNoException(); + reply.writeStrongBinder(homeActivityToken); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -2717,24 +2730,6 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } @Override - public int createStack(int taskId, int relativeStackBoxId, int position, float weight) - throws RemoteException - { - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInterfaceToken(IActivityManager.descriptor); - data.writeInt(taskId); - data.writeInt(relativeStackBoxId); - data.writeInt(position); - data.writeFloat(weight); - mRemote.transact(CREATE_STACK_TRANSACTION, data, reply, 0); - reply.readException(); - int res = reply.readInt(); - data.recycle(); - reply.recycle(); - return res; - } - @Override public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException { Parcel data = Parcel.obtain(); @@ -2749,44 +2744,44 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } @Override - public void resizeStackBox(int stackBoxId, float weight) throws RemoteException + public void resizeStack(int stackBoxId, Rect r) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(stackBoxId); - data.writeFloat(weight); + r.writeToParcel(data, 0); mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY); reply.readException(); data.recycle(); reply.recycle(); } @Override - public List<StackBoxInfo> getStackBoxes() throws RemoteException + public List<StackInfo> getAllStackInfos() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); - mRemote.transact(GET_STACK_BOXES_TRANSACTION, data, reply, 0); + mRemote.transact(GET_ALL_STACK_INFOS_TRANSACTION, data, reply, 0); reply.readException(); - ArrayList<StackBoxInfo> list = reply.createTypedArrayList(StackBoxInfo.CREATOR); + ArrayList<StackInfo> list = reply.createTypedArrayList(StackInfo.CREATOR); data.recycle(); reply.recycle(); return list; } @Override - public StackBoxInfo getStackBoxInfo(int stackBoxId) throws RemoteException + public StackInfo getStackInfo(int stackId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); - data.writeInt(stackBoxId); - mRemote.transact(GET_STACK_BOX_INFO_TRANSACTION, data, reply, 0); + data.writeInt(stackId); + mRemote.transact(GET_STACK_INFO_TRANSACTION, data, reply, 0); reply.readException(); int res = reply.readInt(); - StackBoxInfo info = null; + StackInfo info = null; if (res != 0) { - info = StackBoxInfo.CREATOR.createFromParcel(reply); + info = StackInfo.CREATOR.createFromParcel(reply); } data.recycle(); reply.recycle(); @@ -3570,26 +3565,6 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return pfd; } - public void goingToSleep() throws RemoteException - { - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInterfaceToken(IActivityManager.descriptor); - mRemote.transact(GOING_TO_SLEEP_TRANSACTION, data, reply, 0); - reply.readException(); - data.recycle(); - reply.recycle(); - } - public void wakingUp() throws RemoteException - { - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInterfaceToken(IActivityManager.descriptor); - mRemote.transact(WAKING_UP_TRANSACTION, data, reply, 0); - reply.readException(); - data.recycle(); - reply.recycle(); - } public void setLockScreenShown(boolean shown) throws RemoteException { Parcel data = Parcel.obtain(); @@ -3677,20 +3652,6 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return res; } - public void startRunning(String pkg, String cls, String action, - String indata) throws RemoteException { - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInterfaceToken(IActivityManager.descriptor); - data.writeString(pkg); - data.writeString(cls); - data.writeString(action); - data.writeString(indata); - mRemote.transact(START_RUNNING_TRANSACTION, data, reply, 0); - reply.readException(); - data.recycle(); - reply.recycle(); - } public boolean testIsSystemReady() { /* this base class version is never called */ @@ -4664,5 +4625,70 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public IActivityContainer createActivityContainer(IBinder parentActivityToken, + IActivityContainerCallback callback) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(parentActivityToken); + data.writeStrongBinder(callback == null ? null : callback.asBinder()); + mRemote.transact(CREATE_ACTIVITY_CONTAINER_TRANSACTION, data, reply, 0); + reply.readException(); + final int result = reply.readInt(); + final IActivityContainer res; + if (result == 1) { + res = IActivityContainer.Stub.asInterface(reply.readStrongBinder()); + } else { + res = null; + } + data.recycle(); + reply.recycle(); + return res; + } + + public void deleteActivityContainer(IActivityContainer activityContainer) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(activityContainer.asBinder()); + mRemote.transact(DELETE_ACTIVITY_CONTAINER_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + public IActivityContainer getEnclosingActivityContainer(IBinder activityToken) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(activityToken); + mRemote.transact(GET_ACTIVITY_CONTAINER_TRANSACTION, data, reply, 0); + reply.readException(); + final int result = reply.readInt(); + final IActivityContainer res; + if (result == 1) { + res = IActivityContainer.Stub.asInterface(reply.readStrongBinder()); + } else { + res = null; + } + data.recycle(); + reply.recycle(); + return res; + } + + public IBinder getHomeActivityToken() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(GET_HOME_ACTIVITY_TOKEN_TRANSACTION, data, reply, 0); + reply.readException(); + IBinder res = reply.readStrongBinder(); + data.recycle(); + reply.recycle(); + return res; + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index c931d79..156eadb 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2250,15 +2250,27 @@ public final class ActivityThread { final Activity activity) { ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token); appContext.setOuterContext(activity); + Context baseContext = appContext; + + final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); + try { + IActivityContainer container = + ActivityManagerNative.getDefault().getEnclosingActivityContainer(r.token); + final int displayId = + container == null ? Display.DEFAULT_DISPLAY : container.getDisplayId(); + if (displayId > Display.DEFAULT_DISPLAY) { + Display display = dm.getRealDisplay(displayId, r.token); + baseContext = appContext.createDisplayContext(display); + } + } catch (RemoteException e) { + } // For debugging purposes, if the activity's package name contains the value of // the "debug.use-second-display" system property as a substring, then show // its content on a secondary display if there is one. - Context baseContext = appContext; String pkgName = SystemProperties.get("debug.second-display.pkg"); if (pkgName != null && !pkgName.isEmpty() && r.packageInfo.mPackageName.contains(pkgName)) { - DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); for (int displayId : dm.getDisplayIds()) { if (displayId != Display.DEFAULT_DISPLAY) { Display display = dm.getRealDisplay(displayId, r.token); diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java new file mode 100644 index 0000000..94ea2c5 --- /dev/null +++ b/core/java/android/app/ActivityView.java @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2013 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.content.Context; +import android.content.ContextWrapper; +import android.content.IIntentSender; +import android.content.Intent; +import android.content.IntentSender; +import android.graphics.SurfaceTexture; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.InputDevice; +import android.view.InputEvent; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.TextureView; +import android.view.TextureView.SurfaceTextureListener; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import dalvik.system.CloseGuard; + +import java.lang.ref.WeakReference; + +/** @hide */ +public class ActivityView extends ViewGroup { + private static final String TAG = "ActivityView"; + private static final boolean DEBUG = false; + + DisplayMetrics mMetrics; + private final TextureView mTextureView; + private ActivityContainerWrapper mActivityContainer; + private Activity mActivity; + private int mWidth; + private int mHeight; + private Surface mSurface; + private int mLastVisibility; + private ActivityViewCallback mActivityViewCallback; + + // Only one IIntentSender or Intent may be queued at a time. Most recent one wins. + IIntentSender mQueuedPendingIntent; + Intent mQueuedIntent; + + public ActivityView(Context context) { + this(context, null); + } + + public ActivityView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ActivityView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + while (context instanceof ContextWrapper) { + if (context instanceof Activity) { + mActivity = (Activity)context; + break; + } + context = ((ContextWrapper)context).getBaseContext(); + } + if (mActivity == null) { + throw new IllegalStateException("The ActivityView's Context is not an Activity."); + } + + try { + mActivityContainer = new ActivityContainerWrapper( + ActivityManagerNative.getDefault().createActivityContainer( + mActivity.getActivityToken(), new ActivityContainerCallback(this))); + } catch (RemoteException e) { + throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + + e); + } + + mTextureView = new TextureView(context); + mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener()); + addView(mTextureView); + + WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE); + mMetrics = new DisplayMetrics(); + wm.getDefaultDisplay().getMetrics(mMetrics); + + mLastVisibility = getVisibility(); + + if (DEBUG) Log.v(TAG, "ctor()"); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + mTextureView.layout(0, 0, r - l, b - t); + } + + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + + if (mSurface != null) { + try { + if (visibility == View.GONE) { + mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi); + } else if (mLastVisibility == View.GONE) { + // Don't change surface when going between View.VISIBLE and View.INVISIBLE. + mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi); + } + } catch (RemoteException e) { + throw new RuntimeException( + "ActivityView: Unable to set surface of ActivityContainer. " + e); + } + } + mLastVisibility = visibility; + } + + private boolean injectInputEvent(InputEvent event) { + return mActivityContainer != null && mActivityContainer.injectEvent(event); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return injectInputEvent(event) || super.onTouchEvent(event); + } + + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { + if (injectInputEvent(event)) { + return true; + } + } + return super.onGenericMotionEvent(event); + } + + @Override + public void onAttachedToWindow() { + if (DEBUG) Log.v(TAG, "onAttachedToWindow(): mActivityContainer=" + mActivityContainer + + " mSurface=" + mSurface); + } + + @Override + public void onDetachedFromWindow() { + if (DEBUG) Log.v(TAG, "onDetachedFromWindow(): mActivityContainer=" + mActivityContainer + + " mSurface=" + mSurface); + } + + public boolean isAttachedToDisplay() { + return mSurface != null; + } + + public void startActivity(Intent intent) { + if (mActivityContainer == null) { + throw new IllegalStateException("Attempt to call startActivity after release"); + } + if (DEBUG) Log.v(TAG, "startActivity(): intent=" + intent + " " + + (isAttachedToDisplay() ? "" : "not") + " attached"); + if (mSurface != null) { + mActivityContainer.startActivity(intent); + } else { + mActivityContainer.checkEmbeddedAllowed(intent); + mQueuedIntent = intent; + mQueuedPendingIntent = null; + } + } + + public void startActivity(IntentSender intentSender) { + if (mActivityContainer == null) { + throw new IllegalStateException("Attempt to call startActivity after release"); + } + if (DEBUG) Log.v(TAG, "startActivityIntentSender(): intentSender=" + intentSender + " " + + (isAttachedToDisplay() ? "" : "not") + " attached"); + final IIntentSender iIntentSender = intentSender.getTarget(); + if (mSurface != null) { + mActivityContainer.startActivityIntentSender(iIntentSender); + } else { + mActivityContainer.checkEmbeddedAllowedIntentSender(iIntentSender); + mQueuedPendingIntent = iIntentSender; + mQueuedIntent = null; + } + } + + public void startActivity(PendingIntent pendingIntent) { + if (mActivityContainer == null) { + throw new IllegalStateException("Attempt to call startActivity after release"); + } + if (DEBUG) Log.v(TAG, "startActivityPendingIntent(): PendingIntent=" + pendingIntent + " " + + (isAttachedToDisplay() ? "" : "not") + " attached"); + final IIntentSender iIntentSender = pendingIntent.getTarget(); + if (mSurface != null) { + mActivityContainer.startActivityIntentSender(iIntentSender); + } else { + mActivityContainer.checkEmbeddedAllowedIntentSender(iIntentSender); + mQueuedPendingIntent = iIntentSender; + mQueuedIntent = null; + } + } + + public void release() { + if (DEBUG) Log.v(TAG, "release() mActivityContainer=" + mActivityContainer + + " mSurface=" + mSurface); + if (mActivityContainer == null) { + Log.e(TAG, "Duplicate call to release"); + return; + } + mActivityContainer.release(); + mActivityContainer = null; + + if (mSurface != null) { + mSurface.release(); + mSurface = null; + } + + mTextureView.setSurfaceTextureListener(null); + } + + private void attachToSurfaceWhenReady() { + final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture(); + if (surfaceTexture == null || mSurface != null) { + // Either not ready to attach, or already attached. + return; + } + + mSurface = new Surface(surfaceTexture); + try { + mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi); + } catch (RemoteException e) { + mSurface.release(); + mSurface = null; + throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + e); + } + + if (DEBUG) Log.v(TAG, "attachToSurfaceWhenReady: " + (mQueuedIntent != null || + mQueuedPendingIntent != null ? "" : "no") + " queued intent"); + if (mQueuedIntent != null) { + mActivityContainer.startActivity(mQueuedIntent); + mQueuedIntent = null; + } else if (mQueuedPendingIntent != null) { + mActivityContainer.startActivityIntentSender(mQueuedPendingIntent); + mQueuedPendingIntent = null; + } + } + + /** + * Set the callback to use to report certain state changes. + * @param callback The callback to report events to. + * + * @see ActivityViewCallback + */ + public void setCallback(ActivityViewCallback callback) { + mActivityViewCallback = callback; + } + + public static abstract class ActivityViewCallback { + /** + * Called when all activities in the ActivityView have completed and been removed. Register + * using {@link ActivityView#setCallback(ActivityViewCallback)}. Each ActivityView may + * have at most one callback registered. + */ + public abstract void onAllActivitiesComplete(ActivityView view); + } + + private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener { + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, + int height) { + if (mActivityContainer == null) { + return; + } + if (DEBUG) Log.d(TAG, "onSurfaceTextureAvailable: width=" + width + " height=" + + height); + mWidth = width; + mHeight = height; + attachToSurfaceWhenReady(); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, + int height) { + if (mActivityContainer == null) { + return; + } + if (DEBUG) Log.d(TAG, "onSurfaceTextureSizeChanged: w=" + width + " h=" + height); + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { + if (mActivityContainer == null) { + return true; + } + if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed"); + mSurface.release(); + mSurface = null; + try { + mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi); + } catch (RemoteException e) { + throw new RuntimeException( + "ActivityView: Unable to set surface of ActivityContainer. " + e); + } + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { +// Log.d(TAG, "onSurfaceTextureUpdated"); + } + + } + + private static class ActivityContainerCallback extends IActivityContainerCallback.Stub { + private final WeakReference<ActivityView> mActivityViewWeakReference; + + ActivityContainerCallback(ActivityView activityView) { + mActivityViewWeakReference = new WeakReference<ActivityView>(activityView); + } + + @Override + public void setVisible(IBinder container, boolean visible) { + if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible + + " ActivityView=" + mActivityViewWeakReference.get()); + } + + @Override + public void onAllActivitiesComplete(IBinder container) { + final ActivityView activityView = mActivityViewWeakReference.get(); + if (activityView != null) { + final ActivityViewCallback callback = activityView.mActivityViewCallback; + if (callback != null) { + activityView.post(new Runnable() { + @Override + public void run() { + callback.onAllActivitiesComplete(activityView); + } + }); + } + } + } + } + + private static class ActivityContainerWrapper { + private final IActivityContainer mIActivityContainer; + private final CloseGuard mGuard = CloseGuard.get(); + boolean mOpened; // Protected by mGuard. + + ActivityContainerWrapper(IActivityContainer container) { + mIActivityContainer = container; + mOpened = true; + mGuard.open("release"); + } + + void attachToDisplay(int displayId) { + try { + mIActivityContainer.attachToDisplay(displayId); + } catch (RemoteException e) { + } + } + + void setSurface(Surface surface, int width, int height, int density) + throws RemoteException { + mIActivityContainer.setSurface(surface, width, height, density); + } + + int startActivity(Intent intent) { + try { + return mIActivityContainer.startActivity(intent); + } catch (RemoteException e) { + throw new RuntimeException("ActivityView: Unable to startActivity. " + e); + } + } + + int startActivityIntentSender(IIntentSender intentSender) { + try { + return mIActivityContainer.startActivityIntentSender(intentSender); + } catch (RemoteException e) { + throw new RuntimeException( + "ActivityView: Unable to startActivity from IntentSender. " + e); + } + } + + void checkEmbeddedAllowed(Intent intent) { + try { + mIActivityContainer.checkEmbeddedAllowed(intent); + } catch (RemoteException e) { + throw new RuntimeException( + "ActivityView: Unable to startActivity from Intent. " + e); + } + } + + void checkEmbeddedAllowedIntentSender(IIntentSender intentSender) { + try { + mIActivityContainer.checkEmbeddedAllowedIntentSender(intentSender); + } catch (RemoteException e) { + throw new RuntimeException( + "ActivityView: Unable to startActivity from IntentSender. " + e); + } + } + + int getDisplayId() { + try { + return mIActivityContainer.getDisplayId(); + } catch (RemoteException e) { + return -1; + } + } + + boolean injectEvent(InputEvent event) { + try { + return mIActivityContainer.injectEvent(event); + } catch (RemoteException e) { + return false; + } + } + + void release() { + synchronized (mGuard) { + if (mOpened) { + if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called"); + try { + mIActivityContainer.release(); + mGuard.close(); + } catch (RemoteException e) { + } + mOpened = false; + } + } + } + + @Override + protected void finalize() throws Throwable { + if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: finalize called"); + try { + if (mGuard != null) { + mGuard.warnIfOpen(); + release(); + } + } finally { + super.finalize(); + } + } + + } +} diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index a280448..02192eb 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -128,6 +128,25 @@ final class ApplicationPackageManager extends PackageManager { return intent; } + /** @hide */ + @Override + public Intent getLeanbackLaunchIntentForPackage(String packageName) { + // Try to find a main leanback_launcher activity. + Intent intentToResolve = new Intent(Intent.ACTION_MAIN); + intentToResolve.addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER); + intentToResolve.setPackage(packageName); + List<ResolveInfo> ris = queryIntentActivities(intentToResolve, 0); + + if (ris == null || ris.size() <= 0) { + return null; + } + Intent intent = new Intent(intentToResolve); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClassName(ris.get(0).activityInfo.packageName, + ris.get(0).activityInfo.name); + return intent; + } + @Override public int[] getPackageGids(String packageName) throws NameNotFoundException { @@ -729,6 +748,39 @@ final class ApplicationPackageManager extends PackageManager { } @Override + public Drawable getActivityBanner(ComponentName activityName) + throws NameNotFoundException { + return getActivityInfo(activityName, 0).loadBanner(this); + } + + @Override + public Drawable getActivityBanner(Intent intent) + throws NameNotFoundException { + if (intent.getComponent() != null) { + return getActivityBanner(intent.getComponent()); + } + + ResolveInfo info = resolveActivity( + intent, PackageManager.MATCH_DEFAULT_ONLY); + if (info != null) { + return info.activityInfo.loadBanner(this); + } + + throw new NameNotFoundException(intent.toUri(0)); + } + + @Override + public Drawable getApplicationBanner(ApplicationInfo info) { + return info.loadBanner(this); + } + + @Override + public Drawable getApplicationBanner(String packageName) + throws NameNotFoundException { + return getApplicationBanner(getApplicationInfo(packageName, 0)); + } + + @Override public Drawable getActivityLogo(ComponentName activityName) throws NameNotFoundException { return getActivityInfo(activityName, 0).loadLogo(this); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index b681220..e49fd67 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -51,6 +51,8 @@ import android.hardware.ConsumerIrManager; import android.hardware.ISerialManager; import android.hardware.SerialManager; import android.hardware.SystemSensorManager; +import android.hardware.hdmi.HdmiCecManager; +import android.hardware.hdmi.IHdmiCecService; import android.hardware.camera2.CameraManager; import android.hardware.display.DisplayManager; import android.hardware.input.InputManager; @@ -357,6 +359,13 @@ class ContextImpl extends Context { return new BluetoothManager(ctx); }}); + registerService(HDMI_CEC_SERVICE, new StaticServiceFetcher() { + public Object createStaticService() { + IBinder b = ServiceManager.getService(HDMI_CEC_SERVICE); + return new HdmiCecManager(IHdmiCecService.Stub.asInterface(b)); + }}); + + registerService(CLIPBOARD_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return new ClipboardManager(ctx.getOuterContext(), diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index cda2c5f..7a362df 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -80,7 +80,7 @@ import java.lang.ref.WeakReference; * </div> */ public class Dialog implements DialogInterface, Window.Callback, - KeyEvent.Callback, OnCreateContextMenuListener { + KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback { private static final String TAG = "Dialog"; private Activity mOwnerActivity; @@ -166,6 +166,7 @@ public class Dialog implements DialogInterface, Window.Callback, Window w = PolicyManager.makeNewWindow(mContext); mWindow = w; w.setCallback(this); + w.setOnWindowDismissedCallback(this); w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler(this); @@ -695,6 +696,12 @@ public class Dialog implements DialogInterface, Window.Callback, public void onDetachedFromWindow() { } + + /** @hide */ + @Override + public void onWindowDismissed() { + dismiss(); + } /** * Called to process key events. You can override this to intercept all diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index d626e5f..af8f177 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -1402,6 +1402,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene mRestored = false; mBackStackNesting = 0; mFragmentManager = null; + mChildFragmentManager = null; mActivity = null; mFragmentId = 0; mContainerId = 0; diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index bf2a629..76f9d97 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1026,6 +1026,7 @@ final class FragmentManagerImpl extends FragmentManager { f.mActivity = null; f.mParentFragment = null; f.mFragmentManager = null; + f.mChildFragmentManager = null; } } } diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl new file mode 100644 index 0000000..52884f7 --- /dev/null +++ b/core/java/android/app/IActivityContainer.aidl @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2013, 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.app.IActivityContainerCallback; +import android.content.Intent; +import android.content.IIntentSender; +import android.os.IBinder; +import android.view.InputEvent; +import android.view.Surface; + +/** @hide */ +interface IActivityContainer { + void attachToDisplay(int displayId); + void setSurface(in Surface surface, int width, int height, int density); + int startActivity(in Intent intent); + int startActivityIntentSender(in IIntentSender intentSender); + void checkEmbeddedAllowed(in Intent intent); + void checkEmbeddedAllowedIntentSender(in IIntentSender intentSender); + int getDisplayId(); + boolean injectEvent(in InputEvent event); + void release(); +} diff --git a/core/java/android/app/IActivityContainerCallback.aidl b/core/java/android/app/IActivityContainerCallback.aidl new file mode 100644 index 0000000..99d0a6f --- /dev/null +++ b/core/java/android/app/IActivityContainerCallback.aidl @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2013, 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.os.IBinder; + +/** @hide */ +interface IActivityContainerCallback { + oneway void setVisible(IBinder container, boolean visible); + oneway void onAllActivitiesComplete(IBinder container); +} diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 8f8d8a6..27e97d4 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -18,7 +18,7 @@ package android.app; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.RunningServiceInfo; -import android.app.ActivityManager.StackBoxInfo; +import android.app.ActivityManager.StackInfo; import android.content.ComponentName; import android.content.ContentProviderNative; import android.content.IContentProvider; @@ -36,6 +36,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; import android.os.Debug; @@ -117,12 +118,10 @@ public interface IActivityManager extends IInterface { public void moveTaskToBack(int task) throws RemoteException; public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException; public void moveTaskBackwards(int task) throws RemoteException; - public int createStack(int taskId, int relativeStackBoxId, int position, float weight) - throws RemoteException; public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException; - public void resizeStackBox(int stackBoxId, float weight) throws RemoteException; - public List<StackBoxInfo> getStackBoxes() throws RemoteException; - public StackBoxInfo getStackBoxInfo(int stackBoxId) throws RemoteException; + public void resizeStack(int stackId, Rect bounds) throws RemoteException; + public List<StackInfo> getAllStackInfos() throws RemoteException; + public StackInfo getStackInfo(int stackId) throws RemoteException; public void setFocusedStack(int stackId) throws RemoteException; public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException; /* oneway */ @@ -230,8 +229,6 @@ public interface IActivityManager extends IInterface { public void forceStopPackage(final String packageName, int userId) throws RemoteException; // Note: probably don't want to allow applications access to these. - public void goingToSleep() throws RemoteException; - public void wakingUp() throws RemoteException; public void setLockScreenShown(boolean shown) throws RemoteException; public void unhandledBack() throws RemoteException; @@ -251,8 +248,6 @@ public interface IActivityManager extends IInterface { public boolean killProcessesBelowForeground(String reason) throws RemoteException; // Special low-level communication with activity manager. - public void startRunning(String pkg, String cls, String action, - String data) throws RemoteException; public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException; public boolean handleApplicationWtf(IBinder app, String tag, @@ -409,6 +404,18 @@ public interface IActivityManager extends IInterface { public void performIdleMaintenance() throws RemoteException; + /** @hide */ + public IActivityContainer createActivityContainer(IBinder parentActivityToken, + IActivityContainerCallback callback) throws RemoteException; + + /** @hide */ + public void deleteActivityContainer(IActivityContainer container) throws RemoteException; + + public IActivityContainer getEnclosingActivityContainer(IBinder activityToken) + throws RemoteException; + + public IBinder getHomeActivityToken() throws RemoteException; + /* * Private non-Binder interfaces */ @@ -515,7 +522,6 @@ public interface IActivityManager extends IInterface { // Please keep these transaction codes the same -- they are also // sent by C++ code. - int START_RUNNING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION; int HANDLE_APPLICATION_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1; int START_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2; int UNHANDLED_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3; @@ -551,8 +557,6 @@ public interface IActivityManager extends IInterface { int UNBIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36; int PUBLISH_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37; int ACTIVITY_RESUMED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38; - int GOING_TO_SLEEP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39; - int WAKING_UP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40; int SET_DEBUG_APP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+41; int SET_ALWAYS_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+42; int START_INSTRUMENTATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43; @@ -679,12 +683,12 @@ public interface IActivityManager extends IInterface { int KILL_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+164; int SET_USER_IS_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+165; int HANG_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+166; - int CREATE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+167; + int CREATE_ACTIVITY_CONTAINER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+167; int MOVE_TASK_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+168; int RESIZE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+169; - int GET_STACK_BOXES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170; + int GET_ALL_STACK_INFOS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170; int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+171; - int GET_STACK_BOX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+172; + int GET_STACK_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+172; int CONVERT_FROM_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173; int CONVERT_TO_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174; int NOTIFY_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+175; @@ -695,4 +699,7 @@ public interface IActivityManager extends IInterface { int RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+180; int GET_PERSISTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+181; int APP_NOT_RESPONDING_VIA_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+182; + int GET_HOME_ACTIVITY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+183; + int GET_ACTIVITY_CONTAINER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+184; + int DELETE_ACTIVITY_CONTAINER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+185; } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 9f933ca..9911467 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -45,7 +45,8 @@ interface INotificationManager void unregisterListener(in INotificationListener listener, int userid); void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id); - void cancelAllNotificationsFromListener(in INotificationListener token); + void cancelNotificationsFromListener(in INotificationListener token, in String[] keys); - StatusBarNotification[] getActiveNotificationsFromListener(in INotificationListener token); + StatusBarNotification[] getActiveNotificationsFromListener(in INotificationListener token, in String[] keys); + String[] getActiveNotificationKeysFromListener(in INotificationListener token); }
\ No newline at end of file diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 3efd3c0..181eb63 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -71,4 +71,14 @@ interface IWallpaperManager { * Returns the desired minimum height for the wallpaper. */ int getHeightHint(); + + /** + * Returns the name of the wallpaper. Private API. + */ + String getName(); + + /** + * Informs the service that wallpaper settings have been restored. Private API. + */ + void settingsRestored(); } diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 96c7246..9ec7f41 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -340,9 +340,11 @@ public abstract class LauncherActivity extends ListActivity { super.onCreate(icicle); mPackageManager = getPackageManager(); - - requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - setProgressBarIndeterminateVisibility(true); + + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) { + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setProgressBarIndeterminateVisibility(true); + } onSetContentView(); mIconResizer = new IconResizer(); @@ -357,7 +359,9 @@ public abstract class LauncherActivity extends ListActivity { updateAlertTitle(); updateButtonText(); - setProgressBarIndeterminateVisibility(false); + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) { + setProgressBarIndeterminateVisibility(false); + } } private void updateAlertTitle() { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index cce6fc4..32284e8 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -16,8 +16,6 @@ package android.app; -import com.android.internal.R; - import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -33,12 +31,18 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.TypedValue; +import android.view.Gravity; import android.view.View; import android.widget.ProgressBar; import android.widget.RemoteViews; +import com.android.internal.R; + import java.text.NumberFormat; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * A class that represents how a persistent notification is to be presented to @@ -128,7 +132,7 @@ public class Notification implements Parcelable * leave it at its default value of 0. * * @see android.widget.ImageView#setImageLevel - * @see android.graphics.drawable#setLevel + * @see android.graphics.drawable.Drawable#setLevel */ public int iconLevel; @@ -314,8 +318,8 @@ public class Notification implements Parcelable /** * Bit to be bitwise-ored into the {@link #flags} field that should be - * set if you want the sound and/or vibration play each time the - * notification is sent, even if it has not been canceled before that. + * set if you would only like the sound, vibrate and ticker to be played + * if the notification was not already showing. */ public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008; @@ -348,6 +352,21 @@ public class Notification implements Parcelable */ public static final int FLAG_HIGH_PRIORITY = 0x00000080; + /** + * Bit to be bitswise-ored into the {@link #flags} field that should be + * set if this notification is relevant to the current device only + * and it is not recommended that it bridge to other devices. + */ + public static final int FLAG_LOCAL_ONLY = 0x00000100; + + /** + * Bit to be bitswise-ored into the {@link #flags} field that should be + * set if this notification is the group summary for a group of notifications. + * Grouped notifications may display in a cluster or stack on devices which + * support such rendering. Requires a group key also be set using {@link Builder#setGroup}. + */ + public static final int FLAG_GROUP_SUMMARY = 0x00000200; + public int flags; /** @@ -394,41 +413,125 @@ public class Notification implements Parcelable public int priority; /** + * Notification category: incoming call (voice or video) or similar synchronous communication request. + * @hide + */ + public static final String CATEGORY_CALL = "call"; + + /** + * Notification category: incoming direct message (SMS, instant message, etc.). + * @hide + */ + public static final String CATEGORY_MESSAGE = "msg"; + + /** + * Notification category: asynchronous bulk message (email). + * @hide + */ + public static final String CATEGORY_EMAIL = "email"; + + /** + * Notification category: calendar event. + * @hide + */ + public static final String CATEGORY_EVENT = "event"; + + /** + * Notification category: promotion or advertisement. + * @hide + */ + public static final String CATEGORY_PROMO = "promo"; + + /** + * Notification category: alarm or timer. + * @hide + */ + public static final String CATEGORY_ALARM = "alarm"; + + /** + * Notification category: progress of a long-running background operation. * @hide - * Notification type: incoming call (voice or video) or similar synchronous communication request. */ - public static final String KIND_CALL = "android.call"; + public static final String CATEGORY_PROGRESS = "progress"; /** + * Notification category: social network or sharing update. * @hide - * Notification type: incoming direct message (SMS, instant message, etc.). */ - public static final String KIND_MESSAGE = "android.message"; + public static final String CATEGORY_SOCIAL = "social"; /** + * Notification category: error in background operation or authentication status. * @hide - * Notification type: asynchronous bulk message (email). */ - public static final String KIND_EMAIL = "android.email"; + public static final String CATEGORY_ERROR = "err"; /** + * Notification category: media transport control for playback. * @hide - * Notification type: calendar event. */ - public static final String KIND_EVENT = "android.event"; + public static final String CATEGORY_TRANSPORT = "transport"; /** + * Notification category: system or device status update. Reserved for system use. * @hide - * Notification type: promotion or advertisement. */ - public static final String KIND_PROMO = "android.promo"; + public static final String CATEGORY_SYSTEM = "sys"; /** + * Notification category: indication of running background service. * @hide - * If this notification matches of one or more special types (see the <code>KIND_*</code> - * constants), add them here, best match first. */ - public String[] kind; + public static final String CATEGORY_SERVICE = "service"; + + /** + * Notification category: a specific, timely recommendation for a single thing. + * For example, a news app might want to recommend a news story it believes the user will + * want to read next. + * @hide + */ + public static final String CATEGORY_RECOMMENDATION = "recommendation"; + + /** + * Notification category: ongoing information about device or contextual status. + * @hide + */ + public static final String CATEGORY_STATUS = "status"; + + /** + * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants) + * that best describes this Notification. May be used by the system for ranking and filtering. + * @hide + */ + public String category; + + private String mGroupKey; + + /** + * Get the key used to group this notification into a cluster or stack + * with other notifications on devices which support such rendering. + */ + public String getGroup() { + return mGroupKey; + } + + private String mSortKey; + + /** + * Get a sort key that orders this notification among other notifications from the + * same package. This can be useful if an external sort was already applied and an app + * would like to preserve this. Notifications will be sorted lexicographically using this + * value, although providing different priorities in addition to providing sort key may + * cause this value to be ignored. + * + * <p>This sort key can also be used to order members of a notification group. See + * {@link Builder#setGroup}. + * + * @see String#compareTo(String) + */ + public String getSortKey() { + return mSortKey; + } /** * Additional semantic data to be carried around with this Notification. @@ -561,6 +664,13 @@ public class Notification implements Parcelable public static final String EXTRA_AS_HEADS_UP = "headsup"; /** + * Allow certain system-generated notifications to appear before the device is provisioned. + * Only available to notifications coming from the android package. + * @hide + */ + public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup"; + + /** * Value for {@link #EXTRA_AS_HEADS_UP}. * @hide */ @@ -583,48 +693,180 @@ public class Notification implements Parcelable * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is * selected by the user. * <p> - * Apps should use {@link Builder#addAction(int, CharSequence, PendingIntent)} to create and - * attach actions. + * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)} + * or {@link Notification.Builder#addAction(Notification.Action)} + * to attach actions. */ public static class Action implements Parcelable { + private final Bundle mExtras; + private final RemoteInput[] mRemoteInputs; + /** * Small icon representing the action. */ public int icon; + /** * Title of the action. */ public CharSequence title; + /** * Intent to send when the user invokes this action. May be null, in which case the action * may be rendered in a disabled presentation by the system UI. */ public PendingIntent actionIntent; - - private Action() { } + private Action(Parcel in) { icon = in.readInt(); title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); if (in.readInt() == 1) { actionIntent = PendingIntent.CREATOR.createFromParcel(in); } + mExtras = in.readBundle(); + mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR); } + /** - * Use {@link Builder#addAction(int, CharSequence, PendingIntent)}. + * Use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}. */ public Action(int icon, CharSequence title, PendingIntent intent) { + this(icon, title, intent, new Bundle(), null); + } + + private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras, + RemoteInput[] remoteInputs) { this.icon = icon; this.title = title; this.actionIntent = intent; + this.mExtras = extras != null ? extras : new Bundle(); + this.mRemoteInputs = remoteInputs; + } + + /** + * Get additional metadata carried around with this Action. + */ + public Bundle getExtras() { + return mExtras; + } + + /** + * Get the list of inputs to be collected from the user when this action is sent. + * May return null if no remote inputs were added. + */ + public RemoteInput[] getRemoteInputs() { + return mRemoteInputs; + } + + /** + * Builder class for {@link Action} objects. + */ + public static final class Builder { + private final int mIcon; + private final CharSequence mTitle; + private final PendingIntent mIntent; + private final Bundle mExtras; + private ArrayList<RemoteInput> mRemoteInputs; + + /** + * Construct a new builder for {@link Action} object. + * @param icon icon to show for this action + * @param title the title of the action + * @param intent the {@link PendingIntent} to fire when users trigger this action + */ + public Builder(int icon, CharSequence title, PendingIntent intent) { + this(icon, title, intent, new Bundle(), null); + } + + /** + * Construct a new builder for {@link Action} object using the fields from an + * {@link Action}. + * @param action the action to read fields from. + */ + public Builder(Action action) { + this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras), + action.getRemoteInputs()); + } + + private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras, + RemoteInput[] remoteInputs) { + mIcon = icon; + mTitle = title; + mIntent = intent; + mExtras = extras; + if (remoteInputs != null) { + mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length); + Collections.addAll(mRemoteInputs, remoteInputs); + } + } + + /** + * Merge additional metadata into this builder. + * + * <p>Values within the Bundle will replace existing extras values in this Builder. + * + * @see Notification.Action#extras + */ + public Builder addExtras(Bundle extras) { + if (extras != null) { + mExtras.putAll(extras); + } + return this; + } + + /** + * Get the metadata Bundle used by this Builder. + * + * <p>The returned Bundle is shared with this Builder. + */ + public Bundle getExtras() { + return mExtras; + } + + /** + * Add an input to be collected from the user when this action is sent. + * Response values can be retrieved from the fired intent by using the + * {@link RemoteInput#getResultsFromIntent} function. + * @param remoteInput a {@link RemoteInput} to add to the action + * @return this object for method chaining + */ + public Builder addRemoteInput(RemoteInput remoteInput) { + if (mRemoteInputs == null) { + mRemoteInputs = new ArrayList<RemoteInput>(); + } + mRemoteInputs.add(remoteInput); + return this; + } + + /** + * Apply an extender to this action builder. Extenders may be used to add + * metadata or change options on this builder. + */ + public Builder extend(Extender extender) { + extender.extend(this); + return this; + } + + /** + * Combine all of the options that have been set and return a new {@link Action} + * object. + * @return the built action + */ + public Action build() { + RemoteInput[] remoteInputs = mRemoteInputs != null + ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null; + return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs); + } } @Override public Action clone() { return new Action( - this.icon, - this.title, - this.actionIntent // safe to alias - ); + icon, + title, + actionIntent, // safe to alias + new Bundle(mExtras), + getRemoteInputs()); } @Override public int describeContents() { @@ -640,9 +882,11 @@ public class Notification implements Parcelable } else { out.writeInt(0); } + out.writeBundle(mExtras); + out.writeTypedArray(mRemoteInputs, flags); } - public static final Parcelable.Creator<Action> CREATOR - = new Parcelable.Creator<Action>() { + public static final Parcelable.Creator<Action> CREATOR = + new Parcelable.Creator<Action>() { public Action createFromParcel(Parcel in) { return new Action(in); } @@ -650,6 +894,120 @@ public class Notification implements Parcelable return new Action[size]; } }; + + /** + * Extender interface for use with {@link Builder#extend}. Extenders may be used to add + * metadata or change options on an action builder. + */ + public interface Extender { + /** + * Apply this extender to a notification action builder. + * @param builder the builder to be modified. + * @return the build object for chaining. + */ + public Builder extend(Builder builder); + } + + /** + * Wearable extender for notification actions. To add extensions to an action, + * create a new {@link android.app.Notification.Action.WearableExtender} object using + * the {@code WearableExtender()} constructor and apply it to a + * {@link android.app.Notification.Action.Builder} using + * {@link android.app.Notification.Action.Builder#extend}. + * + * <pre class="prettyprint"> + * Notification.Action action = new Notification.Action.Builder( + * R.drawable.archive_all, "Archive all", actionIntent) + * .extend(new Notification.Action.WearableExtender() + * .setAvailableOffline(false)) + * .build();</pre> + */ + public static final class WearableExtender implements Extender { + /** Notification action extra which contains wearable extensions */ + private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS"; + + private static final String KEY_FLAGS = "flags"; + + // Flags bitwise-ored to mFlags + private static final int FLAG_AVAILABLE_OFFLINE = 0x1; + + // Default value for flags integer + private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE; + + private int mFlags = DEFAULT_FLAGS; + + /** + * Create a {@link android.app.Notification.Action.WearableExtender} with default + * options. + */ + public WearableExtender() { + } + + /** + * Create a {@link android.app.Notification.Action.WearableExtender} by reading + * wearable options present in an existing notification action. + * @param action the notification action to inspect. + */ + public WearableExtender(Action action) { + Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS); + if (wearableBundle != null) { + mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS); + } + } + + /** + * Apply wearable extensions to a notification action that is being built. This is + * typically called by the {@link android.app.Notification.Action.Builder#extend} + * method of {@link android.app.Notification.Action.Builder}. + */ + @Override + public Action.Builder extend(Action.Builder builder) { + Bundle wearableBundle = new Bundle(); + + if (mFlags != DEFAULT_FLAGS) { + wearableBundle.putInt(KEY_FLAGS, mFlags); + } + + builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle); + return builder; + } + + @Override + public WearableExtender clone() { + WearableExtender that = new WearableExtender(); + that.mFlags = this.mFlags; + return that; + } + + /** + * Set whether this action is available when the wearable device is not connected to + * a companion device. The user can still trigger this action when the wearable device is + * offline, but a visual hint will indicate that the action may not be available. + * Defaults to true. + */ + public WearableExtender setAvailableOffline(boolean availableOffline) { + setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline); + return this; + } + + /** + * Get whether this action is available when the wearable device is not connected to + * a companion device. The user can still trigger this action when the wearable device is + * offline, but a visual hint will indicate that the action may not be available. + * Defaults to true. + */ + public boolean isAvailableOffline() { + return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0; + } + + private void setFlag(int mask, boolean value) { + if (value) { + mFlags |= mask; + } else { + mFlags &= ~mask; + } + } + } } /** @@ -750,7 +1108,11 @@ public class Notification implements Parcelable priority = parcel.readInt(); - kind = parcel.createStringArray(); // may set kind to null + category = parcel.readString(); + + mGroupKey = parcel.readString(); + + mSortKey = parcel.readString(); extras = parcel.readBundle(); // may be null @@ -815,12 +1177,11 @@ public class Notification implements Parcelable that.priority = this.priority; - final String[] thiskind = this.kind; - if (thiskind != null) { - final int N = thiskind.length; - final String[] thatkind = that.kind = new String[N]; - System.arraycopy(thiskind, 0, thatkind, 0, N); - } + that.category = this.category; + + that.mGroupKey = this.mGroupKey; + + that.mSortKey = this.mSortKey; if (this.extras != null) { try { @@ -957,7 +1318,11 @@ public class Notification implements Parcelable parcel.writeInt(priority); - parcel.writeStringArray(kind); // ok for null + parcel.writeString(category); + + parcel.writeString(mGroupKey); + + parcel.writeString(mSortKey); parcel.writeBundle(extras); // null ok @@ -1077,16 +1442,18 @@ public class Notification implements Parcelable sb.append(Integer.toHexString(this.defaults)); sb.append(" flags=0x"); sb.append(Integer.toHexString(this.flags)); - sb.append(" kind=["); - if (this.kind == null) { - sb.append("null"); - } else { - for (int i=0; i<this.kind.length; i++) { - if (i>0) sb.append(","); - sb.append(this.kind[i]); - } + if (this.category != null) { + sb.append(" category="); + sb.append(this.category); + } + if (this.mGroupKey != null) { + sb.append(" groupKey="); + sb.append(this.mGroupKey); + } + if (this.mSortKey != null) { + sb.append(" sortKey="); + sb.append(this.mSortKey); } - sb.append("]"); if (actions != null) { sb.append(" "); sb.append(actions.length); @@ -1165,7 +1532,9 @@ public class Notification implements Parcelable private int mProgressMax; private int mProgress; private boolean mProgressIndeterminate; - private ArrayList<String> mKindList = new ArrayList<String>(1); + private String mCategory; + private String mGroupKey; + private String mSortKey; private Bundle mExtras; private int mPriority; private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS); @@ -1204,7 +1573,7 @@ public class Notification implements Parcelable /** * Add a timestamp pertaining to the notification (usually the time the event occurred). * It will be shown in the notification content view by default; use - * {@link Builder#setShowWhen(boolean) setShowWhen} to control this. + * {@link #setShowWhen(boolean) setShowWhen} to control this. * * @see Notification#when */ @@ -1214,7 +1583,7 @@ public class Notification implements Parcelable } /** - * Control whether the timestamp set with {@link Builder#setWhen(long) setWhen} is shown + * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown * in the content view. */ public Builder setShowWhen(boolean show) { @@ -1532,6 +1901,17 @@ public class Notification implements Parcelable } /** + * Set whether or not this notification should not bridge to other devices. + * + * <p>Some notifications can be bridged to other devices for remote display. + * This hint can be set to recommend this notification not be bridged. + */ + public Builder setLocalOnly(boolean localOnly) { + setFlag(FLAG_LOCAL_ONLY, localOnly); + return this; + } + + /** * Set which notification properties will be inherited from system defaults. * <p> * The value should be one or more of the following fields combined with @@ -1556,32 +1936,114 @@ public class Notification implements Parcelable } /** + * Set the notification category. + * + * @see Notification#category * @hide + */ + public Builder setCategory(String category) { + mCategory = category; + return this; + } + + /** + * Set this notification to be part of a group of notifications sharing the same key. + * Grouped notifications may display in a cluster or stack on devices which + * support such rendering. * - * Add a kind (category) to this notification. Optional. + * <p>To make this notification the summary for its group, also call + * {@link #setGroupSummary}. A sort order can be specified for group members by using + * {@link #setSortKey}. + * @param groupKey The group key of the group. + * @return this object for method chaining + */ + public Builder setGroup(String groupKey) { + mGroupKey = groupKey; + return this; + } + + /** + * Set this notification to be the group summary for a group of notifications. + * Grouped notifications may display in a cluster or stack on devices which + * support such rendering. Requires a group key also be set using {@link #setGroup}. + * @param isGroupSummary Whether this notification should be a group summary. + * @return this object for method chaining + */ + public Builder setGroupSummary(boolean isGroupSummary) { + setFlag(FLAG_GROUP_SUMMARY, isGroupSummary); + return this; + } + + /** + * Set a sort key that orders this notification among other notifications from the + * same package. This can be useful if an external sort was already applied and an app + * would like to preserve this. Notifications will be sorted lexicographically using this + * value, although providing different priorities in addition to providing sort key may + * cause this value to be ignored. + * + * <p>This sort key can also be used to order members of a notification group. See + * {@link #setGroup}. + * + * @see String#compareTo(String) + */ + public Builder setSortKey(String sortKey) { + mSortKey = sortKey; + return this; + } + + /** + * Merge additional metadata into this notification. + * + * <p>Values within the Bundle will replace existing extras values in this Builder. * - * @see Notification#kind + * @see Notification#extras */ - public Builder addKind(String k) { - mKindList.add(k); + public Builder addExtras(Bundle extras) { + if (extras != null) { + if (mExtras == null) { + mExtras = new Bundle(extras); + } else { + mExtras.putAll(extras); + } + } return this; } /** - * Add metadata to this notification. + * Set metadata for this notification. * - * A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's + * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's * current contents are copied into the Notification each time {@link #build()} is * called. * + * <p>Replaces any existing extras values with those from the provided Bundle. + * Use {@link #addExtras} to merge in metadata instead. + * * @see Notification#extras */ - public Builder setExtras(Bundle bag) { - mExtras = bag; + public Builder setExtras(Bundle extras) { + mExtras = extras; return this; } /** + * Get the current metadata Bundle used by this notification Builder. + * + * <p>The returned Bundle is shared with this Builder. + * + * <p>The current contents of this Bundle are copied into the Notification each time + * {@link #build()} is called. + * + * @see Notification#extras + */ + public Bundle getExtras() { + if (mExtras == null) { + mExtras = new Bundle(); + } + return mExtras; + } + + /** * Add an action to this notification. Actions are typically displayed by * the system as a button adjacent to the notification content. * <p> @@ -1604,6 +2066,26 @@ public class Notification implements Parcelable } /** + * Add an action to this notification. Actions are typically displayed by + * the system as a button adjacent to the notification content. + * <p> + * Every action must have an icon (32dp square and matching the + * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo + * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}. + * <p> + * A notification in its expanded form can display up to 3 actions, from left to right in + * the order they were added. Actions will not be displayed when the notification is + * collapsed, however, so be sure that any essential functions may be accessed by the user + * in some other way (for example, in the Activity pointed to by {@link #contentIntent}). + * + * @param action The action to add. + */ + public Builder addAction(Action action) { + mActions.add(action); + return this; + } + + /** * Add a rich notification style to be applied at build time. * * @param style Object responsible for modifying the notification style. @@ -1618,6 +2100,15 @@ public class Notification implements Parcelable return this; } + /** + * Apply an extender to this notification builder. Extenders may be used to add + * metadata or change options on this builder. + */ + public Builder extend(Extender extender) { + extender.extend(this); + return this; + } + private void setFlag(int mask, boolean value) { if (value) { mFlags |= mask; @@ -1819,18 +2310,14 @@ public class Notification implements Parcelable if ((mDefaults & DEFAULT_LIGHTS) != 0) { n.flags |= FLAG_SHOW_LIGHTS; } - if (mKindList.size() > 0) { - n.kind = new String[mKindList.size()]; - mKindList.toArray(n.kind); - } else { - n.kind = null; - } + n.category = mCategory; + n.mGroupKey = mGroupKey; + n.mSortKey = mSortKey; n.priority = mPriority; if (mActions.size() > 0) { n.actions = new Action[mActions.size()]; mActions.toArray(n.actions); } - return n; } @@ -1839,7 +2326,7 @@ public class Notification implements Parcelable * this Notification object. * @hide */ - public void addExtras(Bundle extras) { + public void populateExtras(Bundle extras) { // Store original information used in the construction of this object extras.putCharSequence(EXTRA_TITLE, mContentTitle); extras.putCharSequence(EXTRA_TEXT, mContentText); @@ -1877,7 +2364,7 @@ public class Notification implements Parcelable n.extras = mExtras != null ? new Bundle(mExtras) : new Bundle(); - addExtras(n.extras); + populateExtras(n.extras); if (mStyle != null) { mStyle.addExtras(n.extras); } @@ -1900,8 +2387,7 @@ public class Notification implements Parcelable * An object that can apply a rich notification style to a {@link Notification.Builder} * object. */ - public static abstract class Style - { + public static abstract class Style { private CharSequence mBigContentTitle; private CharSequence mSummaryText = null; private boolean mSummaryTextSet = false; @@ -2298,4 +2784,670 @@ public class Notification implements Parcelable return wip; } } + + /** + * Extender interface for use with {@link Builder#extend}. Extenders may be used to add + * metadata or change options on a notification builder. + */ + public interface Extender { + /** + * Apply this extender to a notification builder. + * @param builder the builder to be modified. + * @return the build object for chaining. + */ + public Builder extend(Builder builder); + } + + /** + * Helper class to add wearable extensions to notifications. + * <p class="note"> See + * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications + * for Android Wear</a> for more information on how to use this class. + * <p> + * To create a notification with wearable extensions: + * <ol> + * <li>Create a {@link android.app.Notification.Builder}, setting any desired + * properties. + * <li>Create a {@link android.app.Notification.WearableExtender}. + * <li>Set wearable-specific properties using the + * {@code add} and {@code set} methods of {@link android.app.Notification.WearableExtender}. + * <li>Call {@link android.app.Notification.Builder#extend} to apply the extensions to a + * notification. + * <li>Post the notification to the notification system with the + * {@code NotificationManager.notify(...)} methods. + * </ol> + * + * <pre class="prettyprint"> + * Notification notif = new Notification.Builder(mContext) + * .setContentTitle("New mail from " + sender.toString()) + * .setContentText(subject) + * .setSmallIcon(R.drawable.new_mail) + * .extend(new Notification.WearableExtender() + * .setContentIcon(R.drawable.new_mail)) + * .build(); + * NotificationManager notificationManger = + * (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + * notificationManger.notify(0, notif);</pre> + * + * <p>Wearable extensions can be accessed on an existing notification by using the + * {@code WearableExtender(Notification)} constructor, + * and then using the {@code get} methods to access values. + * + * <pre class="prettyprint"> + * Notification.WearableExtender wearableExtender = new Notification.WearableExtender( + * notification); + * List<Notification> pages = wearableExtender.getPages();</pre> + */ + public static final class WearableExtender implements Extender { + /** + * Sentinel value for an action index that is unset. + */ + public static final int UNSET_ACTION_INDEX = -1; + + /** + * Size value for use with {@link #setCustomSizePreset} to show this notification with + * default sizing. + * <p>For custom display notifications created using {@link #setDisplayIntent}, + * the default is {@link #SIZE_LARGE}. All other notifications size automatically based + * on their content. + */ + public static final int SIZE_DEFAULT = 0; + + /** + * Size value for use with {@link #setCustomSizePreset} to show this notification + * with an extra small size. + * <p>This value is only applicable for custom display notifications created using + * {@link #setDisplayIntent}. + */ + public static final int SIZE_XSMALL = 1; + + /** + * Size value for use with {@link #setCustomSizePreset} to show this notification + * with a small size. + * <p>This value is only applicable for custom display notifications created using + * {@link #setDisplayIntent}. + */ + public static final int SIZE_SMALL = 2; + + /** + * Size value for use with {@link #setCustomSizePreset} to show this notification + * with a medium size. + * <p>This value is only applicable for custom display notifications created using + * {@link #setDisplayIntent}. + */ + public static final int SIZE_MEDIUM = 3; + + /** + * Size value for use with {@link #setCustomSizePreset} to show this notification + * with a large size. + * <p>This value is only applicable for custom display notifications created using + * {@link #setDisplayIntent}. + */ + public static final int SIZE_LARGE = 4; + + /** + * Size value for use with {@link #setCustomSizePreset} to show this notification + * full screen. + * <p>This value is only applicable for custom display notifications created using + * {@link #setDisplayIntent}. + */ + public static final int SIZE_FULL_SCREEN = 5; + + /** Notification extra which contains wearable extensions */ + private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS"; + + // Keys within EXTRA_WEARABLE_OPTIONS for wearable options. + private static final String KEY_ACTIONS = "actions"; + private static final String KEY_FLAGS = "flags"; + private static final String KEY_DISPLAY_INTENT = "displayIntent"; + private static final String KEY_PAGES = "pages"; + private static final String KEY_BACKGROUND = "background"; + private static final String KEY_CONTENT_ICON = "contentIcon"; + private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity"; + private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex"; + private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset"; + private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight"; + private static final String KEY_GRAVITY = "gravity"; + + // Flags bitwise-ored to mFlags + private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1; + private static final int FLAG_HINT_HIDE_ICON = 1 << 1; + private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2; + private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3; + + // Default value for flags integer + private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE; + + private static final int DEFAULT_CONTENT_ICON_GRAVITY = Gravity.END; + private static final int DEFAULT_GRAVITY = Gravity.BOTTOM; + + private ArrayList<Action> mActions = new ArrayList<Action>(); + private int mFlags = DEFAULT_FLAGS; + private PendingIntent mDisplayIntent; + private ArrayList<Notification> mPages = new ArrayList<Notification>(); + private Bitmap mBackground; + private int mContentIcon; + private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY; + private int mContentActionIndex = UNSET_ACTION_INDEX; + private int mCustomSizePreset = SIZE_DEFAULT; + private int mCustomContentHeight; + private int mGravity = DEFAULT_GRAVITY; + + /** + * Create a {@link android.app.Notification.WearableExtender} with default + * options. + */ + public WearableExtender() { + } + + public WearableExtender(Notification notif) { + Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS); + if (wearableBundle != null) { + List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS); + if (actions != null) { + mActions.addAll(actions); + } + + mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS); + mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT); + + Notification[] pages = getNotificationArrayFromBundle( + wearableBundle, KEY_PAGES); + if (pages != null) { + Collections.addAll(mPages, pages); + } + + mBackground = wearableBundle.getParcelable(KEY_BACKGROUND); + mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON); + mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY, + DEFAULT_CONTENT_ICON_GRAVITY); + mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX, + UNSET_ACTION_INDEX); + mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET, + SIZE_DEFAULT); + mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT); + mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY); + } + } + + /** + * Apply wearable extensions to a notification that is being built. This is typically + * called by the {@link android.app.Notification.Builder#extend} method of + * {@link android.app.Notification.Builder}. + */ + @Override + public Notification.Builder extend(Notification.Builder builder) { + Bundle wearableBundle = new Bundle(); + + if (!mActions.isEmpty()) { + wearableBundle.putParcelableArrayList(KEY_ACTIONS, mActions); + } + if (mFlags != DEFAULT_FLAGS) { + wearableBundle.putInt(KEY_FLAGS, mFlags); + } + if (mDisplayIntent != null) { + wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent); + } + if (!mPages.isEmpty()) { + wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray( + new Notification[mPages.size()])); + } + if (mBackground != null) { + wearableBundle.putParcelable(KEY_BACKGROUND, mBackground); + } + if (mContentIcon != 0) { + wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon); + } + if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) { + wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity); + } + if (mContentActionIndex != UNSET_ACTION_INDEX) { + wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX, + mContentActionIndex); + } + if (mCustomSizePreset != SIZE_DEFAULT) { + wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset); + } + if (mCustomContentHeight != 0) { + wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight); + } + if (mGravity != DEFAULT_GRAVITY) { + wearableBundle.putInt(KEY_GRAVITY, mGravity); + } + + builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle); + return builder; + } + + @Override + public WearableExtender clone() { + WearableExtender that = new WearableExtender(); + that.mActions = new ArrayList<Action>(this.mActions); + that.mFlags = this.mFlags; + that.mDisplayIntent = this.mDisplayIntent; + that.mPages = new ArrayList<Notification>(this.mPages); + that.mBackground = this.mBackground; + that.mContentIcon = this.mContentIcon; + that.mContentIconGravity = this.mContentIconGravity; + that.mContentActionIndex = this.mContentActionIndex; + that.mCustomSizePreset = this.mCustomSizePreset; + that.mCustomContentHeight = this.mCustomContentHeight; + that.mGravity = this.mGravity; + return that; + } + + /** + * Add a wearable action to this notification. + * + * <p>When wearable actions are added using this method, the set of actions that + * show on a wearable device splits from devices that only show actions added + * using {@link android.app.Notification.Builder#addAction}. This allows for customization + * of which actions display on different devices. + * + * @param action the action to add to this notification + * @return this object for method chaining + * @see android.app.Notification.Action + */ + public WearableExtender addAction(Action action) { + mActions.add(action); + return this; + } + + /** + * Adds wearable actions to this notification. + * + * <p>When wearable actions are added using this method, the set of actions that + * show on a wearable device splits from devices that only show actions added + * using {@link android.app.Notification.Builder#addAction}. This allows for customization + * of which actions display on different devices. + * + * @param actions the actions to add to this notification + * @return this object for method chaining + * @see android.app.Notification.Action + */ + public WearableExtender addActions(List<Action> actions) { + mActions.addAll(actions); + return this; + } + + /** + * Clear all wearable actions present on this builder. + * @return this object for method chaining. + * @see #addAction + */ + public WearableExtender clearActions() { + mActions.clear(); + return this; + } + + /** + * Get the wearable actions present on this notification. + */ + public List<Action> getActions() { + return mActions; + } + + /** + * Set an intent to launch inside of an activity view when displaying + * this notification. The {@link PendingIntent} provided should be for an activity. + * + * <pre class="prettyprint"> + * Intent displayIntent = new Intent(context, MyDisplayActivity.class); + * PendingIntent displayPendingIntent = PendingIntent.getActivity(context, + * 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT); + * Notification notif = new Notification.Builder(context) + * .extend(new Notification.WearableExtender() + * .setDisplayIntent(displayPendingIntent) + * .setCustomSizePreset(Notification.WearableExtender.SIZE_MEDIUM)) + * .build();</pre> + * + * <p>The activity to launch needs to allow embedding, must be exported, and + * should have an empty task affinity. + * + * <p>Example AndroidManifest.xml entry: + * <pre class="prettyprint"> + * <activity android:name="com.example.MyDisplayActivity" + * android:exported="true" + * android:allowEmbedded="true" + * android:taskAffinity="" /></pre> + * + * @param intent the {@link PendingIntent} for an activity + * @return this object for method chaining + * @see android.app.Notification.WearableExtender#getDisplayIntent + */ + public WearableExtender setDisplayIntent(PendingIntent intent) { + mDisplayIntent = intent; + return this; + } + + /** + * Get the intent to launch inside of an activity view when displaying this + * notification. This {@code PendingIntent} should be for an activity. + */ + public PendingIntent getDisplayIntent() { + return mDisplayIntent; + } + + /** + * Add an additional page of content to display with this notification. The current + * notification forms the first page, and pages added using this function form + * subsequent pages. This field can be used to separate a notification into multiple + * sections. + * + * @param page the notification to add as another page + * @return this object for method chaining + * @see android.app.Notification.WearableExtender#getPages + */ + public WearableExtender addPage(Notification page) { + mPages.add(page); + return this; + } + + /** + * Add additional pages of content to display with this notification. The current + * notification forms the first page, and pages added using this function form + * subsequent pages. This field can be used to separate a notification into multiple + * sections. + * + * @param pages a list of notifications + * @return this object for method chaining + * @see android.app.Notification.WearableExtender#getPages + */ + public WearableExtender addPages(List<Notification> pages) { + mPages.addAll(pages); + return this; + } + + /** + * Clear all additional pages present on this builder. + * @return this object for method chaining. + * @see #addPage + */ + public WearableExtender clearPages() { + mPages.clear(); + return this; + } + + /** + * Get the array of additional pages of content for displaying this notification. The + * current notification forms the first page, and elements within this array form + * subsequent pages. This field can be used to separate a notification into multiple + * sections. + * @return the pages for this notification + */ + public List<Notification> getPages() { + return mPages; + } + + /** + * Set a background image to be displayed behind the notification content. + * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background + * will work with any notification style. + * + * @param background the background bitmap + * @return this object for method chaining + * @see android.app.Notification.WearableExtender#getBackground + */ + public WearableExtender setBackground(Bitmap background) { + mBackground = background; + return this; + } + + /** + * Get a background image to be displayed behind the notification content. + * Contrary to the {@link android.app.Notification.BigPictureStyle}, this background + * will work with any notification style. + * + * @return the background image + * @see android.app.Notification.WearableExtender#setBackground + */ + public Bitmap getBackground() { + return mBackground; + } + + /** + * Set an icon that goes with the content of this notification. + */ + public WearableExtender setContentIcon(int icon) { + mContentIcon = icon; + return this; + } + + /** + * Get an icon that goes with the content of this notification. + */ + public int getContentIcon() { + return mContentIcon; + } + + /** + * Set the gravity that the content icon should have within the notification display. + * Supported values include {@link android.view.Gravity#START} and + * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}. + * @see #setContentIcon + */ + public WearableExtender setContentIconGravity(int contentIconGravity) { + mContentIconGravity = contentIconGravity; + return this; + } + + /** + * Get the gravity that the content icon should have within the notification display. + * Supported values include {@link android.view.Gravity#START} and + * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}. + * @see #getContentIcon + */ + public int getContentIconGravity() { + return mContentIconGravity; + } + + /** + * Set an action from this notification's actions to be clickable with the content of + * this notification. This action will no longer display separately from the + * notification's content. + * + * <p>For notifications with multiple pages, child pages can also have content actions + * set, although the list of available actions comes from the main notification and not + * from the child page's notification. + * + * @param actionIndex The index of the action to hoist onto the current notification page. + * If wearable actions were added to the main notification, this index + * will apply to that list, otherwise it will apply to the regular + * actions list. + */ + public WearableExtender setContentAction(int actionIndex) { + mContentActionIndex = actionIndex; + return this; + } + + /** + * Get the index of the notification action, if any, that was specified as being clickable + * with the content of this notification. This action will no longer display separately + * from the notification's content. + * + * <p>For notifications with multiple pages, child pages can also have content actions + * set, although the list of available actions comes from the main notification and not + * from the child page's notification. + * + * <p>If wearable specific actions were added to the main notification, this index will + * apply to that list, otherwise it will apply to the regular actions list. + * + * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected. + */ + public int getContentAction() { + return mContentActionIndex; + } + + /** + * Set the gravity that this notification should have within the available viewport space. + * Supported values include {@link android.view.Gravity#TOP}, + * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}. + * The default value is {@link android.view.Gravity#BOTTOM}. + */ + public WearableExtender setGravity(int gravity) { + mGravity = gravity; + return this; + } + + /** + * Get the gravity that this notification should have within the available viewport space. + * Supported values include {@link android.view.Gravity#TOP}, + * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}. + * The default value is {@link android.view.Gravity#BOTTOM}. + */ + public int getGravity() { + return mGravity; + } + + /** + * Set the custom size preset for the display of this notification out of the available + * presets found in {@link android.app.Notification.WearableExtender}, e.g. + * {@link #SIZE_LARGE}. + * <p>Some custom size presets are only applicable for custom display notifications created + * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. Check the + * documentation for the preset in question. See also + * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}. + */ + public WearableExtender setCustomSizePreset(int sizePreset) { + mCustomSizePreset = sizePreset; + return this; + } + + /** + * Get the custom size preset for the display of this notification out of the available + * presets found in {@link android.app.Notification.WearableExtender}, e.g. + * {@link #SIZE_LARGE}. + * <p>Some custom size presets are only applicable for custom display notifications created + * using {@link #setDisplayIntent}. Check the documentation for the preset in question. + * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}. + */ + public int getCustomSizePreset() { + return mCustomSizePreset; + } + + /** + * Set the custom height in pixels for the display of this notification's content. + * <p>This option is only available for custom display notifications created + * using {@link android.app.Notification.WearableExtender#setDisplayIntent}. See also + * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and + * {@link #getCustomContentHeight}. + */ + public WearableExtender setCustomContentHeight(int height) { + mCustomContentHeight = height; + return this; + } + + /** + * Get the custom height in pixels for the display of this notification's content. + * <p>This option is only available for custom display notifications created + * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and + * {@link #setCustomContentHeight}. + */ + public int getCustomContentHeight() { + return mCustomContentHeight; + } + + /** + * Set whether the scrolling position for the contents of this notification should start + * at the bottom of the contents instead of the top when the contents are too long to + * display within the screen. Default is false (start scroll at the top). + */ + public WearableExtender setStartScrollBottom(boolean startScrollBottom) { + setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom); + return this; + } + + /** + * Get whether the scrolling position for the contents of this notification should start + * at the bottom of the contents instead of the top when the contents are too long to + * display within the screen. Default is false (start scroll at the top). + */ + public boolean getStartScrollBottom() { + return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0; + } + + /** + * Set whether the content intent is available when the wearable device is not connected + * to a companion device. The user can still trigger this intent when the wearable device + * is offline, but a visual hint will indicate that the content intent may not be available. + * Defaults to true. + */ + public WearableExtender setContentIntentAvailableOffline( + boolean contentIntentAvailableOffline) { + setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline); + return this; + } + + /** + * Get whether the content intent is available when the wearable device is not connected + * to a companion device. The user can still trigger this intent when the wearable device + * is offline, but a visual hint will indicate that the content intent may not be available. + * Defaults to true. + */ + public boolean getContentIntentAvailableOffline() { + return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0; + } + + /** + * Set a hint that this notification's icon should not be displayed. + * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise. + * @return this object for method chaining + */ + public WearableExtender setHintHideIcon(boolean hintHideIcon) { + setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon); + return this; + } + + /** + * Get a hint that this notification's icon should not be displayed. + * @return {@code true} if this icon should not be displayed, false otherwise. + * The default value is {@code false} if this was never set. + */ + public boolean getHintHideIcon() { + return (mFlags & FLAG_HINT_HIDE_ICON) != 0; + } + + /** + * Set a visual hint that only the background image of this notification should be + * displayed, and other semantic content should be hidden. This hint is only applicable + * to sub-pages added using {@link #addPage}. + */ + public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) { + setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly); + return this; + } + + /** + * Get a visual hint that only the background image of this notification should be + * displayed, and other semantic content should be hidden. This hint is only applicable + * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}. + */ + public boolean getHintShowBackgroundOnly() { + return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0; + } + + private void setFlag(int mask, boolean value) { + if (value) { + mFlags |= mask; + } else { + mFlags &= ~mask; + } + } + } + + /** + * Get an array of Notification objects from a parcelable array bundle field. + * Update the bundle to have a typed array so fetches in the future don't need + * to do an array copy. + */ + private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) { + Parcelable[] array = bundle.getParcelableArray(key); + if (array instanceof Notification[] || array == null) { + return (Notification[]) array; + } + Notification[] typedArray = Arrays.copyOf(array, array.length, + Notification[].class); + bundle.putParcelableArray(key, typedArray); + return typedArray; + } } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 45467b8..7129e9e 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -95,8 +95,8 @@ public final class PendingIntent implements Parcelable { */ public static final int FLAG_ONE_SHOT = 1<<30; /** - * Flag indicating that if the described PendingIntent already - * exists, then simply return null instead of creating it. + * Flag indicating that if the described PendingIntent does not + * already exist, then simply return null instead of creating it. * For use with {@link #getActivity}, {@link #getBroadcast}, and * {@link #getService}. */ diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java new file mode 100644 index 0000000..11420c5 --- /dev/null +++ b/core/java/android/app/RemoteInput.java @@ -0,0 +1,311 @@ +/* + * 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.app; + +import android.content.ClipData; +import android.content.ClipDescription; +import android.content.Intent; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A {@code RemoteInput} object specifies input to be collected from a user to be passed along with + * an intent inside a {@link android.app.PendingIntent} that is sent. + * Always use {@link RemoteInput.Builder} to create instances of this class. + * <p class="note"> See + * <a href="{@docRoot}wear/notifications/remote-input.html">Receiving Voice Input from + * a Notification</a> for more information on how to use this class. + * + * <p>The following example adds a {@code RemoteInput} to a {@link Notification.Action}, + * sets the result key as {@code quick_reply}, and sets the label as {@code Quick reply}. + * Users are prompted to input a response when they trigger the action. The results are sent along + * with the intent and can be retrieved with the result key (provided to the {@link Builder} + * constructor) from the Bundle returned by {@link #getResultsFromIntent}. + * + * <pre class="prettyprint"> + * public static final String KEY_QUICK_REPLY_TEXT = "quick_reply"; + * Notification.Action action = new Notification.Action.Builder( + * R.drawable.reply, "Reply", actionIntent) + * <b>.addRemoteInput(new RemoteInput.Builder(KEY_QUICK_REPLY_TEXT) + * .setLabel("Quick reply").build()</b>) + * .build();</pre> + * + * <p>When the {@link android.app.PendingIntent} is fired, the intent inside will contain the + * input results if collected. To access these results, use the {@link #getResultsFromIntent} + * function. The result values will present under the result key passed to the {@link Builder} + * constructor. + * + * <pre class="prettyprint"> + * public static final String KEY_QUICK_REPLY_TEXT = "quick_reply"; + * Bundle results = RemoteInput.getResultsFromIntent(intent); + * if (results != null) { + * CharSequence quickReplyResult = results.getCharSequence(KEY_QUICK_REPLY_TEXT); + * }</pre> + */ +public final class RemoteInput implements Parcelable { + /** Label used to denote the clip data type used for remote input transport */ + public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results"; + + /** Extra added to a clip data intent object to hold the results bundle. */ + public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData"; + + // Flags bitwise-ored to mFlags + private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1; + + // Default value for flags integer + private static final int DEFAULT_FLAGS = FLAG_ALLOW_FREE_FORM_INPUT; + + private final String mResultKey; + private final CharSequence mLabel; + private final CharSequence[] mChoices; + private final int mFlags; + private final Bundle mExtras; + + private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices, + int flags, Bundle extras) { + this.mResultKey = resultKey; + this.mLabel = label; + this.mChoices = choices; + this.mFlags = flags; + this.mExtras = extras; + } + + /** + * Get the key that the result of this input will be set in from the Bundle returned by + * {@link #getResultsFromIntent} when the {@link android.app.PendingIntent} is sent. + */ + public String getResultKey() { + return mResultKey; + } + + /** + * Get the label to display to users when collecting this input. + */ + public CharSequence getLabel() { + return mLabel; + } + + /** + * Get possible input choices. This can be {@code null} if there are no choices to present. + */ + public CharSequence[] getChoices() { + return mChoices; + } + + /** + * Get whether or not users can provide an arbitrary value for + * input. If you set this to {@code false}, users must select one of the + * choices in {@link #getChoices}. An {@link IllegalArgumentException} is thrown + * if you set this to false and {@link #getChoices} returns {@code null} or empty. + */ + public boolean getAllowFreeFormInput() { + return (mFlags & FLAG_ALLOW_FREE_FORM_INPUT) != 0; + } + + /** + * Get additional metadata carried around with this remote input. + */ + public Bundle getExtras() { + return mExtras; + } + + /** + * Builder class for {@link RemoteInput} objects. + */ + public static final class Builder { + private final String mResultKey; + private CharSequence mLabel; + private CharSequence[] mChoices; + private int mFlags = DEFAULT_FLAGS; + private Bundle mExtras = new Bundle(); + + /** + * Create a builder object for {@link RemoteInput} objects. + * @param resultKey the Bundle key that refers to this input when collected from the user + */ + public Builder(String resultKey) { + if (resultKey == null) { + throw new IllegalArgumentException("Result key can't be null"); + } + mResultKey = resultKey; + } + + /** + * Set a label to be displayed to the user when collecting this input. + * @param label The label to show to users when they input a response. + * @return this object for method chaining + */ + public Builder setLabel(CharSequence label) { + mLabel = Notification.safeCharSequence(label); + return this; + } + + /** + * Specifies choices available to the user to satisfy this input. + * @param choices an array of pre-defined choices for users input. + * You must provide a non-null and non-empty array if + * you disabled free form input using {@link #setAllowFreeFormInput}. + * @return this object for method chaining + */ + public Builder setChoices(CharSequence[] choices) { + if (choices == null) { + mChoices = null; + } else { + mChoices = new CharSequence[choices.length]; + for (int i = 0; i < choices.length; i++) { + mChoices[i] = Notification.safeCharSequence(choices[i]); + } + } + return this; + } + + /** + * Specifies whether the user can provide arbitrary values. + * + * @param allowFreeFormInput The default is {@code true}. + * If you specify {@code false}, you must provide a non-null + * and non-empty array to {@link #setChoices} or an + * {@link IllegalArgumentException} is thrown. + * @return this object for method chaining + */ + public Builder setAllowFreeFormInput(boolean allowFreeFormInput) { + setFlag(mFlags, allowFreeFormInput); + return this; + } + + /** + * Merge additional metadata into this builder. + * + * <p>Values within the Bundle will replace existing extras values in this Builder. + * + * @see RemoteInput#getExtras + */ + public Builder addExtras(Bundle extras) { + if (extras != null) { + mExtras.putAll(extras); + } + return this; + } + + /** + * Get the metadata Bundle used by this Builder. + * + * <p>The returned Bundle is shared with this Builder. + */ + public Bundle getExtras() { + return mExtras; + } + + private void setFlag(int mask, boolean value) { + if (value) { + mFlags |= mask; + } else { + mFlags &= ~mask; + } + } + + /** + * Combine all of the options that have been set and return a new {@link RemoteInput} + * object. + */ + public RemoteInput build() { + return new RemoteInput(mResultKey, mLabel, mChoices, mFlags, mExtras); + } + } + + private RemoteInput(Parcel in) { + mResultKey = in.readString(); + mLabel = in.readCharSequence(); + mChoices = in.readCharSequenceArray(); + mFlags = in.readInt(); + mExtras = in.readBundle(); + } + + /** + * Get the remote input results bundle from an intent. The returned Bundle will + * contain a key/value for every result key populated by remote input collector. + * Use the {@link Bundle#getCharSequence(String)} method to retrieve a value. + * @param intent The intent object that fired in response to an action or content intent + * which also had one or more remote input requested. + */ + public static Bundle getResultsFromIntent(Intent intent) { + ClipData clipData = intent.getClipData(); + if (clipData == null) { + return null; + } + ClipDescription clipDescription = clipData.getDescription(); + if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) { + return null; + } + if (clipDescription.getLabel().equals(RESULTS_CLIP_LABEL)) { + return clipData.getItemAt(0).getIntent().getExtras().getParcelable(EXTRA_RESULTS_DATA); + } + return null; + } + + /** + * Populate an intent object with the results gathered from remote input. This method + * should only be called by remote input collection services when sending results to a + * pending intent. + * @param remoteInputs The remote inputs for which results are being provided + * @param intent The intent to add remote inputs to. The {@link ClipData} + * field of the intent will be modified to contain the results. + * @param results A bundle holding the remote input results. This bundle should + * be populated with keys matching the result keys specified in + * {@code remoteInputs} with values being the result per key. + */ + public static void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent, + Bundle results) { + Bundle resultsBundle = new Bundle(); + for (RemoteInput remoteInput : remoteInputs) { + Object result = results.get(remoteInput.getResultKey()); + if (result instanceof CharSequence) { + resultsBundle.putCharSequence(remoteInput.getResultKey(), (CharSequence) result); + } + } + Intent clipIntent = new Intent(); + clipIntent.putExtra(EXTRA_RESULTS_DATA, resultsBundle); + intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipIntent)); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(mResultKey); + out.writeCharSequence(mLabel); + out.writeCharSequenceArray(mChoices); + out.writeInt(mFlags); + out.writeBundle(mExtras); + } + + public static final Creator<RemoteInput> CREATOR = new Creator<RemoteInput>() { + @Override + public RemoteInput createFromParcel(Parcel in) { + return new RemoteInput(in); + } + + @Override + public RemoteInput[] newArray(int size) { + return new RemoteInput[size]; + } + }; +} diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 0c22740..c6731c9 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -166,9 +166,11 @@ public class UiModeManager { /** * Return the current running mode type. May be one of * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL}, - * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or - * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, or - * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_APPLIANCE}. + * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, + * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, + * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TELEVISION}, + * {@link Configuration#UI_MODE_TYPE_APPLIANCE Configuration.UI_MODE_TYPE_APPLIANCE}, or + * {@link Configuration#UI_MODE_TYPE_WATCH Configuration.UI_MODE_TYPE_WATCH}. */ public int getCurrentModeType() { if (mService != null) { diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index e183177..f87001a 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -284,13 +284,15 @@ public class WallpaperManager { } private Bitmap getCurrentWallpaperLocked(Context context) { + if (mService == null) { + Log.w(TAG, "WallpaperService not running"); + return null; + } + try { Bundle params = new Bundle(); ParcelFileDescriptor fd = mService.getWallpaper(this, params); if (fd != null) { - int width = params.getInt("width", 0); - int height = params.getInt("height", 0); - try { BitmapFactory.Options options = new BitmapFactory.Options(); return BitmapFactory.decodeFileDescriptor( @@ -312,28 +314,21 @@ public class WallpaperManager { } private Bitmap getDefaultWallpaperLocked(Context context) { - try { - InputStream is = context.getResources().openRawResource( - com.android.internal.R.drawable.default_wallpaper); - if (is != null) { - int width = mService.getWidthHint(); - int height = mService.getHeightHint(); - + InputStream is = context.getResources().openRawResource( + com.android.internal.R.drawable.default_wallpaper); + if (is != null) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + return BitmapFactory.decodeStream(is, null, options); + } catch (OutOfMemoryError e) { + Log.w(TAG, "Can't decode stream", e); + } finally { try { - BitmapFactory.Options options = new BitmapFactory.Options(); - return BitmapFactory.decodeStream(is, null, options); - } catch (OutOfMemoryError e) { - Log.w(TAG, "Can't decode stream", e); - } finally { - try { - is.close(); - } catch (IOException e) { - // Ignore - } + is.close(); + } catch (IOException e) { + // Ignore } } - } catch (RemoteException e) { - // Ignore } return null; } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 75b007c..646be06 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -570,7 +570,7 @@ public final class BluetoothAdapter { } /** - * Stop BLE advertising. + * Stop BLE advertising. The callback has to be the same one used for start advertising. * * @param callback - {@link AdvertiseCallback} * @return true if BLE advertising stops, false otherwise. @@ -1989,7 +1989,13 @@ public final class BluetoothAdapter { public void onAdvertiseStateChange(int advertiseState, int status) { Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status); if (advertiseState == STATE_ADVERTISE_STARTED) { - mAdvertiseCallback.onAdvertiseStart(status); + if (status == ADVERTISE_CALLBACK_SUCCESS) { + mAdvertiseCallback.onAdvertiseStart(status); + } else { + // If status is unsuccessful and advertise state is started, it means stop + // advertising fails. + mAdvertiseCallback.onAdvertiseStop(status); + } } else { synchronized (this) { if (status == ADVERTISE_CALLBACK_SUCCESS) { @@ -2011,8 +2017,22 @@ public final class BluetoothAdapter { } } } - mAdvertiseCallback.onAdvertiseStop(status); + if (status == ADVERTISE_CALLBACK_SUCCESS) { + mAdvertiseCallback.onAdvertiseStop(status); + } else{ + // If status is unsuccesful and advertise state is stopped, it means start + // advertising fails. + mAdvertiseCallback.onAdvertiseStart(status); + } } } + + /** + * Callback reporting LE ATT MTU. + * @hide + */ + public void onConfigureMTU(String address, int mtu, int status) { + // no op + } } } diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index ae6ad3b..e7ab8de 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -61,6 +61,7 @@ public final class BluetoothGatt implements BluetoothProfile { private boolean mAutoConnect; private int mConnState; private final Object mStateLock = new Object(); + private Boolean mDeviceBusy = false; private static final int CONN_STATE_IDLE = 0; private static final int CONN_STATE_CONNECTING = 1; @@ -177,6 +178,10 @@ public final class BluetoothGatt implements BluetoothProfile { mConnState = CONN_STATE_IDLE; } } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } } /** @@ -312,6 +317,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } + if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) && mAuthRetry == false) { @@ -359,6 +369,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -436,6 +451,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -485,6 +505,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -530,6 +555,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (!address.equals(mDevice.getAddress())) { return; } + + synchronized(mDeviceBusy) { + mDeviceBusy = false; + } + try { mCallback.onReliableWriteCompleted(BluetoothGatt.this, status); } catch (Exception ex) { @@ -561,6 +591,23 @@ public final class BluetoothGatt implements BluetoothProfile { public void onAdvertiseStateChange(int state, int status) { if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = " + state + " status=" + status); + } + + /** + * Callback invoked when the MTU for a given connection changes + * @hide + */ + public void onConfigureMTU(String address, int mtu, int status) { + if (DBG) Log.d(TAG, "onConfigureMTU() - Device=" + address + + " mtu=" + mtu + " status=" + status); + if (!address.equals(mDevice.getAddress())) { + return; + } + try { + mCallback.onConfigureMTU(BluetoothGatt.this, mtu, status); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } } }; @@ -845,6 +892,11 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + try { mService.readCharacteristic(mClientIf, device.getAddress(), service.getType(), service.getInstanceId(), @@ -852,6 +904,7 @@ public final class BluetoothGatt implements BluetoothProfile { new ParcelUuid(characteristic.getUuid()), AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -884,6 +937,11 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + try { mService.writeCharacteristic(mClientIf, device.getAddress(), service.getType(), service.getInstanceId(), @@ -893,6 +951,7 @@ public final class BluetoothGatt implements BluetoothProfile { characteristic.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -924,6 +983,11 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + try { mService.readDescriptor(mClientIf, device.getAddress(), service.getType(), service.getInstanceId(), new ParcelUuid(service.getUuid()), @@ -932,6 +996,7 @@ public final class BluetoothGatt implements BluetoothProfile { AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -962,6 +1027,11 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothDevice device = service.getDevice(); if (device == null) return false; + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + try { mService.writeDescriptor(mClientIf, device.getAddress(), service.getType(), service.getInstanceId(), new ParcelUuid(service.getUuid()), @@ -971,6 +1041,7 @@ public final class BluetoothGatt implements BluetoothProfile { descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -1028,10 +1099,16 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; + synchronized(mDeviceBusy) { + if (mDeviceBusy) return false; + mDeviceBusy = true; + } + try { mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -1148,6 +1225,36 @@ public final class BluetoothGatt implements BluetoothProfile { } /** + * Configure the MTU used for a given connection. + * + * <p>When performing a write request operation (write without response), + * the data sent is truncated to the MTU size. This function may be used + * to request a larget MTU size to be able to send more data at once. + * + * <p>A {@link BluetoothGattCallback#onConfigureMTU} callback will indicate + * whether this operation was successful. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the new MTU value has been requested successfully + * @hide + */ + public boolean configureMTU(int mtu) { + if (DBG) Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress() + + " mtu: " + mtu); + if (mService == null || mClientIf == 0) return false; + + try { + mService.configureMTU(mClientIf, mDevice.getAddress(), mtu); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} * with {@link BluetoothProfile#GATT} as argument * diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java index 80ea4a6..5180259 100644 --- a/core/java/android/bluetooth/BluetoothGattCallback.java +++ b/core/java/android/bluetooth/BluetoothGattCallback.java @@ -138,4 +138,19 @@ public abstract class BluetoothGattCallback { */ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { } + + /** + * Callback indicating the MTU for a given device connection has changed. + * + * This callback is triggered in response to the + * {@link BluetoothGatt#configureMTU} function, or in response to a connection + * event. + * + * @param gatt GATT client invoked {@link BluetoothGatt#configureMTU} + * @param mtu The new MTU size + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully + * @hide + */ + public void onConfigureMTU(BluetoothGatt gatt, int mtu, int status) { + } } diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java index 844f432..c48b15d 100644 --- a/core/java/android/bluetooth/BluetoothInputDevice.java +++ b/core/java/android/bluetooth/BluetoothInputDevice.java @@ -76,6 +76,19 @@ public final class BluetoothInputDevice implements BluetoothProfile { public static final String ACTION_PROTOCOL_MODE_CHANGED = "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED"; + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_HANDSHAKE = + "android.bluetooth.input.profile.action.HANDSHAKE"; + + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_REPORT = + "android.bluetooth.input.profile.action.REPORT"; /** * @hide @@ -130,17 +143,17 @@ public final class BluetoothInputDevice implements BluetoothProfile { /** * @hide */ - public static final byte REPORT_TYPE_INPUT = 0; + public static final byte REPORT_TYPE_INPUT = 1; /** * @hide */ - public static final byte REPORT_TYPE_OUTPUT = 1; + public static final byte REPORT_TYPE_OUTPUT = 2; /** * @hide */ - public static final byte REPORT_TYPE_FEATURE = 2; + public static final byte REPORT_TYPE_FEATURE = 3; /** * @hide @@ -180,6 +193,11 @@ public final class BluetoothInputDevice implements BluetoothProfile { /** * @hide */ + public static final String EXTRA_STATUS = "android.bluetooth.BluetoothInputDevice.extra.STATUS"; + + /** + * @hide + */ public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; private Context mContext; @@ -603,7 +621,7 @@ public final class BluetoothInputDevice implements BluetoothProfile { * @hide */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { - if (DBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); + if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); if (mService != null && isEnabled() && isValidDevice(device)) { try { return mService.setReport(device, reportType, report); diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index d10eaea..5738b9a 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -86,8 +86,8 @@ import java.nio.ByteBuffer; */ public final class BluetoothSocket implements Closeable { private static final String TAG = "BluetoothSocket"; - private static final boolean DBG = true; - private static final boolean VDBG = false; + private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); /** @hide */ public static final int MAX_RFCOMM_CHANNEL = 30; @@ -190,7 +190,7 @@ public final class BluetoothSocket implements Closeable { BluetoothSocket as = new BluetoothSocket(this); as.mSocketState = SocketState.CONNECTED; FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); - if (VDBG) Log.d(TAG, "socket fd passed by stack fds: " + fds); + if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + fds); if(fds == null || fds.length != 1) { Log.e(TAG, "socket fd passed from stack failed, fds: " + fds); as.close(); @@ -356,24 +356,24 @@ public final class BluetoothSocket implements Closeable { // read out port number try { synchronized(this) { - if (VDBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + + if (DBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd); if(mSocketState != SocketState.INIT) return EBADFD; if(mPfd == null) return -1; FileDescriptor fd = mPfd.getFileDescriptor(); - if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket "); + if (DBG) Log.d(TAG, "bindListen(), new LocalSocket "); mSocket = new LocalSocket(fd); - if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); + if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() "); mSocketIS = mSocket.getInputStream(); mSocketOS = mSocket.getOutputStream(); } - if (VDBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); + if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); int channel = readInt(mSocketIS); synchronized(this) { if(mSocketState == SocketState.INIT) mSocketState = SocketState.LISTENING; } - if (VDBG) Log.d(TAG, "channel: " + channel); + if (DBG) Log.d(TAG, "channel: " + channel); if (mPort == -1) { mPort = channel; } // else ASSERT(mPort == channel) @@ -417,32 +417,33 @@ public final class BluetoothSocket implements Closeable { * if an i/o error occurs. */ /*package*/ void flush() throws IOException { + if (mSocketOS == null) throw new IOException("flush is called on null OutputStream"); if (VDBG) Log.d(TAG, "flush: " + mSocketOS); mSocketOS.flush(); } /*package*/ int read(byte[] b, int offset, int length) throws IOException { - - if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); - int ret = mSocketIS.read(b, offset, length); - if(ret < 0) - throw new IOException("bt socket closed, read return: " + ret); - if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); - return ret; + if (mSocketIS == null) throw new IOException("read is called on null InputStream"); + if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); + int ret = mSocketIS.read(b, offset, length); + if(ret < 0) + throw new IOException("bt socket closed, read return: " + ret); + if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); + return ret; } /*package*/ int write(byte[] b, int offset, int length) throws IOException { - - if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); - mSocketOS.write(b, offset, length); - // There is no good way to confirm since the entire process is asynchronous anyway - if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); - return length; + if (mSocketOS == null) throw new IOException("write is called on null OutputStream"); + if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); + mSocketOS.write(b, offset, length); + // There is no good way to confirm since the entire process is asynchronous anyway + if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); + return length; } @Override public void close() throws IOException { - if (VDBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); + if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); if(mSocketState == SocketState.CLOSED) return; else @@ -452,10 +453,10 @@ public final class BluetoothSocket implements Closeable { if(mSocketState == SocketState.CLOSED) return; mSocketState = SocketState.CLOSED; - if (VDBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + + if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket); if(mSocket != null) { - if (VDBG) Log.d(TAG, "Closing mSocket: " + mSocket); + if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket); mSocket.shutdownInput(); mSocket.shutdownOutput(); mSocket.close(); diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index a9b7176..7745bb7 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -61,6 +61,9 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { private static final boolean DBG = true; private static final boolean VDBG = true; + // Event sent to the mBtdtHandler when DHCP fails so we can tear down the network. + private static final int EVENT_NETWORK_FAILED = 1; + private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); @@ -328,6 +331,7 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { } if (!success) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); + mBtdtHandler.obtainMessage(EVENT_NETWORK_FAILED).sendToTarget(); return; } mLinkProperties = dhcpResults.linkProperties; @@ -420,6 +424,10 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties); mBtdt.stopReverseTether(); break; + case EVENT_NETWORK_FAILED: + if (VDBG) Log.d(TAG, "got EVENT_NETWORK_FAILED"); + mBtdt.teardown(); + break; } } } diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index 784cdcc..c6b5c3d 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -73,6 +73,7 @@ interface IBluetoothGatt { void beginReliableWrite(in int clientIf, in String address); void endReliableWrite(in int clientIf, in String address, in boolean execute); void readRemoteRssi(in int clientIf, in String address); + void configureMTU(in int clientIf, in String address, in int mtu); void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); void unregisterServer(in int serverIf); diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl index 7c69a06..a78c29b 100644 --- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -64,4 +64,5 @@ interface IBluetoothGattCallback { in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); oneway void onAdvertiseStateChange(in int advertiseState, in int status); + void onConfigureMTU(in String address, in int mtu, in int status); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 2e4e209..bfdb46a 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -530,6 +530,9 @@ public abstract class Context { * Open a private file associated with this Context's application package * for writing. Creates the file if it doesn't already exist. * + * <p>No permissions are required to invoke this method, since it uses internal + * storage. + * * @param name The name of the file to open; can not contain path * separators. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the @@ -587,6 +590,9 @@ public abstract class Context { * Returns the absolute path to the directory on the filesystem where * files created with {@link #openFileOutput} are stored. * + * <p>No permissions are required to read or write to the returned path, since this + * path is internal storage. + * * @return The path of the directory holding application files. * * @see #openFileOutput @@ -1798,7 +1804,7 @@ public abstract class Context { * @hide like {@link #stopService(Intent)} but for a specific user. */ public abstract boolean stopServiceAsUser(Intent service, UserHandle user); - + /** * Connect to an application service, creating it if needed. This defines * a dependency between your application and the service. The given @@ -1974,6 +1980,8 @@ public abstract class Context { * @see android.app.SearchManager * @see #SENSOR_SERVICE * @see android.hardware.SensorManager + * @see #HDMI_CEC_SERVICE + * @see android.hardware.hdmi.HdmiCecManager * @see #STORAGE_SERVICE * @see android.os.storage.StorageManager * @see #VIBRATOR_SERVICE @@ -2389,6 +2397,17 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a + * {@link android.hardware.hdmi.HdmiCecManager for controlling and managing + * HDMI-CEC protocol. + * + * @see #getSystemService + * @see android.hardware.hdmi.HdmiCecManager + * @hide + */ + public static final String HDMI_CEC_SERVICE = "hdmi_cec"; + + /** + * Use with {@link #getSystemService} to retrieve a * {@link android.hardware.input.InputManager} for interacting with input devices. * * @see #getSystemService diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index a50b650..b1a743b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1414,15 +1414,38 @@ public class Intent implements Parcelable, Cloneable { // Standard intent broadcast actions (see action variable). /** - * Broadcast Action: Sent after the screen turns off. + * Broadcast Action: Sent when the device goes to sleep and becomes non-interactive. + * <p> + * For historical reasons, the name of this broadcast action refers to the power + * state of the screen but it is actually sent in response to changes in the + * overall interactive state of the device. + * </p><p> + * This broadcast is sent when the device becomes non-interactive which may have + * nothing to do with the screen turning off. To determine the + * actual state of the screen, use {@link android.view.Display#getState}. + * </p><p> + * See {@link android.os.PowerManager#isInteractive} for details. + * </p> * * <p class="note">This is a protected intent that can only be sent * by the system. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_SCREEN_OFF = "android.intent.action.SCREEN_OFF"; + /** - * Broadcast Action: Sent after the screen turns on. + * Broadcast Action: Sent when the device wakes up and becomes interactive. + * <p> + * For historical reasons, the name of this broadcast action refers to the power + * state of the screen but it is actually sent in response to changes in the + * overall interactive state of the device. + * </p><p> + * This broadcast is sent when the device becomes interactive which may have + * nothing to do with the screen turning on. To determine the + * actual state of the screen, use {@link android.view.Display#getState}. + * </p><p> + * See {@link android.os.PowerManager#isInteractive} for details. + * </p> * * <p class="note">This is a protected intent that can only be sent * by the system. @@ -2765,6 +2788,13 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; /** + * Indicates an activity optimized for Leanback mode, and that should + * be displayed in the Leanback launcher. + * @hide + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER"; + /** * Provides information about the package it is in; typically used if * a package does not contain a {@link #CATEGORY_LAUNCHER} to provide * a front-door to the user without having to be shown in the all apps list. @@ -3547,6 +3577,11 @@ public class Intent implements Parcelable, Cloneable { * it will be finished so that the user does not return to them, but * instead returns to whatever activity preceeded it. * + * <p>When this flag is assigned to the root activity all activities up + * to, but not including the root activity, will be cleared. This prevents + * this flag from being used to finish all activities in a task and thereby + * ending the task. + * * <p>This is useful for cases where you have a logical break in your * application. For example, an e-mail application may have a command * to view an attachment, which launches an image view activity to diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java index d4f7f06..00c2d8f 100644 --- a/core/java/android/content/SharedPreferences.java +++ b/core/java/android/content/SharedPreferences.java @@ -355,7 +355,14 @@ public interface SharedPreferences { /** * Registers a callback to be invoked when a change happens to a preference. - * + * + * <p class="caution"><strong>Caution:</strong> The preference manager does + * not currently store a strong reference to the listener. You must store a + * strong reference to the listener, or it will be susceptible to garbage + * collection. We recommend you keep a reference to the listener in the + * instance data of an object that will exist as long as you need the + * listener.</p> + * * @param listener The callback that will run. * @see #unregisterOnSharedPreferenceChangeListener */ diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index b8ac3bf..941b726 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -187,7 +187,7 @@ public class ActivityInfo extends ComponentInfo /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the primary user. Only works with broadcast receivers. Set from the - * {@link android.R.attr#primaryUserOnly} attribute. + * android.R.attr#primaryUserOnly attribute. */ public static final int FLAG_PRIMARY_USER_ONLY = 0x20000000; /** @@ -199,6 +199,13 @@ public class ActivityInfo extends ComponentInfo */ public static final int FLAG_SINGLE_USER = 0x40000000; /** + * @hide Bit in {@link #flags}: If set, this activity may be launched into an + * owned ActivityContainer such as that within an ActivityView. If not set and + * this activity is launched into such a container a SecurityExcception will be + * thrown. Set from the {@link android.R.attr#allowEmbedded} attribute. + */ + public static final int FLAG_ALLOW_EMBEDDED = 0x80000000; + /** * Options that have been set in the activity declaration in the * manifest. * These include: diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index a23cd7f..2639625 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -315,6 +315,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_IS_DATA_ONLY = 1<<24; /** + * Value for {@link #flags}: true if the application was declared to be a game, or + * false if it is a non-game application. + * + * {@hide} + */ + public static final int FLAG_IS_GAME = 1<<25; + + /** * Value for {@link #flags}: set to {@code true} if the application * is permitted to hold privileged permissions. * @@ -483,7 +491,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * @hide */ public int installLocation = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; - + public void dump(Printer pw, String prefix) { super.dumpFront(pw, prefix); if (className != null) { diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java index 4dbcf23..7e8f285 100644 --- a/core/java/android/content/pm/ComponentInfo.java +++ b/core/java/android/content/pm/ComponentInfo.java @@ -128,6 +128,17 @@ public class ComponentInfo extends PackageItemInfo { return logo != 0 ? logo : applicationInfo.logo; } + /** + * Return the banner resource identifier to use for this component. If the + * component defines a banner, that is used; else, the application banner is + * used. + * + * @return The banner associated with this component. + */ + public final int getBannerResource() { + return banner != 0 ? banner : applicationInfo.banner; + } + protected void dumpFront(Printer pw, String prefix) { super.dumpFront(pw, prefix); pw.println(prefix + "enabled=" + enabled + " exported=" + exported @@ -175,6 +186,13 @@ public class ComponentInfo extends PackageItemInfo { /** * @hide */ + @Override protected Drawable loadDefaultBanner(PackageManager pm) { + return applicationInfo.loadBanner(pm); + } + + /** + * @hide + */ @Override protected Drawable loadDefaultLogo(PackageManager pm) { return applicationInfo.loadLogo(pm); diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index a67326e..58f1c84 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -68,6 +68,12 @@ public class PackageItemInfo { /** * A drawable resource identifier (in the package's resources) of this + * component's banner. From the "banner" attribute or, if not set, 0. + */ + public int banner; + + /** + * A drawable resource identifier (in the package's resources) of this * component's logo. Logos may be larger/wider than icons and are * displayed by certain UI elements in place of a name or name/icon * combination. From the "logo" attribute or, if not set, 0. @@ -92,6 +98,7 @@ public class PackageItemInfo { nonLocalizedLabel = orig.nonLocalizedLabel; if (nonLocalizedLabel != null) nonLocalizedLabel = nonLocalizedLabel.toString().trim(); icon = orig.icon; + banner = orig.banner; logo = orig.logo; metaData = orig.metaData; } @@ -146,6 +153,27 @@ public class PackageItemInfo { } /** + * Retrieve the current graphical banner associated with this item. This + * will call back on the given PackageManager to load the banner from + * the application. + * + * @param pm A PackageManager from which the banner can be loaded; usually + * the PackageManager from which you originally retrieved this item. + * + * @return Returns a Drawable containing the item's banner. If the item + * does not have a banner, this method will return null. + */ + public Drawable loadBanner(PackageManager pm) { + if (banner != 0) { + Drawable dr = pm.getDrawable(packageName, banner, getApplicationInfo()); + if (dr != null) { + return dr; + } + } + return loadDefaultBanner(pm); + } + + /** * Retrieve the default graphical icon associated with this item. * * @param pm A PackageManager from which the icon can be loaded; usually @@ -159,7 +187,22 @@ public class PackageItemInfo { protected Drawable loadDefaultIcon(PackageManager pm) { return pm.getDefaultActivityIcon(); } - + + /** + * Retrieve the default graphical banner associated with this item. + * + * @param pm A PackageManager from which the banner can be loaded; usually + * the PackageManager from which you originally retrieved this item. + * + * @return Returns a Drawable containing the item's default banner + * or null if no default logo is available. + * + * @hide + */ + protected Drawable loadDefaultBanner(PackageManager pm) { + return null; + } + /** * Retrieve the current graphical logo associated with this item. This * will call back on the given PackageManager to load the logo from @@ -224,10 +267,11 @@ public class PackageItemInfo { pw.println(prefix + "name=" + name); } pw.println(prefix + "packageName=" + packageName); - if (labelRes != 0 || nonLocalizedLabel != null || icon != 0) { + if (labelRes != 0 || nonLocalizedLabel != null || icon != 0 || banner != 0) { pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes) + " nonLocalizedLabel=" + nonLocalizedLabel - + " icon=0x" + Integer.toHexString(icon)); + + " icon=0x" + Integer.toHexString(icon) + + " banner=0x" + Integer.toHexString(banner)); } } @@ -243,6 +287,7 @@ public class PackageItemInfo { dest.writeInt(icon); dest.writeInt(logo); dest.writeBundle(metaData); + dest.writeInt(banner); } protected PackageItemInfo(Parcel source) { @@ -254,6 +299,7 @@ public class PackageItemInfo { icon = source.readInt(); logo = source.readInt(); metaData = source.readBundle(); + banner = source.readInt(); } /** diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index d8c7906..99d047d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -913,13 +913,21 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has at least one camera pointing in - * some direction. + * some direction, or can support an external camera being connected to it. */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CAMERA_ANY = "android.hardware.camera.any"; /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device can support having an external camera connected to it. + * The external camera may not always be connected or available to applications to use. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device's camera supports flash. */ @SdkConstant(SdkConstantType.FEATURE) @@ -989,6 +997,7 @@ public abstract class PackageManager { * @hide * @deprecated */ + @Deprecated @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_NFC_HCE = "android.hardware.nfc.hce"; @@ -1059,6 +1068,13 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device includes a heart rate monitor. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has a telephony radio with data * communication support. */ @@ -1244,6 +1260,27 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports leanback UI. This is + * typically used in a living room television experience, but is a software + * feature unlike {@link #FEATURE_TELEVISION}. Devices running with this + * feature will use resources associated with the "television" UI mode. + * @hide + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_LEANBACK = "android.software.leanback"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports only leanback UI. Only + * applications designed for this experience should be run, though this is + * not enforced by the system. + * @hide + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports WiFi (802.11) networking. */ @SdkConstant(SdkConstantType.FEATURE) @@ -1268,6 +1305,37 @@ public abstract class PackageManager { public static final String FEATURE_TELEVISION = "android.hardware.type.television"; /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: This is a device dedicated to showing UI + * on a watch. A watch here is defined to be a device worn on the body, perhaps on + * the wrist. The user is very close when interacting with the device. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_WATCH = "android.hardware.type.watch"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device supports printing. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_PRINTING = "android.software.print"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device can perform backup and restore operations on installed applications. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_BACKUP = "android.software.backup"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device has a full implementation of the android.webkit.* APIs. Devices + * lacking this feature will not have a functioning WebView implementation. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_WEBVIEW = "android.software.webview"; + + /** * Action to external storage service to clean out removed apps. * @hide */ @@ -1430,17 +1498,33 @@ public abstract class PackageManager { public abstract Intent getLaunchIntentForPackage(String packageName); /** - * Return an array of all of the secondary group-ids that have been - * assigned to a package. - * - * <p>Throws {@link NameNotFoundException} if a package with the given - * name cannot be found on the system. - * + * @hide Return a "good" intent to launch a front-door Leanback activity in a + * package, for use for example to implement an "open" button when browsing + * through packages. The current implementation will look for a main + * activity in the category {@link Intent#CATEGORY_LEANBACK_LAUNCHER}, or + * return null if no main leanback activities are found. + * <p> + * Throws {@link NameNotFoundException} if a package with the given name + * cannot be found on the system. + * + * @param packageName The name of the package to inspect. + * @return Returns either a fully-qualified Intent that can be used to launch + * the main Leanback activity in the package, or null if the package + * does not contain such an activity. + */ + public abstract Intent getLeanbackLaunchIntentForPackage(String packageName); + + /** + * Return an array of all of the secondary group-ids that have been assigned + * to a package. + * <p> + * Throws {@link NameNotFoundException} if a package with the given name + * cannot be found on the system. + * * @param packageName The full name (i.e. com.google.apps.contacts) of the - * desired package. - * - * @return Returns an int array of the assigned gids, or null if there - * are none. + * desired package. + * @return Returns an int array of the assigned gids, or null if there are + * none. */ public abstract int[] getPackageGids(String packageName) throws NameNotFoundException; @@ -2388,6 +2472,40 @@ public abstract class PackageManager { throws NameNotFoundException; /** + * Retrieve the banner associated with an activity. Given the full name of + * an activity, retrieves the information about it and calls + * {@link ComponentInfo#loadIcon ComponentInfo.loadIcon()} to return its + * banner. If the activity cannot be found, NameNotFoundException is thrown. + * + * @param activityName Name of the activity whose banner is to be retrieved. + * @return Returns the image of the banner, or null if the activity has no + * banner specified. + * @throws NameNotFoundException Thrown if the resources for the given + * activity could not be loaded. + * @see #getActivityBanner(Intent) + */ + public abstract Drawable getActivityBanner(ComponentName activityName) + throws NameNotFoundException; + + /** + * Retrieve the banner associated with an Intent. If intent.getClassName() + * is set, this simply returns the result of + * getActivityBanner(intent.getClassName()). Otherwise it resolves the + * intent's component and returns the banner associated with the resolved + * component. If intent.getClassName() cannot be found or the Intent cannot + * be resolved to a component, NameNotFoundException is thrown. + * + * @param intent The intent for which you would like to retrieve a banner. + * @return Returns the image of the banner, or null if the activity has no + * banner specified. + * @throws NameNotFoundException Thrown if the resources for application + * matching the given intent could not be loaded. + * @see #getActivityBanner(ComponentName) + */ + public abstract Drawable getActivityBanner(Intent intent) + throws NameNotFoundException; + + /** * Return the generic icon for an activity that is used when no specific * icon is defined. * @@ -2428,19 +2546,43 @@ public abstract class PackageManager { throws NameNotFoundException; /** - * Retrieve the logo associated with an activity. Given the full name of - * an activity, retrieves the information about it and calls - * {@link ComponentInfo#loadLogo ComponentInfo.loadLogo()} to return its logo. - * If the activity cannot be found, NameNotFoundException is thrown. + * Retrieve the banner associated with an application. * - * @param activityName Name of the activity whose logo is to be retrieved. - * - * @return Returns the image of the logo or null if the activity has no - * logo specified. + * @param info Information about application being queried. + * @return Returns the image of the banner or null if the application has no + * banner specified. + * @see #getApplicationBanner(String) + */ + public abstract Drawable getApplicationBanner(ApplicationInfo info); + + /** + * Retrieve the banner associated with an application. Given the name of the + * application's package, retrieves the information about it and calls + * getApplicationIcon() to return its banner. If the application cannot be + * found, NameNotFoundException is thrown. * + * @param packageName Name of the package whose application banner is to be + * retrieved. + * @return Returns the image of the banner or null if the application has no + * banner specified. * @throws NameNotFoundException Thrown if the resources for the given - * activity could not be loaded. + * application could not be loaded. + * @see #getApplicationBanner(ApplicationInfo) + */ + public abstract Drawable getApplicationBanner(String packageName) + throws NameNotFoundException; + + /** + * Retrieve the logo associated with an activity. Given the full name of an + * activity, retrieves the information about it and calls + * {@link ComponentInfo#loadLogo ComponentInfo.loadLogo()} to return its + * logo. If the activity cannot be found, NameNotFoundException is thrown. * + * @param activityName Name of the activity whose logo is to be retrieved. + * @return Returns the image of the logo or null if the activity has no logo + * specified. + * @throws NameNotFoundException Thrown if the resources for the given + * activity could not be loaded. * @see #getActivityLogo(Intent) */ public abstract Drawable getActivityLogo(ComponentName activityName) diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index c0963f5..66b2bb2 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -167,18 +167,20 @@ public class PackageParser { final int labelRes; final int iconRes; final int logoRes; + final int bannerRes; String tag; TypedArray sa; ParsePackageItemArgs(Package _owner, String[] _outError, - int _nameRes, int _labelRes, int _iconRes, int _logoRes) { + int _nameRes, int _labelRes, int _iconRes, int _logoRes, int _bannerRes) { owner = _owner; outError = _outError; nameRes = _nameRes; labelRes = _labelRes; iconRes = _iconRes; logoRes = _logoRes; + bannerRes = _bannerRes; } } @@ -190,10 +192,10 @@ public class PackageParser { int flags; ParseComponentArgs(Package _owner, String[] _outError, - int _nameRes, int _labelRes, int _iconRes, int _logoRes, + int _nameRes, int _labelRes, int _iconRes, int _logoRes, int _bannerRes, String[] _sepProcesses, int _processRes, int _descriptionRes, int _enabledRes) { - super(_owner, _outError, _nameRes, _labelRes, _iconRes, _logoRes); + super(_owner, _outError, _nameRes, _labelRes, _iconRes, _logoRes, _bannerRes); sepProcesses = _sepProcesses; processRes = _processRes; descriptionRes = _descriptionRes; @@ -1687,7 +1689,8 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestPermissionGroup_name, com.android.internal.R.styleable.AndroidManifestPermissionGroup_label, com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon, - com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo)) { + com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo, + com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; @@ -1730,7 +1733,8 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestPermission_name, com.android.internal.R.styleable.AndroidManifestPermission_label, com.android.internal.R.styleable.AndroidManifestPermission_icon, - com.android.internal.R.styleable.AndroidManifestPermission_logo)) { + com.android.internal.R.styleable.AndroidManifestPermission_logo, + com.android.internal.R.styleable.AndroidManifestPermission_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; @@ -1799,7 +1803,8 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestPermissionTree_name, com.android.internal.R.styleable.AndroidManifestPermissionTree_label, com.android.internal.R.styleable.AndroidManifestPermissionTree_icon, - com.android.internal.R.styleable.AndroidManifestPermissionTree_logo)) { + com.android.internal.R.styleable.AndroidManifestPermissionTree_logo, + com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; @@ -1844,7 +1849,8 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestInstrumentation_name, com.android.internal.R.styleable.AndroidManifestInstrumentation_label, com.android.internal.R.styleable.AndroidManifestInstrumentation_icon, - com.android.internal.R.styleable.AndroidManifestInstrumentation_logo); + com.android.internal.R.styleable.AndroidManifestInstrumentation_logo, + com.android.internal.R.styleable.AndroidManifestInstrumentation_banner); mParseInstrumentationArgs.tag = "<instrumentation>"; } @@ -1960,6 +1966,8 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestApplication_icon, 0); ai.logo = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestApplication_logo, 0); + ai.banner = sa.getResourceId( + com.android.internal.R.styleable.AndroidManifestApplication_banner, 0); ai.theme = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestApplication_theme, 0); ai.descriptionRes = sa.getResourceId( @@ -2081,6 +2089,11 @@ public class PackageParser { ai.enabled = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_enabled, true); + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_isGame, false)) { + ai.flags |= ApplicationInfo.FLAG_IS_GAME; + } + if (false) { if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState, @@ -2253,7 +2266,7 @@ public class PackageParser { private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo, String[] outError, String tag, TypedArray sa, - int nameRes, int labelRes, int iconRes, int logoRes) { + int nameRes, int labelRes, int iconRes, int logoRes, int bannerRes) { String name = sa.getNonConfigurationString(nameRes, 0); if (name == null) { outError[0] = tag + " does not specify android:name"; @@ -2277,6 +2290,11 @@ public class PackageParser { outInfo.logo = logoVal; } + int bannerVal = sa.getResourceId(bannerRes, 0); + if (bannerVal != 0) { + outInfo.banner = bannerVal; + } + TypedValue v = sa.peekValue(labelRes); if (v != null && (outInfo.labelRes=v.resourceId) == 0) { outInfo.nonLocalizedLabel = v.coerceToString(); @@ -2300,6 +2318,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivity_label, com.android.internal.R.styleable.AndroidManifestActivity_icon, com.android.internal.R.styleable.AndroidManifestActivity_logo, + com.android.internal.R.styleable.AndroidManifestActivity_banner, mSeparateProcesses, com.android.internal.R.styleable.AndroidManifestActivity_process, com.android.internal.R.styleable.AndroidManifestActivity_description, @@ -2426,6 +2445,12 @@ public class PackageParser { a.info.flags |= ActivityInfo.FLAG_IMMERSIVE; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded, + false)) { + a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED; + } + if (!receiver) { if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestActivity_hardwareAccelerated, @@ -2585,6 +2610,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivityAlias_label, com.android.internal.R.styleable.AndroidManifestActivityAlias_icon, com.android.internal.R.styleable.AndroidManifestActivityAlias_logo, + com.android.internal.R.styleable.AndroidManifestActivityAlias_banner, mSeparateProcesses, 0, com.android.internal.R.styleable.AndroidManifestActivityAlias_description, @@ -2619,6 +2645,7 @@ public class PackageParser { info.flags = target.info.flags; info.icon = target.info.icon; info.logo = target.info.logo; + info.banner = target.info.banner; info.labelRes = target.info.labelRes; info.nonLocalizedLabel = target.info.nonLocalizedLabel; info.launchMode = target.info.launchMode; @@ -2732,6 +2759,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestProvider_label, com.android.internal.R.styleable.AndroidManifestProvider_icon, com.android.internal.R.styleable.AndroidManifestProvider_logo, + com.android.internal.R.styleable.AndroidManifestProvider_banner, mSeparateProcesses, com.android.internal.R.styleable.AndroidManifestProvider_process, com.android.internal.R.styleable.AndroidManifestProvider_description, @@ -3038,6 +3066,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestService_label, com.android.internal.R.styleable.AndroidManifestService_icon, com.android.internal.R.styleable.AndroidManifestService_logo, + com.android.internal.R.styleable.AndroidManifestService_banner, mSeparateProcesses, com.android.internal.R.styleable.AndroidManifestService_process, com.android.internal.R.styleable.AndroidManifestService_description, @@ -3335,6 +3364,9 @@ public class PackageParser { outInfo.logo = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestIntentFilter_logo, 0); + outInfo.banner = sa.getResourceId( + com.android.internal.R.styleable.AndroidManifestIntentFilter_banner, 0); + sa.recycle(); int outerDepth = parser.getDepth(); @@ -3704,6 +3736,11 @@ public class PackageParser { outInfo.logo = logoVal; } + int bannerVal = args.sa.getResourceId(args.bannerRes, 0); + if (bannerVal != 0) { + outInfo.banner = bannerVal; + } + TypedValue v = args.sa.peekValue(args.labelRes); if (v != null && (outInfo.labelRes=v.resourceId) == 0) { outInfo.nonLocalizedLabel = v.coerceToString(); @@ -4128,6 +4165,7 @@ public class PackageParser { public CharSequence nonLocalizedLabel; public int icon; public int logo; + public int banner; public int preferred; } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 48b6fca..a07fc97 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -440,6 +440,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> * resource qualifier. */ public static final int UI_MODE_TYPE_APPLIANCE = 0x05; + /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK} + * value that corresponds to the + * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> + * resource qualifier. */ + public static final int UI_MODE_TYPE_WATCH = 0x06; /** Constant for {@link #uiMode}: bits that encode the night mode. */ public static final int UI_MODE_NIGHT_MASK = 0x30; @@ -462,8 +467,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration * <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED}, * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK}, - * {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION}, or - * {@link #UI_MODE_TYPE_APPLIANCE}. + * {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION}, + * {@link #UI_MODE_TYPE_APPLIANCE}, or {@link #UI_MODE_TYPE_WATCH}. * * <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED}, @@ -700,6 +705,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration case UI_MODE_TYPE_CAR: sb.append(" car"); break; case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break; case UI_MODE_TYPE_APPLIANCE: sb.append(" appliance"); break; + case UI_MODE_TYPE_WATCH: sb.append(" watch"); break; default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break; } switch ((uiMode&UI_MODE_NIGHT_MASK)) { diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 3d9daca..7318652 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1461,6 +1461,11 @@ public class Resources { private final AssetManager mAssets; private final long mTheme; + + // Needed by layoutlib. + /*package*/ long getNativeTheme() { + return mTheme; + } } /** diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 60ccc61..433d5d1 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -738,14 +738,16 @@ public final class SQLiteDatabase extends SQLiteClosable { File dir = file.getParentFile(); if (dir != null) { final String prefix = file.getName() + "-mj"; - final FileFilter filter = new FileFilter() { + File[] files = dir.listFiles(new FileFilter() { @Override public boolean accept(File candidate) { return candidate.getName().startsWith(prefix); } - }; - for (File masterJournal : dir.listFiles(filter)) { - deleted |= masterJournal.delete(); + }); + if (files != null) { + for (File masterJournal : files) { + deleted |= masterJournal.delete(); + } } } return deleted; diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 89a5819..d95bcb5 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -38,6 +38,13 @@ public final class Sensor { public static final int TYPE_ACCELEROMETER = 1; /** + * A constant string describing an accelerometer sensor type. + * + * @see #TYPE_ACCELEROMETER + */ + public static final String STRING_TYPE_ACCELEROMETER = "android.sensor.accelerometer"; + + /** * A constant describing a magnetic field sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. @@ -45,6 +52,13 @@ public final class Sensor { public static final int TYPE_MAGNETIC_FIELD = 2; /** + * A constant string describing a magnetic field sensor type. + * + * @see #TYPE_MAGNETIC_FIELD + */ + public static final String STRING_TYPE_MAGNETIC_FIELD = "android.sensor.magnetic_field"; + + /** * A constant describing an orientation sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. @@ -55,24 +69,58 @@ public final class Sensor { @Deprecated public static final int TYPE_ORIENTATION = 3; - /** A constant describing a gyroscope sensor type. + /** + * A constant string describing an orientation sensor type. + * + * @see #TYPE_ORIENTATION + * @deprecated use {@link android.hardware.SensorManager#getOrientation + * SensorManager.getOrientation()} instead. + */ + @Deprecated + public static final String STRING_TYPE_ORIENTATION = "android.sensor.orientation"; + + /** + * A constant describing a gyroscope sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. */ public static final int TYPE_GYROSCOPE = 4; /** + * A constant string describing a gyroscope sensor type. + * + * @see #TYPE_GYROSCOPE + */ + public static final String STRING_TYPE_GYROSCOPE = "android.sensor.gyroscope"; + + /** * A constant describing a light sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. */ public static final int TYPE_LIGHT = 5; - /** A constant describing a pressure sensor type. + /** + * A constant string describing a light sensor type. + * + * @see #TYPE_LIGHT + */ + public static final String STRING_TYPE_LIGHT = "android.sensor.light"; + + /** + * A constant describing a pressure sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} - * for more details. */ + * for more details. + */ public static final int TYPE_PRESSURE = 6; /** + * A constant string describing a pressure sensor type. + * + * @see #TYPE_PRESSURE + */ + public static final String STRING_TYPE_PRESSURE = "android.sensor.pressure"; + + /** * A constant describing a temperature sensor type * * @deprecated use @@ -83,6 +131,17 @@ public final class Sensor { public static final int TYPE_TEMPERATURE = 7; /** + * A constant string describing a temperature sensor type + * + * @see #TYPE_TEMPERATURE + * @deprecated use + * {@link android.hardware.Sensor#STRING_TYPE_AMBIENT_TEMPERATURE + * Sensor.STRING_TYPE_AMBIENT_TEMPERATURE} instead. + */ + @Deprecated + public static final String STRING_TYPE_TEMPERATURE = "android.sensor.temperature"; + + /** * A constant describing a proximity sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. @@ -90,6 +149,13 @@ public final class Sensor { public static final int TYPE_PROXIMITY = 8; /** + * A constant string describing a proximity sensor type. + * + * @see #TYPE_PROXIMITY + */ + public static final String STRING_TYPE_PROXIMITY = "android.sensor.proximity"; + + /** * A constant describing a gravity sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. @@ -97,6 +163,13 @@ public final class Sensor { public static final int TYPE_GRAVITY = 9; /** + * A constant string describing a gravity sensor type. + * + * @see #TYPE_GRAVITY + */ + public static final String STRING_TYPE_GRAVITY = "android.sensor.gravity"; + + /** * A constant describing a linear acceleration sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. @@ -104,6 +177,14 @@ public final class Sensor { public static final int TYPE_LINEAR_ACCELERATION = 10; /** + * A constant string describing a linear acceleration sensor type. + * + * @see #TYPE_LINEAR_ACCELERATION + */ + public static final String STRING_TYPE_LINEAR_ACCELERATION = + "android.sensor.linear_acceleration"; + + /** * A constant describing a rotation vector sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. @@ -111,18 +192,42 @@ public final class Sensor { public static final int TYPE_ROTATION_VECTOR = 11; /** + * A constant string describing a rotation vector sensor type. + * + * @see #TYPE_ROTATION_VECTOR + */ + public static final String STRING_TYPE_ROTATION_VECTOR = "android.sensor.rotation_vector"; + + /** * A constant describing a relative humidity sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. */ public static final int TYPE_RELATIVE_HUMIDITY = 12; - /** A constant describing an ambient temperature sensor type. + /** + * A constant string describing a relative humidity sensor type + * + * @see #TYPE_RELATIVE_HUMIDITY + */ + public static final String STRING_TYPE_RELATIVE_HUMIDITY = "android.sensor.relative_humidity"; + + /** + * A constant describing an ambient temperature sensor type. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} - * for more details. */ + * for more details. + */ public static final int TYPE_AMBIENT_TEMPERATURE = 13; /** + * A constant string describing an ambient temperature sensor type. + * + * @see #TYPE_AMBIENT_TEMPERATURE + */ + public static final String STRING_TYPE_AMBIENT_TEMPERATURE = + "android.sensor.ambient_temperature"; + + /** * A constant describing an uncalibrated magnetic field sensor type. * <p> * Similar to {@link #TYPE_MAGNETIC_FIELD} but the hard iron calibration (device calibration @@ -139,6 +244,13 @@ public final class Sensor { * details. */ public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; + /** + * A constant string describing an uncalibrated magnetic field sensor type. + * + * @see #TYPE_MAGNETIC_FIELD_UNCALIBRATED + */ + public static final String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED = + "android.sensor.magnetic_field_uncalibrated"; /** * A constant describing an uncalibrated rotation vector sensor type. @@ -156,10 +268,17 @@ public final class Sensor { * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} for more * details. */ - public static final int TYPE_GAME_ROTATION_VECTOR = 15; /** + * A constant string describing an uncalibrated rotation vector sensor type. + * + * @see #TYPE_GAME_ROTATION_VECTOR + */ + public static final String STRING_TYPE_GAME_ROTATION_VECTOR = + "android.sensor.game_rotation_vector"; + + /** * A constant describing an uncalibrated gyroscope sensor type. * <p>Similar to {@link #TYPE_GYROSCOPE} but no gyro-drift compensation has been performed * to adjust the given sensor values. However, such gyro-drift bias values @@ -174,6 +293,14 @@ public final class Sensor { public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16; /** + * A constant string describing an uncalibrated gyroscope sensor type. + * + * @see #TYPE_GYROSCOPE_UNCALIBRATED + */ + public static final String STRING_TYPE_GYROSCOPE_UNCALIBRATED = + "android.sensor.gyroscope_uncalibrated"; + + /** * A constant describing a significant motion trigger sensor. * <p> * It triggers when an event occurs and then automatically disables @@ -186,6 +313,14 @@ public final class Sensor { public static final int TYPE_SIGNIFICANT_MOTION = 17; /** + * A constant string describing a significant motion trigger sensor. + * + * @see #TYPE_SIGNIFICANT_MOTION + */ + public static final String STRING_TYPE_SIGNIFICANT_MOTION = + "android.sensor.significant_motion"; + + /** * A constant describing a step detector sensor. * <p> * A sensor of this type triggers an event each time a step is taken by the user. The only @@ -198,6 +333,13 @@ public final class Sensor { public static final int TYPE_STEP_DETECTOR = 18; /** + * A constant string describing a step detector sensor. + * + * @see #TYPE_STEP_DETECTOR + */ + public static final String STRING_TYPE_STEP_DETECTOR = "android.sensor.step_detector"; + + /** * A constant describing a step counter sensor. * <p> * A sensor of this type returns the number of steps taken by the user since the last reboot @@ -211,7 +353,14 @@ public final class Sensor { public static final int TYPE_STEP_COUNTER = 19; /** - * A constant describing the geo-magnetic rotation vector. + * A constant string describing a step counter sensor. + * + * @see #TYPE_STEP_COUNTER + */ + public static final String STRING_TYPE_STEP_COUNTER = "android.sensor.step_counter"; + + /** + * A constant describing a geo-magnetic rotation vector. * <p> * Similar to {@link #TYPE_ROTATION_VECTOR}, but using a magnetometer instead of using a * gyroscope. This sensor uses lower power than the other rotation vectors, because it doesn't @@ -222,6 +371,38 @@ public final class Sensor { public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20; /** + * A constant string describing a geo-magnetic rotation vector. + * + * @see #TYPE_GEOMAGNETIC_ROTATION_VECTOR + */ + public static final String STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR = + "android.sensor.geomagnetic_rotation_vector"; + + /** + * A constant describing a heart rate monitor. + * <p> + * The reported value is the heart rate in beats per minute. + * <p> + * The reported accuracy represents the status of the monitor during the reading. See the + * {@code SENSOR_STATUS_*} constants in {@link android.hardware.SensorManager SensorManager} + * for more details on accuracy/status values. In particular, when the accuracy is + * {@code SENSOR_STATUS_UNRELIABLE} or {@code SENSOR_STATUS_NO_CONTACT}, the heart rate + * value should be discarded. + * <p> + * This sensor requires permission {@code android.permission.BODY_SENSORS}. + * It will not be returned by {@code SensorManager.getSensorsList} nor + * {@code SensorManager.getDefaultSensor} if the application doesn't have this permission. + */ + public static final int TYPE_HEART_RATE = 21; + + /** + * A constant string describing a heart rate monitor. + * + * @see #TYPE_HEART_RATE + */ + public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; + + /** * A constant describing all sensor types. */ public static final int TYPE_ALL = -1; @@ -265,7 +446,8 @@ public final class Sensor { // added post 4.3 REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_STEP_DETECTOR REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_STEP_COUNTER - REPORTING_MODE_CONTINUOUS, 5 // SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR + REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR + REPORTING_MODE_ON_CHANGE, 1 // SENSOR_TYPE_HEART_RATE_MONITOR }; static int getReportingMode(Sensor sensor) { @@ -321,6 +503,8 @@ public final class Sensor { private int mMinDelay; private int mFifoReservedEventCount; private int mFifoMaxEventCount; + private String mStringType; + private String mRequiredPermission; Sensor() { } @@ -401,6 +585,21 @@ public final class Sensor { return mFifoMaxEventCount; } + /** + * @return The type of this sensor as a string. + */ + public String getStringType() { + return mStringType; + } + + /** + * @return The permission required to access this sensor. If empty, no permission is required. + * @hide + */ + public String getRequiredPermission() { + return mRequiredPermission; + } + /** @hide */ public int getHandle() { return mHandle; diff --git a/core/java/android/hardware/SensorEventListener.java b/core/java/android/hardware/SensorEventListener.java index 677d244..0d859fb 100644 --- a/core/java/android/hardware/SensorEventListener.java +++ b/core/java/android/hardware/SensorEventListener.java @@ -39,11 +39,13 @@ public interface SensorEventListener { public void onSensorChanged(SensorEvent event); /** - * Called when the accuracy of a sensor has changed. - * <p>See {@link android.hardware.SensorManager SensorManager} - * for details. + * Called when the accuracy of the registered sensor has changed. + * + * <p>See the SENSOR_STATUS_* constants in + * {@link android.hardware.SensorManager SensorManager} for details. * - * @param accuracy The new accuracy of this sensor + * @param accuracy The new accuracy of this sensor, one of + * {@code SensorManager.SENSOR_STATUS_*} */ - public void onAccuracyChanged(Sensor sensor, int accuracy); + public void onAccuracyChanged(Sensor sensor, int accuracy); } diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 5f2b5f0..25c7630 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -321,6 +321,13 @@ public abstract class SensorManager { /** + * The values returned by this sensor cannot be trusted because the sensor + * had no contact with what it was measuring (for example, the heart rate + * monitor is not in contact with the user). + */ + public static final int SENSOR_STATUS_NO_CONTACT = -1; + + /** * The values returned by this sensor cannot be trusted, calibration is * needed or the environment doesn't allow readings */ @@ -421,9 +428,10 @@ public abstract class SensorManager { * {@link SensorManager#getSensorList(int) getSensorList}. * * @param type - * of sensors requested + * of sensors requested * - * @return the default sensors matching the asked type. + * @return the default sensor matching the requested type if one exists and the application + * has the necessary permissions, or null otherwise. * * @see #getSensorList(int) * @see Sensor diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 8684a04..b66ec86 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -395,25 +395,12 @@ public class SystemSensorManager extends SensorManager { t.timestamp = timestamp; t.accuracy = inAccuracy; t.sensor = sensor; - switch (t.sensor.getType()) { - // Only report accuracy for sensors that support it. - case Sensor.TYPE_MAGNETIC_FIELD: - case Sensor.TYPE_ORIENTATION: - // call onAccuracyChanged() only if the value changes - final int accuracy = mSensorAccuracies.get(handle); - if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { - mSensorAccuracies.put(handle, t.accuracy); - mListener.onAccuracyChanged(t.sensor, t.accuracy); - } - break; - default: - // For other sensors, just report the accuracy once - if (mFirstEvent.get(handle) == false) { - mFirstEvent.put(handle, true); - mListener.onAccuracyChanged( - t.sensor, SENSOR_STATUS_ACCURACY_HIGH); - } - break; + + // call onAccuracyChanged() only if the value changes + final int accuracy = mSensorAccuracies.get(handle); + if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { + mSensorAccuracies.put(handle, t.accuracy); + mListener.onAccuracyChanged(t.sensor, t.accuracy); } mListener.onSensorChanged(t); } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 093e0e9..79673b3 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -115,6 +115,7 @@ public final class DisplayManager { * </p> * * @see #createVirtualDisplay + * @see #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY */ public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1 << 0; @@ -171,6 +172,22 @@ public final class DisplayManager { */ public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 1 << 2; + /** + * Virtual display flag: Only show this display's own content; do not mirror + * the content of another display. + * + * <p> + * This flag is used in conjunction with {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}. + * Ordinarily public virtual displays will automatically mirror the content of the + * default display if they have no windows of their own. When this flag is + * specified, the virtual display will only ever show its own content and + * will be blanked instead if it has no windows. + * </p> + * + * @see #createVirtualDisplay + */ + public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 1 << 3; + /** @hide */ public DisplayManager(Context context) { mContext = context; @@ -420,6 +437,14 @@ public final class DisplayManager { * The behavior of the virtual display depends on the flags that are provided * to this method. By default, virtual displays are created to be private, * non-presentation and unsecure. Permissions may be required to use certain flags. + * </p><p> + * As of {@link android.os.Build.VERSION_CODES#KITKAT_WATCH}, the surface may + * be attached or detached dynamically using {@link VirtualDisplay#setSurface}. + * Previously, the surface had to be non-null when {@link #createVirtualDisplay} + * was called and could not be changed for the lifetime of the display. + * </p><p> + * Detaching the surface that backs a virtual display has a similar effect to + * turning off the screen. * </p> * * @param name The name of the virtual display, must be non-empty. @@ -427,10 +452,10 @@ public final class DisplayManager { * @param height The height of the virtual display in pixels, must be greater than 0. * @param densityDpi The density of the virtual display in dpi, must be greater than 0. * @param surface The surface to which the content of the virtual display should - * be rendered, must be non-null. + * be rendered, or null if there is none initially. * @param flags A combination of virtual display flags: - * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION} - * or {@link #VIRTUAL_DISPLAY_FLAG_SECURE}. + * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION}, + * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, or {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}. * @return The newly created virtual display, or null if the application could * not create the virtual display. * diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 3417430..a8d55e8 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -377,9 +377,6 @@ public final class DisplayManagerGlobal { throw new IllegalArgumentException("width, height, and densityDpi must be " + "greater than 0"); } - if (surface == null) { - throw new IllegalArgumentException("surface must not be null"); - } Binder token = new Binder(); int displayId; @@ -404,7 +401,15 @@ public final class DisplayManagerGlobal { } return null; } - return new VirtualDisplay(this, display, token); + return new VirtualDisplay(this, display, token, surface); + } + + public void setVirtualDisplaySurface(IBinder token, Surface surface) { + try { + mDm.setVirtualDisplaySurface(token, surface); + } catch (RemoteException ex) { + Log.w(TAG, "Failed to set virtual display surface.", ex); + } } public void releaseVirtualDisplay(IBinder token) { diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java new file mode 100644 index 0000000..cec90cd --- /dev/null +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -0,0 +1,261 @@ +/* + * 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.display; + +import android.hardware.SensorManager; +import android.os.Handler; +import android.os.PowerManager; +import android.view.DisplayInfo; + +/** + * Display manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class DisplayManagerInternal { + /** + * Called by the power manager to initialize power management facilities. + */ + public abstract void initPowerManagement(DisplayPowerCallbacks callbacks, + Handler handler, SensorManager sensorManager); + + /** + * Called by the power manager to request a new power state. + * <p> + * The display power controller makes a copy of the provided object and then + * begins adjusting the power state to match what was requested. + * </p> + * + * @param request The requested power state. + * @param waitForNegativeProximity If true, issues a request to wait for + * negative proximity before turning the screen back on, assuming the screen + * was turned off by the proximity sensor. + * @return True if display is ready, false if there are important changes that must + * be made asynchronously (such as turning the screen on), in which case the caller + * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()} + * then try the request again later until the state converges. + */ + public abstract boolean requestPowerState(DisplayPowerRequest request, + boolean waitForNegativeProximity); + + /** + * Returns true if the proximity sensor screen-off function is available. + */ + public abstract boolean isProximitySensorAvailable(); + + /** + * Returns information about the specified logical display. + * + * @param displayId The logical display id. + * @return The logical display info, or null if the display does not exist. The + * returned object must be treated as immutable. + */ + public abstract DisplayInfo getDisplayInfo(int displayId); + + /** + * Registers a display transaction listener to provide the client a chance to + * update its surfaces within the same transaction as any display layout updates. + * + * @param listener The listener to register. + */ + public abstract void registerDisplayTransactionListener(DisplayTransactionListener listener); + + /** + * Unregisters a display transaction listener to provide the client a chance to + * update its surfaces within the same transaction as any display layout updates. + * + * @param listener The listener to unregister. + */ + public abstract void unregisterDisplayTransactionListener(DisplayTransactionListener listener); + + /** + * Overrides the display information of a particular logical display. + * This is used by the window manager to control the size and characteristics + * of the default display. It is expected to apply the requested change + * to the display information synchronously so that applications will immediately + * observe the new state. + * + * NOTE: This method must be the only entry point by which the window manager + * influences the logical configuration of displays. + * + * @param displayId The logical display id. + * @param info The new data to be stored. + */ + public abstract void setDisplayInfoOverrideFromWindowManager( + int displayId, DisplayInfo info); + + /** + * Called by the window manager to perform traversals while holding a + * surface flinger transaction. + */ + public abstract void performTraversalInTransactionFromWindowManager(); + + /** + * Tells the display manager whether there is interesting unique content on the + * specified logical display. This is used to control automatic mirroring. + * <p> + * If the display has unique content, then the display manager arranges for it + * to be presented on a physical display if appropriate. Otherwise, the display manager + * may choose to make the physical display mirror some other logical display. + * </p> + * + * @param displayId The logical display id to update. + * @param hasContent True if the logical display has content. + * @param inTraversal True if called from WindowManagerService during a window traversal + * prior to call to performTraversalInTransactionFromWindowManager. + */ + public abstract void setDisplayHasContent(int displayId, boolean hasContent, + boolean inTraversal); + + /** + * Describes the requested power state of the display. + * + * This object is intended to describe the general characteristics of the + * power state, such as whether the screen should be on or off and the current + * brightness controls leaving the DisplayPowerController to manage the + * details of how the transitions between states should occur. The goal is for + * the PowerManagerService to focus on the global power state and not + * have to micro-manage screen off animations, auto-brightness and other effects. + */ + public static final class DisplayPowerRequest { + public static final int SCREEN_STATE_OFF = 0; + public static final int SCREEN_STATE_DOZE = 1; + public static final int SCREEN_STATE_DIM = 2; + public static final int SCREEN_STATE_BRIGHT = 3; + + // The requested minimum screen power state: off, doze, dim or bright. + public int screenState; + + // If true, the proximity sensor overrides the screen state when an object is + // nearby, turning it off temporarily until the object is moved away. + public boolean useProximitySensor; + + // The desired screen brightness in the range 0 (minimum / off) to 255 (brightest). + // The display power controller may choose to clamp the brightness. + // When auto-brightness is enabled, this field should specify a nominal default + // value to use while waiting for the light sensor to report enough data. + public int screenBrightness; + + // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter). + public float screenAutoBrightnessAdjustment; + + // If true, enables automatic brightness control. + public boolean useAutoBrightness; + + // If true, prevents the screen from completely turning on if it is currently off. + // The display does not enter a "ready" state if this flag is true and screen on is + // blocked. The window manager policy blocks screen on while it prepares the keyguard to + // prevent the user from seeing intermediate updates. + // + // Technically, we may not block the screen itself from turning on (because that introduces + // extra unnecessary latency) but we do prevent content on screen from becoming + // visible to the user. + public boolean blockScreenOn; + + public DisplayPowerRequest() { + screenState = SCREEN_STATE_BRIGHT; + useProximitySensor = false; + screenBrightness = PowerManager.BRIGHTNESS_ON; + screenAutoBrightnessAdjustment = 0.0f; + useAutoBrightness = false; + blockScreenOn = false; + } + + public DisplayPowerRequest(DisplayPowerRequest other) { + copyFrom(other); + } + + // Returns true if we want the screen on in any mode, including doze. + public boolean wantScreenOnAny() { + return screenState != SCREEN_STATE_OFF; + } + + // Returns true if we want the screen on in a normal mode, excluding doze. + // This is usually what we want to tell the rest of the system. For compatibility + // reasons, we pretend the screen is off when dozing. + public boolean wantScreenOnNormal() { + return screenState == SCREEN_STATE_DIM || screenState == SCREEN_STATE_BRIGHT; + } + + public boolean wantLightSensorEnabled() { + // Specifically, we don't want the light sensor while dozing. + return useAutoBrightness && wantScreenOnNormal(); + } + + public void copyFrom(DisplayPowerRequest other) { + screenState = other.screenState; + useProximitySensor = other.useProximitySensor; + screenBrightness = other.screenBrightness; + screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment; + useAutoBrightness = other.useAutoBrightness; + blockScreenOn = other.blockScreenOn; + } + + @Override + public boolean equals(Object o) { + return o instanceof DisplayPowerRequest + && equals((DisplayPowerRequest)o); + } + + public boolean equals(DisplayPowerRequest other) { + return other != null + && screenState == other.screenState + && useProximitySensor == other.useProximitySensor + && screenBrightness == other.screenBrightness + && screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment + && useAutoBrightness == other.useAutoBrightness + && blockScreenOn == other.blockScreenOn; + } + + @Override + public int hashCode() { + return 0; // don't care + } + + @Override + public String toString() { + return "screenState=" + screenState + + ", useProximitySensor=" + useProximitySensor + + ", screenBrightness=" + screenBrightness + + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment + + ", useAutoBrightness=" + useAutoBrightness + + ", blockScreenOn=" + blockScreenOn; + } + } + + /** + * Asynchronous callbacks from the power controller to the power manager service. + */ + public interface DisplayPowerCallbacks { + void onStateChanged(); + void onProximityPositive(); + void onProximityNegative(); + void onDisplayStateChange(int state); // one of the Display state constants + + void acquireSuspendBlocker(); + void releaseSuspendBlocker(); + } + + /** + * Called within a Surface transaction whenever the size or orientation of a + * display may have changed. Provides an opportunity for the client to + * update the position of its surfaces as part of the same transaction. + */ + public interface DisplayTransactionListener { + void onDisplayTransaction(); + } +} diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java new file mode 100644 index 0000000..c2d498b --- /dev/null +++ b/core/java/android/hardware/display/DisplayViewport.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +import android.graphics.Rect; + +/** + * Describes how the pixels of physical display device reflects the content of + * a logical display. + * <p> + * This information is used by the input system to translate touch input from + * physical display coordinates into logical display coordinates. + * </p> + * + * @hide Only for use within the system server. + */ +public final class DisplayViewport { + // True if this viewport is valid. + public boolean valid; + + // The logical display id. + public int displayId; + + // The rotation applied to the physical coordinate system. + public int orientation; + + // The portion of the logical display that are presented on this physical display. + public final Rect logicalFrame = new Rect(); + + // The portion of the (rotated) physical display that shows the logical display contents. + // The relation between logical and physical frame defines how the coordinate system + // should be scaled or translated after rotation. + public final Rect physicalFrame = new Rect(); + + // The full width and height of the display device, rotated in the same + // manner as physicalFrame. This expresses the full native size of the display device. + // The physical frame should usually fit within this area. + public int deviceWidth; + public int deviceHeight; + + public void copyFrom(DisplayViewport viewport) { + valid = viewport.valid; + displayId = viewport.displayId; + orientation = viewport.orientation; + logicalFrame.set(viewport.logicalFrame); + physicalFrame.set(viewport.physicalFrame); + deviceWidth = viewport.deviceWidth; + deviceHeight = viewport.deviceHeight; + } + + // For debugging purposes. + @Override + public String toString() { + return "DisplayViewport{valid=" + valid + + ", displayId=" + displayId + + ", orientation=" + orientation + + ", logicalFrame=" + logicalFrame + + ", physicalFrame=" + physicalFrame + + ", deviceWidth=" + deviceWidth + + ", deviceHeight=" + deviceHeight + + "}"; + } +} diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 68eb13f..23c58c8 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -63,5 +63,8 @@ interface IDisplayManager { String name, int width, int height, int densityDpi, in Surface surface, int flags); // No permissions required but must be same Uid as the creator. + void setVirtualDisplaySurface(in IBinder token, in Surface surface); + + // No permissions required but must be same Uid as the creator. void releaseVirtualDisplay(in IBinder token); } diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java index 01e5bac..691d6a0 100644 --- a/core/java/android/hardware/display/VirtualDisplay.java +++ b/core/java/android/hardware/display/VirtualDisplay.java @@ -17,15 +17,18 @@ package android.hardware.display; import android.os.IBinder; import android.view.Display; +import android.view.Surface; /** * Represents a virtual display. The content of a virtual display is rendered to a * {@link android.view.Surface} that you must provide to {@link DisplayManager#createVirtualDisplay * createVirtualDisplay()}. - * <p>Because a virtual display renders to a surface provided by the application, it will be + * <p> + * Because a virtual display renders to a surface provided by the application, it will be * released automatically when the process terminates and all remaining windows on it will - * be forcibly removed. However, you should also explicitly call {@link #release} when you're - * done with it. + * be forcibly removed. However, you should also explicitly call {@link #release} when + * you're done with it. + * </p> * * @see DisplayManager#createVirtualDisplay */ @@ -33,11 +36,14 @@ public final class VirtualDisplay { private final DisplayManagerGlobal mGlobal; private final Display mDisplay; private IBinder mToken; + private Surface mSurface; - VirtualDisplay(DisplayManagerGlobal global, Display display, IBinder token) { + VirtualDisplay(DisplayManagerGlobal global, Display display, IBinder token, + Surface surface) { mGlobal = global; mDisplay = display; mToken = token; + mSurface = surface; } /** @@ -48,6 +54,32 @@ public final class VirtualDisplay { } /** + * Gets the surface that backs the virtual display. + */ + public Surface getSurface() { + return mSurface; + } + + /** + * Sets the surface that backs the virtual display. + * <p> + * Detaching the surface that backs a virtual display has a similar effect to + * turning off the screen. + * </p><p> + * It is still the caller's responsibility to destroy the surface after it has + * been detached. + * </p> + * + * @param surface The surface to set, or null to detach the surface from the virtual display. + */ + public void setSurface(Surface surface) { + if (mSurface != surface) { + mGlobal.setVirtualDisplaySurface(mToken, surface); + mSurface = surface; + } + } + + /** * Releases the virtual display and destroys its underlying surface. * <p> * All remaining windows on the virtual display will be forcibly removed @@ -63,6 +95,7 @@ public final class VirtualDisplay { @Override public String toString() { - return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken + "}"; + return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken + + ", surface=" + mSurface + "}"; } } diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java new file mode 100644 index 0000000..5c2612f --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiCec.java @@ -0,0 +1,259 @@ +/* + * 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.hdmi; + +/** + * Defines constants and utility methods related to HDMI-CEC protocol. + * + * @hide + */ +public final class HdmiCec { + + /** TV device type. */ + public static final int DEVICE_TV = 0; + + /** Recording device type. */ + public static final int DEVICE_RECORDER = 1; + + /** Device type reserved for future usage. */ + public static final int DEVICE_RESERVED = 2; + + /** Tuner device type. */ + public static final int DEVICE_TUNER = 3; + + /** Playback device type. */ + public static final int DEVICE_PLAYBACK = 4; + + /** Audio system device type. */ + public static final int DEVICE_AUDIO_SYSTEM = 5; + + // Value indicating the device is not an active source. + public static final int DEVICE_INACTIVE = -1; + + /** Logical address for TV */ + public static final int ADDR_TV = 0; + + /** Logical address for recorder 1 */ + public static final int ADDR_RECORDER_1 = 1; + + /** Logical address for recorder 2 */ + public static final int ADDR_RECORDER_2 = 2; + + /** Logical address for tuner 1 */ + public static final int ADDR_TUNER_1 = 3; + + /** Logical address for playback 1 */ + public static final int ADDR_PLAYBACK_1 = 4; + + /** Logical address for audio system */ + public static final int ADDR_AUDIO_SYSTEM = 5; + + /** Logical address for tuner 2 */ + public static final int ADDR_TUNER_2 = 6; + + /** Logical address for tuner 3 */ + public static final int ADDR_TUNER_3 = 7; + + /** Logical address for playback 2 */ + public static final int ADDR_PLAYBACK_2 = 8; + + /** Logical address for recorder 3 */ + public static final int ADDR_RECORDER_3 = 9; + + /** Logical address for tuner 4 */ + public static final int ADDR_TUNER_4 = 10; + + /** Logical address for playback 3 */ + public static final int ADDR_PLAYBACK_3 = 11; + + /** Logical address reserved for future usage */ + public static final int ADDR_RESERVED_1 = 12; + + /** Logical address reserved for future usage */ + public static final int ADDR_RESERVED_2 = 13; + + /** Logical address for TV other than the one assigned with {@link #ADDR_TV} */ + public static final int ADDR_FREE_USE = 14; + + /** Logical address for devices to which address cannot be allocated */ + public static final int ADDR_UNREGISTERED = 15; + + /** Logical address used in the destination address field for broadcast messages */ + public static final int ADDR_BROADCAST = 15; + + /** Logical address used to indicate it is not initialized or invalid. */ + public static final int ADDR_INVALID = -1; + + // TODO: Complete the list of CEC messages definition. + public static final int MESSAGE_FEATURE_ABORT = 0x00; + public static final int MESSAGE_IMAGE_VIEW_ON = 0x04; + public static final int MESSAGE_TUNER_STEP_INCREMENT = 0x05; + public static final int MESSAGE_TUNER_STEP_DECREMENT = 0x06; + public static final int MESSAGE_TUNER_DEVICE_STATUS = 0x07; + public static final int MESSAGE_GIVE_TUNER_DEVICE_STATUS = 0x08; + public static final int MESSAGE_RECORD_ON = 0x09; + public static final int MESSAGE_RECORD_STATUS = 0x0A; + public static final int MESSAGE_RECORD_OFF = 0x0B; + public static final int MESSAGE_TEXT_VIEW_ON = 0x0D; + public static final int MESSAGE_RECORD_TV_SCREEN = 0x0F; + public static final int MESSAGE_GIVE_DECK_STATUS = 0x1A; + public static final int MESSAGE_DECK_STATUS = 0x1B; + public static final int MESSAGE_SET_MENU_LANGUAGE = 0x32; + public static final int MESSAGE_CLEAR_ANALOG_TIMER = 0x33; + public static final int MESSAGE_SET_ANALOG_TIMER = 0x34; + public static final int MESSAGE_TIMER_STATUS = 0x35; + public static final int MESSAGE_STANDBY = 0x36; + public static final int MESSAGE_PLAY = 0x41; + public static final int MESSAGE_DECK_CONTROL = 0x42; + public static final int MESSAGE_TIMER_CLEARED_STATUS = 0x043; + public static final int MESSAGE_USER_CONTROL_PRESSED = 0x44; + public static final int MESSAGE_USER_CONTROL_RELEASED = 0x45; + public static final int MESSAGE_GET_OSD_NAME = 0x46; + public static final int MESSAGE_SET_OSD_NAME = 0x47; + public static final int MESSAGE_SET_OSD_STRING = 0x64; + public static final int MESSAGE_SET_TIMER_PROGRAM_TITLE = 0x67; + public static final int MESSAGE_SYSTEM_AUDIO_MODE_REQUEST = 0x70; + public static final int MESSAGE_GIVE_AUDIO_STATUS = 0x71; + public static final int MESSAGE_SET_SYSTEM_AUDIO_MODE = 0x72; + public static final int MESSAGE_REPORT_AUDIO_STATUS = 0x7A; + public static final int MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D; + public static final int MESSAGE_SYSTEM_AUDIO_MODE_STATUS = 0x7E; + public static final int MESSAGE_ROUTING_CHANGE = 0x80; + public static final int MESSAGE_ROUTING_INFORMATION = 0x81; + public static final int MESSAGE_ACTIVE_SOURCE = 0x82; + public static final int MESSAGE_GIVE_PHYSICAL_ADDRESS = 0x83; + public static final int MESSAGE_REPORT_PHYSICAL_ADDRESS = 0x84; + public static final int MESSAGE_REQUEST_ACTIVE_SOURCE = 0x85; + public static final int MESSAGE_SET_STREAM_PATH = 0x86; + public static final int MESSAGE_DEVICE_VENDOR_ID = 0x87; + public static final int MESSAGE_VENDOR_COMMAND = 0x89; + public static final int MESSAGE_VENDOR_REMOTE_BUTTON_DOWN = 0x8A; + public static final int MESSAGE_VENDOR_REMOTE_BUTTON_UP = 0x8B; + public static final int MESSAGE_GIVE_DEVICE_VENDOR_ID = 0x8C; + public static final int MESSAGE_MENU_REQUEST = 0x8D; + public static final int MESSAGE_MENU_STATUS = 0x8E; + public static final int MESSAGE_GIVE_DEVICE_POWER_STATUS = 0x8F; + public static final int MESSAGE_REPORT_POWER_STATUS = 0x90; + public static final int MESSAGE_GET_MENU_LANGUAGE = 0x91; + public static final int MESSAGE_SELECT_ANALOG_SERVICE = 0x92; + public static final int MESSAGE_SELECT_DIGITAL_SERVICE = 0x93; + public static final int MESSAGE_SET_DIGITAL_TIMER = 0x97; + public static final int MESSAGE_CLEAR_DIGITAL_TIMER = 0x99; + public static final int MESSAGE_SET_AUDIO_RATE = 0x9A; + public static final int MESSAGE_INACTIVE_SOURCE = 0x9D; + public static final int MESSAGE_CEC_VERSION = 0x9E; + public static final int MESSAGE_GET_CEC_VERSION = 0x9F; + public static final int MESSAGE_VENDOR_COMMAND_WITH_ID = 0xA0; + public static final int MESSAGE_CLEAR_EXTERNAL_TIMER = 0xA1; + public static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2; + public static final int MESSAGE_ABORT = 0xFF; + + public static final int POWER_STATUS_UNKNOWN = -1; + public static final int POWER_STATUS_ON = 0; + public static final int POWER_STATUS_STANDBY = 1; + public static final int POWER_TRANSIENT_TO_ON = 2; + public static final int POWER_TRANSIENT_TO_STANDBY = 3; + + private static final int[] ADDRESS_TO_TYPE = { + DEVICE_TV, // ADDR_TV + DEVICE_RECORDER, // ADDR_RECORDER_1 + DEVICE_RECORDER, // ADDR_RECORDER_2 + DEVICE_TUNER, // ADDR_TUNER_1 + DEVICE_PLAYBACK, // ADDR_PLAYBACK_1 + DEVICE_AUDIO_SYSTEM, // ADDR_AUDIO_SYSTEM + DEVICE_TUNER, // ADDR_TUNER_2 + DEVICE_TUNER, // ADDR_TUNER_3 + DEVICE_PLAYBACK, // ADDR_PLAYBACK_2 + DEVICE_RECORDER, // ADDR_RECORDER_3 + DEVICE_TUNER, // ADDR_TUNER_4 + DEVICE_PLAYBACK, // ADDR_PLAYBACK_3 + }; + + private static final String[] DEFAULT_NAMES = { + "TV", + "Recorder_1", + "Recorder_2", + "Tuner_1", + "Playback_1", + "AudioSystem", + "Tuner_2", + "Tuner_3", + "Playback_2", + "Recorder_3", + "Tuner_4", + "Playback_3", + }; + + private HdmiCec() { } // Prevents instantiation. + + /** + * Check if the given type is valid. A valid type is one of the actual + * logical device types defined in the standard ({@link #DEVICE_TV}, + * {@link #DEVICE_PLAYBACK}, {@link #DEVICE_TUNER}, {@link #DEVICE_RECORDER}, + * and {@link #DEVICE_AUDIO_SYSTEM}). + * + * @param type device type + * @return true if the given type is valid + */ + public static boolean isValidType(int type) { + return (DEVICE_TV <= type && type <= DEVICE_AUDIO_SYSTEM) + && type != DEVICE_RESERVED; + } + + /** + * Check if the given logical address is valid. A logical address is valid + * if it is one allocated for an actual device which allows communication + * with other logical devices. + * + * @param address logical address + * @return true if the given address is valid + */ + public static boolean isValidAddress(int address) { + // TODO: We leave out the address 'free use(14)' for now. Check this later + // again to make sure it is a valid address for communication. + return (ADDR_TV <= address && address <= ADDR_PLAYBACK_3); + } + + /** + * Return the device type for the given logical address. + * + * @param address logical address + * @return device type for the given logical address; DEVICE_INACTIVE + * if the address is not valid. + */ + public static int getTypeFromAddress(int address) { + if (isValidAddress(address)) { + return ADDRESS_TO_TYPE[address]; + } + return DEVICE_INACTIVE; + } + + /** + * Return the default device name for a logical address. This is the name + * by which the logical device is known to others until a name is + * set explicitly using HdmiCecService.setOsdName. + * + * @param address logical address + * @return default device name; empty string if the address is not valid + */ + public static String getDefaultDeviceName(int address) { + if (isValidAddress(address)) { + return DEFAULT_NAMES[address]; + } + return ""; + } +} diff --git a/core/java/android/hardware/hdmi/HdmiCecClient.java b/core/java/android/hardware/hdmi/HdmiCecClient.java new file mode 100644 index 0000000..d8833d9 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiCecClient.java @@ -0,0 +1,145 @@ +/* + * 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.hdmi; + +import android.os.IBinder; +import android.os.RemoteException; + +import android.util.Log; + +/** + * HdmiCecClient is used to control HDMI-CEC logical device instance in the system. + * It is connected to actual hardware part via HdmiCecService. It provides with methods + * to send CEC messages to other device on the bus, and listener that allows to receive + * incoming messages to the device. + * + * @hide + */ +public final class HdmiCecClient { + private static final String TAG = "HdmiCecClient"; + + private final IHdmiCecService mService; + private final IBinder mBinder; + + /** + * Listener used by the client to get the incoming messages. + */ + public static abstract class Listener { + /** + * Called when CEC message arrives. Override this method to receive the incoming + * CEC messages from other device on the bus. + * + * @param message {@link HdmiCecMessage} object + */ + public void onMessageReceived(HdmiCecMessage message) { } + + /** + * Called when hotplug event occurs. Override this method to receive the events. + * + * @param connected true if the cable is connected; otherwise false. + */ + public void onCableStatusChanged(boolean connected) { } + } + + // Private constructor. + private HdmiCecClient(IHdmiCecService service, IBinder b) { + mService = service; + mBinder = b; + } + + // Factory method for HdmiCecClient. + // Declared package-private. Accessed by HdmiCecManager only. + static HdmiCecClient create(IHdmiCecService service, IBinder b) { + return new HdmiCecClient(service, b); + } + + /** + * Send <Active Source> message. + */ + public void sendActiveSource() { + try { + mService.sendActiveSource(mBinder); + } catch (RemoteException e) { + Log.e(TAG, "sendActiveSource threw exception ", e); + } + } + + /** + * Send <Inactive Source> message. + */ + public void sendInactiveSource() { + try { + mService.sendInactiveSource(mBinder); + } catch (RemoteException e) { + Log.e(TAG, "sendInactiveSource threw exception ", e); + } + } + + /** + * Send <Text View On> message. + */ + public void sendTextViewOn() { + try { + mService.sendTextViewOn(mBinder); + } catch (RemoteException e) { + Log.e(TAG, "sendTextViewOn threw exception ", e); + } + } + + /** + * Send <Image View On> message. + */ + public void sendImageViewOn() { + try { + mService.sendImageViewOn(mBinder); + } catch (RemoteException e) { + Log.e(TAG, "sendImageViewOn threw exception ", e); + } + } + + /** + * Send <Give Device Power Status> message. + * + * @param address logical address of the device to send the message to, such as + * {@link HdmiCec#ADDR_TV}. + */ + public void sendGiveDevicePowerStatus(int address) { + try { + mService.sendGiveDevicePowerStatus(mBinder, address); + } catch (RemoteException e) { + Log.e(TAG, "sendGiveDevicePowerStatus threw exception ", e); + } + } + + /** + * Returns true if the TV or attached display is powered on. + * <p> + * The result of this method is only meaningful on playback devices (where the device + * type is {@link HdmiCec#DEVICE_PLAYBACK}). + * </p> + * + * @return true if TV is on; otherwise false. + */ + public boolean isTvOn() { + try { + return mService.isTvOn(mBinder); + } catch (RemoteException e) { + Log.e(TAG, "isTvOn threw exception ", e); + } + return false; + } +} diff --git a/core/java/android/hardware/hdmi/HdmiCecManager.java b/core/java/android/hardware/hdmi/HdmiCecManager.java new file mode 100644 index 0000000..d18c7a9 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiCecManager.java @@ -0,0 +1,78 @@ +/* + * 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.hdmi; + +import android.os.IBinder; +import android.os.RemoteException; + +/** + * The HdmiCecManager class is used to provide an HdmiCecClient instance, + * get various information on HDMI ports configuration. It is connected to actual hardware + * via HdmiCecService. + * + * @hide + */ +public final class HdmiCecManager { + private final IHdmiCecService mService; + + /** + * @hide - hide this constructor because it has a parameter of type IHdmiCecService, + * which is a system private class. The right way to create an instance of this class + * is using the factory Context.getSystemService. + */ + public HdmiCecManager(IHdmiCecService service) { + mService = service; + } + + /** + * Provide the HdmiCecClient instance of the given type. It also registers the listener + * for client to get the events coming to the device. + * + * @param type type of the HDMI-CEC logical device + * @param listener listener to be called + * @return {@link HdmiCecClient} instance. {@code null} on failure. + */ + public HdmiCecClient getClient(int type, HdmiCecClient.Listener listener) { + if (mService == null) { + return null; + } + try { + IBinder b = mService.allocateLogicalDevice(type, getListenerWrapper(listener)); + return HdmiCecClient.create(mService, b); + } catch (RemoteException e) { + return null; + } + } + + private IHdmiCecListener getListenerWrapper(final HdmiCecClient.Listener listener) { + // TODO: The message/events are not yet forwarded to client since it is not clearly + // defined as to how/who to handle them. Revisit it once the decision is + // made on what messages will have to reach the clients, what will be + // handled by service/manager. + return new IHdmiCecListener.Stub() { + @Override + public void onMessageReceived(HdmiCecMessage message) { + // Do nothing. + } + + @Override + public void onCableStatusChanged(boolean connected) { + // Do nothing. + } + }; + } +} diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.aidl b/core/java/android/hardware/hdmi/HdmiCecMessage.aidl new file mode 100644 index 0000000..6687ba4 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiCecMessage.aidl @@ -0,0 +1,19 @@ +/* + * 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.hdmi; + +parcelable HdmiCecMessage; diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java new file mode 100644 index 0000000..63add2e --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java @@ -0,0 +1,147 @@ +/* + * 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.hdmi; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * A class to encapsulate HDMI-CEC message used for the devices connected via + * HDMI cable to communicate with one another. A message is defined by its + * source and destination address, command (or opcode), and optional parameters. + * + * @hide + */ +public final class HdmiCecMessage implements Parcelable { + + private static final int MAX_MESSAGE_LENGTH = 16; + + private final int mSource; + private final int mDestination; + + private final int mOpcode; + private final byte[] mParams; + + /** + * Constructor. + */ + public HdmiCecMessage(int source, int destination, int opcode, byte[] params) { + mSource = source; + mDestination = destination; + mOpcode = opcode; + mParams = Arrays.copyOf(params, params.length); + } + + /** + * Return the source address field of the message. It is the logical address + * of the device which generated the message. + * + * @return source address + */ + public int getSource() { + return mSource; + } + + /** + * Return the destination address field of the message. It is the logical address + * of the device to which the message is sent. + * + * @return destination address + */ + public int getDestination() { + return mDestination; + } + + /** + * Return the opcode field of the message. It is the type of the message that + * tells the destination device what to do. + * + * @return opcode + */ + public int getOpcode() { + return mOpcode; + } + + /** + * Return the parameter field of the message. The contents of parameter varies + * from opcode to opcode, and is used together with opcode to describe + * the action for the destination device to take. + * + * @return parameter + */ + public byte[] getParams() { + return mParams; + } + + /** + * Describe the kinds of special objects contained in this Parcelable's + * marshalled representation. + */ + @Override + public int describeContents() { + return 0; + } + + /** + * Flatten this object in to a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSource); + dest.writeInt(mDestination); + dest.writeInt(mOpcode); + dest.writeInt(mParams.length); + dest.writeByteArray(mParams); + } + + public static final Parcelable.Creator<HdmiCecMessage> CREATOR + = new Parcelable.Creator<HdmiCecMessage>() { + /** + * Rebuild a HdmiCecMessage previously stored with writeToParcel(). + * @param p HdmiCecMessage object to read the Rating from + * @return a new HdmiCecMessage created from the data in the parcel + */ + public HdmiCecMessage createFromParcel(Parcel p) { + int source = p.readInt(); + int destination = p.readInt(); + int opcode = p.readInt(); + byte[] params = new byte[p.readInt()]; + p.readByteArray(params); + return new HdmiCecMessage(source, destination, opcode, params); + } + public HdmiCecMessage[] newArray(int size) { + return new HdmiCecMessage[size]; + } + }; + + @Override + public String toString() { + StringBuffer s = new StringBuffer(); + s.append(String.format("src: %d dst: %d op: %2X params: ", mSource, mDestination, mOpcode)); + for (byte data : mParams) { + s.append(String.format("%02X ", data)); + } + return s.toString(); + } +} + diff --git a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl new file mode 100644 index 0000000..d281ce6 --- /dev/null +++ b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl @@ -0,0 +1,29 @@ +/* + * 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.hdmi; + +import android.hardware.hdmi.HdmiCecMessage; + +/** + * Interface definition for HdmiCecService to do interprocess communcation. + * + * @hide + */ +oneway interface IHdmiCecListener { + void onMessageReceived(in HdmiCecMessage message); + void onCableStatusChanged(in boolean connected); +} diff --git a/core/java/android/hardware/hdmi/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl new file mode 100644 index 0000000..ecdd345 --- /dev/null +++ b/core/java/android/hardware/hdmi/IHdmiCecService.aidl @@ -0,0 +1,40 @@ +/* + * 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.hdmi; + +import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.IHdmiCecListener; +import android.os.IBinder; + +/** + * Binder interface that components running in the appplication process + * will use to enable HDMI-CEC protocol exchange with other devices. + * + * @hide + */ +interface IHdmiCecService { + IBinder allocateLogicalDevice(int type, IHdmiCecListener listener); + void removeServiceListener(IBinder b, IHdmiCecListener listener); + void sendActiveSource(IBinder b); + void sendInactiveSource(IBinder b); + void sendImageViewOn(IBinder b); + void sendTextViewOn(IBinder b); + void sendGiveDevicePowerStatus(IBinder b, int address); + boolean isTvOn(IBinder b); + void sendMessage(IBinder b, in HdmiCecMessage message); +} + diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 9b6f82a..f1e7e98 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -16,6 +16,7 @@ package android.hardware.input; +import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.KeyboardLayout; import android.hardware.input.IInputDevicesChangedListener; import android.os.IBinder; @@ -41,13 +42,13 @@ interface IInputManager { // Keyboard layouts configuration. KeyboardLayout[] getKeyboardLayouts(); KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor); - String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor); - void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + String getCurrentKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier); + void setCurrentKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier, String keyboardLayoutDescriptor); - String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor); - void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + String[] getKeyboardLayoutsForInputDevice(in InputDeviceIdentifier identifier); + void addKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier, String keyboardLayoutDescriptor); - void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + void removeKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier, String keyboardLayoutDescriptor); // Registers an input devices changed listener. diff --git a/core/java/android/hardware/input/InputDeviceIdentifier.aidl b/core/java/android/hardware/input/InputDeviceIdentifier.aidl new file mode 100644 index 0000000..7234a91 --- /dev/null +++ b/core/java/android/hardware/input/InputDeviceIdentifier.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2013 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.input; + +parcelable InputDeviceIdentifier; diff --git a/core/java/android/hardware/input/InputDeviceIdentifier.java b/core/java/android/hardware/input/InputDeviceIdentifier.java new file mode 100644 index 0000000..5e832e3 --- /dev/null +++ b/core/java/android/hardware/input/InputDeviceIdentifier.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 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.input; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Wrapper for passing identifying information for input devices. + * + * @hide + */ +public final class InputDeviceIdentifier implements Parcelable { + private final String mDescriptor; + private final int mVendorId; + private final int mProductId; + + public InputDeviceIdentifier(String descriptor, int vendorId, int productId) { + this.mDescriptor = descriptor; + this.mVendorId = vendorId; + this.mProductId = productId; + } + + private InputDeviceIdentifier(Parcel src) { + mDescriptor = src.readString(); + mVendorId = src.readInt(); + mProductId = src.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mDescriptor); + dest.writeInt(mVendorId); + dest.writeInt(mProductId); + } + + public String getDescriptor() { + return mDescriptor; + } + + public int getVendorId() { + return mVendorId; + } + + public int getProductId() { + return mProductId; + } + + public static final Parcelable.Creator<InputDeviceIdentifier> CREATOR = + new Parcelable.Creator<InputDeviceIdentifier>() { + + @Override + public InputDeviceIdentifier createFromParcel(Parcel source) { + return new InputDeviceIdentifier(source); + } + + @Override + public InputDeviceIdentifier[] newArray(int size) { + return new InputDeviceIdentifier[size]; + } + + }; +} diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 30e69a6..a2aeafb 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -26,6 +26,8 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.Vibrator; @@ -373,20 +375,17 @@ public final class InputManager { } /** - * Gets the current keyboard layout descriptor for the specified input device. - * - * @param inputDeviceDescriptor The input device descriptor. - * @return The keyboard layout descriptor, or null if no keyboard layout has been set. + * Gets the current keyboard layout descriptor for the specified input + * device. * + * @param identifier Identifier for the input device + * @return The keyboard layout descriptor, or null if no keyboard layout has + * been set. * @hide */ - public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) { - if (inputDeviceDescriptor == null) { - throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); - } - + public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) { try { - return mIm.getCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor); + return mIm.getCurrentKeyboardLayoutForInputDevice(identifier); } catch (RemoteException ex) { Log.w(TAG, "Could not get current keyboard layout for input device.", ex); return null; @@ -394,28 +393,29 @@ public final class InputManager { } /** - * Sets the current keyboard layout descriptor for the specified input device. + * Sets the current keyboard layout descriptor for the specified input + * device. * <p> - * This method may have the side-effect of causing the input device in question - * to be reconfigured. + * This method may have the side-effect of causing the input device in + * question to be reconfigured. * </p> * - * @param inputDeviceDescriptor The input device descriptor. - * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null. - * + * @param identifier The identifier for the input device. + * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, + * must not be null. * @hide */ - public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor) { - if (inputDeviceDescriptor == null) { - throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); + if (identifier == null) { + throw new IllegalArgumentException("identifier must not be null"); } if (keyboardLayoutDescriptor == null) { throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); } try { - mIm.setCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor, + mIm.setCurrentKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); } catch (RemoteException ex) { Log.w(TAG, "Could not set current keyboard layout for input device.", ex); @@ -423,20 +423,20 @@ public final class InputManager { } /** - * Gets all keyboard layout descriptors that are enabled for the specified input device. + * Gets all keyboard layout descriptors that are enabled for the specified + * input device. * - * @param inputDeviceDescriptor The input device descriptor. + * @param identifier The identifier for the input device. * @return The keyboard layout descriptors. - * * @hide */ - public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) { - if (inputDeviceDescriptor == null) { + public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) { + if (identifier == null) { throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } try { - return mIm.getKeyboardLayoutsForInputDevice(inputDeviceDescriptor); + return mIm.getKeyboardLayoutsForInputDevice(identifier); } catch (RemoteException ex) { Log.w(TAG, "Could not get keyboard layouts for input device.", ex); return ArrayUtils.emptyArray(String.class); @@ -446,18 +446,18 @@ public final class InputManager { /** * Adds the keyboard layout descriptor for the specified input device. * <p> - * This method may have the side-effect of causing the input device in question - * to be reconfigured. + * This method may have the side-effect of causing the input device in + * question to be reconfigured. * </p> * - * @param inputDeviceDescriptor The input device descriptor. - * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to add. - * + * @param identifier The identifier for the input device. + * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to + * add. * @hide */ - public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor) { - if (inputDeviceDescriptor == null) { + if (identifier == null) { throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } if (keyboardLayoutDescriptor == null) { @@ -465,7 +465,7 @@ public final class InputManager { } try { - mIm.addKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor); + mIm.addKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); } catch (RemoteException ex) { Log.w(TAG, "Could not add keyboard layout for input device.", ex); } @@ -474,18 +474,18 @@ public final class InputManager { /** * Removes the keyboard layout descriptor for the specified input device. * <p> - * This method may have the side-effect of causing the input device in question - * to be reconfigured. + * This method may have the side-effect of causing the input device in + * question to be reconfigured. * </p> * - * @param inputDeviceDescriptor The input device descriptor. - * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to remove. - * + * @param identifier The identifier for the input device. + * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to + * remove. * @hide */ - public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier, String keyboardLayoutDescriptor) { - if (inputDeviceDescriptor == null) { + if (identifier == null) { throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } if (keyboardLayoutDescriptor == null) { @@ -493,7 +493,7 @@ public final class InputManager { } try { - mIm.removeKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor); + mIm.removeKeyboardLayoutForInputDevice(identifier, keyboardLayoutDescriptor); } catch (RemoteException ex) { Log.w(TAG, "Could not remove keyboard layout for input device.", ex); } diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java new file mode 100644 index 0000000..6a392dd --- /dev/null +++ b/core/java/android/hardware/input/InputManagerInternal.java @@ -0,0 +1,42 @@ +/* + * 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.input; + +import android.hardware.display.DisplayViewport; +import android.view.InputEvent; + +/** + * Input manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class InputManagerInternal { + public abstract boolean injectInputEvent(InputEvent event, int displayId, int mode); + + /** + * Called by the display manager to set information about the displays as needed + * by the input system. The input system must copy this information to retain it. + */ + public abstract void setDisplayViewports(DisplayViewport defaultViewport, + DisplayViewport externalTouchViewport); + + /** + * Called by the power manager to tell the input manager whether it should start + * watching for wake events. + */ + public abstract void setInteractive(boolean interactive); +} diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java index d1e63f6..320ccfe 100644 --- a/core/java/android/hardware/usb/UsbDevice.java +++ b/core/java/android/hardware/usb/UsbDevice.java @@ -91,6 +91,7 @@ public class UsbDevice implements Parcelable { * Returns the manufacturer name of the device. * * @return the manufacturer name + * @hide */ public String getManufacturerName() { return mManufacturerName; @@ -100,6 +101,7 @@ public class UsbDevice implements Parcelable { * Returns the product name of the device. * * @return the product name + * @hide */ public String getProductName() { return mProductName; @@ -109,6 +111,7 @@ public class UsbDevice implements Parcelable { * Returns the serial number of the device. * * @return the serial number name + * @hide */ public String getSerialNumber() { return mSerialNumber; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 70c8750..4eecfa9 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -361,11 +361,17 @@ public class ConnectivityManager { */ public static final int TYPE_MOBILE_IA = 14; + /** + * The network that uses proxy to achieve connectivity. + * {@hide} + */ + public static final int TYPE_PROXY = 16; + /** {@hide} */ - public static final int MAX_RADIO_TYPE = TYPE_MOBILE_IA; + public static final int MAX_RADIO_TYPE = TYPE_PROXY; /** {@hide} */ - public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_IA; + public static final int MAX_NETWORK_TYPE = TYPE_PROXY; /** * If you want to set the default network preference,you can directly @@ -446,6 +452,8 @@ public class ConnectivityManager { return "WIFI_P2P"; case TYPE_MOBILE_IA: return "MOBILE_IA"; + case TYPE_PROXY: + return "PROXY"; default: return Integer.toString(type); } diff --git a/core/java/android/net/ProxyDataTracker.java b/core/java/android/net/ProxyDataTracker.java new file mode 100644 index 0000000..3eebef9 --- /dev/null +++ b/core/java/android/net/ProxyDataTracker.java @@ -0,0 +1,206 @@ +/* + * 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.net; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A data tracker responsible for bringing up and tearing down the system proxy server. + * + * {@hide} + */ +public class ProxyDataTracker extends BaseNetworkStateTracker { + private static final String TAG = "ProxyDataTracker"; + private static final String NETWORK_TYPE = "PROXY"; + + // TODO: investigate how to get these DNS addresses from the system. + private static final String DNS1 = "8.8.8.8"; + private static final String DNS2 = "8.8.4.4"; + private static final String INTERFACE_NAME = "ifb0"; + private static final String REASON_ENABLED = "enabled"; + private static final String REASON_DISABLED = "disabled"; + private static final String REASON_PROXY_DOWN = "proxy_down"; + + private static final int MSG_TEAR_DOWN_REQUEST = 1; + private static final int MSG_SETUP_REQUEST = 2; + + private static final String PERMISSION_PROXY_STATUS_SENDER = + "android.permission.ACCESS_NETWORK_CONDITIONS"; + private static final String ACTION_PROXY_STATUS_CHANGE = + "com.android.net.PROXY_STATUS_CHANGE"; + private static final String KEY_IS_PROXY_AVAILABLE = "is_proxy_available"; + private static final String KEY_REPLY_TO_MESSENGER_BINDER = "reply_to_messenger_binder"; + private static final String KEY_REPLY_TO_MESSENGER_BINDER_BUNDLE = + "reply_to_messenger_binder_bundle"; + + private Handler mTarget; + private Messenger mProxyStatusService; + private AtomicBoolean mReconnectRequested = new AtomicBoolean(false); + private AtomicBoolean mIsProxyAvailable = new AtomicBoolean(false); + private final AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); + + private final BroadcastReceiver mProxyStatusServiceListener = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(ACTION_PROXY_STATUS_CHANGE)) { + mIsProxyAvailable.set(intent.getBooleanExtra(KEY_IS_PROXY_AVAILABLE, false)); + if (mIsProxyAvailable.get()) { + Bundle bundle = intent.getBundleExtra(KEY_REPLY_TO_MESSENGER_BINDER_BUNDLE); + if (bundle == null || bundle.getBinder(KEY_REPLY_TO_MESSENGER_BINDER) == null) { + Log.e(TAG, "no messenger binder in the intent to send future requests"); + mIsProxyAvailable.set(false); + return; + } + mProxyStatusService = + new Messenger(bundle.getBinder(KEY_REPLY_TO_MESSENGER_BINDER)); + // If there is a pending reconnect request, do it now. + if (mReconnectRequested.get()) { + reconnect(); + } + } else { + setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, + REASON_PROXY_DOWN, null); + } + } else { + Log.d(TAG, "Unrecognized broadcast intent"); + } + } + }; + + /** + * Create a new ProxyDataTracker + */ + public ProxyDataTracker() { + mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_PROXY, 0, NETWORK_TYPE, ""); + mLinkProperties = new LinkProperties(); + mLinkCapabilities = new LinkCapabilities(); + mNetworkInfo.setIsAvailable(true); + try { + mLinkProperties.addDns(InetAddress.getByName(DNS1)); + mLinkProperties.addDns(InetAddress.getByName(DNS2)); + mLinkProperties.setInterfaceName(INTERFACE_NAME); + } catch (UnknownHostException e) { + Log.e(TAG, "Could not add DNS address", e); + } + } + + public Object Clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + @Override + public void startMonitoring(Context context, Handler target) { + mContext = context; + mTarget = target; + mContext.registerReceiver(mProxyStatusServiceListener, + new IntentFilter(ACTION_PROXY_STATUS_CHANGE), + PERMISSION_PROXY_STATUS_SENDER, + null); + } + + /** + * Disable connectivity to the network. + */ + public boolean teardown() { + setTeardownRequested(true); + mReconnectRequested.set(false); + try { + if (mIsProxyAvailable.get() && mProxyStatusService != null) { + mProxyStatusService.send(Message.obtain(null, MSG_TEAR_DOWN_REQUEST)); + } + } catch (RemoteException e) { + Log.e(TAG, "Unable to connect to proxy status service", e); + return false; + } + setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, REASON_DISABLED, null); + return true; + } + + /** + * Re-enable proxy data connectivity after a {@link #teardown()}. + */ + public boolean reconnect() { + mReconnectRequested.set(true); + setTeardownRequested(false); + if (!mIsProxyAvailable.get()) { + Log.w(TAG, "Reconnect requested even though proxy service is not up. Bailing."); + return false; + } + setDetailedState(NetworkInfo.DetailedState.CONNECTING, REASON_ENABLED, null); + + try { + mProxyStatusService.send(Message.obtain(null, MSG_SETUP_REQUEST)); + } catch (RemoteException e) { + Log.e(TAG, "Unable to connect to proxy status service", e); + setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, REASON_PROXY_DOWN, null); + return false; + } + // We'll assume proxy is set up successfully. If not, a status change broadcast will be + // received afterwards to indicate any failure. + setDetailedState(NetworkInfo.DetailedState.CONNECTED, REASON_ENABLED, null); + return true; + } + + /** + * Fetch default gateway address for the network + */ + public int getDefaultGatewayAddr() { + return mDefaultGatewayAddr.get(); + } + + /** + * Return the system properties name associated with the tcp buffer sizes + * for this network. + */ + public String getTcpBufferSizesPropName() { + return "net.tcp.buffersize.wifi"; + } + + /** + * Record the detailed state of a network, and if it is a + * change from the previous state, send a notification to + * any listeners. + * @param state the new @{code DetailedState} + * @param reason a {@code String} indicating a reason for the state change, + * if one was supplied. May be {@code null}. + * @param extraInfo optional {@code String} providing extra information about the state change + */ + private void setDetailedState(NetworkInfo.DetailedState state, String reason, + String extraInfo) { + mNetworkInfo.setDetailedState(state, reason, extraInfo); + Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); + msg.sendToTarget(); + } +} diff --git a/core/java/android/net/nsd/NsdServiceInfo.java b/core/java/android/net/nsd/NsdServiceInfo.java index 205a21d..8f52a7c 100644 --- a/core/java/android/net/nsd/NsdServiceInfo.java +++ b/core/java/android/net/nsd/NsdServiceInfo.java @@ -18,8 +18,15 @@ package android.net.nsd; import android.os.Parcelable; import android.os.Parcel; +import android.util.Log; +import android.util.ArrayMap; +import java.io.UnsupportedEncodingException; import java.net.InetAddress; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Map; + /** * A class representing service information for network service discovery @@ -27,11 +34,13 @@ import java.net.InetAddress; */ public final class NsdServiceInfo implements Parcelable { + private static final String TAG = "NsdServiceInfo"; + private String mServiceName; private String mServiceType; - private DnsSdTxtRecord mTxtRecord; + private final ArrayMap<String, byte[]> mTxtRecord = new ArrayMap<String, byte[]>(); private InetAddress mHost; @@ -41,10 +50,9 @@ public final class NsdServiceInfo implements Parcelable { } /** @hide */ - public NsdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) { + public NsdServiceInfo(String sn, String rt) { mServiceName = sn; mServiceType = rt; - mTxtRecord = tr; } /** Get the service name */ @@ -67,16 +75,6 @@ public final class NsdServiceInfo implements Parcelable { mServiceType = s; } - /** @hide */ - public DnsSdTxtRecord getTxtRecord() { - return mTxtRecord; - } - - /** @hide */ - public void setTxtRecord(DnsSdTxtRecord t) { - mTxtRecord = new DnsSdTxtRecord(t); - } - /** Get the host address. The host address is valid for a resolved service. */ public InetAddress getHost() { return mHost; @@ -97,14 +95,139 @@ public final class NsdServiceInfo implements Parcelable { mPort = p; } + /** @hide */ + public void setAttribute(String key, byte[] value) { + // Key must be printable US-ASCII, excluding =. + for (int i = 0; i < key.length(); ++i) { + char character = key.charAt(i); + if (character < 0x20 || character > 0x7E) { + throw new IllegalArgumentException("Key strings must be printable US-ASCII"); + } else if (character == 0x3D) { + throw new IllegalArgumentException("Key strings must not include '='"); + } + } + + // Key length + value length must be < 255. + if (key.length() + (value == null ? 0 : value.length) >= 255) { + throw new IllegalArgumentException("Key length + value length must be < 255 bytes"); + } + + // Warn if key is > 9 characters, as recommended by RFC 6763 section 6.4. + if (key.length() > 9) { + Log.w(TAG, "Key lengths > 9 are discouraged: " + key); + } + + // Check against total TXT record size limits. + // Arbitrary 400 / 1300 byte limits taken from RFC 6763 section 6.2. + int txtRecordSize = getTxtRecordSize(); + int futureSize = txtRecordSize + key.length() + (value == null ? 0 : value.length) + 2; + if (futureSize > 1300) { + throw new IllegalArgumentException("Total length of attributes must be < 1300 bytes"); + } else if (futureSize > 400) { + Log.w(TAG, "Total length of all attributes exceeds 400 bytes; truncation may occur"); + } + + mTxtRecord.put(key, value); + } + + /** + * Add a service attribute as a key/value pair. + * + * <p> Service attributes are included as DNS-SD TXT record pairs. + * + * <p> The key must be US-ASCII printable characters, excluding the '=' character. Values may + * be UTF-8 strings or null. The total length of key + value must be less than 255 bytes. + * + * <p> Keys should be short, ideally no more than 9 characters, and unique per instance of + * {@link NsdServiceInfo}. Calling {@link #setAttribute} twice with the same key will overwrite + * first value. + * @hide + */ + public void setAttribute(String key, String value) { + try { + setAttribute(key, value == null ? (byte []) null : value.getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Value must be UTF-8"); + } + } + + /** + * Remove an attribute by key + * @hide + */ + public void removeAttribute(String key) { + mTxtRecord.remove(key); + } + + /** + * Retrive attributes as a map of String keys to byte[] values. + * + * <p> The returned map is unmodifiable; changes must be made through {@link #setAttribute} and + * {@link #removeAttribute}. + * @hide + */ + public Map<String, byte[]> getAttributes() { + return Collections.unmodifiableMap(mTxtRecord); + } + + private int getTxtRecordSize() { + int txtRecordSize = 0; + for (Map.Entry<String, byte[]> entry : mTxtRecord.entrySet()) { + txtRecordSize += 2; // One for the length byte, one for the = between key and value. + txtRecordSize += entry.getKey().length(); + byte[] value = entry.getValue(); + txtRecordSize += value == null ? 0 : value.length; + } + return txtRecordSize; + } + + /** @hide */ + public byte[] getTxtRecord() { + int txtRecordSize = getTxtRecordSize(); + if (txtRecordSize == 0) { + return null; + } + + byte[] txtRecord = new byte[txtRecordSize]; + int ptr = 0; + for (Map.Entry<String, byte[]> entry : mTxtRecord.entrySet()) { + String key = entry.getKey(); + byte[] value = entry.getValue(); + + // One byte to record the length of this key/value pair. + txtRecord[ptr++] = (byte) (key.length() + (value == null ? 0 : value.length) + 1); + + // The key, in US-ASCII. + // Note: use the StandardCharsets const here because it doesn't raise exceptions and we + // already know the key is ASCII at this point. + System.arraycopy(key.getBytes(StandardCharsets.US_ASCII), 0, txtRecord, ptr, + key.length()); + ptr += key.length(); + + // US-ASCII '=' character. + txtRecord[ptr++] = (byte)'='; + + // The value, as any raw bytes. + if (value != null) { + System.arraycopy(value, 0, txtRecord, ptr, value.length); + ptr += value.length; + } + } + return txtRecord; + } + public String toString() { StringBuffer sb = new StringBuffer(); - sb.append("name: ").append(mServiceName). - append("type: ").append(mServiceType). - append("host: ").append(mHost). - append("port: ").append(mPort). - append("txtRecord: ").append(mTxtRecord); + sb.append("name: ").append(mServiceName) + .append(", type: ").append(mServiceType) + .append(", host: ").append(mHost) + .append(", port: ").append(mPort); + + byte[] txtRecord = getTxtRecord(); + if (txtRecord != null) { + sb.append(", txtRecord: ").append(new String(txtRecord, StandardCharsets.UTF_8)); + } return sb.toString(); } @@ -117,14 +240,27 @@ public final class NsdServiceInfo implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(mServiceName); dest.writeString(mServiceType); - dest.writeParcelable(mTxtRecord, flags); if (mHost != null) { - dest.writeByte((byte)1); + dest.writeInt(1); dest.writeByteArray(mHost.getAddress()); } else { - dest.writeByte((byte)0); + dest.writeInt(0); } dest.writeInt(mPort); + + // TXT record key/value pairs. + dest.writeInt(mTxtRecord.size()); + for (String key : mTxtRecord.keySet()) { + byte[] value = mTxtRecord.get(key); + if (value != null) { + dest.writeInt(1); + dest.writeInt(value.length); + dest.writeByteArray(value); + } else { + dest.writeInt(0); + } + dest.writeString(key); + } } /** Implement the Parcelable interface */ @@ -134,15 +270,26 @@ public final class NsdServiceInfo implements Parcelable { NsdServiceInfo info = new NsdServiceInfo(); info.mServiceName = in.readString(); info.mServiceType = in.readString(); - info.mTxtRecord = in.readParcelable(null); - if (in.readByte() == 1) { + if (in.readInt() == 1) { try { info.mHost = InetAddress.getByAddress(in.createByteArray()); } catch (java.net.UnknownHostException e) {} } info.mPort = in.readInt(); + + // TXT record key/value pairs. + int recordCount = in.readInt(); + for (int i = 0; i < recordCount; ++i) { + byte[] valueArray = null; + if (in.readInt() == 1) { + int valueLength = in.readInt(); + valueArray = new byte[valueLength]; + in.readByteArray(valueArray); + } + info.mTxtRecord.put(in.readString(), valueArray); + } return info; } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index b1a9ea3..5736c7a 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -782,7 +782,9 @@ public abstract class BatteryStats implements Parcelable { * {@hide} */ public abstract long getScreenOnTime(long batteryRealtime, int which); - + + public abstract long getInteractiveTime(long batteryRealtime, int which); + public static final int SCREEN_BRIGHTNESS_DARK = 0; public static final int SCREEN_BRIGHTNESS_DIM = 1; public static final int SCREEN_BRIGHTNESS_MEDIUM = 2; @@ -804,8 +806,6 @@ public abstract class BatteryStats implements Parcelable { public abstract long getScreenBrightnessTime(int brightnessBin, long batteryRealtime, int which); - public abstract int getInputEventCount(int which); - /** * Returns the time in microseconds that the phone has been on while the device was * running on battery. @@ -1303,7 +1303,7 @@ public abstract class BatteryStats implements Parcelable { wifiRunningTime / 1000, bluetoothOnTime / 1000, mobileRxTotal, mobileTxTotal, wifiRxTotal, wifiTxTotal, fullWakeLockTimeTotal, partialWakeLockTimeTotal, - getInputEventCount(which)); + 0 /*legacy input event count*/); // Dump screen brightness stats Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS]; @@ -1564,16 +1564,22 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); final long screenOnTime = getScreenOnTime(batteryRealtime, which); + final long interactiveTime = getInteractiveTime(batteryRealtime, which); final long phoneOnTime = getPhoneOnTime(batteryRealtime, which); final long wifiRunningTime = getGlobalWifiRunningTime(batteryRealtime, which); final long wifiOnTime = getWifiOnTime(batteryRealtime, which); final long bluetoothOnTime = getBluetoothOnTime(batteryRealtime, which); sb.setLength(0); sb.append(prefix); + sb.append(" Interactive: "); formatTimeMs(sb, interactiveTime / 1000); + sb.append("("); sb.append(formatRatioLocked(interactiveTime, whichBatteryRealtime)); + sb.append(")"); + pw.println(sb.toString()); + sb.setLength(0); + sb.append(prefix); sb.append(" Screen on: "); formatTimeMs(sb, screenOnTime / 1000); sb.append("("); sb.append(formatRatioLocked(screenOnTime, whichBatteryRealtime)); - sb.append("), Input events: "); sb.append(getInputEventCount(which)); - sb.append(", Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000); + sb.append("), Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000); sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime)); sb.append(")"); pw.println(sb.toString()); diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index a23ecd7..65f62ed 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -501,6 +501,18 @@ public class Build { * </ul> */ public static final int KITKAT = 19; + + /** + * Android 4.4W: KitKat for watches, snacks on the run. + * + * <p>Applications targeting this or a later release will get these + * new changes in behavior:</p> + * <ul> + * <li>{@link android.app.AlertDialog} might not have a default background if the theme does + * not specify one.</li> + * </ul> + */ + public static final int KITKAT_WATCH = 20; } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 2de1204..f08afb9 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -627,9 +627,8 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * in relative terms (e.g. was run #1 faster than run #2). The times * for native methods will not change, so don't try to use this to * compare the performance of interpreted and native implementations of the - * same method. As an alternative, consider using sampling-based method - * tracing via {@link #startMethodTracingSampling(String, int, int)} or - * "native" tracing in the emulator via {@link #startNativeTracing()}. + * same method. As an alternative, consider using "native" tracing in the emulator via + * {@link #startNativeTracing()}. * </p> * * @param traceName Name for the trace log file to create. @@ -657,6 +656,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * If the trace file given does not end in ".trace", it will be appended for you. * @param bufferSize The maximum amount of trace data we gather. If not given, it defaults to 8MB. * @param intervalUs The amount of time between each sample in microseconds. + * @hide */ public static void startMethodTracingSampling(String traceName, int bufferSize, int intervalUs) { diff --git a/core/java/android/os/FactoryTest.java b/core/java/android/os/FactoryTest.java index ec99697..7a252f9 100644 --- a/core/java/android/os/FactoryTest.java +++ b/core/java/android/os/FactoryTest.java @@ -25,6 +25,20 @@ package android.os; * {@hide} */ public final class FactoryTest { + public static final int FACTORY_TEST_OFF = 0; + public static final int FACTORY_TEST_LOW_LEVEL = 1; + public static final int FACTORY_TEST_HIGH_LEVEL = 2; + + /** + * Gets the current factory test mode. + * + * @return One of: {@link #FACTORY_TEST_OFF}, {@link #FACTORY_TEST_LOW_LEVEL}, + * or {@link #FACTORY_TEST_HIGH_LEVEL}. + */ + public static int getMode() { + return SystemProperties.getInt("ro.factorytest", FACTORY_TEST_OFF); + } + /** * When true, long-press on power should immediately cause the device to * shut down, without prompting the user. diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 56176a4..e7330bb 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -35,10 +35,10 @@ interface IPowerManager void userActivity(long time, int event, int flags); void wakeUp(long time); - void goToSleep(long time, int reason); + void goToSleep(long time, int reason, int flags); void nap(long time); + boolean isInteractive(); - boolean isScreenOn(); void reboot(boolean confirm, String reason, boolean wait); void shutdown(boolean confirm, boolean wait); void crash(String message); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 5e0d489..96cfa29 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -191,6 +191,18 @@ public final class PowerManager { public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020; /** + * Wake lock level: Put the screen in a low power state and allow the CPU to suspend + * if no other wake locks are held. + * <p> + * This is used by the dream manager to implement doze mode. It currently + * has no effect unless the power manager is in the dozing state. + * </p> + * + * {@hide} + */ + public static final int DOZE_WAKE_LOCK = 0x00000040; + + /** * Mask for the wake lock level component of a combined wake lock level and flags integer. * * @hide @@ -290,6 +302,12 @@ public final class PowerManager { */ public static final int GO_TO_SLEEP_REASON_TIMEOUT = 2; + /** + * Go to sleep flag: Skip dozing state and directly go to full sleep. + * @hide + */ + public static final int GO_TO_SLEEP_FLAG_NO_DOZE = 1 << 0; + final Context mContext; final IPowerManager mService; final Handler mHandler; @@ -418,6 +436,7 @@ public final class PowerManager { case SCREEN_BRIGHT_WAKE_LOCK: case FULL_WAKE_LOCK: case PROXIMITY_SCREEN_OFF_WAKE_LOCK: + case DOZE_WAKE_LOCK: break; default: throw new IllegalArgumentException("Must specify a valid wake lock level."); @@ -477,8 +496,15 @@ public final class PowerManager { * @see #wakeUp */ public void goToSleep(long time) { + goToSleep(time, GO_TO_SLEEP_REASON_USER, 0); + } + + /** + * @hide + */ + public void goToSleep(long time, int reason, int flags) { try { - mService.goToSleep(time, GO_TO_SLEEP_REASON_USER); + mService.goToSleep(time, reason, flags); } catch (RemoteException e) { } } @@ -569,21 +595,64 @@ public final class PowerManager { } /** - * Returns whether the screen is currently on. + * Returns true if the device is in an interactive state. * <p> - * Only indicates whether the screen is on. The screen could be either bright or dim. + * For historical reasons, the name of this method refers to the power state of + * the screen but it actually describes the overall interactive state of + * the device. This method has been replaced by {@link #isInteractive}. * </p><p> - * {@samplecode - * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - * boolean isScreenOn = pm.isScreenOn(); - * } + * The value returned by this method only indicates whether the device is + * in an interactive state which may have nothing to do with the screen being + * on or off. To determine the actual state of the screen, + * use {@link android.view.Display#getState}. * </p> * - * @return whether the screen is on (bright or dim). + * @return True if the device is in an interactive state. + * + * @deprecated Use {@link #isInteractive} instead. */ + @Deprecated public boolean isScreenOn() { + return isInteractive(); + } + + /** + * Returns true if the device is in an interactive state. + * <p> + * When this method returns true, the device is awake and ready to interact + * with the user (although this is not a guarantee that the user is actively + * interacting with the device just this moment). The main screen is usually + * turned on while in this state. Certain features, such as the proximity + * sensor, may temporarily turn off the screen while still leaving the device in an + * interactive state. Note in particular that the device is still considered + * to be interactive while dreaming (since dreams can be interactive) but not + * when it is dozing or asleep. + * </p><p> + * When this method returns false, the device is dozing or asleep and must + * be awoken before it will become ready to interact with the user again. The + * main screen is usually turned off while in this state. Certain features, + * such as "ambient mode" may cause the main screen to remain on (albeit in a + * low power state) to display system-provided content while the device dozes. + * </p><p> + * The system will send a {@link android.content.Intent#ACTION_SCREEN_ON screen on} + * or {@link android.content.Intent#ACTION_SCREEN_OFF screen off} broadcast + * whenever the interactive state of the device changes. For historical reasons, + * the names of these broadcasts refer to the power state of the screen + * but they are actually sent in response to changes in the overall interactive + * state of the device, as described by this method. + * </p><p> + * Services may use the non-interactive state as a hint to conserve power + * since the user is not present. + * </p> + * + * @return True if the device is in an interactive state. + * + * @see android.content.Intent#ACTION_SCREEN_ON + * @see android.content.Intent#ACTION_SCREEN_OFF + */ + public boolean isInteractive() { try { - return mService.isScreenOn(); + return mService.isInteractive(); } catch (RemoteException e) { return false; } diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java new file mode 100644 index 0000000..cb3d528 --- /dev/null +++ b/core/java/android/os/PowerManagerInternal.java @@ -0,0 +1,60 @@ +/* + * 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.os; + +import android.view.WindowManagerPolicy; + +/** + * Power manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class PowerManagerInternal { + /** + * Used by the window manager to override the screen brightness based on the + * current foreground activity. + * + * This method must only be called by the window manager. + * + * @param brightness The overridden brightness, or -1 to disable the override. + */ + public abstract void setScreenBrightnessOverrideFromWindowManager(int brightness); + + /** + * Used by the window manager to override the button brightness based on the + * current foreground activity. + * + * This method must only be called by the window manager. + * + * @param brightness The overridden brightness, or -1 to disable the override. + */ + public abstract void setButtonBrightnessOverrideFromWindowManager(int brightness); + + /** + * Used by the window manager to override the user activity timeout based on the + * current foreground activity. It can only be used to make the timeout shorter + * than usual, not longer. + * + * This method must only be called by the window manager. + * + * @param timeoutMillis The overridden timeout, or -1 to disable the override. + */ + public abstract void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis); + + // TODO: Remove this and retrieve as a local service instead. + public abstract void setPolicy(WindowManagerPolicy policy); +} diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 5b9b5b0..86c749a 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -945,19 +945,6 @@ public class Process { } /** - * Set the out-of-memory badness adjustment for a process. - * - * @param pid The process identifier to set. - * @param amt Adjustment value -- linux allows -16 to +15. - * - * @return Returns true if the underlying system supports this - * feature, else false. - * - * {@hide} - */ - public static final native boolean setOomAdj(int pid, int amt); - - /** * Adjust the swappiness level for a process. * * @param pid The process identifier to set. diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index b692ffd..f671ed9 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -338,11 +338,11 @@ public class RecoverySystem { } /** - * Reboots the device and wipes the user data partition. This is - * sometimes called a "factory reset", which is something of a - * misnomer because the system partition is not restored to its - * factory state. - * Requires the {@link android.Manifest.permission#REBOOT} permission. + * Reboots the device and wipes the user data and cache + * partitions. This is sometimes called a "factory reset", which + * is something of a misnomer because the system partition is not + * restored to its factory state. Requires the + * {@link android.Manifest.permission#REBOOT} permission. * * @param context the Context to use * @@ -350,6 +350,28 @@ public class RecoverySystem { * fails, or if the reboot itself fails. */ public static void rebootWipeUserData(Context context) throws IOException { + rebootWipeUserData(context, false); + } + + /** + * Reboots the device and wipes the user data and cache + * partitions. This is sometimes called a "factory reset", which + * is something of a misnomer because the system partition is not + * restored to its factory state. Requires the + * {@link android.Manifest.permission#REBOOT} permission. + * + * @param context the Context to use + * @param shutdown if true, the device will be powered down after + * the wipe completes, rather than being rebooted + * back to the regular system. + * + * @throws IOException if writing the recovery command file + * fails, or if the reboot itself fails. + * + * @hide + */ + public static void rebootWipeUserData(Context context, boolean shutdown) + throws IOException { final ConditionVariable condition = new ConditionVariable(); Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION"); @@ -365,7 +387,13 @@ public class RecoverySystem { // Block until the ordered broadcast has completed. condition.block(); - bootCommand(context, "--wipe_data\n--locale=" + Locale.getDefault().toString()); + String shutdownArg = ""; + if (shutdown) { + shutdownArg = "--shutdown_after\n"; + } + + bootCommand(context, shutdownArg + "--wipe_data\n--locale=" + + Locale.getDefault().toString()); } /** diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index d794ca6..ea71ad8 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -1449,7 +1449,11 @@ public final class StrictMode { if (policy.classInstanceLimit.size() == 0) { return; } - Runtime.getRuntime().gc(); + + System.gc(); + System.runFinalization(); + System.gc(); + // Note: classInstanceLimit is immutable, so this is lock-free for (Map.Entry<Class, Integer> entry : policy.classInstanceLimit.entrySet()) { Class klass = entry.getKey(); @@ -2005,7 +2009,10 @@ public final class StrictMode { // noticeably less responsive during orientation changes when activities are // being restarted. Granted, it is only a problem when StrictMode is enabled // but it is annoying. - Runtime.getRuntime().gc(); + + System.gc(); + System.runFinalization(); + System.gc(); long instances = VMDebug.countInstancesOfClass(klass, false); if (instances > limit) { diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index a34d9c3..3853003 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -25,6 +25,7 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.graphics.BitmapFactory; import android.net.Uri; +import android.os.Build; import android.provider.BrowserContract.Bookmarks; import android.provider.BrowserContract.Combined; import android.provider.BrowserContract.History; @@ -155,8 +156,8 @@ public class Browser { * @param title Title for the bookmark. Can be null or empty string. * @param url Url for the bookmark. Can be null or empty string. */ - public static final void saveBookmark(Context c, - String title, + public static final void saveBookmark(Context c, + String title, String url) { Intent i = new Intent(Intent.ACTION_INSERT, Browser.BOOKMARKS_URI); i.putExtra("title", title); @@ -233,10 +234,10 @@ public class Browser { * * @param cr The ContentResolver used to access the database. */ - public static final Cursor getAllBookmarks(ContentResolver cr) throws + public static final Cursor getAllBookmarks(ContentResolver cr) throws IllegalStateException { return cr.query(Bookmarks.CONTENT_URI, - new String[] { Bookmarks.URL }, + new String[] { Bookmarks.URL }, Bookmarks.IS_FOLDER + " = 0", null, null); } @@ -397,19 +398,17 @@ public class Browser { // TODO make a single request to the provider to do this in a single transaction Cursor cursor = null; try { - + // Select non-bookmark history, ordered by date cursor = cr.query(History.CONTENT_URI, new String[] { History._ID, History.URL, History.DATE_LAST_VISITED }, null, null, History.DATE_LAST_VISITED + " ASC"); if (cursor.moveToFirst() && cursor.getCount() >= MAX_HISTORY_COUNT) { - final WebIconDatabase iconDb = WebIconDatabase.getInstance(); /* eliminate oldest history items */ for (int i = 0; i < TRUNCATE_N_OLDEST; i++) { cr.delete(ContentUris.withAppendedId(History.CONTENT_URI, cursor.getLong(0)), - null, null); - iconDb.releaseIconForPageUrl(cursor.getString(1)); + null, null); if (!cursor.moveToNext()) break; } } @@ -469,13 +468,6 @@ public class Browser { cursor = cr.query(History.CONTENT_URI, new String[] { History.URL }, whereClause, null, null); if (cursor.moveToFirst()) { - final WebIconDatabase iconDb = WebIconDatabase.getInstance(); - do { - // Delete favicons - // TODO don't release if the URL is bookmarked - iconDb.releaseIconForPageUrl(cursor.getString(0)); - } while (cursor.moveToNext()); - cr.delete(History.CONTENT_URI, whereClause, null); } } catch (IllegalStateException e) { @@ -520,7 +512,7 @@ public class Browser { * @param cr The ContentResolver used to access the database. * @param url url to remove. */ - public static final void deleteFromHistory(ContentResolver cr, + public static final void deleteFromHistory(ContentResolver cr, String url) { cr.delete(History.CONTENT_URI, History.URL + "=?", new String[] { url }); } @@ -554,7 +546,7 @@ public class Browser { Log.e(LOGTAG, "clearSearches", e); } } - + /** * Request all icons from the database. This call must either be called * in the main thread or have had Looper.prepare() invoked in the calling @@ -563,12 +555,12 @@ public class Browser { * @param cr The ContentResolver used to access the database. * @param where Clause to be used to limit the query from the database. * Must be an allowable string to be passed into a database query. - * @param listener IconListener that gets the icons once they are + * @param listener IconListener that gets the icons once they are * retrieved. */ public static final void requestAllIcons(ContentResolver cr, String where, WebIconDatabase.IconListener listener) { - WebIconDatabase.getInstance().bulkRequestIconForPageUrl(cr, where, listener); + // Do nothing: this is no longer used. } /** diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index f69cad0..457afcc 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -109,14 +109,18 @@ public final class MediaStore { * An intent to perform a search for music media and automatically play content from the * result when possible. This can be fired, for example, by the result of a voice recognition * command to listen to music. - * <p> - * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string - * that can contain any type of unstructured music search, like the name of an artist, - * an album, a song, a genre, or any combination of these. - * <p> - * Because this intent includes an open-ended unstructured search string, it makes the most - * sense for apps that can support large-scale search of music, such as services connected - * to an online database of music which can be streamed and played on the device. + * <p>This intent always includes the {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS} + * and {@link android.app.SearchManager#QUERY} extras. The + * {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS} extra determines the search mode, and + * the value of the {@link android.app.SearchManager#QUERY} extra depends on the search mode. + * For more information about the search modes for this intent, see + * <a href="{@docRoot}guide/components/intents-common.html#PlaySearch">Play music based + * on a search query</a> in <a href="{@docRoot}guide/components/intents-common.html">Common + * Intents</a>.</p> + * + * <p>This intent makes the most sense for apps that can support large-scale search of music, + * such as services connected to an online database of music which can be streamed and played + * on the device.</p> */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH = diff --git a/core/java/android/service/dreams/DozeHardware.java b/core/java/android/service/dreams/DozeHardware.java new file mode 100644 index 0000000..b5e7f43 --- /dev/null +++ b/core/java/android/service/dreams/DozeHardware.java @@ -0,0 +1,77 @@ +/** + * 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.service.dreams; + +import android.os.RemoteException; +import android.util.Log; + +/** + * Provides access to low-level hardware features that a dream may use to provide + * a richer user experience while dozing. + * <p> + * This class contains functions that should be called by the dream to configure + * hardware before starting to doze and allowing the application processor to suspend. + * For example, the dream may provide the hardware with enough information to render + * some content on its own without any further assistance from the application processor. + * </p><p> + * This object is obtained by calling {@link DreamService#getDozeHardware()}. + * </p> + * + * @hide experimental + */ +public final class DozeHardware { + private static final String TAG = "DozeHardware"; + + public static final String MSG_ENABLE_MCU = "enable_mcu"; + + public static final byte[] VALUE_ON = "on".getBytes(); + public static final byte[] VALUE_OFF = "off".getBytes(); + + private final IDozeHardware mHardware; + + DozeHardware(IDozeHardware hardware) { + mHardware = hardware; + } + + /** + * Sets whether to enable the microcontroller. + * + * @param enable If true, enables the MCU otherwise disables it. + */ + public void setEnableMcu(boolean enable) { + sendMessage(MSG_ENABLE_MCU, enable ? VALUE_ON : VALUE_OFF); + } + + /** + * Sends a message to the doze hardware module. + * + * @param msg The name of the message to send. + * @param arg An optional argument data blob, may be null. + * @return A result data blob, may be null. + */ + public byte[] sendMessage(String msg, byte[] arg) { + if (msg == null) { + throw new IllegalArgumentException("msg must not be null"); + } + + try { + return mHardware.sendMessage(msg, arg); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to send message to doze hardware module.", ex); + return null; + } + } +} diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java new file mode 100644 index 0000000..9f7ddba --- /dev/null +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -0,0 +1,39 @@ +/* + * 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.service.dreams; + +/** + * Dream manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class DreamManagerInternal { + /** + * Called by the power manager to start a dream. + */ + public abstract void startDream(boolean doze); + + /** + * Called by the power manager to stop a dream. + */ + public abstract void stopDream(); + + /** + * Called by the power manager to determine whether a dream is running. + */ + public abstract boolean isDreaming(); +} diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index f6b6c89..b02a79d 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -20,12 +20,14 @@ import java.io.PrintWriter; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.AlarmManager; import android.app.Service; import android.content.Intent; import android.graphics.PixelFormat; import android.graphics.drawable.ColorDrawable; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; import android.view.ActionMode; @@ -42,6 +44,8 @@ import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; import com.android.internal.policy.PolicyManager; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.DumpUtils.Dump; /** * Extend this class to implement a custom dream (available to the user as a "Daydream"). @@ -145,19 +149,26 @@ public class DreamService extends Service implements Window.Callback { */ public static final String DREAM_META_DATA = "android.service.dream"; + private final IDreamManager mSandman; private final Handler mHandler = new Handler(); private IBinder mWindowToken; private Window mWindow; - private WindowManager mWindowManager; - private IDreamManager mSandman; - private boolean mInteractive = false; + private boolean mInteractive; private boolean mLowProfile = true; - private boolean mFullscreen = false; + private boolean mFullscreen; private boolean mScreenBright = true; + private boolean mStarted; private boolean mFinished; + private boolean mCanDoze; + private boolean mDozing; + private DozeHardware mDozeHardware; private boolean mDebug = false; + public DreamService() { + mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); + } + /** * @hide */ @@ -325,7 +336,7 @@ public class DreamService extends Service implements Window.Callback { * @return The current window manager, or null if the dream is not started. */ public WindowManager getWindowManager() { - return mWindowManager; + return mWindow != null ? mWindow.getWindowManager() : null; } /** @@ -444,9 +455,11 @@ public class DreamService extends Service implements Window.Callback { * correct interactions with it (seeing when it is cleared etc). */ public void setLowProfile(boolean lowProfile) { - mLowProfile = lowProfile; - int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; - applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag); + if (mLowProfile != lowProfile) { + mLowProfile = lowProfile; + int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; + applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag); + } } /** @@ -467,9 +480,11 @@ public class DreamService extends Service implements Window.Callback { * will be cleared. */ public void setFullscreen(boolean fullscreen) { - mFullscreen = fullscreen; - int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; - applyWindowFlags(mFullscreen ? flag : 0, flag); + if (mFullscreen != fullscreen) { + mFullscreen = fullscreen; + int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; + applyWindowFlags(mFullscreen ? flag : 0, flag); + } } /** @@ -487,14 +502,16 @@ public class DreamService extends Service implements Window.Callback { * @param screenBright True to keep the screen bright while dreaming. */ public void setScreenBright(boolean screenBright) { - mScreenBright = screenBright; - int flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; - applyWindowFlags(mScreenBright ? flag : 0, flag); + if (mScreenBright != screenBright) { + mScreenBright = screenBright; + int flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + applyWindowFlags(mScreenBright ? flag : 0, flag); + } } /** - * Returns whether or not this dream keeps the screen bright while dreaming. Defaults to false, - * allowing the screen to dim if necessary. + * Returns whether or not this dream keeps the screen bright while dreaming. + * Defaults to false, allowing the screen to dim if necessary. * * @see #setScreenBright(boolean) */ @@ -503,6 +520,119 @@ public class DreamService extends Service implements Window.Callback { } /** + * Returns true if this dream is allowed to doze. + * <p> + * The value returned by this method is only meaningful when the dream has started. + * </p> + * + * @return True if this dream can doze. + * @see #startDozing + * @hide experimental + */ + public boolean canDoze() { + return mCanDoze; + } + + /** + * Starts dozing, entering a deep dreamy sleep. + * <p> + * Dozing enables the system to conserve power while the user is not actively interacting + * with the device. While dozing, the display will remain on in a low-power state + * and will continue to show its previous contents but the application processor and + * other system components will be allowed to suspend when possible. + * </p><p> + * While the application processor is suspended, the dream may stop executing code + * for long periods of time. Prior to being suspended, the dream may schedule periodic + * wake-ups to render new content by scheduling an alarm with the {@link AlarmManager}. + * The dream may also keep the CPU awake by acquiring a + * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock} when necessary. + * Note that since the purpose of doze mode is to conserve power (especially when + * running on battery), the dream should not wake the CPU very often or keep it + * awake for very long. + * </p><p> + * It is a good idea to call this method some time after the dream's entry animation + * has completed and the dream is ready to doze. It is important to completely + * finish all of the work needed before dozing since the application processor may + * be suspended at any moment once this method is called unless other wake locks + * are being held. + * </p><p> + * Call {@link #stopDozing} or {@link #finish} to stop dozing. + * </p> + * + * @see #stopDozing + * @hide experimental + */ + public void startDozing() { + if (mCanDoze && !mDozing) { + mDozing = true; + try { + mSandman.startDozing(mWindowToken); + } catch (RemoteException ex) { + // system server died + } + } + } + + /** + * Stops dozing, returns to active dreaming. + * <p> + * This method reverses the effect of {@link #startDozing}. From this moment onward, + * the application processor will be kept awake as long as the dream is running + * or until the dream starts dozing again. + * </p> + * + * @see #startDozing + * @hide experimental + */ + public void stopDozing() { + if (mDozing) { + mDozing = false; + try { + mSandman.stopDozing(mWindowToken); + } catch (RemoteException ex) { + // system server died + } + } + } + + /** + * Returns true if the dream will allow the system to enter a low-power state while + * it is running without actually turning off the screen. Defaults to false, + * keeping the application processor awake while the dream is running. + * + * @return True if the dream is dozing. + * + * @see #setDozing(boolean) + * @hide experimental + */ + public boolean isDozing() { + return mDozing; + } + + /** + * Gets an object that may be used to access low-level hardware features that a + * dream may use to provide a richer user experience while dozing. + * + * @return An instance of {@link DozeHardware} or null if this device does not offer + * hardware support for dozing. + * + * @hide experimental + */ + public DozeHardware getDozeHardware() { + if (mCanDoze && mDozeHardware == null && mWindowToken != null) { + try { + IDozeHardware hardware = mSandman.getDozeHardware(mWindowToken); + if (hardware != null) { + mDozeHardware = new DozeHardware(hardware); + } + } catch (RemoteException ex) { + // system server died + } + } + return mDozeHardware; + } + + /** * Called when this Dream is constructed. */ @Override @@ -536,7 +666,11 @@ public class DreamService extends Service implements Window.Callback { } /** - * Stops the dream, detaches from the window, and wakes up. + * Stops the dream and detaches from the window. + * <p> + * When the dream ends, the system will be allowed to go to sleep fully unless there + * is a reason for it to be awake such as recent user activity or wake locks being held. + * </p> */ public final void finish() { if (mDebug) Slog.v(TAG, "finish()"); @@ -557,41 +691,31 @@ public class DreamService extends Service implements Window.Callback { // end public api - private void loadSandman() { - mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); - } - /** * Called by DreamController.stopDream() when the Dream is about to be unbound and destroyed. * * Must run on mHandler. */ private final void detach() { - if (mWindow == null) { - // already detached! - return; - } - - try { + if (mStarted) { + if (mDebug) Slog.v(TAG, "detach(): Calling onDreamingStopped()"); + mStarted = false; onDreamingStopped(); - } catch (Throwable t) { - Slog.w(TAG, "Crashed in onDreamingStopped()", t); - // we were going to stop anyway } - if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); - try { + if (mWindow != null) { // force our window to be removed synchronously - mWindowManager.removeViewImmediate(mWindow.getDecorView()); + if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); + mWindow.getWindowManager().removeViewImmediate(mWindow.getDecorView()); + mWindow = null; + } + + if (mWindowToken != null) { // the following will print a log message if it finds any other leaked windows WindowManagerGlobal.getInstance().closeAll(mWindowToken, this.getClass().getName(), "Dream"); - } catch (Throwable t) { - Slog.w(TAG, "Crashed removing window view", t); + mWindowToken = null; } - - mWindow = null; - mWindowToken = null; } /** @@ -601,18 +725,26 @@ public class DreamService extends Service implements Window.Callback { * * @param windowToken A window token that will allow a window to be created in the correct layer. */ - private final void attach(IBinder windowToken) { + private final void attach(IBinder windowToken, boolean canDoze) { if (mWindowToken != null) { Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); return; } + if (mFinished) { + Slog.w(TAG, "attach() called after dream already finished"); + try { + mSandman.finishSelf(windowToken); + } catch (RemoteException ex) { + // system server died + } + return; + } if (mDebug) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId()); - if (mSandman == null) { - loadSandman(); - } mWindowToken = windowToken; + mCanDoze = canDoze; + mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); mWindow.requestFeature(Window.FEATURE_NO_TITLE); @@ -635,33 +767,35 @@ public class DreamService extends Service implements Window.Callback { | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) ); mWindow.setAttributes(lp); - - if (mDebug) Slog.v(TAG, "Created and attached window: " + mWindow); - mWindow.setWindowManager(null, windowToken, "dream", true); - mWindowManager = mWindow.getWindowManager(); - if (mDebug) Slog.v(TAG, "Window added on thread " + Thread.currentThread().getId()); + applySystemUiVisibilityFlags( + (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), + View.SYSTEM_UI_FLAG_LOW_PROFILE); + try { - applySystemUiVisibilityFlags( - (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), - View.SYSTEM_UI_FLAG_LOW_PROFILE); getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); - } catch (Throwable t) { - Slog.w(TAG, "Crashed adding window view", t); - safelyFinish(); + } catch (WindowManager.BadTokenException ex) { + // This can happen because the dream manager service will remove the token + // immediately without necessarily waiting for the dream to start. + // We should receive a finish message soon. + Slog.i(TAG, "attach() called after window token already removed, dream will " + + "finish soon"); + mWindow = null; return; } - // start it up + // We need to defer calling onDreamingStarted until after onWindowAttached, + // which is posted to the handler by addView, so we post onDreamingStarted + // to the handler also. Need to watch out here in case detach occurs before + // this callback is invoked. mHandler.post(new Runnable() { @Override public void run() { - try { + if (mWindow != null) { + if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); + mStarted = true; onDreamingStarted(); - } catch (Throwable t) { - Slog.w(TAG, "Crashed in onDreamingStarted()", t); - safelyFinish(); } } }); @@ -669,13 +803,8 @@ public class DreamService extends Service implements Window.Callback { private void safelyFinish() { if (mDebug) Slog.v(TAG, "safelyFinish()"); - try { - finish(); - } catch (Throwable t) { - Slog.w(TAG, "Crashed in safelyFinish()", t); - finishInternal(); - return; - } + + finish(); if (!mFinished) { Slog.w(TAG, "Bad dream, did not call super.finish()"); @@ -685,19 +814,21 @@ public class DreamService extends Service implements Window.Callback { private void finishInternal() { if (mDebug) Slog.v(TAG, "finishInternal() mFinished = " + mFinished); - if (mFinished) return; - try { + + if (!mFinished) { mFinished = true; - if (mSandman != null) { - mSandman.finishSelf(mWindowToken); + if (mWindowToken == null) { + Slog.w(TAG, "Finish was called before the dream was attached."); } else { - Slog.w(TAG, "No dream manager found"); + try { + mSandman.finishSelf(mWindowToken); + } catch (RemoteException ex) { + // system server died + } } - stopSelf(); // if launched via any other means - } catch (Throwable t) { - Slog.w(TAG, "Crashed in finishInternal()", t); + stopSelf(); // if launched via any other means } } @@ -710,7 +841,7 @@ public class DreamService extends Service implements Window.Callback { WindowManager.LayoutParams lp = mWindow.getAttributes(); lp.flags = applyFlags(lp.flags, flags, mask); mWindow.setAttributes(lp); - mWindowManager.updateViewLayout(mWindow.getDecorView(), lp); + mWindow.getWindowManager().updateViewLayout(mWindow.getDecorView(), lp); } } @@ -732,32 +863,39 @@ public class DreamService extends Service implements Window.Callback { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - super.dump(fd, pw, args); - - pw.print(TAG + ": "); - if (mWindowToken == null) { - pw.println("stopped"); - } else { - pw.println("running (token=" + mWindowToken + ")"); - } - pw.println(" window: " + mWindow); - pw.print(" flags:"); - if (isInteractive()) pw.print(" interactive"); - if (isLowProfile()) pw.print(" lowprofile"); - if (isFullscreen()) pw.print(" fullscreen"); - if (isScreenBright()) pw.print(" bright"); - pw.println(); + DumpUtils.dumpAsync(mHandler, new Dump() { + @Override + public void dump(PrintWriter pw) { + pw.print(TAG + ": "); + if (mWindowToken == null) { + pw.println("stopped"); + } else { + pw.println("running (token=" + mWindowToken + ")"); + } + pw.println(" window: " + mWindow); + pw.print(" flags:"); + if (isInteractive()) pw.print(" interactive"); + if (isLowProfile()) pw.print(" lowprofile"); + if (isFullscreen()) pw.print(" fullscreen"); + if (isScreenBright()) pw.print(" bright"); + if (isDozing()) pw.print(" dozing"); + pw.println(); + } + }, pw, 1000); } - private class DreamServiceWrapper extends IDreamService.Stub { - public void attach(final IBinder windowToken) { + private final class DreamServiceWrapper extends IDreamService.Stub { + @Override + public void attach(final IBinder windowToken, final boolean canDoze) { mHandler.post(new Runnable() { @Override public void run() { - DreamService.this.attach(windowToken); + DreamService.this.attach(windowToken, canDoze); } }); } + + @Override public void detach() { mHandler.post(new Runnable() { @Override diff --git a/core/java/android/service/dreams/IDozeHardware.aidl b/core/java/android/service/dreams/IDozeHardware.aidl new file mode 100644 index 0000000..f5a657b --- /dev/null +++ b/core/java/android/service/dreams/IDozeHardware.aidl @@ -0,0 +1,24 @@ +/* + * 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.service.dreams; + +/** + * @hide + */ +interface IDozeHardware { + byte[] sendMessage(String msg, in byte[] arg); +} diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index 1c1b390..2718e31 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -16,10 +16,11 @@ package android.service.dreams; +import android.content.ComponentName; import android.os.Bundle; import android.os.ParcelFileDescriptor; -import android.content.ComponentName; import android.os.IBinder; +import android.service.dreams.IDozeHardware; /** @hide */ interface IDreamManager { @@ -31,4 +32,7 @@ interface IDreamManager { void testDream(in ComponentName componentName); boolean isDreaming(); void finishSelf(in IBinder token); + void startDozing(in IBinder token); + void stopDozing(in IBinder token); + IDozeHardware getDozeHardware(in IBinder token); }
\ No newline at end of file diff --git a/core/java/android/service/dreams/IDreamService.aidl b/core/java/android/service/dreams/IDreamService.aidl index 99dc0b7..bd58f1d 100644 --- a/core/java/android/service/dreams/IDreamService.aidl +++ b/core/java/android/service/dreams/IDreamService.aidl @@ -20,6 +20,6 @@ package android.service.dreams; * @hide */ oneway interface IDreamService { - void attach(IBinder windowToken); + void attach(IBinder windowToken, boolean canDoze); void detach(); } diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index 425fdc1..d4b29d8 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -21,6 +21,7 @@ import android.service.notification.StatusBarNotification; /** @hide */ oneway interface INotificationListener { + void onListenerConnected(in String[] notificationKeys); void onNotificationPosted(in StatusBarNotification notification); void onNotificationRemoved(in StatusBarNotification notification); }
\ No newline at end of file diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 2e0e59b..eb2de2c 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -83,6 +83,18 @@ public abstract class NotificationListenerService extends Service { */ public abstract void onNotificationRemoved(StatusBarNotification sbn); + /** + * Implement this method to learn about when the listener is enabled and connected to + * the notification manager. You are safe to call {@link #getActiveNotifications(String[]) + * at this time. + * + * @param notificationKeys The notification keys for all currently posted notifications. + * @hide + */ + public void onListenerConnected(String[] notificationKeys) { + // optional + } + private final INotificationManager getNotificationInterface() { if (mNoMan == null) { mNoMan = INotificationManager.Stub.asInterface( @@ -112,6 +124,7 @@ public abstract class NotificationListenerService extends Service { * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. */ public final void cancelNotification(String pkg, String tag, int id) { + if (!isBound()) return; try { getNotificationInterface().cancelNotificationFromListener(mWrapper, pkg, tag, id); } catch (android.os.RemoteException ex) { @@ -131,8 +144,24 @@ public abstract class NotificationListenerService extends Service { * {@see #cancelNotification(String, String, int)} */ public final void cancelAllNotifications() { + cancelNotifications(null /*all*/); + } + + /** + * Inform the notification manager about dismissal of specific notifications. + * <p> + * Use this if your listener has a user interface that allows the user to dismiss + * multiple notifications at once. + * + * @param keys Notifications to dismiss, or {@code null} to dismiss all. + * + * {@see #cancelNotification(String, String, int)} + * @hide + */ + public final void cancelNotifications(String[] keys) { + if (!isBound()) return; try { - getNotificationInterface().cancelAllNotificationsFromListener(mWrapper); + getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } @@ -140,13 +169,26 @@ public abstract class NotificationListenerService extends Service { /** * Request the list of outstanding notifications (that is, those that are visible to the - * current user). Useful when starting up and you don't know what's already been posted. + * current user). Useful when you don't know what's already been posted. * * @return An array of active notifications. */ public StatusBarNotification[] getActiveNotifications() { + return getActiveNotifications(null /*all*/); + } + + /** + * Request the list of outstanding notifications (that is, those that are visible to the + * current user). Useful when you don't know what's already been posted. + * + * @param keys A specific list of notification keys, or {@code null} for all. + * @return An array of active notifications. + * @hide + */ + public StatusBarNotification[] getActiveNotifications(String[] keys) { + if (!isBound()) return null; try { - return getNotificationInterface().getActiveNotificationsFromListener(mWrapper); + return getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } @@ -161,6 +203,14 @@ public abstract class NotificationListenerService extends Service { return mWrapper; } + private boolean isBound() { + if (mWrapper == null) { + Log.w(TAG, "Notification listener service not yet bound."); + return false; + } + return true; + } + private class INotificationListenerWrapper extends INotificationListener.Stub { @Override public void onNotificationPosted(StatusBarNotification sbn) { @@ -178,5 +228,13 @@ public abstract class NotificationListenerService extends Service { Log.w(TAG, "Error running onNotificationRemoved", t); } } + @Override + public void onListenerConnected(String[] notificationKeys) { + try { + NotificationListenerService.this.onListenerConnected(notificationKeys); + } catch (Throwable t) { + Log.w(TAG, "Error running onListenerConnected", t); + } + } } } diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index b5b9e14..96dd143d 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -29,6 +29,7 @@ public class StatusBarNotification implements Parcelable { private final String pkg; private final int id; private final String tag; + private final String key; private final int uid; private final String basePkg; @@ -70,6 +71,7 @@ public class StatusBarNotification implements Parcelable { this.notification.setUser(user); this.postTime = postTime; + this.key = key(); } public StatusBarNotification(Parcel in) { @@ -88,6 +90,11 @@ public class StatusBarNotification implements Parcelable { this.user = UserHandle.readFromParcel(in); this.notification.setUser(this.user); this.postTime = in.readLong(); + this.key = key(); + } + + private String key() { + return pkg + '|' + basePkg + '|' + id + '|' + tag + '|' + uid; } public void writeToParcel(Parcel out, int flags) { @@ -148,9 +155,9 @@ public class StatusBarNotification implements Parcelable { @Override public String toString() { return String.format( - "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d: %s)", + "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)", this.pkg, this.user, this.id, this.tag, - this.score, this.notification); + this.score, this.key, this.notification); } /** Convenience method to check the notification's flags for @@ -230,4 +237,11 @@ public class StatusBarNotification implements Parcelable { public int getScore() { return score; } + + /** + * A unique instance key for this notification record. + */ + public String getKey() { + return key; + } } diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java index 94aedbd..91c3799 100644 --- a/core/java/android/speech/SpeechRecognizer.java +++ b/core/java/android/speech/SpeechRecognizer.java @@ -409,7 +409,7 @@ public class SpeechRecognizer { * Internal wrapper of IRecognitionListener which will propagate the results to * RecognitionListener */ - private class InternalListener extends IRecognitionListener.Stub { + private static class InternalListener extends IRecognitionListener.Stub { private RecognitionListener mInternalListener; private final static int MSG_BEGINNING_OF_SPEECH = 1; diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java index 77cd71e..6f00707 100644 --- a/core/java/android/text/BoringLayout.java +++ b/core/java/android/text/BoringLayout.java @@ -188,10 +188,6 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback spacing = metrics.descent - metrics.ascent; } - if (spacingmult != 1 || spacingadd != 0) { - spacing = (int)(spacing * spacingmult + spacingadd + 0.5f); - } - mBottom = spacing; if (includepad) { diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index e7d6fda..814326c 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -633,7 +633,11 @@ public class StaticLayout extends Layout { bottom = fm.bottom; } - if (j == 0) { + boolean firstLine = (j == 0); + boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount); + boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd); + + if (firstLine) { if (trackPad) { mTopPadding = top - above; } @@ -642,7 +646,10 @@ public class StaticLayout extends Layout { above = top; } } - if (end == bufEnd) { + + int extra; + + if (lastLine) { if (trackPad) { mBottomPadding = bottom - below; } @@ -652,9 +659,8 @@ public class StaticLayout extends Layout { } } - int extra; - if (needMultiply) { + if (needMultiply && !lastLine) { double ex = (below - above) * (spacingmult - 1) + spacingadd; if (ex >= 0) { extra = (int)(ex + EXTRA_ROUNDING); @@ -691,8 +697,6 @@ public class StaticLayout extends Layout { if (ellipsize != null) { // If there is only one line, then do any type of ellipsis except when it is MARQUEE // if there are multiple lines, just allow END ellipsis on the last line - boolean firstLine = (j == 0); - boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount); boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount); boolean doEllipsis = diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java index deb138d..c1341e1 100644 --- a/core/java/android/text/util/Linkify.java +++ b/core/java/android/text/util/Linkify.java @@ -465,32 +465,39 @@ public class Linkify { String address; int base = 0; - while ((address = WebView.findAddress(string)) != null) { - int start = string.indexOf(address); + try { + while ((address = WebView.findAddress(string)) != null) { + int start = string.indexOf(address); - if (start < 0) { - break; - } + if (start < 0) { + break; + } - LinkSpec spec = new LinkSpec(); - int length = address.length(); - int end = start + length; - - spec.start = base + start; - spec.end = base + end; - string = string.substring(end); - base += end; - - String encodedAddress = null; - - try { - encodedAddress = URLEncoder.encode(address,"UTF-8"); - } catch (UnsupportedEncodingException e) { - continue; - } + LinkSpec spec = new LinkSpec(); + int length = address.length(); + int end = start + length; - spec.url = "geo:0,0?q=" + encodedAddress; - links.add(spec); + spec.start = base + start; + spec.end = base + end; + string = string.substring(end); + base += end; + + String encodedAddress = null; + + try { + encodedAddress = URLEncoder.encode(address,"UTF-8"); + } catch (UnsupportedEncodingException e) { + continue; + } + + spec.url = "geo:0,0?q=" + encodedAddress; + links.add(spec); + } + } catch (UnsupportedOperationException e) { + // findAddress may fail with an unsupported exception on platforms without a WebView. + // In this case, we will not append anything to the links variable: it would have died + // in WebView.findAddress. + return; } } diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index ed45298..931fb81 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -290,18 +290,14 @@ public class TypedValue { return -1; } + /** + * @hide Was accidentally exposed in API level 1 for debugging purposes. + * Kept for compatibility just in case although the debugging code has been removed. + */ + @Deprecated public static float complexToDimensionNoisy(int data, DisplayMetrics metrics) { - float res = complexToDimension(data, metrics); - System.out.println( - "Dimension (0x" + ((data>>TypedValue.COMPLEX_MANTISSA_SHIFT) - & TypedValue.COMPLEX_MANTISSA_MASK) - + "*" + (RADIX_MULTS[(data>>TypedValue.COMPLEX_RADIX_SHIFT) - & TypedValue.COMPLEX_RADIX_MASK] / MANTISSA_MULT) - + ")" + DIMENSION_UNIT_STRS[(data>>COMPLEX_UNIT_SHIFT) - & COMPLEX_UNIT_MASK] - + " = " + res); - return res; + return complexToDimension(data, metrics); } /** diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 7d310a2..c4494f4 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -204,6 +204,36 @@ public final class Display { public static final int TYPE_VIRTUAL = 5; /** + * Display state: The display state is unknown. + * + * @see #getState + */ + public static final int STATE_UNKNOWN = 0; + + /** + * Display state: The display is off. + * + * @see #getState + */ + public static final int STATE_OFF = 1; + + /** + * Display state: The display is on. + * + * @see #getState + */ + public static final int STATE_ON = 2; + + /** + * Display state: The display is dozing in a low-power state; it may be showing + * system-provided content while the device is in a non-interactive state. + * + * @see #getState + * @see android.os.PowerManager#isInteractive + */ + public static final int STATE_DOZING = 3; + + /** * Internal method to create a display. * Applications should use {@link android.view.WindowManager#getDefaultDisplay()} * or {@link android.hardware.display.DisplayManager#getDisplay} @@ -628,6 +658,19 @@ public final class Display { } /** + * Gets the state of the display, such as whether it is on or off. + * + * @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON}, + * {@link #STATE_DOZING}, or {@link #STATE_UNKNOWN}. + */ + public int getState() { + synchronized (this) { + updateDisplayInfoLocked(); + return mIsValid ? mDisplayInfo.state : STATE_UNKNOWN; + } + } + + /** * Returns true if the specified UID has access to this display. * @hide */ @@ -718,5 +761,22 @@ public final class Display { return Integer.toString(type); } } -} + /** + * @hide + */ + public static String stateToString(int state) { + switch (state) { + case STATE_UNKNOWN: + return "UNKNOWN"; + case STATE_OFF: + return "OFF"; + case STATE_ON: + return "ON"; + case STATE_DOZING: + return "DOZING"; + default: + return Integer.toString(state); + } + } +} diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 8944207..5f840d3 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -180,6 +180,11 @@ public final class DisplayInfo implements Parcelable { public float physicalYDpi; /** + * The state of the display, such as {@link android.view.Display#STATE_ON}. + */ + public int state; + + /** * The UID of the application that owns this display, or zero if it is owned by the system. * <p> * If the display is private, then only the owner can use it. @@ -248,6 +253,7 @@ public final class DisplayInfo implements Parcelable { && logicalDensityDpi == other.logicalDensityDpi && physicalXDpi == other.physicalXDpi && physicalYDpi == other.physicalYDpi + && state == other.state && ownerUid == other.ownerUid && Objects.equal(ownerPackageName, other.ownerPackageName); } @@ -280,6 +286,7 @@ public final class DisplayInfo implements Parcelable { logicalDensityDpi = other.logicalDensityDpi; physicalXDpi = other.physicalXDpi; physicalYDpi = other.physicalYDpi; + state = other.state; ownerUid = other.ownerUid; ownerPackageName = other.ownerPackageName; } @@ -307,6 +314,7 @@ public final class DisplayInfo implements Parcelable { logicalDensityDpi = source.readInt(); physicalXDpi = source.readFloat(); physicalYDpi = source.readFloat(); + state = source.readInt(); ownerUid = source.readInt(); ownerPackageName = source.readString(); } @@ -335,6 +343,7 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(logicalDensityDpi); dest.writeFloat(physicalXDpi); dest.writeFloat(physicalYDpi); + dest.writeInt(state); dest.writeInt(ownerUid); dest.writeString(ownerPackageName); } @@ -431,7 +440,7 @@ public final class DisplayInfo implements Parcelable { sb.append(smallestNominalAppHeight); sb.append(", "); sb.append(refreshRate); - sb.append(" fps, rotation"); + sb.append(" fps, rotation "); sb.append(rotation); sb.append(", density "); sb.append(logicalDensityDpi); @@ -446,6 +455,8 @@ public final class DisplayInfo implements Parcelable { if (address != null) { sb.append(", address ").append(address); } + sb.append(", state "); + sb.append(Display.stateToString(state)); if (ownerUid != 0 || ownerPackageName != null) { sb.append(", owner ").append(ownerPackageName); sb.append(" (uid ").append(ownerUid).append(")"); diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 8ec07ef..3670eed 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -50,7 +50,6 @@ oneway interface IWindow { void moved(int newX, int newY); void dispatchAppVisibility(boolean visible); void dispatchGetNewSurface(); - void dispatchScreenState(boolean on); /** * Tell the window that it is either gaining or losing focus. Keep it up diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index c92a104..905cfda 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -215,12 +215,6 @@ interface IWindowManager oneway void statusBarVisibilityChanged(int visibility); /** - * Block until the given window has been drawn to the screen. - * Returns true if really waiting, false if the window does not exist. - */ - boolean waitForWindowDrawn(IBinder token, in IRemoteCallback callback); - - /** * Device has a software navigation bar (separate from the status bar). */ boolean hasNavigationBar(); diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index e829116..88c722b 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -17,6 +17,7 @@ package android.view; import android.content.Context; +import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputManager; import android.os.Parcel; import android.os.Parcelable; @@ -49,6 +50,7 @@ public final class InputDevice implements Parcelable { private final int mVendorId; private final int mProductId; private final String mDescriptor; + private final InputDeviceIdentifier mIdentifier; private final boolean mIsExternal; private final int mSources; private final int mKeyboardType; @@ -61,7 +63,7 @@ public final class InputDevice implements Parcelable { /** * A mask for input source classes. - * + * * Each distinct input source constant has one or more input source class bits set to * specify the desired interpretation for its input events. */ @@ -77,46 +79,46 @@ public final class InputDevice implements Parcelable { /** * The input source has buttons or keys. * Examples: {@link #SOURCE_KEYBOARD}, {@link #SOURCE_DPAD}. - * + * * A {@link KeyEvent} should be interpreted as a button or key press. - * + * * Use {@link #getKeyCharacterMap} to query the device's button and key mappings. */ public static final int SOURCE_CLASS_BUTTON = 0x00000001; - + /** * The input source is a pointing device associated with a display. * Examples: {@link #SOURCE_TOUCHSCREEN}, {@link #SOURCE_MOUSE}. - * + * * A {@link MotionEvent} should be interpreted as absolute coordinates in * display units according to the {@link View} hierarchy. Pointer down/up indicated when * the finger touches the display or when the selection button is pressed/released. - * + * * Use {@link #getMotionRange} to query the range of the pointing device. Some devices permit * touches outside the display area so the effective range may be somewhat smaller or larger * than the actual display size. */ public static final int SOURCE_CLASS_POINTER = 0x00000002; - + /** * The input source is a trackball navigation device. * Examples: {@link #SOURCE_TRACKBALL}. - * + * * A {@link MotionEvent} should be interpreted as relative movements in device-specific * units used for navigation purposes. Pointer down/up indicates when the selection button * is pressed/released. - * + * * Use {@link #getMotionRange} to query the range of motion. */ public static final int SOURCE_CLASS_TRACKBALL = 0x00000004; - + /** * The input source is an absolute positioning device not associated with a display * (unlike {@link #SOURCE_CLASS_POINTER}). - * + * * A {@link MotionEvent} should be interpreted as absolute coordinates in * device-specific surface units. - * + * * Use {@link #getMotionRange} to query the range of positions. */ public static final int SOURCE_CLASS_POSITION = 0x00000008; @@ -134,7 +136,7 @@ public final class InputDevice implements Parcelable { * The input source is unknown. */ public static final int SOURCE_UNKNOWN = 0x00000000; - + /** * The input source is a keyboard. * @@ -145,10 +147,10 @@ public final class InputDevice implements Parcelable { * @see #SOURCE_CLASS_BUTTON */ public static final int SOURCE_KEYBOARD = 0x00000100 | SOURCE_CLASS_BUTTON; - + /** * The input source is a DPad. - * + * * @see #SOURCE_CLASS_BUTTON */ public static final int SOURCE_DPAD = 0x00000200 | SOURCE_CLASS_BUTTON; @@ -163,16 +165,16 @@ public final class InputDevice implements Parcelable { /** * The input source is a touch screen pointing device. - * + * * @see #SOURCE_CLASS_POINTER */ public static final int SOURCE_TOUCHSCREEN = 0x00001000 | SOURCE_CLASS_POINTER; - + /** * The input source is a mouse pointing device. * This code is also used for other mouse-like pointing devices such as trackpads * and trackpoints. - * + * * @see #SOURCE_CLASS_POINTER */ public static final int SOURCE_MOUSE = 0x00002000 | SOURCE_CLASS_POINTER; @@ -199,15 +201,15 @@ public final class InputDevice implements Parcelable { /** * The input source is a trackball. - * + * * @see #SOURCE_CLASS_TRACKBALL */ public static final int SOURCE_TRACKBALL = 0x00010000 | SOURCE_CLASS_TRACKBALL; - + /** * The input source is a touch pad or digitizer tablet that is not * associated with a display (unlike {@link #SOURCE_TOUCHSCREEN}). - * + * * @see #SOURCE_CLASS_POSITION */ public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION; @@ -239,7 +241,7 @@ public final class InputDevice implements Parcelable { /** * Constant for retrieving the range of values for {@link MotionEvent#AXIS_X}. - * + * * @see #getMotionRange * @deprecated Use {@link MotionEvent#AXIS_X} instead. */ @@ -248,7 +250,7 @@ public final class InputDevice implements Parcelable { /** * Constant for retrieving the range of values for {@link MotionEvent#AXIS_Y}. - * + * * @see #getMotionRange * @deprecated Use {@link MotionEvent#AXIS_Y} instead. */ @@ -257,7 +259,7 @@ public final class InputDevice implements Parcelable { /** * Constant for retrieving the range of values for {@link MotionEvent#AXIS_PRESSURE}. - * + * * @see #getMotionRange * @deprecated Use {@link MotionEvent#AXIS_PRESSURE} instead. */ @@ -266,7 +268,7 @@ public final class InputDevice implements Parcelable { /** * Constant for retrieving the range of values for {@link MotionEvent#AXIS_SIZE}. - * + * * @see #getMotionRange * @deprecated Use {@link MotionEvent#AXIS_SIZE} instead. */ @@ -275,7 +277,7 @@ public final class InputDevice implements Parcelable { /** * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOUCH_MAJOR}. - * + * * @see #getMotionRange * @deprecated Use {@link MotionEvent#AXIS_TOUCH_MAJOR} instead. */ @@ -284,7 +286,7 @@ public final class InputDevice implements Parcelable { /** * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOUCH_MINOR}. - * + * * @see #getMotionRange * @deprecated Use {@link MotionEvent#AXIS_TOUCH_MINOR} instead. */ @@ -293,7 +295,7 @@ public final class InputDevice implements Parcelable { /** * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOOL_MAJOR}. - * + * * @see #getMotionRange * @deprecated Use {@link MotionEvent#AXIS_TOOL_MAJOR} instead. */ @@ -302,7 +304,7 @@ public final class InputDevice implements Parcelable { /** * Constant for retrieving the range of values for {@link MotionEvent#AXIS_TOOL_MINOR}. - * + * * @see #getMotionRange * @deprecated Use {@link MotionEvent#AXIS_TOOL_MINOR} instead. */ @@ -311,24 +313,24 @@ public final class InputDevice implements Parcelable { /** * Constant for retrieving the range of values for {@link MotionEvent#AXIS_ORIENTATION}. - * + * * @see #getMotionRange * @deprecated Use {@link MotionEvent#AXIS_ORIENTATION} instead. */ @Deprecated public static final int MOTION_RANGE_ORIENTATION = MotionEvent.AXIS_ORIENTATION; - + /** * There is no keyboard. */ public static final int KEYBOARD_TYPE_NONE = 0; - + /** * The keyboard is not fully alphabetic. It may be a numeric keypad or an assortment * of buttons that are not mapped as alphabetic keys suitable for text input. */ public static final int KEYBOARD_TYPE_NON_ALPHABETIC = 1; - + /** * The keyboard supports a complement of alphabetic keys. */ @@ -361,6 +363,7 @@ public final class InputDevice implements Parcelable { mKeyCharacterMap = keyCharacterMap; mHasVibrator = hasVibrator; mHasButtonUnderPad = hasButtonUnderPad; + mIdentifier = new InputDeviceIdentifier(descriptor, vendorId, productId); } private InputDevice(Parcel in) { @@ -377,6 +380,7 @@ public final class InputDevice implements Parcelable { mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in); mHasVibrator = in.readInt() != 0; mHasButtonUnderPad = in.readInt() != 0; + mIdentifier = new InputDeviceIdentifier(mDescriptor, mVendorId, mProductId); for (;;) { int axis = in.readInt(); @@ -396,7 +400,7 @@ public final class InputDevice implements Parcelable { public static InputDevice getDevice(int id) { return InputManager.getInstance().getInputDevice(id); } - + /** * Gets the ids of all input devices in the system. * @return The input device ids. @@ -441,6 +445,18 @@ public final class InputDevice implements Parcelable { } /** + * The set of identifying information for type of input device. This + * information can be used by the system to configure appropriate settings + * for the device. + * + * @return The identifier object for this device + * @hide + */ + public InputDeviceIdentifier getIdentifier() { + return mIdentifier; + } + + /** * Gets a generation number for this input device. * The generation number is incremented whenever the device is reconfigured and its * properties may have changed. @@ -553,7 +569,7 @@ public final class InputDevice implements Parcelable { public String getName() { return mName; } - + /** * Gets the input sources supported by this input device as a combined bitfield. * @return The supported input sources. @@ -561,7 +577,20 @@ public final class InputDevice implements Parcelable { public int getSources() { return mSources; } - + + /** + * Determines whether the input device supports the given source or sources. + * + * @param source The input source or sources to check against. This can be a generic device + * type such as {@link InputDevice#SOURCE_MOUSE}, a more generic device class, such as + * {@link InputDevice#SOURCE_CLASS_POINTER}, or a combination of sources bitwise ORed together. + * @return Whether the device can produce all of the given sources. + * @hide + */ + public boolean supportsSource(int source) { + return (mSources & source) == source; + } + /** * Gets the keyboard type. * @return The keyboard type. @@ -569,7 +598,7 @@ public final class InputDevice implements Parcelable { public int getKeyboardType() { return mKeyboardType; } - + /** * Gets the key character map associated with this input device. * @return The key character map. diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 5a5fc10..214fd12 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -629,11 +629,19 @@ public class KeyEvent extends InputEvent implements Parcelable { /** Key code constant: Brightness Up key. * Adjusts the screen brightness up. */ public static final int KEYCODE_BRIGHTNESS_UP = 221; - /** Key code constant: Audio Track key + /** Key code constant: Audio Track key. * Switches the audio tracks. */ public static final int KEYCODE_MEDIA_AUDIO_TRACK = 222; + /** Key code constant: Sleep key. + * Puts the device to sleep. Behaves somewhat like {@link #KEYCODE_POWER} but it + * has no effect if the device is already asleep. */ + public static final int KEYCODE_SLEEP = 223; + /** Key code constant: Wakeup key. + * Wakes up the device. Behaves somewhat like {@link #KEYCODE_POWER} but it + * has no effect if the device is already awake. */ + public static final int KEYCODE_WAKEUP = 224; - private static final int LAST_KEYCODE = KEYCODE_MEDIA_AUDIO_TRACK; + private static final int LAST_KEYCODE = KEYCODE_WAKEUP; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -878,6 +886,8 @@ public class KeyEvent extends InputEvent implements Parcelable { names.append(KEYCODE_BRIGHTNESS_DOWN, "KEYCODE_BRIGHTNESS_DOWN"); names.append(KEYCODE_BRIGHTNESS_UP, "KEYCODE_BRIGHTNESS_UP"); names.append(KEYCODE_MEDIA_AUDIO_TRACK, "KEYCODE_MEDIA_AUDIO_TRACK"); + names.append(KEYCODE_SLEEP, "KEYCODE_SLEEP"); + names.append(KEYCODE_WAKEUP, "KEYCODE_WAKEUP"); }; // Symbolic names of all metakeys in bit order from least significant to most significant. @@ -1157,9 +1167,13 @@ public class KeyEvent extends InputEvent implements Parcelable { /** * This mask is set if the device woke because of this key event. + * + * @deprecated This flag will never be set by the system since the system + * consumes all wake keys itself. */ + @Deprecated public static final int FLAG_WOKE_HERE = 0x1; - + /** * This mask is set if the key event was generated by a software keyboard. */ @@ -1837,13 +1851,34 @@ public class KeyEvent extends InputEvent implements Parcelable { } } - /** Whether key will, by default, trigger a click on the focused view. + /** + * Returns true if the key event should be treated as a confirming action. + * @return True for a confirmation key, such as {@link #KEYCODE_DPAD_CENTER}, + * {@link #KEYCODE_ENTER}, or {@link #KEYCODE_BUTTON_A}. * @hide */ - public static final boolean isConfirmKey(int keyCode) { - switch (keyCode) { + public final boolean isConfirmKey() { + switch (mKeyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_BUTTON_A: + return true; + default: + return false; + } + } + + /** + * Returns true if the key event should be treated as a cancelling action. + * @return True for a cancellation key, such as {@link #KEYCODE_ESCAPE}, + * {@link #KEYCODE_BACK}, or {@link #KEYCODE_BUTTON_B}. + * @hide + */ + public final boolean isCancelKey() { + switch (mKeyCode) { + case KeyEvent.KEYCODE_BUTTON_B: + case KeyEvent.KEYCODE_ESCAPE: + case KeyEvent.KEYCODE_BACK: return true; default: return false; diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java index a7ee12b..71296fa 100644 --- a/core/java/android/view/MenuInflater.java +++ b/core/java/android/view/MenuInflater.java @@ -161,6 +161,7 @@ public class MenuInflater { } else if (tagName.equals(XML_MENU)) { // A menu start tag denotes a submenu for an item SubMenu subMenu = menuState.addSubMenuItem(); + registerMenu(subMenu, attrs); // Parse the submenu into returned SubMenu parseMenu(parser, attrs, subMenu); @@ -183,9 +184,9 @@ public class MenuInflater { if (!menuState.hasAddedItem()) { if (menuState.itemActionProvider != null && menuState.itemActionProvider.hasSubMenu()) { - menuState.addSubMenuItem(); + registerMenu(menuState.addSubMenuItem(), attrs); } else { - menuState.addItem(); + registerMenu(menuState.addItem(), attrs); } } } else if (tagName.equals(XML_MENU)) { @@ -200,7 +201,30 @@ public class MenuInflater { eventType = parser.next(); } } - + + /** + * The method is a hook for layoutlib to do its magic. + * Nothing is needed outside of LayoutLib. However, it should not be deleted because it + * appears to do nothing. + */ + private void registerMenu(@SuppressWarnings("unused") MenuItem item, + @SuppressWarnings("unused") AttributeSet set) { + } + + /** + * The method is a hook for layoutlib to do its magic. + * Nothing is needed outside of LayoutLib. However, it should not be deleted because it + * appears to do nothing. + */ + private void registerMenu(@SuppressWarnings("unused") SubMenu subMenu, + @SuppressWarnings("unused") AttributeSet set) { + } + + // Needed by layoutlib. + /*package*/ Context getContext() { + return mContext; + } + private static class InflatedOnMenuItemClickListener implements MenuItem.OnMenuItemClickListener { private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class }; @@ -446,9 +470,11 @@ public class MenuInflater { } } - public void addItem() { + public MenuItem addItem() { itemAdded = true; - setItem(menu.add(groupId, itemId, itemCategoryOrder, itemTitle)); + MenuItem item = menu.add(groupId, itemId, itemCategoryOrder, itemTitle); + setItem(item); + return item; } public SubMenu addSubMenuItem() { diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index ddce3ce..6378ffd 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -3372,11 +3372,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { throw new IllegalArgumentException("Axis out of range."); } final long bits = mPackedAxisBits; - final long axisBit = 1L << axis; + final long axisBit = 0x8000000000000000L >>> axis; if ((bits & axisBit) == 0) { return 0; } - final int index = Long.bitCount(bits & (axisBit - 1L)); + final int index = Long.bitCount(bits & ~(0xFFFFFFFFFFFFFFFFL >>> axis)); return mPackedAxisValues[index]; } } @@ -3425,8 +3425,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { throw new IllegalArgumentException("Axis out of range."); } final long bits = mPackedAxisBits; - final long axisBit = 1L << axis; - final int index = Long.bitCount(bits & (axisBit - 1L)); + final long axisBit = 0x8000000000000000L >>> axis; + final int index = Long.bitCount(bits & ~(0xFFFFFFFFFFFFFFFFL >>> axis)); float[] values = mPackedAxisValues; if ((bits & axisBit) == 0) { if (values == null) { diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 62afa60..eea5884 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -79,9 +79,6 @@ public class SurfaceControl { private final String mName; long mNativeObject; // package visibility only for Surface.java access - private static final boolean HEADLESS = "1".equals( - SystemProperties.get("ro.config.headless", "0")); - /* flags used in constructor (keep in sync with ISurfaceComposerClient.h) */ /** @@ -106,18 +103,18 @@ public class SurfaceControl { * surfaces are pre-multiplied, which means that each color component is * already multiplied by its alpha value. In this case the blending * equation used is: - * - * DEST = SRC + DEST * (1-SRC_ALPHA) - * + * <p> + * <code>DEST = SRC + DEST * (1-SRC_ALPHA)</code> + * <p> * By contrast, non pre-multiplied surfaces use the following equation: - * - * DEST = SRC * SRC_ALPHA * DEST * (1-SRC_ALPHA) - * + * <p> + * <code>DEST = SRC * SRC_ALPHA * DEST * (1-SRC_ALPHA)</code> + * <p> * pre-multiplied surfaces must always be used if transparent pixels are * composited on top of each-other into the surface. A pre-multiplied * surface can never lower the value of the alpha component of a given * pixel. - * + * <p> * In some rare situations, a non pre-multiplied surface is preferable. * */ @@ -128,7 +125,17 @@ public class SurfaceControl { * even if its pixel format is set to translucent. This can be useful if an * application needs full RGBA 8888 support for instance but will * still draw every pixel opaque. - * + * <p> + * This flag is ignored if setAlpha() is used to make the surface non-opaque. + * Combined effects are (assuming a buffer format with an alpha channel): + * <ul> + * <li>OPAQUE + alpha(1.0) == opaque composition + * <li>OPAQUE + alpha(0.x) == blended composition + * <li>!OPAQUE + alpha(1.0) == blended composition + * <li>!OPAQUE + alpha(0.x) == blended composition + * </ul> + * If the underlying buffer lacks an alpha channel, the OPAQUE flag is effectively + * set automatically. */ public static final int OPAQUE = 0x00000400; @@ -169,9 +176,16 @@ public class SurfaceControl { /** * Surface flag: Hide the surface. * Equivalent to calling hide(). + * Updates the value set during Surface creation (see {@link #HIDDEN}). */ public static final int SURFACE_HIDDEN = 0x01; + /** + * Surface flag: composite without blending when possible. + * Updates the value set during Surface creation (see {@link #OPAQUE}). + */ + public static final int SURFACE_OPAQUE = 0x02; + /* built-in physical display ids (keep in sync with ISurfaceComposer.h) * these are different from the logical display ids used elsewhere in the framework */ @@ -192,14 +206,14 @@ public class SurfaceControl { /** * Create a surface with a name. - * + * <p> * The surface creation flags specify what kind of surface to create and * certain options such as whether the surface can be assumed to be opaque * and whether it should be initially hidden. Surfaces should always be * created with the {@link #HIDDEN} flag set to ensure that they are not * made visible prematurely before all of the surface's properties have been * configured. - * + * <p> * Good practice is to first create the surface with the {@link #HIDDEN} flag * specified, open a transaction, set the surface layer, layer stack, alpha, * and position, call {@link #show} if appropriate, and close the transaction. @@ -232,8 +246,6 @@ public class SurfaceControl { new Throwable()); } - checkHeadless(); - mName = name; mNativeObject = nativeCreate(session, name, w, h, format, flags); if (mNativeObject == 0) { @@ -344,6 +356,10 @@ public class SurfaceControl { nativeSetTransparentRegionHint(mNativeObject, region); } + /** + * Sets an alpha value for the entire Surface. This value is combined with the + * per-pixel alpha. It may be used with opaque Surfaces. + */ public void setAlpha(float alpha) { checkNotReleased(); nativeSetAlpha(mNativeObject, alpha); @@ -354,6 +370,13 @@ public class SurfaceControl { nativeSetMatrix(mNativeObject, dsdx, dtdx, dsdy, dtdy); } + /** + * Sets and clears flags, such as {@link #SURFACE_HIDDEN}. The new value will be: + * <p> + * <code>newFlags = (oldFlags & ~mask) | (flags & mask)</code> + * <p> + * Note this does not take the same set of flags as the constructor. + */ public void setFlags(int flags, int mask) { checkNotReleased(); nativeSetFlags(mNativeObject, flags, mask); @@ -374,6 +397,19 @@ public class SurfaceControl { nativeSetLayerStack(mNativeObject, layerStack); } + /** + * Sets the opacity of the surface. Setting the flag is equivalent to creating the + * Surface with the {@link #OPAQUE} flag. + */ + public void setOpaque(boolean isOpaque) { + checkNotReleased(); + if (isOpaque) { + nativeSetFlags(mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE); + } else { + nativeSetFlags(mNativeObject, 0, SURFACE_OPAQUE); + } + } + /* * set display parameters. * needs to be inside open/closeTransaction block @@ -619,10 +655,4 @@ public class SurfaceControl { } nativeScreenshot(display, consumer, width, height, minLayer, maxLayer, allLayers); } - - private static void checkHeadless() { - if (HEADLESS) { - throw new UnsupportedOperationException("Device is headless"); - } - } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ef60755..872cbe7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2269,6 +2269,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, static final int PFLAG3_CALLED_SUPER = 0x10; + /** + * Flag indicating that we're in the process of applying window insets. + */ + static final int PFLAG3_APPLYING_INSETS = 0x40; + + /** + * Flag indicating that we're in the process of fitting system windows using the old method. + */ + static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x80; + /* End of masks for mPrivateFlags3 */ static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED; @@ -3178,6 +3188,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private OnDragListener mOnDragListener; private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener; + + OnApplyWindowInsetsListener mOnApplyWindowInsetsListener; } ListenerInfo mListenerInfo; @@ -5903,8 +5915,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #getFitsSystemWindows() * @see #setFitsSystemWindows(boolean) * @see #setSystemUiVisibility(int) + * + * @deprecated As of API XX use {@link #dispatchApplyWindowInsets(WindowInsets)} to apply + * insets to views. Views should override {@link #onApplyWindowInsets(WindowInsets)} or use + * {@link #setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener)} + * to implement handling their own insets. */ protected boolean fitSystemWindows(Rect insets) { + if ((mPrivateFlags3 & PFLAG3_APPLYING_INSETS) == 0) { + // If we're not in the process of dispatching the newer apply insets call, + // that means we're not in the compatibility path. Dispatch into the newer + // apply insets path and take things from there. + try { + mPrivateFlags3 |= PFLAG3_FITTING_SYSTEM_WINDOWS; + return !dispatchApplyWindowInsets(new WindowInsets(insets)).hasInsets(); + } finally { + mPrivateFlags3 &= PFLAG3_FITTING_SYSTEM_WINDOWS; + } + } else { + // We're being called from the newer apply insets path. + // Perform the standard fallback behavior. + return fitSystemWindowsInt(insets); + } + } + + private boolean fitSystemWindowsInt(Rect insets) { if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) { mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; @@ -5924,6 +5959,97 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Called when the view should apply {@link WindowInsets} according to its internal policy. + * + * <p>This method should be overridden by views that wish to apply a policy different from or + * in addition to the default behavior. Clients that wish to force a view subtree + * to apply insets should call {@link #dispatchApplyWindowInsets(WindowInsets)}.</p> + * + * <p>Clients may supply an {@link OnApplyWindowInsetsListener} to a view. If one is set + * it will be called during dispatch instead of this method. The listener may optionally + * call this method from its own implementation if it wishes to apply the view's default + * insets policy in addition to its own.</p> + * + * <p>Implementations of this method should either return the insets parameter unchanged + * or a new {@link WindowInsets} cloned from the supplied insets with any insets consumed + * that this view applied itself. This allows new inset types added in future platform + * versions to pass through existing implementations unchanged without being erroneously + * consumed.</p> + * + * <p>By default if a view's {@link #setFitsSystemWindows(boolean) fitsSystemWindows} + * property is set then the view will consume the system window insets and apply them + * as padding for the view.</p> + * + * @param insets Insets to apply + * @return The supplied insets with any applied insets consumed + */ + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + if ((mPrivateFlags3 & PFLAG3_FITTING_SYSTEM_WINDOWS) == 0) { + // We weren't called from within a direct call to fitSystemWindows, + // call into it as a fallback in case we're in a class that overrides it + // and has logic to perform. + if (fitSystemWindows(insets.getSystemWindowInsets())) { + return insets.consumeSystemWindowInsets(); + } + } else { + // We were called from within a direct call to fitSystemWindows. + if (fitSystemWindowsInt(insets.getSystemWindowInsets())) { + return insets.consumeSystemWindowInsets(); + } + } + return insets; + } + + /** + * Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying + * window insets to this view. The listener's + * {@link OnApplyWindowInsetsListener#onApplyWindowInsets(View, WindowInsets) onApplyWindowInsets} + * method will be called instead of the view's + * {@link #onApplyWindowInsets(WindowInsets) onApplyWindowInsets} method. + * + * @param listener Listener to set + * + * @see #onApplyWindowInsets(WindowInsets) + */ + public void setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener listener) { + getListenerInfo().mOnApplyWindowInsetsListener = listener; + } + + /** + * Request to apply the given window insets to this view or another view in its subtree. + * + * <p>This method should be called by clients wishing to apply insets corresponding to areas + * obscured by window decorations or overlays. This can include the status and navigation bars, + * action bars, input methods and more. New inset categories may be added in the future. + * The method returns the insets provided minus any that were applied by this view or its + * children.</p> + * + * <p>Clients wishing to provide custom behavior should override the + * {@link #onApplyWindowInsets(WindowInsets)} method or alternatively provide a + * {@link OnApplyWindowInsetsListener} via the + * {@link #setOnApplyWindowInsetsListener(View.OnApplyWindowInsetsListener) setOnApplyWindowInsetsListener} + * method.</p> + * + * <p>This method replaces the older {@link #fitSystemWindows(Rect) fitSystemWindows} method. + * </p> + * + * @param insets Insets to apply + * @return The provided insets minus the insets that were consumed + */ + public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { + try { + mPrivateFlags3 |= PFLAG3_APPLYING_INSETS; + if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) { + return mListenerInfo.mOnApplyWindowInsetsListener.onApplyWindowInsets(this, insets); + } else { + return onApplyWindowInsets(insets); + } + } finally { + mPrivateFlags3 &= ~PFLAG3_APPLYING_INSETS; + } + } + + /** * @hide Compute the insets that should be consumed by this view and the ones * that should propagate to those under it. */ @@ -5995,6 +6121,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Ask that a new dispatch of {@link #fitSystemWindows(Rect)} be performed. + * @deprecated Use {@link #requestApplyInsets()} for newer platform versions. */ public void requestFitSystemWindows() { if (mParent != null) { @@ -6003,6 +6130,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Ask that a new dispatch of {@link #onApplyWindowInsets(WindowInsets)} be performed. + */ + public void requestApplyInsets() { + requestFitSystemWindows(); + } + + /** * For use by PhoneWindow to make its own system window fitting optional. * @hide */ @@ -8186,7 +8320,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public boolean onKeyDown(int keyCode, KeyEvent event) { boolean result = false; - if (KeyEvent.isConfirmKey(keyCode)) { + if (event.isConfirmKey()) { if ((mViewFlags & ENABLED_MASK) == DISABLED) { return true; } @@ -8228,7 +8362,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param event The KeyEvent object that defines the button action. */ public boolean onKeyUp(int keyCode, KeyEvent event) { - if (KeyEvent.isConfirmKey(keyCode)) { + if (event.isConfirmKey()) { if ((mViewFlags & ENABLED_MASK) == DISABLED) { return true; } @@ -9493,6 +9627,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // View was rejected last time it was drawn by its parent; this may have changed invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9544,6 +9679,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // View was rejected last time it was drawn by its parent; this may have changed invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9595,6 +9731,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // View was rejected last time it was drawn by its parent; this may have changed invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9638,6 +9775,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // View was rejected last time it was drawn by its parent; this may have changed invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9681,6 +9819,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // View was rejected last time it was drawn by its parent; this may have changed invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9866,6 +10005,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mDisplayList != null) { mDisplayList.setAlpha(getFinalAlpha()); } + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } } @@ -10299,6 +10440,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // View was rejected last time it was drawn by its parent; this may have changed invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10340,6 +10482,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // View was rejected last time it was drawn by its parent; this may have changed invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10486,6 +10629,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10534,6 +10678,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -16499,7 +16644,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { long value = mMeasureCache.valueAt(cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed - setMeasuredDimension((int) (value >> 32), (int) value); + setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } @@ -16594,6 +16739,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback, measuredWidth += optical ? opticalWidth : -opticalWidth; measuredHeight += optical ? opticalHeight : -opticalHeight; } + setMeasuredDimensionRaw(measuredWidth, measuredHeight); + } + + /** + * Sets the measured dimension without extra processing for things like optical bounds. + * Useful for reapplying consistent values that have already been cooked with adjustments + * for optical bounds, etc. such as those from the measurement cache. + * + * @param measuredWidth The measured width of this view. May be a complex + * bit mask as defined by {@link #MEASURED_SIZE_MASK} and + * {@link #MEASURED_STATE_TOO_SMALL}. + * @param measuredHeight The measured height of this view. May be a complex + * bit mask as defined by {@link #MEASURED_SIZE_MASK} and + * {@link #MEASURED_STATE_TOO_SMALL}. + */ + private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) { mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; @@ -16824,8 +16985,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // If the screen is off assume the animation start time is now instead of // the next frame we draw. Keeping the START_ON_FIRST_FRAME start time // would cause the animation to start when the screen turns back on - if (mAttachInfo != null && !mAttachInfo.mScreenOn && - animation.getStartTime() == Animation.START_ON_FIRST_FRAME) { + if (mAttachInfo != null && mAttachInfo.mDisplayState == Display.STATE_OFF + && animation.getStartTime() == Animation.START_ON_FIRST_FRAME) { animation.setStartTime(AnimationUtils.currentAnimationTimeMillis()); } animation.reset(); @@ -18361,7 +18522,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } static int adjust(int measureSpec, int delta) { - return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec)); + final int mode = getMode(measureSpec); + if (mode == UNSPECIFIED) { + // No need to adjust size for UNSPECIFIED mode. + return makeMeasureSpec(0, UNSPECIFIED); + } + int size = getSize(measureSpec) + delta; + if (size < 0) { + Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size + + ") spec: " + toString(measureSpec) + " delta: " + delta); + size = 0; + } + return makeMeasureSpec(size, mode); } /** @@ -18641,6 +18813,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void onViewDetachedFromWindow(View v); } + /** + * Listener for applying window insets on a view in a custom way. + * + * <p>Apps may choose to implement this interface if they want to apply custom policy + * to the way that window insets are treated for a view. If an OnApplyWindowInsetsListener + * is set, its + * {@link OnApplyWindowInsetsListener#onApplyWindowInsets(View, WindowInsets) onApplyWindowInsets} + * method will be called instead of the View's own + * {@link #onApplyWindowInsets(WindowInsets) onApplyWindowInsets} method. The listener + * may optionally call the parameter View's <code>onApplyWindowInsets</code> method to apply + * the View's normal behavior as part of its own.</p> + */ + public interface OnApplyWindowInsetsListener { + /** + * When {@link View#setOnApplyWindowInsetsListener(View.OnApplyWindowInsetsListener) set} + * on a View, this listener method will be called instead of the view's own + * {@link View#onApplyWindowInsets(WindowInsets) onApplyWindowInsets} method. + * + * @param v The view applying window insets + * @param insets The insets to apply + * @return The insets supplied, minus any insets that were consumed + */ + public WindowInsets onApplyWindowInsets(View v, WindowInsets insets); + } + private final class UnsetPressedState implements Runnable { public void run() { setPressed(false); @@ -18686,7 +18883,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * A set of information given to a view when it is attached to its parent * window. */ - static class AttachInfo { + final static class AttachInfo { interface Callbacks { void playSoundEffect(int effectId); boolean performHapticFeedback(int effectId, boolean always); @@ -18752,7 +18949,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean mHardwareAccelerationRequested; HardwareRenderer mHardwareRenderer; - boolean mScreenOn; + /** + * The state of the display to which the window is attached, as reported + * by {@link Display#getState()}. Note that the display state constants + * declared by {@link Display} do not exactly line up with the screen state + * constants declared by {@link View} (there are more display states than + * screen states). + */ + int mDisplayState = Display.STATE_UNKNOWN; /** * Scale factor used by the compatibility mode diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index e67659c..ad64ca7 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -234,6 +234,7 @@ public class ViewConfiguration { private final int mOverscrollDistance; private final int mOverflingDistance; private final boolean mFadingMarqueeEnabled; + private final long mGlobalActionsKeyTimeout; private boolean sHasPermanentMenuKey; private boolean sHasPermanentMenuKeySet; @@ -261,6 +262,7 @@ public class ViewConfiguration { mOverscrollDistance = OVERSCROLL_DISTANCE; mOverflingDistance = OVERFLING_DISTANCE; mFadingMarqueeEnabled = true; + mGlobalActionsKeyTimeout = GLOBAL_ACTIONS_KEY_TIMEOUT; } /** @@ -287,8 +289,6 @@ public class ViewConfiguration { mEdgeSlop = (int) (sizeAndDensity * EDGE_SLOP + 0.5f); mFadingEdgeLength = (int) (sizeAndDensity * FADING_EDGE_LENGTH + 0.5f); - mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f); - mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f); mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f); mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f); mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f); @@ -339,6 +339,13 @@ public class ViewConfiguration { mPagingTouchSlop = mTouchSlop * 2; mDoubleTapTouchSlop = mTouchSlop; + + mMinimumFlingVelocity = res.getDimensionPixelSize( + com.android.internal.R.dimen.config_viewMinFlingVelocity); + mMaximumFlingVelocity = res.getDimensionPixelSize( + com.android.internal.R.dimen.config_viewMaxFlingVelocity); + mGlobalActionsKeyTimeout = res.getInteger( + com.android.internal.R.integer.config_globalActionsKeyTimeout); } /** @@ -695,12 +702,26 @@ public class ViewConfiguration { * * @return how long a user needs to press the relevant key to bring up * the global actions dialog. + * @deprecated This timeout should not be used by applications */ + @Deprecated public static long getGlobalActionKeyTimeout() { return GLOBAL_ACTIONS_KEY_TIMEOUT; } /** + * The amount of time a user needs to press the relevant key to bring up + * the global actions dialog. + * + * @return how long a user needs to press the relevant key to bring up + * the global actions dialog. + * @hide + */ + public long getDeviceGlobalActionKeyTimeout() { + return mGlobalActionsKeyTimeout; + } + + /** * The amount of friction applied to scrolls and flings. * * @return A scalar dimensionless value representing the coefficient of diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 9414237..dda5a60 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -463,13 +463,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager public ViewGroup(Context context, AttributeSet attrs) { super(context, attrs); initViewGroup(); - initFromAttributes(context, attrs); + initFromAttributes(context, attrs, 0); } public ViewGroup(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initViewGroup(); - initFromAttributes(context, attrs); + initFromAttributes(context, attrs, defStyle); } private boolean debugDraw() { @@ -499,9 +499,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE; } - private void initFromAttributes(Context context, AttributeSet attrs) { - TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.ViewGroup); + private void initFromAttributes(Context context, AttributeSet attrs, int defStyle) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewGroup, defStyle, 0); final int N = a.getIndexCount(); for (int i = 0; i < N; i++) { @@ -4574,6 +4573,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (invalidate) { invalidateViewProperty(false, false); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } /** @@ -5430,21 +5430,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - @Override - protected boolean fitSystemWindows(Rect insets) { - boolean done = super.fitSystemWindows(insets); - if (!done) { - final int count = mChildrenCount; - final View[] children = mChildren; + public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { + insets = super.dispatchApplyWindowInsets(insets); + if (insets.hasInsets()) { + final int count = getChildCount(); for (int i = 0; i < count; i++) { - done = children[i].fitSystemWindows(insets); - if (done) { + insets = getChildAt(i).dispatchApplyWindowInsets(insets); + if (!insets.hasInsets()) { break; } } } - return done; + return insets; } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5b2a452..1cb0473 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -36,6 +36,8 @@ import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.Drawable; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManager.DisplayListener; import android.media.AudioManager; import android.os.Binder; import android.os.Bundle; @@ -134,6 +136,7 @@ public final class ViewRootImpl implements ViewParent, final Context mContext; final IWindowSession mWindowSession; final Display mDisplay; + final DisplayManager mDisplayManager; final String mBasePackageName; final int[] mTmpLocation = new int[2]; @@ -368,9 +371,7 @@ public final class ViewRootImpl implements ViewParent, mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi; mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context); mChoreographer = Choreographer.getInstance(); - - PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - mAttachInfo.mScreenOn = powerManager.isScreenOn(); + mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); loadSystemProperties(); } @@ -425,6 +426,10 @@ public final class ViewRootImpl implements ViewParent, synchronized (this) { if (mView == null) { mView = view; + + mAttachInfo.mDisplayState = mDisplay.getState(); + mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); + mViewLayoutDirectionInitial = mView.getRawLayoutDirection(); mFallbackEventHandler.setView(view); mWindowAttributes.copyFrom(attrs); @@ -794,18 +799,43 @@ public final class ViewRootImpl implements ViewParent, scheduleTraversals(); } - void handleScreenStateChange(boolean on) { - if (on != mAttachInfo.mScreenOn) { - mAttachInfo.mScreenOn = on; - if (mView != null) { - mView.dispatchScreenStateChanged(on ? View.SCREEN_STATE_ON : View.SCREEN_STATE_OFF); - } - if (on) { - mFullRedrawNeeded = true; - scheduleTraversals(); + private final DisplayListener mDisplayListener = new DisplayListener() { + @Override + public void onDisplayChanged(int displayId) { + if (mView != null && mDisplay.getDisplayId() == displayId) { + final int oldDisplayState = mAttachInfo.mDisplayState; + final int newDisplayState = mDisplay.getState(); + if (oldDisplayState != newDisplayState) { + mAttachInfo.mDisplayState = newDisplayState; + if (oldDisplayState != Display.STATE_UNKNOWN) { + final int oldScreenState = toViewScreenState(oldDisplayState); + final int newScreenState = toViewScreenState(newDisplayState); + if (oldScreenState != newScreenState) { + mView.dispatchScreenStateChanged(newScreenState); + } + if (oldDisplayState == Display.STATE_OFF) { + // Draw was suppressed so we need to for it to happen here. + mFullRedrawNeeded = true; + scheduleTraversals(); + } + } + } } } - } + + @Override + public void onDisplayRemoved(int displayId) { + } + + @Override + public void onDisplayAdded(int displayId) { + } + + private int toViewScreenState(int displayState) { + return displayState == Display.STATE_OFF ? + View.SCREEN_STATE_OFF : View.SCREEN_STATE_ON; + } + }; @Override public void requestFitSystemWindows() { @@ -1121,6 +1151,19 @@ public final class ViewRootImpl implements ViewParent, return windowSizeMayChange; } + void dispatchApplyInsets(View host) { + mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); + boolean isRound = false; + if ((mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0 + && mDisplay.getDisplayId() == 0) { + // we're fullscreen and not hosted in an ActivityView + isRound = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_windowIsRound); + } + host.dispatchApplyWindowInsets(new WindowInsets( + mFitSystemWindowsInsets, isRound)); + } + private void performTraversals() { // cache mView since it is used so much below... final View host = mView; @@ -1212,8 +1255,7 @@ public final class ViewRootImpl implements ViewParent, } host.dispatchAttachedToWindow(attachInfo, 0); attachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true); - mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); - host.fitSystemWindows(mFitSystemWindowsInsets); + dispatchApplyInsets(host); //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); } else { @@ -1338,9 +1380,8 @@ public final class ViewRootImpl implements ViewParent, if (mFitSystemWindowsRequested) { mFitSystemWindowsRequested = false; - mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); mLastOverscanRequested = mAttachInfo.mOverscanRequested; - host.fitSystemWindows(mFitSystemWindowsInsets); + dispatchApplyInsets(host); if (mLayoutRequested) { // Short-circuit catching a new layout request here, so // we don't need to go through two layout passes when things @@ -1519,8 +1560,7 @@ public final class ViewRootImpl implements ViewParent, mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; mLastOverscanRequested = mAttachInfo.mOverscanRequested; mFitSystemWindowsRequested = false; - mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); - host.fitSystemWindows(mFitSystemWindowsInsets); + dispatchApplyInsets(host); } if (visibleInsetsChanged) { mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets); @@ -2236,7 +2276,7 @@ public final class ViewRootImpl implements ViewParent, } private void performDraw() { - if (!mAttachInfo.mScreenOn && !mReportNextDraw) { + if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { return; } @@ -2872,6 +2912,8 @@ public final class ViewRootImpl implements ViewParent, mInputChannel = null; } + mDisplayManager.unregisterDisplayListener(mDisplayListener); + unscheduleTraversals(); } @@ -2951,7 +2993,6 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17; private final static int MSG_UPDATE_CONFIGURATION = 18; private final static int MSG_PROCESS_INPUT_EVENTS = 19; - private final static int MSG_DISPATCH_SCREEN_STATE = 20; 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; @@ -2998,8 +3039,6 @@ public final class ViewRootImpl implements ViewParent, return "MSG_UPDATE_CONFIGURATION"; case MSG_PROCESS_INPUT_EVENTS: return "MSG_PROCESS_INPUT_EVENTS"; - case MSG_DISPATCH_SCREEN_STATE: - return "MSG_DISPATCH_SCREEN_STATE"; case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST"; case MSG_DISPATCH_DONE_ANIMATING: @@ -3215,11 +3254,6 @@ public final class ViewRootImpl implements ViewParent, } updateConfiguration(config, false); } break; - case MSG_DISPATCH_SCREEN_STATE: { - if (mView != null) { - handleScreenStateChange(msg.arg1 == 1); - } - } break; case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: { setAccessibilityFocus(null, null); } break; @@ -3692,7 +3726,8 @@ public final class ViewRootImpl implements ViewParent, if (result == InputMethodManager.DISPATCH_HANDLED) { return FINISH_HANDLED; } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) { - return FINISH_NOT_HANDLED; + // The IME could not handle it, so skip along to the next InputStage + return FORWARD; } else { return DEFER; // callback will be invoked later } @@ -4319,6 +4354,7 @@ public final class ViewRootImpl implements ViewParent, * Creates dpad events from unhandled joystick movements. */ final class SyntheticJoystickHandler extends Handler { + private final static String TAG = "SyntheticJoystickHandler"; private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1; private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2; @@ -4351,10 +4387,21 @@ public final class ViewRootImpl implements ViewParent, } public void process(MotionEvent event) { - update(event, true); + switch(event.getActionMasked()) { + case MotionEvent.ACTION_CANCEL: + cancel(event); + break; + case MotionEvent.ACTION_MOVE: + update(event, true); + break; + default: + Log.w(TAG, "Unexpected action: " + event.getActionMasked()); + } } - public void cancel(MotionEvent event) { + private void cancel(MotionEvent event) { + removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT); + removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT); update(event, false); } @@ -5793,12 +5840,6 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } - public void dispatchScreenStateChange(boolean on) { - Message msg = mHandler.obtainMessage(MSG_DISPATCH_SCREEN_STATE); - msg.arg1 = on ? 1 : 0; - mHandler.sendMessage(msg); - } - public void dispatchGetNewSurface() { Message msg = mHandler.obtainMessage(MSG_DISPATCH_GET_NEW_SURFACE); mHandler.sendMessage(msg); @@ -6137,14 +6178,6 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void dispatchScreenState(boolean on) { - final ViewRootImpl viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchScreenStateChange(on); - } - } - - @Override public void dispatchGetNewSurface() { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index b3a0699..59d8fbc 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -91,10 +91,15 @@ public abstract class Window { public static final int FEATURE_ACTION_MODE_OVERLAY = 10; /** + * Flag for requesting a decoration-free window that is dismissed by swiping from the left. + */ + public static final int FEATURE_SWIPE_TO_DISMISS = 11; + + /** * Max value used as a feature ID * @hide */ - public static final int FEATURE_MAX = FEATURE_ACTION_MODE_OVERLAY; + public static final int FEATURE_MAX = FEATURE_SWIPE_TO_DISMISS; /** Flag for setting the progress bar's visibility to VISIBLE */ public static final int PROGRESS_VISIBILITY_ON = -1; @@ -129,6 +134,7 @@ public abstract class Window { private TypedArray mWindowStyle; private Callback mCallback; + private OnWindowDismissedCallback mOnWindowDismissedCallback; private WindowManager mWindowManager; private IBinder mAppToken; private String mAppName; @@ -387,6 +393,15 @@ public abstract class Window { public void onActionModeFinished(ActionMode mode); } + /** @hide */ + public interface OnWindowDismissedCallback { + /** + * Called when a window is dismissed. This informs the callback that the + * window is gone, and it should finish itself. + */ + public void onWindowDismissed(); + } + public Window(Context context) { mContext = context; } @@ -560,6 +575,18 @@ public abstract class Window { return mCallback; } + /** @hide */ + public final void setOnWindowDismissedCallback(OnWindowDismissedCallback dcb) { + mOnWindowDismissedCallback = dcb; + } + + /** @hide */ + public final void dispatchOnWindowDismissed() { + if (mOnWindowDismissedCallback != null) { + mOnWindowDismissedCallback.onWindowDismissed(); + } + } + /** * Take ownership of this window's surface. The window's view hierarchy * will no longer draw into the surface, though it will otherwise continue diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java new file mode 100644 index 0000000..57e774e --- /dev/null +++ b/core/java/android/view/WindowInsets.java @@ -0,0 +1,349 @@ +/* + * 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.view; + +import android.graphics.Rect; + +/** + * Describes a set of insets for window content. + * + * <p>WindowInsets are immutable and may be expanded to include more inset types in the future. + * To adjust insets, use one of the supplied clone methods to obtain a new WindowInsets instance + * with the adjusted properties.</p> + * + * @see View.OnApplyWindowInsetsListener + * @see View#onApplyWindowInsets(WindowInsets) + */ +public final class WindowInsets { + private Rect mSystemWindowInsets; + private Rect mWindowDecorInsets; + private Rect mTempRect; + private boolean mIsRound; + + private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0); + + /** + * Since new insets may be added in the future that existing apps couldn't + * know about, this fully empty constant shouldn't be made available to apps + * since it would allow them to inadvertently consume unknown insets by returning it. + * @hide + */ + public static final WindowInsets EMPTY = new WindowInsets(EMPTY_RECT, EMPTY_RECT); + + /** @hide */ + public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets) { + this(systemWindowInsets, windowDecorInsets, false); + } + + /** @hide */ + public WindowInsets(Rect systemWindowInsets, boolean isRound) { + this(systemWindowInsets, EMPTY_RECT, isRound); + } + + /** @hide */ + public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, boolean isRound) { + mSystemWindowInsets = systemWindowInsets; + mWindowDecorInsets = windowDecorInsets; + mIsRound = isRound; + } + + /** + * Construct a new WindowInsets, copying all values from a source WindowInsets. + * + * @param src Source to copy insets from + */ + public WindowInsets(WindowInsets src) { + mSystemWindowInsets = src.mSystemWindowInsets; + mWindowDecorInsets = src.mWindowDecorInsets; + mIsRound = src.mIsRound; + } + + /** @hide */ + public WindowInsets(Rect systemWindowInsets) { + this(systemWindowInsets, EMPTY_RECT); + } + + /** + * Used to provide a safe copy of the system window insets to pass through + * to the existing fitSystemWindows method and other similar internals. + * @hide + */ + public Rect getSystemWindowInsets() { + if (mTempRect == null) { + mTempRect = new Rect(); + } + if (mSystemWindowInsets != null) { + mTempRect.set(mSystemWindowInsets); + } else { + // If there were no system window insets, this is just empty. + mTempRect.setEmpty(); + } + return mTempRect; + } + + /** + * Returns the left system window inset in pixels. + * + * <p>The system window inset represents the area of a full-screen window that is + * partially or fully obscured by the status bar, navigation bar, IME or other system windows. + * </p> + * + * @return The left system window inset + */ + public int getSystemWindowInsetLeft() { + return mSystemWindowInsets.left; + } + + /** + * Returns the top system window inset in pixels. + * + * <p>The system window inset represents the area of a full-screen window that is + * partially or fully obscured by the status bar, navigation bar, IME or other system windows. + * </p> + * + * @return The top system window inset + */ + public int getSystemWindowInsetTop() { + return mSystemWindowInsets.top; + } + + /** + * Returns the right system window inset in pixels. + * + * <p>The system window inset represents the area of a full-screen window that is + * partially or fully obscured by the status bar, navigation bar, IME or other system windows. + * </p> + * + * @return The right system window inset + */ + public int getSystemWindowInsetRight() { + return mSystemWindowInsets.right; + } + + /** + * Returns the bottom system window inset in pixels. + * + * <p>The system window inset represents the area of a full-screen window that is + * partially or fully obscured by the status bar, navigation bar, IME or other system windows. + * </p> + * + * @return The bottom system window inset + */ + public int getSystemWindowInsetBottom() { + return mSystemWindowInsets.bottom; + } + + /** + * Returns the left window decor inset in pixels. + * + * <p>The window decor inset represents the area of the window content area that is + * partially or fully obscured by decorations within the window provided by the framework. + * This can include action bars, title bars, toolbars, etc.</p> + * + * @return The left window decor inset + * @hide pending API + */ + public int getWindowDecorInsetLeft() { + return mWindowDecorInsets.left; + } + + /** + * Returns the top window decor inset in pixels. + * + * <p>The window decor inset represents the area of the window content area that is + * partially or fully obscured by decorations within the window provided by the framework. + * This can include action bars, title bars, toolbars, etc.</p> + * + * @return The top window decor inset + * @hide pending API + */ + public int getWindowDecorInsetTop() { + return mWindowDecorInsets.top; + } + + /** + * Returns the right window decor inset in pixels. + * + * <p>The window decor inset represents the area of the window content area that is + * partially or fully obscured by decorations within the window provided by the framework. + * This can include action bars, title bars, toolbars, etc.</p> + * + * @return The right window decor inset + * @hide pending API + */ + public int getWindowDecorInsetRight() { + return mWindowDecorInsets.right; + } + + /** + * Returns the bottom window decor inset in pixels. + * + * <p>The window decor inset represents the area of the window content area that is + * partially or fully obscured by decorations within the window provided by the framework. + * This can include action bars, title bars, toolbars, etc.</p> + * + * @return The bottom window decor inset + * @hide pending API + */ + public int getWindowDecorInsetBottom() { + return mWindowDecorInsets.bottom; + } + + /** + * Returns true if this WindowInsets has nonzero system window insets. + * + * <p>The system window inset represents the area of a full-screen window that is + * partially or fully obscured by the status bar, navigation bar, IME or other system windows. + * </p> + * + * @return true if any of the system window inset values are nonzero + */ + public boolean hasSystemWindowInsets() { + return mSystemWindowInsets.left != 0 || mSystemWindowInsets.top != 0 || + mSystemWindowInsets.right != 0 || mSystemWindowInsets.bottom != 0; + } + + /** + * Returns true if this WindowInsets has nonzero window decor insets. + * + * <p>The window decor inset represents the area of the window content area that is + * partially or fully obscured by decorations within the window provided by the framework. + * This can include action bars, title bars, toolbars, etc.</p> + * + * @return true if any of the window decor inset values are nonzero + * @hide pending API + */ + public boolean hasWindowDecorInsets() { + return mWindowDecorInsets.left != 0 || mWindowDecorInsets.top != 0 || + mWindowDecorInsets.right != 0 || mWindowDecorInsets.bottom != 0; + } + + /** + * Returns true if this WindowInsets has any nonzero insets. + * + * @return true if any inset values are nonzero + */ + public boolean hasInsets() { + return hasSystemWindowInsets() || hasWindowDecorInsets(); + } + + /** + * Returns true if the associated window has a round shape. + * + * <p>A round window's left, top, right and bottom edges reach all the way to the + * associated edges of the window but the corners may not be visible. Views responding + * to round insets should take care to not lay out critical elements within the corners + * where they may not be accessible.</p> + * + * @return True if the window is round + */ + public boolean isRound() { + return mIsRound; + } + + /** + * Returns a copy of this WindowInsets with the system window insets fully consumed. + * + * @return A modified copy of this WindowInsets + */ + public WindowInsets consumeSystemWindowInsets() { + final WindowInsets result = new WindowInsets(this); + result.mSystemWindowInsets = new Rect(0, 0, 0, 0); + return result; + } + + /** + * Returns a copy of this WindowInsets with selected system window insets fully consumed. + * + * @param left true to consume the left system window inset + * @param top true to consume the top system window inset + * @param right true to consume the right system window inset + * @param bottom true to consume the bottom system window inset + * @return A modified copy of this WindowInsets + * @hide pending API + */ + public WindowInsets consumeSystemWindowInsets(boolean left, boolean top, + boolean right, boolean bottom) { + if (left || top || right || bottom) { + final WindowInsets result = new WindowInsets(this); + result.mSystemWindowInsets = new Rect(left ? 0 : mSystemWindowInsets.left, + top ? 0 : mSystemWindowInsets.top, + right ? 0 : mSystemWindowInsets.right, + bottom ? 0 : mSystemWindowInsets.bottom); + return result; + } + return this; + } + + /** + * Returns a copy of this WindowInsets with selected system window insets replaced + * with new values. + * + * @param left New left inset in pixels + * @param top New top inset in pixels + * @param right New right inset in pixels + * @param bottom New bottom inset in pixels + * @return A modified copy of this WindowInsets + */ + public WindowInsets replaceSystemWindowInsets(int left, int top, + int right, int bottom) { + final WindowInsets result = new WindowInsets(this); + result.mSystemWindowInsets = new Rect(left, top, right, bottom); + return result; + } + + /** + * @hide + */ + public WindowInsets consumeWindowDecorInsets() { + final WindowInsets result = new WindowInsets(this); + result.mWindowDecorInsets.set(0, 0, 0, 0); + return result; + } + + /** + * @hide + */ + public WindowInsets consumeWindowDecorInsets(boolean left, boolean top, + boolean right, boolean bottom) { + if (left || top || right || bottom) { + final WindowInsets result = new WindowInsets(this); + result.mWindowDecorInsets = new Rect(left ? 0 : mWindowDecorInsets.left, + top ? 0 : mWindowDecorInsets.top, + right ? 0 : mWindowDecorInsets.right, + bottom ? 0 : mWindowDecorInsets.bottom); + return result; + } + return this; + } + + /** + * @hide + */ + public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) { + final WindowInsets result = new WindowInsets(this); + result.mWindowDecorInsets = new Rect(left, top, right, bottom); + return result; + } + + @Override + public String toString() { + return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets + " windowDecorInsets=" + + mWindowDecorInsets + (isRound() ? "round}" : "}"); + } +} diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 53a4c0d0..d5a7d33 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -610,7 +610,10 @@ public interface WindowManager extends ViewManager { * screen is pressed, you will receive this first touch event. Usually * the first touch event is consumed by the system since the user can * not see what they are pressing on. + * + * @deprecated This flag has no effect. */ + @Deprecated public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040; /** Window flag: as long as this window is visible to the user, keep diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java new file mode 100644 index 0000000..e50487d --- /dev/null +++ b/core/java/android/view/WindowManagerInternal.java @@ -0,0 +1,39 @@ +/* + * 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.view; + +import android.hardware.display.DisplayManagerInternal; +import android.os.IRemoteCallback; + +/** + * Window manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class WindowManagerInternal { + /** + * Request that the window manager call + * {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager} + * within a surface transaction at a later time. + */ + public abstract void requestTraversalFromDisplayManager(); + /** + * Invalidate all visible windows. Then report back on the callback once all windows have + * redrawn. + */ + public abstract void waitForAllWindowsDrawn(IRemoteCallback callback, long timeout); +}
\ No newline at end of file diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index c5a1b86..ae7cd26 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -86,8 +86,7 @@ public interface WindowManagerPolicy { public final static int FLAG_FILTERED = 0x04000000; public final static int FLAG_DISABLE_KEY_REPEAT = 0x08000000; - public final static int FLAG_WOKE_HERE = 0x10000000; - public final static int FLAG_BRIGHT_HERE = 0x20000000; + public final static int FLAG_INTERACTIVE = 0x20000000; public final static int FLAG_PASS_TO_USER = 0x40000000; // Flags used for indicating whether the internal and/or external input devices @@ -115,20 +114,6 @@ public interface WindowManagerPolicy { public final static int ACTION_PASS_TO_USER = 0x00000001; /** - * This key event should wake the device. - * To be returned from {@link #interceptKeyBeforeQueueing}. - * Do not return this and {@link #ACTION_GO_TO_SLEEP} or {@link #ACTION_PASS_TO_USER}. - */ - public final static int ACTION_WAKE_UP = 0x00000002; - - /** - * This key event should put the device to sleep (and engage keyguard if necessary) - * To be returned from {@link #interceptKeyBeforeQueueing}. - * Do not return this and {@link #ACTION_WAKE_UP} or {@link #ACTION_PASS_TO_USER}. - */ - public final static int ACTION_GO_TO_SLEEP = 0x00000004; - - /** * Interface to the Window Manager state associated with a particular * window. You can hold on to an instance of this interface from the call * to prepareAddWindow() until removeWindow(). @@ -461,8 +446,6 @@ public interface WindowManagerPolicy { public final int OFF_BECAUSE_OF_USER = 2; /** Screen turned off because of timeout */ public final int OFF_BECAUSE_OF_TIMEOUT = 3; - /** Screen turned off because of proximity sensor */ - public final int OFF_BECAUSE_OF_PROX_SENSOR = 4; /** When not otherwise specified by the activity's screenOrientation, rotation should be * determined by the system (that is, using sensors). */ @@ -749,12 +732,10 @@ public interface WindowManagerPolicy { * because it's the most fragile. * @param event The key event. * @param policyFlags The policy flags associated with the key. - * @param isScreenOn True if the screen is already on * - * @return The bitwise or of the {@link #ACTION_PASS_TO_USER}, - * {@link #ACTION_WAKE_UP} and {@link #ACTION_GO_TO_SLEEP} flags. + * @return Actions flags: may be {@link #ACTION_PASS_TO_USER}. */ - public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn); + public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags); /** * Called from the input reader thread before a motion is enqueued when the screen is off. @@ -765,10 +746,9 @@ public interface WindowManagerPolicy { * because it's the most fragile. * @param policyFlags The policy flags associated with the motion. * - * @return The bitwise or of the {@link #ACTION_PASS_TO_USER}, - * {@link #ACTION_WAKE_UP} and {@link #ACTION_GO_TO_SLEEP} flags. + * @return Actions flags: may be {@link #ACTION_PASS_TO_USER}. */ - public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags); + public int interceptWakeMotionBeforeQueueing(long whenNanos, int policyFlags); /** * Called from the input dispatcher thread before a key is dispatched to a window. @@ -916,23 +896,23 @@ public interface WindowManagerPolicy { public int focusChangedLw(WindowState lastFocus, WindowState newFocus); /** - * Called after the screen turns off. + * Called when the device is going to sleep. * * @param why {@link #OFF_BECAUSE_OF_USER} or * {@link #OFF_BECAUSE_OF_TIMEOUT}. */ - public void screenTurnedOff(int why); + public void goingToSleep(int why); public interface ScreenOnListener { void onScreenOn(); } /** - * Called when the power manager would like to turn the screen on. + * Called when the device is waking up. * Must call back on the listener to tell it when the higher-level system * is ready for the screen to go on (i.e. the lock screen is shown). */ - public void screenTurningOn(ScreenOnListener screenOnListener); + public void wakingUp(ScreenOnListener screenOnListener); /** * Return whether the screen is about to turn on or is currently on. diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 00f4adb..879e58f 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -177,7 +177,8 @@ public final class AccessibilityManager { userId = UserHandle.myUserId(); } IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); - IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder); + IAccessibilityManager service = iBinder == null + ? null : IAccessibilityManager.Stub.asInterface(iBinder); sInstance = new AccessibilityManager(context, service, userId); } } @@ -197,10 +198,14 @@ public final class AccessibilityManager { mHandler = new MyHandler(context.getMainLooper()); mService = service; mUserId = userId; - + if (mService == null) { + mIsEnabled = false; + } try { - final int stateFlags = mService.addClient(mClient, userId); - setState(stateFlags); + if (mService != null) { + final int stateFlags = mService.addClient(mClient, userId); + setState(stateFlags); + } } catch (RemoteException re) { Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); } @@ -322,14 +327,16 @@ public final class AccessibilityManager { public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() { List<AccessibilityServiceInfo> services = null; try { - services = mService.getInstalledAccessibilityServiceList(mUserId); - if (DEBUG) { - Log.i(LOG_TAG, "Installed AccessibilityServices " + services); + if (mService != null) { + services = mService.getInstalledAccessibilityServiceList(mUserId); + if (DEBUG) { + Log.i(LOG_TAG, "Installed AccessibilityServices " + services); + } } } catch (RemoteException re) { Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); } - return Collections.unmodifiableList(services); + return services != null ? Collections.unmodifiableList(services) : Collections.EMPTY_LIST; } /** @@ -349,14 +356,16 @@ public final class AccessibilityManager { int feedbackTypeFlags) { List<AccessibilityServiceInfo> services = null; try { - services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags, mUserId); - if (DEBUG) { - Log.i(LOG_TAG, "Installed AccessibilityServices " + services); + if (mService != null) { + services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags, mUserId); + if (DEBUG) { + Log.i(LOG_TAG, "Installed AccessibilityServices " + services); + } } } catch (RemoteException re) { Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re); } - return Collections.unmodifiableList(services); + return services != null ? Collections.unmodifiableList(services) : Collections.EMPTY_LIST; } /** @@ -466,6 +475,9 @@ public final class AccessibilityManager { */ public int addAccessibilityInteractionConnection(IWindow windowToken, IAccessibilityInteractionConnection connection) { + if (mService == null) { + return View.NO_ID; + } try { return mService.addAccessibilityInteractionConnection(windowToken, connection, mUserId); } catch (RemoteException re) { @@ -482,7 +494,9 @@ public final class AccessibilityManager { */ public void removeAccessibilityInteractionConnection(IWindow windowToken) { try { - mService.removeAccessibilityInteractionConnection(windowToken); + if (mService != null) { + mService.removeAccessibilityInteractionConnection(windowToken); + } } catch (RemoteException re) { Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re); } diff --git a/core/java/android/webkit/EventLogTags.logtags b/core/java/android/webkit/EventLogTags.logtags index b0b5493..a90aebd 100644 --- a/core/java/android/webkit/EventLogTags.logtags +++ b/core/java/android/webkit/EventLogTags.logtags @@ -8,3 +8,4 @@ option java_package android.webkit; # 70103- used by the browser app itself 70150 browser_snap_center +70151 exp_det_attempt_to_call_object_getclass (app_signature|3) diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index b9131bf..1379d18 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -32,6 +32,9 @@ public final class WebViewFactory { private static final String CHROMIUM_WEBVIEW_FACTORY = "com.android.webview.chromium.WebViewChromiumFactoryProvider"; + private static final String NULL_WEBVIEW_FACTORY = + "com.android.webview.nullwebview.NullWebViewFactoryProvider"; + private static final String LOGTAG = "WebViewFactory"; private static final boolean DEBUG = false; @@ -112,6 +115,11 @@ public final class WebViewFactory { } private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException { - return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY); + try { + return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY); + } catch (ClassNotFoundException e) { + Log.e(LOGTAG, "Chromium WebView does not exist"); + return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY); + } } } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 25a43a6..bbaa33d 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3039,7 +3039,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te @Override public boolean onKeyUp(int keyCode, KeyEvent event) { - if (KeyEvent.isConfirmKey(keyCode)) { + if (event.isConfirmKey()) { if (!isEnabled()) { return true; } diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index 78ba6e0..6dd93a7 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -1228,7 +1228,7 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList @Override public boolean onKeyUp(int keyCode, KeyEvent event) { - if (KeyEvent.isConfirmKey(keyCode)) { + if (event.isConfirmKey()) { if (mReceivedInvokeKeyDown) { if (mItemCount > 0) { dispatchPress(mSelectedChild); diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 66fe46f..13f3eb6 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -843,7 +843,7 @@ public class ListPopupWindow { // to select one of its items if (keyCode != KeyEvent.KEYCODE_SPACE && (mDropDownList.getSelectedItemPosition() >= 0 - || !KeyEvent.isConfirmKey(keyCode))) { + || !event.isConfirmKey())) { int curIndex = mDropDownList.getSelectedItemPosition(); boolean consumed; @@ -931,7 +931,7 @@ public class ListPopupWindow { public boolean onKeyUp(int keyCode, KeyEvent event) { if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) { boolean consumed = mDropDownList.onKeyUp(keyCode, event); - if (consumed && KeyEvent.isConfirmKey(keyCode)) { + if (consumed && event.isConfirmKey()) { // if the list accepts the key events and the key event was a click, the text view // gets the selected item from the drop down as its content dismiss(); diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 26c5732..2c44703 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -427,12 +427,12 @@ public class NumberPicker extends LinearLayout { * Flag whether to ignore move events - we ignore such when we show in IME * to prevent the content from scrolling. */ - private boolean mIngonreMoveEvents; + private boolean mIgnoreMoveEvents; /** - * Flag whether to show soft input on tap. + * Flag whether to perform a click on tap. */ - private boolean mShowSoftInputOnTap; + private boolean mPerformClickOnTap; /** * The top of the top selection divider. @@ -808,8 +808,8 @@ public class NumberPicker extends LinearLayout { mInputText.setVisibility(View.INVISIBLE); mLastDownOrMoveEventY = mLastDownEventY = event.getY(); mLastDownEventTime = event.getEventTime(); - mIngonreMoveEvents = false; - mShowSoftInputOnTap = false; + mIgnoreMoveEvents = false; + mPerformClickOnTap = false; // Handle pressed state before any state change. if (mLastDownEventY < mTopSelectionDividerTop) { if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) { @@ -840,7 +840,7 @@ public class NumberPicker extends LinearLayout { postChangeCurrentByOneFromLongPress( true, ViewConfiguration.getLongPressTimeout()); } else { - mShowSoftInputOnTap = true; + mPerformClickOnTap = true; postBeginSoftInputOnLongPressCommand(); } return true; @@ -861,7 +861,7 @@ public class NumberPicker extends LinearLayout { int action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_MOVE: { - if (mIngonreMoveEvents) { + if (mIgnoreMoveEvents) { break; } float currentMoveY = event.getY(); @@ -893,9 +893,9 @@ public class NumberPicker extends LinearLayout { int deltaMoveY = (int) Math.abs(eventY - mLastDownEventY); long deltaTime = event.getEventTime() - mLastDownEventTime; if (deltaMoveY <= mTouchSlop && deltaTime < ViewConfiguration.getTapTimeout()) { - if (mShowSoftInputOnTap) { - mShowSoftInputOnTap = false; - showSoftInput(); + if (mPerformClickOnTap) { + mPerformClickOnTap = false; + performClick(); } else { int selectorIndexOffset = (eventY / mSelectorElementHeight) - SELECTOR_MIDDLE_ITEM_INDEX; @@ -1188,6 +1188,27 @@ public class NumberPicker extends LinearLayout { setValueInternal(value, false); } + @Override + public boolean performClick() { + if (!mHasSelectorWheel) { + return super.performClick(); + } else if (!super.performClick()) { + showSoftInput(); + } + return true; + } + + @Override + public boolean performLongClick() { + if (!mHasSelectorWheel) { + return super.performLongClick(); + } else if (!super.performLongClick()) { + showSoftInput(); + mIgnoreMoveEvents = true; + } + return true; + } + /** * Shows the soft input for its input text. */ @@ -2175,8 +2196,7 @@ public class NumberPicker extends LinearLayout { @Override public void run() { - showSoftInput(); - mIngonreMoveEvents = true; + performLongClick(); } } @@ -2304,7 +2324,14 @@ public class NumberPicker extends LinearLayout { } case AccessibilityNodeInfo.ACTION_CLICK: { if (NumberPicker.this.isEnabled()) { - showSoftInput(); + performClick(); + return true; + } + return false; + } + case AccessibilityNodeInfo.ACTION_LONG_CLICK: { + if (NumberPicker.this.isEnabled()) { + performLongClick(); return true; } return false; diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index bdaaa01..fd6ca4c 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -161,9 +161,11 @@ public class ShareActionProvider extends ActionProvider { @Override public View onCreateActionView() { // Create the view and set its data model. - ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); ActivityChooserView activityChooserView = new ActivityChooserView(mContext); - activityChooserView.setActivityChooserModel(dataModel); + if (!activityChooserView.isInEditMode()) { + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); + activityChooserView.setActivityChooserModel(dataModel); + } // Lookup and set the expand action icon. TypedValue outTypedValue = new TypedValue(); diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index 1cda631..b204dfd 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -731,14 +731,10 @@ public class SpellChecker implements SpellCheckerSessionListener { } } - if (scheduleOtherSpellCheck && wordStart <= end) { + if (scheduleOtherSpellCheck) { // Update range span: start new spell check from last wordStart setRangeSpan(editable, wordStart, end); } else { - if (DBG && scheduleOtherSpellCheck) { - Log.w(TAG, "Trying to schedule spellcheck for invalid region, from " - + wordStart + " to " + end); - } removeRangeSpan(editable); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5ece016..8460375 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -710,19 +710,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextAppearance_shadowColor: - shadowcolor = a.getInt(attr, 0); + shadowcolor = appearance.getInt(attr, 0); break; case com.android.internal.R.styleable.TextAppearance_shadowDx: - dx = a.getFloat(attr, 0); + dx = appearance.getFloat(attr, 0); break; case com.android.internal.R.styleable.TextAppearance_shadowDy: - dy = a.getFloat(attr, 0); + dy = appearance.getFloat(attr, 0); break; case com.android.internal.R.styleable.TextAppearance_shadowRadius: - r = a.getFloat(attr, 0); + r = appearance.getFloat(attr, 0); break; } } diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 066d6c3..ad45894 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -174,6 +174,15 @@ public class ActionBarImpl extends ActionBar { init(dialog.getWindow().getDecorView()); } + /** + * Only for edit mode. + * @hide + */ + public ActionBarImpl(View layout) { + assert layout.isInEditMode(); + init(layout); + } + private void init(View decor) { mContext = decor.getContext(); mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById( @@ -559,8 +568,8 @@ public class ActionBarImpl extends ActionBar { return; } - final FragmentTransaction trans = mActivity.getFragmentManager().beginTransaction() - .disallowAddToBackStack(); + final FragmentTransaction trans = mActionView.isInEditMode() ? null : + mActivity.getFragmentManager().beginTransaction().disallowAddToBackStack(); if (mSelectedTab == tab) { if (mSelectedTab != null) { @@ -578,7 +587,7 @@ public class ActionBarImpl extends ActionBar { } } - if (!trans.isEmpty()) { + if (trans != null && !trans.isEmpty()) { trans.commit(); } } diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index fe532b0..19c0a44 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -26,6 +26,7 @@ import android.content.DialogInterface; import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Handler; import android.os.Message; import android.text.TextUtils; @@ -38,6 +39,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.Window; +import android.view.WindowInsets; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; @@ -239,6 +241,7 @@ public class AlertController { } mWindow.setContentView(mAlertDialogLayout); setupView(); + setupDecor(); } public void setTitle(CharSequence title) { @@ -389,7 +392,28 @@ public class AlertController { public boolean onKeyUp(int keyCode, KeyEvent event) { return mScrollView != null && mScrollView.executeKeyEvent(event); } - + + private void setupDecor() { + final View decor = mWindow.getDecorView(); + final View parent = mWindow.findViewById(R.id.parentPanel); + if (parent != null && decor != null) { + decor.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @Override + public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) { + if (insets.isRound()) { + // TODO: Get the padding as a function of the window size. + int roundOffset = mContext.getResources().getDimensionPixelOffset( + R.dimen.alert_dialog_round_padding); + parent.setPadding(roundOffset, roundOffset, roundOffset, roundOffset); + } + return insets.consumeSystemWindowInsets(); + } + }); + decor.setFitsSystemWindows(true); + decor.requestApplyInsets(); + } + } + private void setupView() { LinearLayout contentPanel = (LinearLayout) mWindow.findViewById(R.id.contentPanel); setupContent(contentPanel); @@ -601,25 +625,36 @@ public class AlertController { View buttonPanel) { /* Get all the different background required */ - int fullDark = a.getResourceId( - R.styleable.AlertDialog_fullDark, R.drawable.popup_full_dark); - int topDark = a.getResourceId( - R.styleable.AlertDialog_topDark, R.drawable.popup_top_dark); - int centerDark = a.getResourceId( - R.styleable.AlertDialog_centerDark, R.drawable.popup_center_dark); - int bottomDark = a.getResourceId( - R.styleable.AlertDialog_bottomDark, R.drawable.popup_bottom_dark); - int fullBright = a.getResourceId( - R.styleable.AlertDialog_fullBright, R.drawable.popup_full_bright); - int topBright = a.getResourceId( - R.styleable.AlertDialog_topBright, R.drawable.popup_top_bright); - int centerBright = a.getResourceId( - R.styleable.AlertDialog_centerBright, R.drawable.popup_center_bright); - int bottomBright = a.getResourceId( - R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright); - int bottomMedium = a.getResourceId( - R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium); - + int fullDark = 0; + int topDark = 0; + int centerDark = 0; + int bottomDark = 0; + int fullBright = 0; + int topBright = 0; + int centerBright = 0; + int bottomBright = 0; + int bottomMedium = 0; + if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.KITKAT) { + fullDark = R.drawable.popup_full_dark; + topDark = R.drawable.popup_top_dark; + centerDark = R.drawable.popup_center_dark; + bottomDark = R.drawable.popup_bottom_dark; + fullBright = R.drawable.popup_full_bright; + topBright = R.drawable.popup_top_bright; + centerBright = R.drawable.popup_center_bright; + bottomBright = R.drawable.popup_bottom_bright; + bottomMedium = R.drawable.popup_bottom_medium; + } + fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark); + topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark); + centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark); + bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark); + fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright); + topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright); + centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright); + bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright); + bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium); + /* * We now set the background of all of the sections of the alert. * First collect together each section that is being displayed along diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 43c4b49..5413113 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -41,11 +41,10 @@ interface IBatteryStats { void noteVibratorOff(int uid); void noteStartGps(int uid); void noteStopGps(int uid); - void noteScreenOn(); + void noteScreenState(int state); void noteScreenBrightness(int brightness); - void noteScreenOff(); - void noteInputEvent(); void noteUserActivity(int uid, int event); + void noteInteractive(boolean interactive); void notePhoneOn(); void notePhoneOff(); void notePhoneSignalStrength(in SignalStrength signalStrength); diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index 101fba9..cd90cdb 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -1004,7 +1004,7 @@ public final class ProcessStats implements Parcelable { for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) { ProcessState ps = pkgState.mProcesses.valueAt(iproc); if (ps.isInUse() || ps.mCommonProcess.isInUse()) { - ps.resetSafely(now); + pkgState.mProcesses.valueAt(iproc).resetSafely(now); } else { pkgState.mProcesses.valueAt(iproc).makeDead(); pkgState.mProcesses.removeAt(iproc); @@ -1013,7 +1013,7 @@ public final class ProcessStats implements Parcelable { for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) { ServiceState ss = pkgState.mServices.valueAt(isvc); if (ss.isInUse()) { - ss.resetSafely(now); + pkgState.mServices.valueAt(isvc).resetSafely(now); } else { pkgState.mServices.removeAt(isvc); } @@ -3014,7 +3014,7 @@ public final class ProcessStats implements Parcelable { } public boolean isInUse() { - return mOwner != null || mRestarting; + return mOwner != null; } void add(ServiceState other) { diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index 98599d0..420f8a9 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -17,8 +17,10 @@ package com.android.internal.net; import android.app.PendingIntent; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; @@ -47,7 +49,10 @@ public class VpnConfig implements Parcelable { public static Intent getIntentForConfirmation() { Intent intent = new Intent(); - intent.setClassName(DIALOGS_PACKAGE, DIALOGS_PACKAGE + ".ConfirmDialog"); + ComponentName componentName = ComponentName.unflattenFromString( + Resources.getSystem().getString( + com.android.internal.R.string.config_customVpnConfirmDialogComponent)); + intent.setClassName(componentName.getPackageName(), componentName.getClassName()); return intent; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 2e5fcec..9c82fac 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -26,6 +26,7 @@ import android.os.BatteryManager; import android.os.BatteryStats; import android.os.FileUtils; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.os.Parcel; import android.os.ParcelFormatException; @@ -44,6 +45,7 @@ import android.util.Printer; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.net.NetworkStatsFactory; @@ -84,7 +86,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 67 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 68 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -113,6 +115,10 @@ public final class BatteryStatsImpl extends BatteryStats { } final class MyHandler extends Handler { + public MyHandler(Looper looper) { + super(looper, null, true); + } + @Override public void handleMessage(Message msg) { BatteryCallback cb = mCallback; @@ -206,13 +212,14 @@ public final class BatteryStatsImpl extends BatteryStats { long mRealtimeStart; long mLastRealtime; - boolean mScreenOn; + int mScreenState = Display.STATE_UNKNOWN; StopwatchTimer mScreenOnTimer; int mScreenBrightnessBin = -1; final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS]; - Counter mInputEventCounter; + boolean mInteractive; + StopwatchTimer mInteractiveTimer; boolean mPhoneOn; StopwatchTimer mPhoneOnTimer; @@ -1737,7 +1744,7 @@ public final class BatteryStatsImpl extends BatteryStats { public int startAddingCpuLocked() { mHandler.removeMessages(MSG_UPDATE_WAKELOCKS); - if (mScreenOn) { + if (mScreenState == Display.STATE_ON) { return 0; } @@ -1912,46 +1919,49 @@ public final class BatteryStatsImpl extends BatteryStats { getUidStatsLocked(uid).noteStopGps(); } - public void noteScreenOnLocked() { - if (!mScreenOn) { - mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG; - if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: " - + Integer.toHexString(mHistoryCur.states)); - addHistoryRecordLocked(SystemClock.elapsedRealtime()); - mScreenOn = true; - mScreenOnTimer.startRunningLocked(this); - if (mScreenBrightnessBin >= 0) { - mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(this); - } + public void noteScreenStateLocked(int state) { + if (mScreenState != state) { + final int oldState = mScreenState; + mScreenState = state; + if (DEBUG) Slog.v(TAG, "Screen state: oldState=" + Display.stateToString(oldState) + + ", newState=" + Display.stateToString(state)); - // Fake a wake lock, so we consider the device waked as long - // as the screen is on. - noteStartWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL); - - // Update discharge amounts. - if (mOnBatteryInternal) { - updateDischargeScreenLevelsLocked(false, true); - } - } - } + if (state == Display.STATE_ON) { + // Screen turning on. + mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: " + + Integer.toHexString(mHistoryCur.states)); + addHistoryRecordLocked(SystemClock.elapsedRealtime()); + mScreenOnTimer.startRunningLocked(this); + if (mScreenBrightnessBin >= 0) { + mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(this); + } - public void noteScreenOffLocked() { - if (mScreenOn) { - mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG; - if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: " - + Integer.toHexString(mHistoryCur.states)); - addHistoryRecordLocked(SystemClock.elapsedRealtime()); - mScreenOn = false; - mScreenOnTimer.stopRunningLocked(this); - if (mScreenBrightnessBin >= 0) { - mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this); - } + // Fake a wake lock, so we consider the device waked as long + // as the screen is on. + noteStartWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL); - noteStopWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL); - - // Update discharge amounts. - if (mOnBatteryInternal) { - updateDischargeScreenLevelsLocked(true, false); + // Update discharge amounts. + if (mOnBatteryInternal) { + updateDischargeScreenLevelsLocked(false, true); + } + } else if (oldState == Display.STATE_ON) { + // Screen turning off or dozing. + mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: " + + Integer.toHexString(mHistoryCur.states)); + addHistoryRecordLocked(SystemClock.elapsedRealtime()); + mScreenOnTimer.stopRunningLocked(this); + if (mScreenBrightnessBin >= 0) { + mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this); + } + + noteStopWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL); + + // Update discharge amounts. + if (mOnBatteryInternal) { + updateDischargeScreenLevelsLocked(true, false); + } } } } @@ -1967,7 +1977,7 @@ public final class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(SystemClock.elapsedRealtime()); - if (mScreenOn) { + if (mScreenState == Display.STATE_ON) { if (mScreenBrightnessBin >= 0) { mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this); } @@ -1977,14 +1987,22 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public void noteInputEventAtomic() { - mInputEventCounter.stepAtomic(); - } - public void noteUserActivityLocked(int uid, int event) { getUidStatsLocked(uid).noteUserActivityLocked(event); } + public void noteInteractiveLocked(boolean interactive) { + if (mInteractive != interactive) { + mInteractive = interactive; + if (DEBUG) Slog.v(TAG, "Interactive: " + interactive); + if (interactive) { + mInteractiveTimer.startRunningLocked(this); + } else { + mInteractiveTimer.stopRunningLocked(this); + } + } + } + public void notePhoneOnLocked() { if (!mPhoneOn) { mHistoryCur.states |= HistoryItem.STATE_PHONE_IN_CALL_FLAG; @@ -2524,8 +2542,8 @@ public final class BatteryStatsImpl extends BatteryStats { batteryRealtime, which); } - @Override public int getInputEventCount(int which) { - return mInputEventCounter.getCountLocked(which); + @Override public long getInteractiveTime(long batteryRealtime, int which) { + return mInteractiveTimer.getTotalTimeLocked(batteryRealtime, which); } @Override public long getPhoneOnTime(long batteryRealtime, int which) { @@ -4487,15 +4505,14 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public BatteryStatsImpl(String filename) { + public BatteryStatsImpl(String filename, Handler handler) { mFile = new JournaledFile(new File(filename), new File(filename + ".tmp")); - mHandler = new MyHandler(); + mHandler = new MyHandler(handler.getLooper()); mStartCount++; mScreenOnTimer = new StopwatchTimer(null, -1, null, mUnpluggables); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mUnpluggables); } - mInputEventCounter = new Counter(mUnpluggables); mPhoneOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null, mUnpluggables); @@ -4512,6 +4529,7 @@ public final class BatteryStatsImpl extends BatteryStats { mBluetoothOnTimer = new StopwatchTimer(null, -5, null, mUnpluggables); mAudioOnTimer = new StopwatchTimer(null, -6, null, mUnpluggables); mVideoOnTimer = new StopwatchTimer(null, -7, null, mUnpluggables); + mInteractiveTimer = new StopwatchTimer(null, -8, null, mUnpluggables); mOnBattery = mOnBatteryInternal = false; initTimes(); mTrackBatteryPastUptime = 0; @@ -4644,7 +4662,7 @@ public final class BatteryStatsImpl extends BatteryStats { } public boolean isScreenOn() { - return mScreenOn; + return mScreenState == Display.STATE_ON; } void initTimes() { @@ -4672,7 +4690,7 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i].reset(this, false); } - mInputEventCounter.reset(false); + mInteractiveTimer.reset(this, false); mPhoneOnTimer.reset(this, false); mAudioOnTimer.reset(this, false); mVideoOnTimer.reset(this, false); @@ -4748,6 +4766,7 @@ public final class BatteryStatsImpl extends BatteryStats { long uptime = SystemClock.uptimeMillis() * 1000; long mSecRealtime = SystemClock.elapsedRealtime(); long realtime = mSecRealtime * 1000; + final boolean screenOn = mScreenState == Display.STATE_ON; if (onBattery) { // We will reset our status if we are unplugging after the // battery was last full, or the level is at 100, or @@ -4772,7 +4791,7 @@ public final class BatteryStatsImpl extends BatteryStats { mUnpluggedBatteryUptime = getBatteryUptimeLocked(uptime); mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(realtime); mDischargeCurrentLevel = mDischargeUnplugLevel = level; - if (mScreenOn) { + if (screenOn) { mDischargeScreenOnUnplugLevel = level; mDischargeScreenOffUnplugLevel = 0; } else { @@ -4797,7 +4816,7 @@ public final class BatteryStatsImpl extends BatteryStats { mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1; mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level; } - updateDischargeScreenLevelsLocked(mScreenOn, mScreenOn); + updateDischargeScreenLevelsLocked(screenOn, screenOn); doPlugLocked(realtime, getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime)); } if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) { @@ -5100,7 +5119,7 @@ public final class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOn() { synchronized(this) { int val = mDischargeAmountScreenOn; - if (mOnBattery && mScreenOn + if (mOnBattery && mScreenState == Display.STATE_ON && mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) { val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel; } @@ -5111,7 +5130,7 @@ public final class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOnSinceCharge() { synchronized(this) { int val = mDischargeAmountScreenOnSinceCharge; - if (mOnBattery && mScreenOn + if (mOnBattery && mScreenState == Display.STATE_ON && mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) { val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel; } @@ -5122,7 +5141,7 @@ public final class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOff() { synchronized(this) { int val = mDischargeAmountScreenOff; - if (mOnBattery && !mScreenOn + if (mOnBattery && mScreenState != Display.STATE_ON && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) { val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel; } @@ -5133,7 +5152,7 @@ public final class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOffSinceCharge() { synchronized(this) { int val = mDischargeAmountScreenOffSinceCharge; - if (mOnBattery && !mScreenOn + if (mOnBattery && mScreenState != Display.STATE_ON && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) { val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel; } @@ -5517,12 +5536,13 @@ public final class BatteryStatsImpl extends BatteryStats { mStartCount++; - mScreenOn = false; + mScreenState = Display.STATE_UNKNOWN; mScreenOnTimer.readSummaryFromParcelLocked(in); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i].readSummaryFromParcelLocked(in); } - mInputEventCounter.readSummaryFromParcelLocked(in); + mInteractive = false; + mInteractiveTimer.readSummaryFromParcelLocked(in); mPhoneOn = false; mPhoneOnTimer.readSummaryFromParcelLocked(in); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { @@ -5743,7 +5763,7 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL); } - mInputEventCounter.writeSummaryFromParcelLocked(out); + mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL); mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL); @@ -5965,13 +5985,12 @@ public final class BatteryStatsImpl extends BatteryStats { mBatteryLastUptime = 0; mBatteryRealtime = in.readLong(); mBatteryLastRealtime = 0; - mScreenOn = false; + mScreenState = Display.STATE_UNKNOWN; mScreenOnTimer = new StopwatchTimer(null, -1, null, mUnpluggables, in); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mUnpluggables, in); } - mInputEventCounter = new Counter(mUnpluggables, in); mPhoneOn = false; mPhoneOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { @@ -5987,11 +6006,17 @@ public final class BatteryStatsImpl extends BatteryStats { mNetworkActivityCounters[i] = new LongSamplingCounter(mUnpluggables, in); } mWifiOn = false; - mWifiOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in); + mWifiOnTimer = new StopwatchTimer(null, -3, null, mUnpluggables, in); mGlobalWifiRunning = false; - mGlobalWifiRunningTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in); + mGlobalWifiRunningTimer = new StopwatchTimer(null, -4, null, mUnpluggables, in); mBluetoothOn = false; - mBluetoothOnTimer = new StopwatchTimer(null, -2, null, mUnpluggables, in); + mBluetoothOnTimer = new StopwatchTimer(null, -5, null, mUnpluggables, in); + mAudioOn = false; + mAudioOnTimer = new StopwatchTimer(null, -6, null, mUnpluggables); + mVideoOn = false; + mVideoOnTimer = new StopwatchTimer(null, -7, null, mUnpluggables); + mInteractive = false; + mInteractiveTimer = new StopwatchTimer(null, -8, null, mUnpluggables, in); mUptime = in.readLong(); mUptimeStart = in.readLong(); mLastUptime = 0; @@ -6084,7 +6109,7 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i].writeToParcel(out, batteryRealtime); } - mInputEventCounter.writeToParcel(out); + mInteractiveTimer.writeToParcel(out, batteryRealtime); mPhoneOnTimer.writeToParcel(out, batteryRealtime); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { mPhoneSignalStrengthsTimer[i].writeToParcel(out, batteryRealtime); @@ -6183,8 +6208,8 @@ public final class BatteryStatsImpl extends BatteryStats { pr.println("*** Screen brightness #" + i + ":"); mScreenBrightnessTimer[i].logState(pr, " "); } - pr.println("*** Input event counter:"); - mInputEventCounter.logState(pr, " "); + pr.println("*** Interactive timer:"); + mInteractiveTimer.logState(pr, " "); pr.println("*** Phone timer:"); mPhoneOnTimer.logState(pr, " "); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java index f54a3e9..3b0f0f4 100644 --- a/core/java/com/android/internal/os/BinderInternal.java +++ b/core/java/com/android/internal/os/BinderInternal.java @@ -16,18 +16,11 @@ package com.android.internal.os; -import android.os.Binder; import android.os.IBinder; import android.os.SystemClock; import android.util.EventLog; -import android.util.Log; -import java.io.FileDescriptor; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; import java.lang.ref.WeakReference; -import java.lang.reflect.Modifier; /** * Private and debugging Binder APIs. diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 02bd4ac..86c9fe3 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -57,10 +57,6 @@ public class BaseIWindow extends IWindow.Stub { } @Override - public void dispatchScreenState(boolean on) { - } - - @Override public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { } diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java index 70e2bfc..6295314 100644 --- a/core/java/com/android/internal/view/RotationPolicy.java +++ b/core/java/com/android/internal/view/RotationPolicy.java @@ -21,7 +21,6 @@ import android.content.pm.PackageManager; import android.database.ContentObserver; import android.net.Uri; import android.os.AsyncTask; -import android.os.Build; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -52,7 +51,9 @@ public final class RotationPolicy { PackageManager pm = context.getPackageManager(); return pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_ACCELEROMETER) && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT) - && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE); + && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE) + && context.getResources().getBoolean( + com.android.internal.R.bool.config_supportAutoRotation); } /** @@ -176,6 +177,7 @@ public final class RotationPolicy { */ public static abstract class RotationPolicyListener { final ContentObserver mObserver = new ContentObserver(new Handler()) { + @Override public void onChange(boolean selfChange, Uri uri) { RotationPolicyListener.this.onChange(); } diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index 195a00d..5464284 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -392,8 +392,8 @@ public class MenuBuilder implements Menu { private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) { final int ordering = getOrdering(categoryOrder); - final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder, - ordering, title, mDefaultShowAsAction); + final MenuItemImpl item = createNewMenuItem(group, id, categoryOrder, ordering, title, + mDefaultShowAsAction); if (mCurrentMenuInfo != null) { // Pass along the current menu info @@ -405,7 +405,14 @@ public class MenuBuilder implements Menu { return item; } - + + // Layoutlib overrides this method to return its custom implementation of MenuItemImpl + private MenuItemImpl createNewMenuItem(int group, int id, int categoryOrder, int ordering, + CharSequence title, int defaultShowAsAction) { + return new MenuItemImpl(this, group, id, categoryOrder, ordering, title, + defaultShowAsAction); + } + public MenuItem add(CharSequence title) { return addInternal(0, 0, 0, title); } diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index 5469b63..c957b67 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -20,6 +20,7 @@ import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Build; import android.view.ViewGroup; +import android.view.WindowInsets; import com.android.internal.app.ActionBarImpl; import android.content.Context; @@ -96,7 +97,7 @@ public class ActionBarOverlayLayout extends ViewGroup { if (mLastSystemUiVisibility != 0) { int newVis = mLastSystemUiVisibility; onWindowSystemUiVisibilityChanged(newVis); - requestFitSystemWindows(); + requestApplyInsets(); } } } @@ -152,7 +153,7 @@ public class ActionBarOverlayLayout extends ViewGroup { } if ((diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { if (mActionBar != null) { - requestFitSystemWindows(); + requestApplyInsets(); } } } @@ -190,19 +191,20 @@ public class ActionBarOverlayLayout extends ViewGroup { } @Override - protected boolean fitSystemWindows(Rect insets) { + public WindowInsets onApplyWindowInsets(WindowInsets insets) { pullChildren(); final int vis = getWindowSystemUiVisibility(); final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0; + final Rect systemInsets = insets.getSystemWindowInsets(); // The top and bottom action bars are always within the content area. - boolean changed = applyInsets(mActionBarTop, insets, true, true, false, true); + boolean changed = applyInsets(mActionBarTop, systemInsets, true, true, false, true); if (mActionBarBottom != null) { - changed |= applyInsets(mActionBarBottom, insets, true, false, true, true); + changed |= applyInsets(mActionBarBottom, systemInsets, true, false, true, true); } - mBaseInnerInsets.set(insets); + mBaseInnerInsets.set(systemInsets); computeFitSystemWindows(mBaseInnerInsets, mBaseContentInsets); if (!mLastBaseContentInsets.equals(mBaseContentInsets)) { changed = true; @@ -215,9 +217,9 @@ public class ActionBarOverlayLayout extends ViewGroup { // We don't do any more at this point. To correctly compute the content/inner // insets in all cases, we need to know the measured size of the various action - // bar elements. fitSystemWindows() happens before the measure pass, so we can't + // bar elements. onApplyWindowInsets() happens before the measure pass, so we can't // do that here. Instead we will take this up in onMeasure(). - return true; + return WindowInsets.EMPTY; } @Override @@ -321,7 +323,7 @@ public class ActionBarOverlayLayout extends ViewGroup { // the app's fitSystemWindows(). We do this before measuring the content // view to keep the same semantics as the normal fitSystemWindows() call. mLastInnerInsets.set(mInnerInsets); - super.fitSystemWindows(mInnerInsets); + mContent.dispatchApplyWindowInsets(new WindowInsets(mInnerInsets)); } measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0); diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java new file mode 100644 index 0000000..002573e --- /dev/null +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.animation.TimeInterpolator; +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.FrameLayout; + +/** + * Special layout that finishes its activity when swiped away. + */ +public class SwipeDismissLayout extends FrameLayout { + private static final String TAG = "SwipeDismissLayout"; + + private static final float DISMISS_MIN_DRAG_WIDTH_RATIO = .33f; + + public interface OnDismissedListener { + void onDismissed(SwipeDismissLayout layout); + } + + public interface OnSwipeProgressChangedListener { + /** + * Called when the layout has been swiped and the position of the window should change. + * + * @param progress A number in [-1, 1] representing how far to the left + * or right the window has been swiped. Negative values are swipes + * left, and positives are right. + * @param translate A number in [-w, w], where w is the width of the + * layout. This is equivalent to progress * layout.getWidth(). + */ + void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate); + + void onSwipeCancelled(SwipeDismissLayout layout); + } + + // Cached ViewConfiguration and system-wide constant values + private int mSlop; + private int mMinFlingVelocity; + private int mMaxFlingVelocity; + private long mAnimationTime; + private TimeInterpolator mCancelInterpolator; + private TimeInterpolator mDismissInterpolator; + + // Transient properties + private int mActiveTouchId; + private float mDownX; + private float mDownY; + private boolean mSwiping; + private boolean mDismissed; + private boolean mDiscardIntercept; + private VelocityTracker mVelocityTracker; + private float mTranslationX; + + private OnDismissedListener mDismissedListener; + private OnSwipeProgressChangedListener mProgressListener; + + private float mLastX; + + public SwipeDismissLayout(Context context) { + super(context); + init(context); + } + + public SwipeDismissLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + private void init(Context context) { + ViewConfiguration vc = ViewConfiguration.get(getContext()); + mSlop = vc.getScaledTouchSlop(); + mMinFlingVelocity = vc.getScaledMinimumFlingVelocity(); + mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); + mAnimationTime = getContext().getResources().getInteger( + android.R.integer.config_shortAnimTime); + mCancelInterpolator = new DecelerateInterpolator(1.5f); + mDismissInterpolator = new AccelerateInterpolator(1.5f); + } + + public void setOnDismissedListener(OnDismissedListener listener) { + mDismissedListener = listener; + } + + public void setOnSwipeProgressChangedListener(OnSwipeProgressChangedListener listener) { + mProgressListener = listener; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + // offset because the view is translated during swipe + ev.offsetLocation(mTranslationX, 0); + + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + resetMembers(); + mDownX = ev.getRawX(); + mDownY = ev.getRawY(); + mActiveTouchId = ev.getPointerId(0); + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(ev); + break; + + case MotionEvent.ACTION_POINTER_DOWN: + int actionIndex = ev.getActionIndex(); + mActiveTouchId = ev.getPointerId(actionIndex); + break; + case MotionEvent.ACTION_POINTER_UP: + actionIndex = ev.getActionIndex(); + int pointerId = ev.getPointerId(actionIndex); + if (pointerId == mActiveTouchId) { + // This was our active pointer going up. Choose a new active pointer. + int newActionIndex = actionIndex == 0 ? 1 : 0; + mActiveTouchId = ev.getPointerId(newActionIndex); + } + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + resetMembers(); + break; + + case MotionEvent.ACTION_MOVE: + if (mVelocityTracker == null || mDiscardIntercept) { + break; + } + + int pointerIndex = ev.findPointerIndex(mActiveTouchId); + if (pointerIndex == -1) { + Log.e(TAG, "Invalid pointer index: ignoring."); + mDiscardIntercept = true; + break; + } + float dx = ev.getRawX() - mDownX; + float x = ev.getX(pointerIndex); + float y = ev.getY(pointerIndex); + if (dx != 0 && canScroll(this, false, dx, x, y)) { + mDiscardIntercept = true; + break; + } + updateSwiping(ev); + break; + } + + return !mDiscardIntercept && mSwiping; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (mVelocityTracker == null) { + return super.onTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_UP: + updateDismiss(ev); + if (mDismissed) { + dismiss(); + } else if (mSwiping) { + cancel(); + } + resetMembers(); + break; + + case MotionEvent.ACTION_CANCEL: + cancel(); + resetMembers(); + break; + + case MotionEvent.ACTION_MOVE: + mVelocityTracker.addMovement(ev); + mLastX = ev.getRawX(); + updateSwiping(ev); + if (mSwiping) { + setProgress(ev.getRawX() - mDownX); + break; + } + } + return true; + } + + private void setProgress(float deltaX) { + mTranslationX = deltaX; + if (mProgressListener != null) { + mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX); + } + } + + private void dismiss() { + if (mDismissedListener != null) { + mDismissedListener.onDismissed(this); + } + } + + protected void cancel() { + if (mProgressListener != null) { + mProgressListener.onSwipeCancelled(this); + } + } + + /** + * Resets internal members when canceling. + */ + private void resetMembers() { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + } + mVelocityTracker = null; + mTranslationX = 0; + mDownX = 0; + mDownY = 0; + mSwiping = false; + mDismissed = false; + mDiscardIntercept = false; + } + + private void updateSwiping(MotionEvent ev) { + if (!mSwiping) { + float deltaX = ev.getRawX() - mDownX; + float deltaY = ev.getRawY() - mDownY; + if ((deltaX * deltaX) + (deltaY * deltaY) > mSlop * mSlop) { + mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2; + } else { + mSwiping = false; + } + } + } + + private void updateDismiss(MotionEvent ev) { + float deltaX = ev.getRawX() - mDownX; + if (!mDismissed) { + mVelocityTracker.addMovement(ev); + mVelocityTracker.computeCurrentVelocity(1000); + + if (deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) && + ev.getRawX() >= mLastX) { + mDismissed = true; + } + } + // Check if the user tried to undo this. + if (mDismissed && mSwiping) { + // Check if the user's finger is actually back + if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO)) { + mDismissed = false; + } + } + } + + /** + * Tests scrollability within child views of v in the direction of dx. + * + * @param v View to test for horizontal scrollability + * @param checkV Whether the view v passed should itself be checked for scrollability (true), + * or just its children (false). + * @param dx Delta scrolled in pixels. Only the sign of this is used. + * @param x X coordinate of the active touch point + * @param y Y coordinate of the active touch point + * @return true if child views of v can be scrolled by delta of dx. + */ + protected boolean canScroll(View v, boolean checkV, float dx, float x, float y) { + if (v instanceof ViewGroup) { + final ViewGroup group = (ViewGroup) v; + final int scrollX = v.getScrollX(); + final int scrollY = v.getScrollY(); + final int count = group.getChildCount(); + for (int i = count - 1; i >= 0; i--) { + final View child = group.getChildAt(i); + if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() && + y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && + canScroll(child, true, dx, x + scrollX - child.getLeft(), + y + scrollY - child.getTop())) { + return true; + } + } + } + + return checkV && v.canScrollHorizontally((int) -dx); + } +} diff --git a/core/java/com/android/server/LocalServices.java b/core/java/com/android/server/LocalServices.java new file mode 100644 index 0000000..25dcb30 --- /dev/null +++ b/core/java/com/android/server/LocalServices.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.util.ArrayMap; + +/** + * This class is used in a similar way as ServiceManager, except the services registered here + * are not Binder objects and are only available in the same process. + * + * Once all services are converted to the SystemService interface, this class can be absorbed + * into SystemServiceManager. + * + * {@hide} + */ +public final class LocalServices { + private LocalServices() {} + + private static final ArrayMap<Class<?>, Object> sLocalServiceObjects = + new ArrayMap<Class<?>, Object>(); + + /** + * Returns a local service instance that implements the specified interface. + * + * @param type The type of service. + * @return The service object. + */ + @SuppressWarnings("unchecked") + public static <T> T getService(Class<T> type) { + synchronized (sLocalServiceObjects) { + return (T) sLocalServiceObjects.get(type); + } + } + + /** + * Adds a service instance of the specified interface to the global registry of local services. + */ + public static <T> void addService(Class<T> type, T service) { + synchronized (sLocalServiceObjects) { + if (sLocalServiceObjects.containsKey(type)) { + throw new IllegalStateException("Overriding service registration"); + } + sLocalServiceObjects.put(type, service); + } + } +} diff --git a/core/java/com/android/server/SystemService.java b/core/java/com/android/server/SystemService.java new file mode 100644 index 0000000..e374563 --- /dev/null +++ b/core/java/com/android/server/SystemService.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.Context; +import android.os.IBinder; +import android.os.ServiceManager; + +/** + * The base class for services running in the system process. Override and implement + * the lifecycle event callback methods as needed. + * <p> + * The lifecycle of a SystemService: + * </p><ul> + * <li>The constructor is called and provided with the system {@link Context} + * to initialize the system service. + * <li>{@link #onStart()} is called to get the service running. The service should + * publish its binder interface at this point using + * {@link #publishBinderService(String, IBinder)}. It may also publish additional + * local interfaces that other services within the system server may use to access + * privileged internal functions. + * <li>Then {@link #onBootPhase(int)} is called as many times as there are boot phases + * until {@link #PHASE_BOOT_COMPLETE} is sent, which is the last boot phase. Each phase + * is an opportunity to do special work, like acquiring optional service dependencies, + * waiting to see if SafeMode is enabled, or registering with a service that gets + * started after this one. + * </ul><p> + * NOTE: All lifecycle methods are called from the system server's main looper thread. + * </p> + * + * {@hide} + */ +public abstract class SystemService { + /* + * Boot Phases + */ + public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // maybe should be a dependency? + + /** + * After receiving this boot phase, services can obtain lock settings data. + */ + public static final int PHASE_LOCK_SETTINGS_READY = 480; + + /** + * After receiving this boot phase, services can safely call into core system services + * such as the PowerManager or PackageManager. + */ + public static final int PHASE_SYSTEM_SERVICES_READY = 500; + + /** + * After receiving this boot phase, services can broadcast Intents. + */ + public static final int PHASE_ACTIVITY_MANAGER_READY = 550; + + /** + * After receiving this boot phase, services can start/bind to third party apps. + * Apps will be able to make Binder calls into services at this point. + */ + public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600; + + /** + * After receiving this boot phase, services must have finished all boot-related work. + */ + public static final int PHASE_BOOT_COMPLETE = 1000; + + private final Context mContext; + + /** + * Initializes the system service. + * <p> + * Subclasses must define a single argument constructor that accepts the context + * and passes it to super. + * </p> + * + * @param context The system server context. + */ + public SystemService(Context context) { + mContext = context; + } + + /** + * Gets the system context. + */ + public final Context getContext() { + return mContext; + } + + /** + * Returns true if the system is running in safe mode. + * TODO: we should define in which phase this becomes valid + */ + public final boolean isSafeMode() { + return getManager().isSafeMode(); + } + + /** + * Called when the dependencies listed in the @Service class-annotation are available + * and after the chosen start phase. + * When this method returns, the service should be published. + */ + public abstract void onStart(); + + /** + * Called on each phase of the boot process. Phases before the service's start phase + * (as defined in the @Service annotation) are never received. + * + * @param phase The current boot phase. + */ + public void onBootPhase(int phase) {} + + /** + * Publish the service so it is accessible to other services and apps. + */ + protected final void publishBinderService(String name, IBinder service) { + publishBinderService(name, service, false); + } + + /** + * Publish the service so it is accessible to other services and apps. + */ + protected final void publishBinderService(String name, IBinder service, + boolean allowIsolated) { + ServiceManager.addService(name, service, allowIsolated); + } + + /** + * Get a binder service by its name. + */ + protected final IBinder getBinderService(String name) { + return ServiceManager.getService(name); + } + + /** + * Publish the service so it is only accessible to the system process. + */ + protected final <T> void publishLocalService(Class<T> type, T service) { + LocalServices.addService(type, service); + } + + /** + * Get a local service by interface. + */ + protected final <T> T getLocalService(Class<T> type) { + return LocalServices.getService(type); + } + + private SystemServiceManager getManager() { + return LocalServices.getService(SystemServiceManager.class); + } + +// /** +// * Called when a new user has been created. If your service deals with multiple users, this +// * method should be overridden. +// * +// * @param userHandle The user that was created. +// */ +// public void onUserCreated(int userHandle) { +// } +// +// /** +// * Called when an existing user has started a new session. If your service deals with multiple +// * users, this method should be overridden. +// * +// * @param userHandle The user who started a new session. +// */ +// public void onUserStarted(int userHandle) { +// } +// +// /** +// * Called when a background user session has entered the foreground. If your service deals with +// * multiple users, this method should be overridden. +// * +// * @param userHandle The user who's session entered the foreground. +// */ +// public void onUserForeground(int userHandle) { +// } +// +// /** +// * Called when a foreground user session has entered the background. If your service deals with +// * multiple users, this method should be overridden; +// * +// * @param userHandle The user who's session entered the background. +// */ +// public void onUserBackground(int userHandle) { +// } +// +// /** +// * Called when a user's active session has stopped. If your service deals with multiple users, +// * this method should be overridden. +// * +// * @param userHandle The user who's session has stopped. +// */ +// public void onUserStopped(int userHandle) { +// } +// +// /** +// * Called when a user has been removed from the system. If your service deals with multiple +// * users, this method should be overridden. +// * +// * @param userHandle The user who has been removed. +// */ +// public void onUserRemoved(int userHandle) { +// } +} diff --git a/core/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java new file mode 100644 index 0000000..eb8df0e --- /dev/null +++ b/core/java/com/android/server/SystemServiceManager.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.Context; +import android.util.Slog; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; + +/** + * Manages creating, starting, and other lifecycle events of + * {@link com.android.server.SystemService system services}. + * + * {@hide} + */ +public class SystemServiceManager { + private static final String TAG = "SystemServiceManager"; + + private final Context mContext; + private boolean mSafeMode; + + // Services that should receive lifecycle events. + private final ArrayList<SystemService> mServices = new ArrayList<SystemService>(); + + private int mCurrentPhase = -1; + + public SystemServiceManager(Context context) { + mContext = context; + } + + /** + * Starts a service by class name. + * + * @return The service instance. + */ + @SuppressWarnings("unchecked") + public SystemService startService(String className) throws ClassNotFoundException { + return startService((Class<SystemService>) Class.forName(className)); + } + + /** + * Creates and starts a system service. The class must be a subclass of + * {@link com.android.server.SystemService}. + * + * @param serviceClass A Java class that implements the SystemService interface. + * @return The service instance, never null. + * @throws RuntimeException if the service fails to start. + */ + @SuppressWarnings("unchecked") + public <T extends SystemService> T startService(Class<T> serviceClass) { + final String name = serviceClass.getName(); + Slog.i(TAG, "Starting " + name); + + // Create the service. + if (!SystemService.class.isAssignableFrom(serviceClass)) { + throw new RuntimeException("Failed to create " + name + + ": service must extend " + SystemService.class.getName()); + } + final T service; + try { + Constructor<T> constructor = serviceClass.getConstructor(Context.class); + service = constructor.newInstance(mContext); + } catch (InstantiationException ex) { + throw new RuntimeException("Failed to create service " + name + + ": service could not be instantiated", ex); + } catch (IllegalAccessException ex) { + throw new RuntimeException("Failed to create service " + name + + ": service must have a public constructor with a Context argument", ex); + } catch (NoSuchMethodException ex) { + throw new RuntimeException("Failed to create service " + name + + ": service must have a public constructor with a Context argument", ex); + } catch (InvocationTargetException ex) { + throw new RuntimeException("Failed to create service " + name + + ": service constructor threw an exception", ex); + } + + // Register it. + mServices.add(service); + + // Start it. + try { + service.onStart(); + } catch (RuntimeException ex) { + throw new RuntimeException("Failed to start service " + name + + ": onStart threw an exception", ex); + } + return service; + } + + /** + * Starts the specified boot phase for all system services that have been started up to + * this point. + * + * @param phase The boot phase to start. + */ + public void startBootPhase(final int phase) { + if (phase <= mCurrentPhase) { + throw new IllegalArgumentException("Next phase must be larger than previous"); + } + mCurrentPhase = phase; + + Slog.i(TAG, "Starting phase " + mCurrentPhase); + + final int serviceLen = mServices.size(); + for (int i = 0; i < serviceLen; i++) { + final SystemService service = mServices.get(i); + try { + service.onBootPhase(mCurrentPhase); + } catch (Exception ex) { + throw new RuntimeException("Failed to boot service " + + service.getClass().getName() + + ": onBootPhase threw an exception during phase " + + mCurrentPhase, ex); + } + } + } + + /** Sets the safe mode flag for services to query. */ + public void setSafeMode(boolean safeMode) { + mSafeMode = safeMode; + } + + /** + * Returns whether we are booting into safe mode. + * @return safe mode flag + */ + public boolean isSafeMode() { + return mSafeMode; + } + + /** + * Outputs the state of this manager to the System log. + */ + public void dump() { + StringBuilder builder = new StringBuilder(); + builder.append("Current phase: ").append(mCurrentPhase).append("\n"); + builder.append("Services:\n"); + final int startedLen = mServices.size(); + for (int i = 0; i < startedLen; i++) { + final SystemService service = mServices.get(i); + builder.append("\t") + .append(service.getClass().getSimpleName()) + .append("\n"); + } + + Slog.e(TAG, builder.toString()); + } +} |
