diff options
Diffstat (limited to 'core/java')
162 files changed, 6823 insertions, 1461 deletions
diff --git a/core/java/android/annotation/PrivateApi.java b/core/java/android/annotation/PrivateApi.java new file mode 100644 index 0000000..985eafe --- /dev/null +++ b/core/java/android/annotation/PrivateApi.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Indicates an API is exposed for use by bundled applications. + * <p> + * These APIs are not guaranteed to remain consistent release-to-release, + * and are not for use by apps linking against the SDK. + * @hide + */ +@Retention(RetentionPolicy.SOURCE) +public @interface PrivateApi { +} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 10ef535..63c9fec 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2402,6 +2402,13 @@ public class Activity extends ContextThemeWrapper } return false; } + + /** + * Called when the main window associated with the activity has been dismissed. + */ + public void onWindowDismissed() { + finish(); + } /** * Called to process key events. You can override this to intercept all 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 74266cc..14c495f 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); @@ -1209,20 +1199,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); @@ -1293,17 +1269,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(); @@ -2028,6 +1993,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); @@ -2715,24 +2728,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(); @@ -2747,44 +2742,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(); @@ -3566,26 +3561,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(); @@ -3673,20 +3648,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 */ @@ -4660,5 +4621,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 b103e71..7f8dbba 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -157,7 +157,7 @@ public final class ActivityThread { private static final int LOG_ON_PAUSE_CALLED = 30021; private static final int LOG_ON_RESUME_CALLED = 30022; - static ContextImpl mSystemContext = null; + private ContextImpl mSystemContext; static IPackageManager sPackageManager; @@ -1700,7 +1700,7 @@ public final class ActivityThread { ? mBoundApplication.processName : null) + ")"); packageInfo = - new LoadedApk(this, aInfo, compatInfo, this, baseLoader, + new LoadedApk(this, aInfo, compatInfo, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0); if (includeCode) { @@ -1753,26 +1753,15 @@ public final class ActivityThread { public ContextImpl getSystemContext() { synchronized (this) { if (mSystemContext == null) { - ContextImpl context = - ContextImpl.createSystemContext(this); - LoadedApk info = new LoadedApk(this, "android", context, null, - CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO); - context.init(info, null, this); - context.getResources().updateConfiguration(mResourcesManager.getConfiguration(), - mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY)); - mSystemContext = context; - //Slog.i(TAG, "Created system resources " + context.getResources() - // + ": " + context.getResources().getConfiguration()); + mSystemContext = ContextImpl.createSystemContext(this); } + return mSystemContext; } - return mSystemContext; } public void installSystemApplicationInfo(ApplicationInfo info) { synchronized (this) { - ContextImpl context = getSystemContext(); - context.init(new LoadedApk(this, "android", context, info, - CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO), null, this); + getSystemContext().installSystemApplicationInfo(info); // give ourselves a default profiler mProfiler = new Profiler(); @@ -2258,18 +2247,29 @@ public final class ActivityThread { private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) { - ContextImpl appContext = new ContextImpl(); - appContext.init(r.packageInfo, r.token, this); + 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); @@ -2544,8 +2544,7 @@ public final class ActivityThread { agent = (BackupAgent) cl.loadClass(classname).newInstance(); // set up the agent's context - ContextImpl context = new ContextImpl(); - context.init(packageInfo, null, this); + ContextImpl context = ContextImpl.createAppContext(this, packageInfo); context.setOuterContext(agent); agent.attach(context); @@ -2617,11 +2616,10 @@ public final class ActivityThread { try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); - ContextImpl context = new ContextImpl(); - context.init(packageInfo, null, this); + ContextImpl context = ContextImpl.createAppContext(this, packageInfo); + context.setOuterContext(service); Application app = packageInfo.makeApplication(false, mInstrumentation); - context.setOuterContext(service); service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault()); service.onCreate(); @@ -4217,8 +4215,7 @@ public final class ActivityThread { } updateDefaultDensity(); - final ContextImpl appContext = new ContextImpl(); - appContext.init(data.info, null, this); + final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); if (!Process.isIsolated()) { final File cacheDir = appContext.getCacheDir(); @@ -4334,8 +4331,7 @@ public final class ActivityThread { instrApp.nativeLibraryDir = ii.nativeLibraryDir; LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, appContext.getClassLoader(), false, true); - ContextImpl instrContext = new ContextImpl(); - instrContext.init(pi, null, this); + ContextImpl instrContext = ContextImpl.createAppContext(this, pi); try { java.lang.ClassLoader cl = instrContext.getClassLoader(); @@ -4950,8 +4946,8 @@ public final class ActivityThread { UserHandle.myUserId()); try { mInstrumentation = new Instrumentation(); - ContextImpl context = new ContextImpl(); - context.init(getSystemContext().mPackageInfo, null, this); + ContextImpl context = ContextImpl.createAppContext( + this, getSystemContext().mPackageInfo); Application app = Instrumentation.newApplication(Application.class, context); mAllApplications.add(app); mInitialApplication = app; diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java new file mode 100644 index 0000000..edf21dd --- /dev/null +++ b/core/java/android/app/ActivityView.java @@ -0,0 +1,368 @@ +/* + * 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.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; + + // 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); + + 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); + } + + 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 { + 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 { + 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 { + 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; + } + } + + 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()); + } + } + + private static class ActivityContainerWrapper { + private final IActivityContainer mIActivityContainer; + private final CloseGuard mGuard = CloseGuard.get(); + + ActivityContainerWrapper(IActivityContainer container) { + mIActivityContainer = container; + 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); + } + } + + 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() { + if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called"); + try { + mIActivityContainer.release(); + mGuard.close(); + } catch (RemoteException e) { + } + } + + @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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index aece462..079cf7a 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -36,7 +36,7 @@ import android.os.RemoteException; * API for interacting with "application operation" tracking. * * <p>This API is not generally intended for third party application developers; most - * features are only available to system applicatins. Obtain an instance of it through + * features are only available to system applications. Obtain an instance of it through * {@link Context#getSystemService(String) Context.getSystemService} with * {@link Context#APP_OPS_SERVICE Context.APP_OPS_SERVICE}.</p> */ @@ -878,7 +878,7 @@ public class AppOpsManager { } /** - * Like {@link #checkOp but instead of throwing a {@link SecurityException} it + * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. */ public int checkOpNoThrow(String op, int uid, String packageName) { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index a280448..c5e6ac4 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -129,6 +129,24 @@ final class ApplicationPackageManager extends PackageManager { } @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 { try { @@ -729,6 +747,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 8d127c6..924d656 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; @@ -183,22 +185,31 @@ class ContextImpl extends Context { */ private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs; - /*package*/ LoadedApk mPackageInfo; - private String mBasePackageName; - private String mOpPackageName; - private Resources mResources; - /*package*/ ActivityThread mMainThread; + final ActivityThread mMainThread; + final LoadedApk mPackageInfo; + + private final IBinder mActivityToken; + + private final UserHandle mUser; + + private final ApplicationContentResolver mContentResolver; + + private final String mBasePackageName; + private final String mOpPackageName; + + private final ResourcesManager mResourcesManager; + private final Resources mResources; + private final Display mDisplay; // may be null if default display + private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments(); + private final Configuration mOverrideConfiguration; + + private final boolean mRestricted; + private Context mOuterContext; - private IBinder mActivityToken = null; - private ApplicationContentResolver mContentResolver; private int mThemeResource = 0; private Resources.Theme mTheme = null; private PackageManager mPackageManager; - private Display mDisplay; // may be null if default display private Context mReceiverRestrictedContext = null; - private boolean mRestricted; - private UserHandle mUser; - private ResourcesManager mResourcesManager; private final Object mSync = new Object(); @@ -220,8 +231,6 @@ class ContextImpl extends Context { private static final String[] EMPTY_FILE_LIST = {}; - final private DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments(); - /** * Override this class when the system service constructor needs a * ContextImpl. Else, use StaticServiceFetcher below. @@ -350,16 +359,24 @@ 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(), ctx.mMainThread.getHandler()); }}); - registerService(CONNECTIVITY_SERVICE, new StaticServiceFetcher() { - public Object createStaticService() { + registerService(CONNECTIVITY_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(CONNECTIVITY_SERVICE); - return new ConnectivityManager(IConnectivityManager.Stub.asInterface(b)); + return new ConnectivityManager(IConnectivityManager.Stub.asInterface(b), + ctx.getPackageName()); }}); registerService(COUNTRY_DETECTOR, new StaticServiceFetcher() { @@ -1878,20 +1895,17 @@ class ContextImpl extends Context { @Override public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) throws NameNotFoundException { + final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; if (packageName.equals("system") || packageName.equals("android")) { - final ContextImpl context = new ContextImpl(mMainThread.getSystemContext()); - context.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; - context.init(mPackageInfo, null, mMainThread, mResources, mBasePackageName, user); - return context; + return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, + user, restricted, mDisplay, mOverrideConfiguration); } - LoadedApk pi = - mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags, - user.getIdentifier()); + LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), + flags, user.getIdentifier()); if (pi != null) { - ContextImpl c = new ContextImpl(); - c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; - c.init(pi, null, mMainThread, mResources, mBasePackageName, user); + ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, + user, restricted, mDisplay, mOverrideConfiguration); if (c.mResources != null) { return c; } @@ -1899,7 +1913,7 @@ class ContextImpl extends Context { // Should be a better exception. throw new PackageManager.NameNotFoundException( - "Application package " + packageName + " not found"); + "Application package " + packageName + " not found"); } @Override @@ -1908,12 +1922,8 @@ class ContextImpl extends Context { throw new IllegalArgumentException("overrideConfiguration must not be null"); } - ContextImpl c = new ContextImpl(); - c.init(mPackageInfo, null, mMainThread); - c.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(), - mPackageInfo.getOverlayDirs(), getDisplayId(), overrideConfiguration, - mResources.getCompatibilityInfo(), mActivityToken); - return c; + return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, + mUser, mRestricted, mDisplay, overrideConfiguration); } @Override @@ -1922,15 +1932,8 @@ class ContextImpl extends Context { throw new IllegalArgumentException("display must not be null"); } - int displayId = display.getDisplayId(); - - ContextImpl context = new ContextImpl(); - context.init(mPackageInfo, null, mMainThread); - context.mDisplay = display; - DisplayAdjustments daj = getDisplayAdjustments(displayId); - context.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(), - mPackageInfo.getOverlayDirs(), displayId, null, daj.getCompatibilityInfo(), null); - return context; + return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, + mUser, mRestricted, display, mOverrideConfiguration); } private int getDisplayId() { @@ -1972,43 +1975,76 @@ class ContextImpl extends Context { } static ContextImpl createSystemContext(ActivityThread mainThread) { - final ContextImpl context = new ContextImpl(); - context.init(Resources.getSystem(), mainThread, Process.myUserHandle()); + LoadedApk packageInfo = new LoadedApk(mainThread); + ContextImpl context = new ContextImpl(null, mainThread, + packageInfo, null, null, false, null, null); + context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), + context.mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY)); return context; } - ContextImpl() { - mOuterContext = this; + static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { + if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); + return new ContextImpl(null, mainThread, + packageInfo, null, null, false, null, null); } - /** - * Create a new ApplicationContext from an existing one. The new one - * works and operates the same as the one it is copying. - * - * @param context Existing application context. - */ - public ContextImpl(ContextImpl context) { - mPackageInfo = context.mPackageInfo; - mBasePackageName = context.mBasePackageName; - mOpPackageName = context.mOpPackageName; - mResources = context.mResources; - mMainThread = context.mMainThread; - mContentResolver = context.mContentResolver; - mUser = context.mUser; - mDisplay = context.mDisplay; - mOuterContext = this; - mDisplayAdjustments.setCompatibilityInfo(mPackageInfo.getCompatibilityInfo()); + static ContextImpl createActivityContext(ActivityThread mainThread, + LoadedApk packageInfo, IBinder activityToken) { + if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); + if (activityToken == null) throw new IllegalArgumentException("activityInfo"); + return new ContextImpl(null, mainThread, + packageInfo, activityToken, null, false, null, null); } - final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread) { - init(packageInfo, activityToken, mainThread, null, null, Process.myUserHandle()); - } + private ContextImpl(ContextImpl container, ActivityThread mainThread, + LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, + Display display, Configuration overrideConfiguration) { + mOuterContext = this; + + mMainThread = mainThread; + mActivityToken = activityToken; + mRestricted = restricted; + + if (user == null) { + user = Process.myUserHandle(); + } + mUser = user; - final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread, - Resources container, String basePackageName, UserHandle user) { mPackageInfo = packageInfo; - if (basePackageName != null) { - mBasePackageName = mOpPackageName = basePackageName; + mContentResolver = new ApplicationContentResolver(this, mainThread, user); + mResourcesManager = ResourcesManager.getInstance(); + mDisplay = display; + mOverrideConfiguration = overrideConfiguration; + + final int displayId = getDisplayId(); + CompatibilityInfo compatInfo = null; + if (container != null) { + compatInfo = container.getDisplayAdjustments(displayId).getCompatibilityInfo(); + } + if (compatInfo == null && displayId == Display.DEFAULT_DISPLAY) { + compatInfo = packageInfo.getCompatibilityInfo(); + } + mDisplayAdjustments.setCompatibilityInfo(compatInfo); + mDisplayAdjustments.setActivityToken(activityToken); + + Resources resources = packageInfo.getResources(mainThread); + if (resources != null) { + if (activityToken != null + || displayId != Display.DEFAULT_DISPLAY + || overrideConfiguration != null + || (compatInfo != null && compatInfo.applicationScale + != resources.getCompatibilityInfo().applicationScale)) { + resources = mResourcesManager.getTopLevelResources( + packageInfo.getResDir(), packageInfo.getOverlayDirs(), displayId, + overrideConfiguration, compatInfo, activityToken); + } + } + mResources = resources; + + if (container != null) { + mBasePackageName = container.mBasePackageName; + mOpPackageName = container.mOpPackageName; } else { mBasePackageName = packageInfo.mPackageName; ApplicationInfo ainfo = packageInfo.getApplicationInfo(); @@ -2022,45 +2058,10 @@ class ContextImpl extends Context { mOpPackageName = mBasePackageName; } } - mResources = mPackageInfo.getResources(mainThread); - mResourcesManager = ResourcesManager.getInstance(); - - CompatibilityInfo compatInfo = - container == null ? null : container.getCompatibilityInfo(); - if (mResources != null && - ((compatInfo != null && compatInfo.applicationScale != - mResources.getCompatibilityInfo().applicationScale) - || activityToken != null)) { - if (DEBUG) { - Log.d(TAG, "loaded context has different scaling. Using container's" + - " compatiblity info:" + container.getDisplayMetrics()); - } - if (compatInfo == null) { - compatInfo = packageInfo.getCompatibilityInfo(); - } - mDisplayAdjustments.setCompatibilityInfo(compatInfo); - mDisplayAdjustments.setActivityToken(activityToken); - mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(), - mPackageInfo.getOverlayDirs(), Display.DEFAULT_DISPLAY, null, compatInfo, - activityToken); - } else { - mDisplayAdjustments.setCompatibilityInfo(packageInfo.getCompatibilityInfo()); - mDisplayAdjustments.setActivityToken(activityToken); - } - mMainThread = mainThread; - mActivityToken = activityToken; - mContentResolver = new ApplicationContentResolver(this, mainThread, user); - mUser = user; } - final void init(Resources resources, ActivityThread mainThread, UserHandle user) { - mPackageInfo = null; - mBasePackageName = null; - mOpPackageName = null; - mResources = resources; - mMainThread = mainThread; - mContentResolver = new ApplicationContentResolver(this, mainThread, user); - mUser = user; + void installSystemApplicationInfo(ApplicationInfo info) { + mPackageInfo.installSystemApplicationInfo(info); } final void scheduleFinalCleanup(String who, String what) { diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index cda2c5f..a8277b5 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -695,6 +695,10 @@ public class Dialog implements DialogInterface, Window.Callback, public void onDetachedFromWindow() { } + + 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..cc3b10c --- /dev/null +++ b/core/java/android/app/IActivityContainer.aidl @@ -0,0 +1,35 @@ +/** + * 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); + 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..7f6d2c3 --- /dev/null +++ b/core/java/android/app/IActivityContainerCallback.aidl @@ -0,0 +1,24 @@ +/** + * 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); +} diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 77c2ea0..4e2a57d 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 */ @@ -229,8 +228,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; @@ -250,8 +247,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, @@ -408,6 +403,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 */ @@ -514,7 +521,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; @@ -550,8 +556,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; @@ -678,12 +682,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; @@ -694,4 +698,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/LoadedApk.java b/core/java/android/app/LoadedApk.java index 0115d1b..d409352 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -72,7 +72,7 @@ public final class LoadedApk { private static final String TAG = "LoadedApk"; private final ActivityThread mActivityThread; - private final ApplicationInfo mApplicationInfo; + private ApplicationInfo mApplicationInfo; final String mPackageName; private final String mAppDir; private final String mResDir; @@ -111,8 +111,7 @@ public final class LoadedApk { * so MUST NOT call back out to the activity manager. */ public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo, - CompatibilityInfo compatInfo, - ActivityThread mainThread, ClassLoader baseLoader, + CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode) { mActivityThread = activityThread; mApplicationInfo = aInfo; @@ -134,31 +133,17 @@ public final class LoadedApk { mSecurityViolation = securityViolation; mIncludeCode = includeCode; mDisplayAdjustments.setCompatibilityInfo(compatInfo); - - if (mAppDir == null) { - if (ActivityThread.mSystemContext == null) { - ActivityThread.mSystemContext = - ContextImpl.createSystemContext(mainThread); - ResourcesManager resourcesManager = ResourcesManager.getInstance(); - ActivityThread.mSystemContext.getResources().updateConfiguration( - resourcesManager.getConfiguration(), - resourcesManager.getDisplayMetricsLocked( - Display.DEFAULT_DISPLAY, mDisplayAdjustments), compatInfo); - //Slog.i(TAG, "Created system resources " - // + mSystemContext.getResources() + ": " - // + mSystemContext.getResources().getConfiguration()); - } - mClassLoader = ActivityThread.mSystemContext.getClassLoader(); - mResources = ActivityThread.mSystemContext.getResources(); - } } - public LoadedApk(ActivityThread activityThread, String name, - Context systemContext, ApplicationInfo info, CompatibilityInfo compatInfo) { + /** + * Create information about the system package. + * Must call {@link #installSystemApplicationInfo} later. + */ + LoadedApk(ActivityThread activityThread) { mActivityThread = activityThread; - mApplicationInfo = info != null ? info : new ApplicationInfo(); - mApplicationInfo.packageName = name; - mPackageName = name; + mApplicationInfo = new ApplicationInfo(); + mApplicationInfo.packageName = "android"; + mPackageName = "android"; mAppDir = null; mResDir = null; mOverlayDirs = null; @@ -169,9 +154,16 @@ public final class LoadedApk { mBaseClassLoader = null; mSecurityViolation = false; mIncludeCode = true; - mClassLoader = systemContext.getClassLoader(); - mResources = systemContext.getResources(); - mDisplayAdjustments.setCompatibilityInfo(compatInfo); + mClassLoader = ClassLoader.getSystemClassLoader(); + mResources = Resources.getSystem(); + } + + /** + * Sets application info about the system package. + */ + void installSystemApplicationInfo(ApplicationInfo info) { + assert info.packageName.equals("android"); + mApplicationInfo = info; } public String getPackageName() { @@ -513,8 +505,7 @@ public final class LoadedApk { try { java.lang.ClassLoader cl = getClassLoader(); - ContextImpl appContext = new ContextImpl(); - appContext.init(this, null, mActivityThread); + ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); appContext.setOuterContext(app); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index c63e586..e6b5de1 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -348,6 +348,13 @@ 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; + public int flags; /** @@ -394,41 +401,82 @@ public class Notification implements Parcelable public int priority; /** - * @hide - * Notification type: incoming call (voice or video) or similar synchronous communication request. + * Notification category: incoming call (voice or video) or similar synchronous communication request. */ - public static final String KIND_CALL = "android.call"; + public static final String CATEGORY_CALL = "call"; /** - * @hide - * Notification type: incoming direct message (SMS, instant message, etc.). + * Notification category: incoming direct message (SMS, instant message, etc.). */ - public static final String KIND_MESSAGE = "android.message"; + public static final String CATEGORY_MESSAGE = "msg"; /** - * @hide - * Notification type: asynchronous bulk message (email). + * Notification category: asynchronous bulk message (email). */ - public static final String KIND_EMAIL = "android.email"; + public static final String CATEGORY_EMAIL = "email"; /** - * @hide - * Notification type: calendar event. + * Notification category: calendar event. */ - public static final String KIND_EVENT = "android.event"; + public static final String CATEGORY_EVENT = "event"; /** - * @hide - * Notification type: promotion or advertisement. + * Notification category: promotion or advertisement. */ - public static final String KIND_PROMO = "android.promo"; + public static final String CATEGORY_PROMO = "promo"; /** - * @hide - * If this notification matches of one or more special types (see the <code>KIND_*</code> - * constants), add them here, best match first. + * Notification category: alarm or timer. + */ + public static final String CATEGORY_ALARM = "alarm"; + + /** + * Notification category: progress of a long-running background operation. + */ + public static final String CATEGORY_PROGRESS = "progress"; + + /** + * Notification category: social network or sharing update. + */ + public static final String CATEGORY_SOCIAL = "social"; + + /** + * Notification category: error in background operation or authentication status. + */ + public static final String CATEGORY_ERROR = "err"; + + /** + * Notification category: media transport control for playback. + */ + public static final String CATEGORY_TRANSPORT = "transport"; + + /** + * Notification category: system or device status update. Reserved for system use. */ - public String[] kind; + public static final String CATEGORY_SYSTEM = "sys"; + + /** + * Notification category: indication of running background service. + */ + 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. + */ + public static final String CATEGORY_RECOMMENDATION = "recommendation"; + + /** + * Notification category: ongoing information about device or contextual status. + */ + 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. + */ + public String category; /** * Additional semantic data to be carried around with this Notification. @@ -561,6 +609,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 */ @@ -750,7 +805,7 @@ public class Notification implements Parcelable priority = parcel.readInt(); - kind = parcel.createStringArray(); // may set kind to null + category = parcel.readString(); extras = parcel.readBundle(); // may be null @@ -815,12 +870,7 @@ 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; if (this.extras != null) { try { @@ -957,7 +1007,7 @@ public class Notification implements Parcelable parcel.writeInt(priority); - parcel.writeStringArray(kind); // ok for null + parcel.writeString(category); parcel.writeBundle(extras); // null ok @@ -1077,16 +1127,7 @@ 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]); - } - } - sb.append("]"); + sb.append(" category="); sb.append(this.category); if (actions != null) { sb.append(" "); sb.append(actions.length); @@ -1165,7 +1206,7 @@ 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 Bundle mExtras; private int mPriority; private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS); @@ -1532,6 +1573,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,24 +1608,41 @@ public class Notification implements Parcelable } /** - * @hide + * Set the notification category. * - * Add a kind (category) to this notification. Optional. + * @see Notification#category + */ + public Builder setCategory(String category) { + mCategory = category; + return this; + } + + /** + * Merge additional metadata into this notification. * - * @see Notification#kind + * <p>Values within the Bundle will replace existing extras values in this Builder. + * + * @see Notification#extras */ - public Builder addKind(String k) { - mKindList.add(k); + public Builder addExtras(Bundle bag) { + if (mExtras == null) { + mExtras = new Bundle(bag); + } else { + mExtras.putAll(bag); + } 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) { @@ -1582,6 +1651,23 @@ public class Notification implements Parcelable } /** + * 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> @@ -1778,7 +1864,7 @@ public class Notification implements Parcelable RemoteViews button = new RemoteViews(mContext.getPackageName(), tombstone ? R.layout.notification_action_tombstone : R.layout.notification_action); - button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0); + button.setTextViewCompoundDrawablesRelative(R.id.action0, action.icon, 0, 0, 0); button.setTextViewText(R.id.action0, action.title); if (!tombstone) { button.setOnClickPendingIntent(R.id.action0, action.actionIntent); @@ -1819,12 +1905,7 @@ 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.priority = mPriority; if (mActions.size() > 0) { n.actions = new Action[mActions.size()]; @@ -1839,7 +1920,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 +1958,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 +1981,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; diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index bdd0adb..7129e9e 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -87,23 +87,25 @@ public final class PendingIntent implements Parcelable { private final IIntentSender mTarget; /** - * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and - * {@link #getService}: this - * PendingIntent can only be used once. If set, after + * Flag indicating that this PendingIntent can be used only once. + * For use with {@link #getActivity}, {@link #getBroadcast}, and + * {@link #getService}. <p>If set, after * {@link #send()} is called on it, it will be automatically * canceled for you and any future attempt to send through it will fail. */ public static final int FLAG_ONE_SHOT = 1<<30; /** - * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and - * {@link #getService}: if the described PendingIntent does not already - * exist, 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}. */ public static final int FLAG_NO_CREATE = 1<<29; /** - * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and - * {@link #getService}: if the described PendingIntent already exists, - * the current one is canceled before generating a new one. You can use + * Flag indicating that if the described PendingIntent already exists, + * the current one should be canceled before generating a new one. + * For use with {@link #getActivity}, {@link #getBroadcast}, and + * {@link #getService}. <p>You can use * this to retrieve a new PendingIntent when you are only changing the * extra data in the Intent; by canceling the previous pending intent, * this ensures that only entities given the new data will be able to @@ -112,10 +114,10 @@ public final class PendingIntent implements Parcelable { */ public static final int FLAG_CANCEL_CURRENT = 1<<28; /** - * Flag for use with {@link #getActivity}, {@link #getBroadcast}, and - * {@link #getService}: if the described PendingIntent already exists, - * then keep it but its replace its extra data with what is in this new - * Intent. This can be used if you are creating intents where only the + * Flag indicating that if the described PendingIntent already exists, + * then keep it but replace its extra data with what is in this new + * Intent. For use with {@link #getActivity}, {@link #getBroadcast}, and + * {@link #getService}. <p>This can be used if you are creating intents where only the * extras change, and don't care that any entities that received your * previous PendingIntent will be able to launch it with your new * extras even if they are not explicitly given to it. @@ -203,7 +205,7 @@ public final class PendingIntent implements Parcelable { * <p class="note">For security reasons, the {@link android.content.Intent} * you supply here should almost always be an <em>explicit intent</em>, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}</p> * * @param context The Context in which this PendingIntent should start * the activity. @@ -234,7 +236,7 @@ public final class PendingIntent implements Parcelable { * <p class="note">For security reasons, the {@link android.content.Intent} * you supply here should almost always be an <em>explicit intent</em>, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}</p> * * @param context The Context in which this PendingIntent should start * the activity. @@ -326,7 +328,7 @@ public final class PendingIntent implements Parcelable { * <p class="note">For security reasons, the {@link android.content.Intent} objects * you supply here should almost always be <em>explicit intents</em>, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}</p> * * @param context The Context in which this PendingIntent should start * the activity. @@ -376,7 +378,7 @@ public final class PendingIntent implements Parcelable { * <p class="note">For security reasons, the {@link android.content.Intent} objects * you supply here should almost always be <em>explicit intents</em>, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}</p> * * @param context The Context in which this PendingIntent should start * the activity. @@ -446,7 +448,7 @@ public final class PendingIntent implements Parcelable { * <p class="note">For security reasons, the {@link android.content.Intent} * you supply here should almost always be an <em>explicit intent</em>, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}</p> * * @param context The Context in which this PendingIntent should perform * the broadcast. @@ -500,7 +502,7 @@ public final class PendingIntent implements Parcelable { * <p class="note">For security reasons, the {@link android.content.Intent} * you supply here should almost always be an <em>explicit intent</em>, * that is specify an explicit component to be delivered to through - * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p> + * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}</p> * * @param context The Context in which this PendingIntent should start * the service. 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 ced72f8..f291e82 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -45,9 +45,7 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; -import android.util.DisplayMetrics; import android.util.Log; -import android.view.WindowManager; import android.view.WindowManagerGlobal; import java.io.BufferedInputStream; @@ -294,9 +292,8 @@ public class WallpaperManager { try { BitmapFactory.Options options = new BitmapFactory.Options(); - Bitmap bm = BitmapFactory.decodeFileDescriptor( + return BitmapFactory.decodeFileDescriptor( fd.getFileDescriptor(), null, options); - return generateBitmap(context, bm, width, height); } catch (OutOfMemoryError e) { Log.w(TAG, "Can't decode file", e); } finally { @@ -323,8 +320,7 @@ public class WallpaperManager { try { BitmapFactory.Options options = new BitmapFactory.Options(); - Bitmap bm = BitmapFactory.decodeStream(is, null, options); - return generateBitmap(context, bm, width, height); + return BitmapFactory.decodeStream(is, null, options); } catch (OutOfMemoryError e) { Log.w(TAG, "Can't decode stream", e); } finally { @@ -1029,62 +1025,4 @@ public class WallpaperManager { public void clear() throws IOException { setResource(com.android.internal.R.drawable.default_wallpaper); } - - static Bitmap generateBitmap(Context context, Bitmap bm, int width, int height) { - if (bm == null) { - return null; - } - - WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); - DisplayMetrics metrics = new DisplayMetrics(); - wm.getDefaultDisplay().getMetrics(metrics); - bm.setDensity(metrics.noncompatDensityDpi); - - if (width <= 0 || height <= 0 - || (bm.getWidth() == width && bm.getHeight() == height)) { - return bm; - } - - // This is the final bitmap we want to return. - try { - Bitmap newbm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - newbm.setDensity(metrics.noncompatDensityDpi); - - Canvas c = new Canvas(newbm); - Rect targetRect = new Rect(); - targetRect.right = bm.getWidth(); - targetRect.bottom = bm.getHeight(); - - int deltaw = width - targetRect.right; - int deltah = height - targetRect.bottom; - - if (deltaw > 0 || deltah > 0) { - // We need to scale up so it covers the entire area. - float scale; - if (deltaw > deltah) { - scale = width / (float)targetRect.right; - } else { - scale = height / (float)targetRect.bottom; - } - targetRect.right = (int)(targetRect.right*scale); - targetRect.bottom = (int)(targetRect.bottom*scale); - deltaw = width - targetRect.right; - deltah = height - targetRect.bottom; - } - - targetRect.offset(deltaw/2, deltah/2); - - Paint paint = new Paint(); - paint.setFilterBitmap(true); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); - c.drawBitmap(bm, null, targetRect, paint); - - bm.recycle(); - c.setBitmap(null); - return newbm; - } catch (OutOfMemoryError e) { - Log.w(TAG, "Can't generate default bitmap", e); - return bm; - } - } } diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index bb4f5f1..12ee3b6 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -43,14 +43,14 @@ interface IBackupManager { void dataChanged(String packageName); /** - * Erase all backed-up data for the given package from the storage + * Erase all backed-up data for the given package from the given storage * destination. * * Any application can invoke this method for its own package, but * only callers who hold the android.permission.BACKUP permission * may invoke it for arbitrary packages. */ - void clearBackupData(String packageName); + void clearBackupData(String transportName, String packageName); /** * Notifies the Backup Manager Service that an agent has become available. This diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index e2bc80a..646be06 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -20,21 +20,23 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.util.Pair; + import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.HashMap; -import java.util.LinkedList; +import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Random; @@ -214,6 +216,22 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; /** + * Broadcast Action: Indicate BLE Advertising is started. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED = + "android.bluetooth.adapter.action.ADVERTISING_STARTED"; + + /** + * Broadcast Action: Indicated BLE Advertising is stopped. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED = + "android.bluetooth.adapter.action.ADVERTISING_STOPPED"; + + /** * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} * intents to request the current scan mode. Possible values are: * {@link #SCAN_MODE_NONE}, @@ -251,7 +269,6 @@ public final class BluetoothAdapter { */ public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23; - /** * Broadcast Action: The local Bluetooth adapter has started the remote * device discovery process. @@ -350,9 +367,27 @@ public final class BluetoothAdapter { /** The profile is in disconnecting state */ public static final int STATE_DISCONNECTING = 3; + /** States for Bluetooth LE advertising */ + /** @hide */ + public static final int STATE_ADVERTISE_STARTING = 0; + /** @hide */ + public static final int STATE_ADVERTISE_STARTED = 1; + /** @hide */ + public static final int STATE_ADVERTISE_STOPPING = 2; + /** @hide */ + public static final int STATE_ADVERTISE_STOPPED = 3; + /** + * Force stopping advertising without callback in case the advertising app dies. + * @hide + */ + public static final int STATE_ADVERTISE_FORCE_STOPPING = 4; + /** @hide */ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; + /** @hide */ + public static final int ADVERTISE_CALLBACK_SUCCESS = 0; + private static final int ADDRESS_LENGTH = 17; /** @@ -365,6 +400,10 @@ public final class BluetoothAdapter { private IBluetooth mService; private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients; + private BluetoothAdvScanData mBluetoothAdvScanData = null; + private GattCallbackWrapper mAdvertisingGattCallback; + private final Handler mHandler; // Handler to post the advertise callback to run on main thread. + private final Object mLock = new Object(); /** * Get a handle to the default local Bluetooth adapter. @@ -400,6 +439,7 @@ public final class BluetoothAdapter { } catch (RemoteException e) {Log.e(TAG, "", e);} mManagerService = managerService; mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>(); + mHandler = new Handler(Looper.getMainLooper()); } /** @@ -438,6 +478,129 @@ public final class BluetoothAdapter { } /** + * Returns a {@link BluetoothAdvScanData} object representing advertising data. + * Data will be reset when bluetooth service is turned off. + * @hide + */ + public BluetoothAdvScanData getAdvScanData() { + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + Log.e(TAG, "failed to start, iGatt null"); + return null; + } + if (mBluetoothAdvScanData == null) { + mBluetoothAdvScanData = new BluetoothAdvScanData(iGatt, BluetoothAdvScanData.AD); + } + return mBluetoothAdvScanData; + } catch (RemoteException e) { + Log.e(TAG, "failed to get advScanData, error: " + e); + return null; + } + } + + /** + * Interface for BLE advertising callback. + * + * @hide + */ + public interface AdvertiseCallback { + /** + * Callback when advertise starts. + * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure. + */ + void onAdvertiseStart(int status); + /** + * Callback when advertise stops. + * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure. + */ + void onAdvertiseStop(int status); + } + + /** + * Start BLE advertising using current {@link BluetoothAdvScanData}. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} + * + * @param callback - {@link AdvertiseCallback} + * @return true if BLE advertising succeeds, false otherwise. + * @hide + */ + public boolean startAdvertising(final AdvertiseCallback callback) { + if (getState() != STATE_ON) return false; + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported. + return false; + } + // Restart/reset advertising packets if advertising is in progress. + if (isAdvertising()) { + // Invalid advertising callback. + if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) { + Log.e(TAG, "failed to restart advertising, invalid callback"); + return false; + } + iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle); + // Run the callback from main thread. + mHandler.post(new Runnable() { + @Override + public void run() { + // callback with status success. + callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS); + } + }); + return true; + } + UUID uuid = UUID.randomUUID(); + GattCallbackWrapper wrapper = + new GattCallbackWrapper(this, null, null, callback); + iGatt.registerClient(new ParcelUuid(uuid), wrapper); + if (!wrapper.advertiseStarted()) { + return false; + } + synchronized (mLock) { + mAdvertisingGattCallback = wrapper; + } + return true; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** + * 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. + * @hide + */ + public boolean stopAdvertising(AdvertiseCallback callback) { + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + if (iGatt == null) { + // BLE is not supported + return false; + } + if (mAdvertisingGattCallback == null) { + // no callback. + return false; + } + // Make sure same callback is used for start and stop advertising. + if (callback != mAdvertisingGattCallback.mAdvertiseCallback) { + Log.e(TAG, "must use the same callback for star/stop advertising"); + return false; + } + mAdvertisingGattCallback.stopAdvertising(); + return true; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** * Return true if Bluetooth is currently enabled and ready for use. * <p>Equivalent to: * <code>getBluetoothState() == STATE_ON</code> @@ -849,6 +1012,23 @@ public final class BluetoothAdapter { } /** + * Returns whether BLE is currently advertising. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. + * + * @hide + */ + public boolean isAdvertising() { + if (getState() != STATE_ON) return false; + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + return iGatt.isAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return false; + } + + /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. * <p>If Bluetooth state is not {@link #STATE_ON}, this API @@ -1272,6 +1452,8 @@ public final class BluetoothAdapter { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; + // Reset bluetooth adv scan data when Gatt service is down. + mBluetoothAdvScanData = null; for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ try { if (cb != null) { @@ -1547,7 +1729,9 @@ public final class BluetoothAdapter { private static final int LE_CALLBACK_REG_TIMEOUT = 2000; private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; + private final AdvertiseCallback mAdvertiseCallback; private final LeScanCallback mLeScanCb; + // mLeHandle 0: not registered // -1: scan stopped // >0: registered and scan started @@ -1561,16 +1745,34 @@ public final class BluetoothAdapter { mLeScanCb = leScanCb; mScanFilter = uuid; mLeHandle = 0; + mAdvertiseCallback = null; + } + + public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb, + UUID[] uuid, AdvertiseCallback callback) { + mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter); + mLeScanCb = leScanCb; + mScanFilter = uuid; + mLeHandle = 0; + mAdvertiseCallback = callback; } public boolean scanStarted() { + return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT); + } + + public boolean advertiseStarted() { + // Wait for registeration callback. + return waitForRegisteration(1); + } + + private boolean waitForRegisteration(int maxWaitCount) { boolean started = false; synchronized(this) { if (mLeHandle == -1) return false; - int count = 0; // wait for callback registration and LE scan to start - while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) { + while (mLeHandle == 0 && count < maxWaitCount) { try { wait(LE_CALLBACK_REG_TIMEOUT); } catch (InterruptedException e) { @@ -1583,6 +1785,27 @@ public final class BluetoothAdapter { return started; } + public void stopAdvertising() { + synchronized (this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); + iGatt.stopAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop advertising" + e); + } + } else { + Log.e(TAG, "stopAdvertising, BluetoothAdapter is null"); + } + notifyAll(); + } + } + public void stopLeScan() { synchronized(this) { if (mLeHandle <= 0) { @@ -1624,14 +1847,18 @@ public final class BluetoothAdapter { BluetoothAdapter adapter = mBluetoothAdapter.get(); if (adapter != null) { iGatt = adapter.getBluetoothManager().getBluetoothGatt(); - if (mScanFilter == null) { - iGatt.startScan(mLeHandle, false); + if (mAdvertiseCallback != null) { + iGatt.startAdvertising(mLeHandle); } else { - ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(mScanFilter[i]); - } - iGatt.startScanWithUuids(mLeHandle, false, uuids); + if (mScanFilter == null) { + iGatt.startScan(mLeHandle, false); + } else { + ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(mScanFilter[i]); + } + iGatt.startScanWithUuids(mLeHandle, false, uuids); + } } } else { Log.e(TAG, "onClientRegistered, BluetoothAdapter null"); @@ -1642,7 +1869,7 @@ public final class BluetoothAdapter { mLeHandle = -1; } if (mLeHandle == -1) { - // registration succeeded but start scan failed + // registration succeeded but start scan or advertise failed if (iGatt != null) { try { iGatt.unregisterClient(mLeHandle); @@ -1670,7 +1897,7 @@ public final class BluetoothAdapter { * @hide */ public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); // Check null in case the scan has been stopped synchronized(this) { @@ -1759,9 +1986,53 @@ public final class BluetoothAdapter { // no op } - public void onListen(int status) { + public void onAdvertiseStateChange(int advertiseState, int status) { + Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status); + if (advertiseState == STATE_ADVERTISE_STARTED) { + 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) { + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = + adapter.getBluetoothManager().getBluetoothGatt(); + Log.d(TAG, "unregistering client " + mLeHandle); + iGatt.unregisterClient(mLeHandle); + // Reset advertise app handle. + mLeHandle = -1; + adapter.mAdvertisingGattCallback = null; + } catch (RemoteException e) { + Log.e(TAG, "Failed to unregister client" + e); + } + } else { + Log.e(TAG, "cannot unregister client, BluetoothAdapter is null"); + } + } + } + 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/BluetoothAdvScanData.java b/core/java/android/bluetooth/BluetoothAdvScanData.java new file mode 100644 index 0000000..df2c256 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothAdvScanData.java @@ -0,0 +1,152 @@ +/* + * 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.bluetooth; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; + +import java.util.Collections; +import java.util.List; + + +/** + * This class provides the public APIs to set advertising and scan response data when BLE device + * operates in peripheral mode. <br> + * The exact format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11 + * @hide + */ +public final class BluetoothAdvScanData { + + /** + * Available data types of {@link BluetoothAdvScanData}. + */ + public static final int AD = 0; // Advertising Data + public static final int SCAN_RESPONSE = 1; // Scan Response + public static final int EIR = 2; // Extended Inquiry Response + + private static final String TAG = "BluetoothAdvScanData"; + + /** + * Data type of BluetoothAdvScanData. + */ + private final int mDataType; + /** + * Bluetooth Gatt Service. + */ + private IBluetoothGatt mBluetoothGatt; + + /** + * @param mBluetoothGatt + * @param dataType + */ + public BluetoothAdvScanData(IBluetoothGatt mBluetoothGatt, int dataType) { + this.mBluetoothGatt = mBluetoothGatt; + this.mDataType = dataType; + } + + /** + * @return advertising data type. + */ + public int getDataType() { + return mDataType; + } + + /** + * Set manufactureCode and manufactureData. + * Returns true if manufacturer data is set, false if there is no enough room to set + * manufacturer data or the data is already set. + * @param manufacturerCode - unique identifier for the manufacturer + * @param manufacturerData - data associated with the specific manufacturer. + */ + public boolean setManufacturerData(int manufacturerCode, byte[] manufacturerData) { + if (mDataType != AD) return false; + try { + return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData); + } catch (RemoteException e) { + Log.e(TAG, "Unable to set manufacturer id and data.", e); + return false; + } + } + + /** + * Set service data. Note the service data can only be set when the data type is {@code AD}; + * @param serviceData + */ + public boolean setServiceData(byte[] serviceData) { + + if (mDataType != AD) return false; + if (serviceData == null) return false; + try { + return mBluetoothGatt.setAdvServiceData(serviceData); + } catch (RemoteException e) { + Log.e(TAG, "Unable to set service data.", e); + return false; + } + } + + /** + * Returns an immutable list of service uuids that will be advertised. + */ + public List<ParcelUuid> getServiceUuids() { + try { + return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids()); + } catch (RemoteException e) { + Log.e(TAG, "Unable to get service uuids.", e); + return null; + } + } + + /** + * Returns manufacturer data. + */ + public byte[] getManufacturerData() { + if (mBluetoothGatt == null) return null; + try { + return mBluetoothGatt.getAdvManufacturerData(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to get manufacturer data.", e); + return null; + } + } + + /** + * Returns service data. + */ + public byte[] getServiceData() { + if (mBluetoothGatt == null) return null; + try { + return mBluetoothGatt.getAdvServiceData(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to get service data.", e); + return null; + } + } + + /** + * Remove manufacturer data based on given manufacturer code. + * @param manufacturerCode + */ + public void removeManufacturerCodeAndData(int manufacturerCode) { + if (mBluetoothGatt != null) { + try { + mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode); + } catch (RemoteException e) { + Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e); + } + } + } +} diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index a2bb78c..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) { @@ -555,11 +585,29 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Listen command status callback + * Advertise state change callback * @hide */ - public void onListen(int status) { - if (DBG) Log.d(TAG, "onListen() - status=" + status); + 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); + } } }; @@ -693,71 +741,6 @@ public final class BluetoothGatt implements BluetoothProfile { return true; } - /** - * Starts or stops sending of advertisement packages to listen for connection - * requests from a central devices. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param start Start or stop advertising - */ - /*package*/ void listen(boolean start) { - if (mContext == null || !mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { - throw new UnsupportedOperationException("BluetoothGatt#listen is blocked"); - } - if (DBG) Log.d(TAG, "listen() - start: " + start); - if (mService == null || mClientIf == 0) return; - - try { - mService.clientListen(mClientIf, start); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - - /** - * Sets the advertising data contained in the adv. response packet. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param advData true to set adv. data, false to set scan response - * @param includeName Inlucde the name in the adv. response - * @param includeTxPower Include TX power value - * @param minInterval Minimum desired scan interval (optional) - * @param maxInterval Maximum desired scan interval (optional) - * @param appearance The appearance flags for the device (optional) - * @param manufacturerData Manufacturer specific data including company ID (optional) - */ - /*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower, - Integer minInterval, Integer maxInterval, - Integer appearance, Byte[] manufacturerData) { - if (mContext == null || !mContext.getResources(). - getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) { - throw new UnsupportedOperationException("BluetoothGatt#setAdvData is blocked"); - } - if (DBG) Log.d(TAG, "setAdvData()"); - if (mService == null || mClientIf == 0) return; - - byte[] data = new byte[0]; - if (manufacturerData != null) { - data = new byte[manufacturerData.length]; - for(int i = 0; i != manufacturerData.length; ++i) { - data[i] = manufacturerData[i]; - } - } - - try { - mService.setAdvData(mClientIf, !advData, - includeName, includeTxPower, - minInterval != null ? minInterval : 0, - maxInterval != null ? maxInterval : 0, - appearance != null ? appearance : 0, data); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - /** * Disconnects an established connection, or cancels a connection attempt * currently in progress. @@ -909,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(), @@ -916,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; } @@ -948,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(), @@ -957,6 +951,7 @@ public final class BluetoothGatt implements BluetoothProfile { characteristic.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -988,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()), @@ -996,6 +996,7 @@ public final class BluetoothGatt implements BluetoothProfile { AUTHENTICATION_NONE); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -1026,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()), @@ -1035,6 +1041,7 @@ public final class BluetoothGatt implements BluetoothProfile { descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); + mDeviceBusy = false; return false; } @@ -1092,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; } @@ -1212,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/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java index 58ee54f..153215c 100644 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -537,7 +537,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.beginServiceDeclaration(mServerIf, service.getType(), service.getInstanceId(), service.getHandles(), - new ParcelUuid(service.getUuid())); + new ParcelUuid(service.getUuid()), service.isAdvertisePreferred()); List<BluetoothGattService> includedServices = service.getIncludedServices(); for (BluetoothGattService includedService : includedServices) { diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java index 1e66369..52bc0f7 100644 --- a/core/java/android/bluetooth/BluetoothGattService.java +++ b/core/java/android/bluetooth/BluetoothGattService.java @@ -15,8 +15,6 @@ */ package android.bluetooth; -import android.bluetooth.BluetoothDevice; - import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -82,6 +80,11 @@ public class BluetoothGattService { protected List<BluetoothGattService> mIncludedServices; /** + * Whether the service uuid should be advertised. + */ + private boolean mAdvertisePreferred; + + /** * Create a new BluetoothGattService. * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * @@ -263,4 +266,20 @@ public class BluetoothGattService { } return null; } + + /** + * Returns whether the uuid of the service should be advertised. + * @hide + */ + public boolean isAdvertisePreferred() { + return mAdvertisePreferred; + } + + /** + * Set whether the service uuid should be advertised. + * @hide + */ + public void setAdvertisePreferred(boolean advertisePreferred) { + this.mAdvertisePreferred = advertisePreferred; + } } 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..1e75fc2 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -417,27 +417,28 @@ 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 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/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index abdf949..4b28516 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -73,6 +73,9 @@ public final class BluetoothUuid { public static final ParcelUuid MAS = ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid BASE_UUID = + ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -211,4 +214,17 @@ public final class BluetoothUuid { long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32; return (int)value; } + + /** + * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. + * @param parcelUuid + * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. + */ + public static boolean isShortUuid(ParcelUuid parcelUuid) { + UUID uuid = parcelUuid.getUuid(); + if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { + return false; + } + return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); + } } diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index df393db..c6b5c3d 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -37,10 +37,15 @@ interface IBluetoothGatt { void unregisterClient(in int clientIf); void clientConnect(in int clientIf, in String address, in boolean isDirect); void clientDisconnect(in int clientIf, in String address); - void clientListen(in int clientIf, in boolean start); - void setAdvData(in int clientIf, in boolean setScanRsp, in boolean inclName, - in boolean inclTxPower, in int minInterval, in int maxInterval, - in int appearance, in byte[] manufacturerData); + void startAdvertising(in int appIf); + void stopAdvertising(); + boolean setAdvServiceData(in byte[] serviceData); + byte[] getAdvServiceData(); + boolean setAdvManufacturerCodeAndData(int manufactureCode, in byte[] manufacturerData); + byte[] getAdvManufacturerData(); + List<ParcelUuid> getAdvServiceUuids(); + void removeAdvManufacturerCodeAndData(int manufacturerCode); + boolean isAdvertising(); void refreshDevice(in int clientIf, in String address); void discoverServices(in int clientIf, in String address); void readCharacteristic(in int clientIf, in String address, in int srvcType, @@ -68,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); @@ -75,7 +81,7 @@ interface IBluetoothGatt { void serverDisconnect(in int serverIf, in String address); void beginServiceDeclaration(in int serverIf, in int srvcType, in int srvcInstanceId, in int minHandles, - in ParcelUuid srvcId); + in ParcelUuid srvcId, boolean advertisePreferred); void addIncludedService(in int serverIf, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId); void addCharacteristic(in int serverIf, in ParcelUuid charId, diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl index 60c297b..a78c29b 100644 --- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -63,5 +63,6 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - void onListen(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/bluetooth/package.html b/core/java/android/bluetooth/package.html index 200a21b..d9ca4f1 100644 --- a/core/java/android/bluetooth/package.html +++ b/core/java/android/bluetooth/package.html @@ -8,17 +8,19 @@ The Bluetooth API supports both "Classic Bluetooth" and Bluetooth Low Energy.</p <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> guide. For more information about Bluetooth Low Energy, see the <a href="{@docRoot}guide/topics/connectivity/bluetooth-le.html"> -Bluetooth Low Energy</a> guide.</p> +Bluetooth Low Energy</a> (BLE) guide.</p> {@more} <p>The Bluetooth APIs let applications:</p> <ul> - <li>Scan for other Bluetooth devices (including Bluetooth Low Energy - devices)</li> - <li>Query the local Bluetooth adapter for paired Bluetooth devices</li> - <li>Establish RFCOMM channels/sockets</li> - <li>Connect to specified sockets on other devices</li> - <li>Transfer data to and from other devices</li> + <li>Scan for other Bluetooth devices (including BLE devices).</li> + <li>Query the local Bluetooth adapter for paired Bluetooth devices.</li> + <li>Establish RFCOMM channels/sockets.</li> + <li>Connect to specified sockets on other devices.</li> + <li>Transfer data to and from other devices.</li> + <li>Communicate with BLE devices, such as proximity sensors, heart rate + monitors, fitness devices, and so on.</li> + <li>Act as a GATT client or a GATT server (BLE).</li> </ul> <p> diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java index 5cb6e77..be35f08 100644 --- a/core/java/android/content/ClipDescription.java +++ b/core/java/android/content/ClipDescription.java @@ -87,7 +87,7 @@ public class ClipDescription implements Parcelable { /** * Helper to compare two MIME types, where one may be a pattern. * @param concreteType A fully-specified MIME type. - * @param desiredType A desired MIME type that may be a pattern such as *\/*. + * @param desiredType A desired MIME type that may be a pattern such as */*. * @return Returns true if the two MIME types match. */ public static boolean compareMimeTypes(String concreteType, String desiredType) { diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 44831fc..02c850b 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -1328,7 +1328,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @param uri The data in the content provider being queried. * @param mimeTypeFilter The type of data the client desires. May be - * a pattern, such as *\/* to retrieve all possible data types. + * a pattern, such as */* to retrieve all possible data types. * @return Returns {@code null} if there are no possible data streams for the * given mimeTypeFilter. Otherwise returns an array of all available * concrete MIME types. @@ -1366,7 +1366,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @param uri The data in the content provider being queried. * @param mimeTypeFilter The type of data the client desires. May be - * a pattern, such as *\/*, if the caller does not have specific type + * a pattern, such as */*, if the caller does not have specific type * requirements; in this case the content provider will pick its best * type matching the pattern. * @param opts Additional options from the client. The definitions of @@ -1427,7 +1427,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * * @param uri The data in the content provider being queried. * @param mimeTypeFilter The type of data the client desires. May be - * a pattern, such as *\/*, if the caller does not have specific type + * a pattern, such as */*, if the caller does not have specific type * requirements; in this case the content provider will pick its best * type matching the pattern. * @param opts Additional options from the client. The definitions of diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 4e6cc92..2bf4d7d 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -344,7 +344,7 @@ public abstract class ContentResolver { * @param url A Uri identifying content (either a list or specific type), * using the content:// scheme. * @param mimeTypeFilter The desired MIME type. This may be a pattern, - * such as *\/*, to query for all available MIME types that match the + * such as */*, to query for all available MIME types that match the * pattern. * @return Returns an array of MIME type strings for all available * data streams that match the given mimeTypeFilter. If there are none, @@ -815,7 +815,7 @@ public abstract class ContentResolver { * * <p>Note that if this function is called for read-only input (mode is "r") * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor} - * for you with a MIME type of "*\/*". This allows such callers to benefit + * for you with a MIME type of "*/*". This allows such callers to benefit * from any built-in data conversion that a provider implements. * * @param uri The desired URI to open. @@ -868,7 +868,7 @@ public abstract class ContentResolver { * * <p>Note that if this function is called for read-only input (mode is "r") * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor} - * for you with a MIME type of "*\/*". This allows such callers to benefit + * for you with a MIME type of "*/*". This allows such callers to benefit * from any built-in data conversion that a provider implements. * * @param uri The desired URI to open. @@ -993,7 +993,7 @@ public abstract class ContentResolver { * * @param uri The desired URI to open. * @param mimeType The desired MIME type of the returned data. This can - * be a pattern such as *\/*, which will allow the content provider to + * be a pattern such as */*, which will allow the content provider to * select a type, though there is no way for you to determine what type * it is returning. * @param opts Additional provider-dependent options. @@ -1026,7 +1026,7 @@ public abstract class ContentResolver { * * @param uri The desired URI to open. * @param mimeType The desired MIME type of the returned data. This can - * be a pattern such as *\/*, which will allow the content provider to + * be a pattern such as */*, which will allow the content provider to * select a type, though there is no way for you to determine what type * it is returning. * @param opts Additional provider-dependent options. @@ -1535,7 +1535,7 @@ public abstract class ContentResolver { * for a whole class of content. * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code> * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI - * specified by <em>uri</em> will cause notifications to be sent. If true, than any URI values + * specified by <em>uri</em> will cause notifications to be sent. If <code>true</code>, any URI values * at or below the specified URI will also trigger a match. * @param observer The object that receives callbacks when changes occur. * @see #unregisterContentObserver diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 2e4e209..5057cf4 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,16 @@ 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 + */ + 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 2e2d4b8..95e27e2 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -844,7 +844,7 @@ public class Intent implements Parcelable, Cloneable { * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, then these flags will also be * set in the returned chooser intent, with its ClipData set appropriately: * either a direct reflection of {@link #getClipData()} if that is non-null, - * or a new ClipData build from {@link #getData()}. + * or a new ClipData built from {@link #getData()}. * * @param target The Intent that the user will be selecting an activity * to perform. @@ -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,12 @@ 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. + */ + @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 +3576,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 @@ -4424,7 +4458,7 @@ public class Intent implements Parcelable, Cloneable { * Return the {@link ClipData} associated with this Intent. If there is * none, returns null. See {@link #setClipData} for more information. * - * @see #setClipData; + * @see #setClipData */ public ClipData getClipData() { return mClipData; @@ -5440,7 +5474,7 @@ public class Intent implements Parcelable, Cloneable { * directly used by Intent. Applications should generally rely on the * MIME type of the Intent itself, not what it may find in the ClipData. * A common practice is to construct a ClipData for use with an Intent - * with a MIME type of "*\/*". + * with a MIME type of "*/*". * * @param clip The new clip to set. May be null to clear the current clip. */ @@ -7162,8 +7196,8 @@ public class Intent implements Parcelable, Cloneable { * * @param type MIME data type to normalize * @return normalized MIME data type, or null if the input was null - * @see {@link #setType} - * @see {@link #setTypeAndNormalize} + * @see #setType + * @see #setTypeAndNormalize */ public static String normalizeMimeType(String type) { if (type == null) { 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 1a1610d..0172509 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -315,6 +315,12 @@ 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. + */ + 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. * @@ -363,7 +369,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_SUPPORTS_XLARGE_SCREENS}, * {@link #FLAG_RESIZEABLE_FOR_SCREENS}, * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE}, - * {@link #FLAG_INSTALLED}. + * {@link #FLAG_INSTALLED}, {@link #FLAG_IS_GAME}. */ public int flags = 0; @@ -483,7 +489,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 8ce7e97..226f5a6 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -989,6 +989,7 @@ public abstract class PackageManager { * @hide * @deprecated */ + @Deprecated @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_NFC_HCE = "android.hardware.nfc.hce"; @@ -1244,6 +1245,26 @@ 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. + */ + @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) @@ -1263,11 +1284,36 @@ public abstract class PackageManager { * room television experience: displayed on a big screen, where the user * is sitting far away from it, and the dominant form of input will be * something like a DPAD, not through touch or mouse. + * @deprecated use {@link #FEATURE_LEANBACK} instead. */ + @Deprecated @SdkConstant(SdkConstantType.FEATURE) 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"; + + /** * Action to external storage service to clean out removed apps. * @hide */ @@ -1430,17 +1476,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. - * + * 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 +2450,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 +2524,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. - * - * @param activityName Name of the activity whose logo is to be retrieved. + * Retrieve the banner associated with an application. * - * @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 52564eb..66d4f50 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(); @@ -3701,6 +3733,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(); @@ -4125,6 +4162,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/Camera.java b/core/java/android/hardware/Camera.java index 111062d..eae4a46 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -45,6 +45,7 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.concurrent.locks.ReentrantLock; @@ -2150,10 +2151,20 @@ public class Camera { private static final String PIXEL_FORMAT_JPEG = "jpeg"; private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb"; - private HashMap<String, String> mMap; + /** + * Order matters: Keys that are {@link #set(String, String) set} later + * will take precedence over keys that are set earlier (if the two keys + * conflict with each other). + * + * <p>One example is {@link #setPreviewFpsRange(int, int)} , since it + * conflicts with {@link #setPreviewFrameRate(int)} whichever key is set later + * is the one that will take precedence. + * </p> + */ + private final LinkedHashMap<String, String> mMap; private Parameters() { - mMap = new HashMap<String, String>(64); + mMap = new LinkedHashMap<String, String>(/*initialCapacity*/64); } /** @@ -2233,7 +2244,7 @@ public class Camera { return; } - mMap.put(key, value); + put(key, value); } /** @@ -2243,7 +2254,18 @@ public class Camera { * @param value the int value of the parameter */ public void set(String key, int value) { - mMap.put(key, Integer.toString(value)); + put(key, Integer.toString(value)); + } + + private void put(String key, String value) { + /* + * Remove the key if it already exists. + * + * This way setting a new value for an already existing key will always move + * that key to be ordered the latest in the map. + */ + mMap.remove(key); + mMap.put(key, value); } private void set(String key, List<Area> areas) { diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 89a5819..4bea9ee 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,32 @@ 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> + * A sensor that measures the heart rate in beats per minute. + * <p> + * value[0] represents the beats per minute when the measurement was taken. + * value[0] is 0 if the heart rate monitor could not measure the rate or the + * rate is 0 beat per minute. + */ + 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 +440,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 +497,8 @@ public final class Sensor { private int mMinDelay; private int mFifoReservedEventCount; private int mFifoMaxEventCount; + private String mStringType; + private String mRequiredPermission; Sensor() { } @@ -401,6 +579,20 @@ 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. + */ + public String getRequiredPermission() { + return mRequiredPermission; + } + /** @hide */ public int getHandle() { return mHandle; diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index ac9189d..5f2b5f0 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -1056,8 +1056,8 @@ public abstract class SensorManager { * is mapped. * * @param outR - * the transformed rotation matrix. inR and outR can be the same - * array, but it is not recommended for performance reason. + * the transformed rotation matrix. inR and outR should not be the same + * array. * * @return <code>true</code> on success. <code>false</code> if the input * parameters are incorrect, for instance if X and Y define the same diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index d5208d9..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; @@ -297,16 +314,31 @@ public final class DisplayManager { } /** - * Initiates a fresh scan of availble Wifi displays. + * Starts scanning for available Wifi displays. * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. * <p> + * Calls to this method nest and must be matched by an equal number of calls to + * {@link #stopWifiDisplayScan()}. + * </p><p> + * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. + * </p> + * + * @hide + */ + public void startWifiDisplayScan() { + mGlobal.startWifiDisplayScan(); + } + + /** + * Stops scanning for available Wifi displays. + * <p> * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. * </p> * * @hide */ - public void scanWifiDisplays() { - mGlobal.scanWifiDisplays(); + public void stopWifiDisplayScan() { + mGlobal.stopWifiDisplayScan(); } /** @@ -405,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. @@ -412,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 936a086..a8d55e8 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -72,6 +72,8 @@ public final class DisplayManagerGlobal { private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>(); private int[] mDisplayIdCache; + private int mWifiDisplayScanNestCount; + private DisplayManagerGlobal(IDisplayManager dm) { mDm = dm; } @@ -267,11 +269,32 @@ public final class DisplayManagerGlobal { } } - public void scanWifiDisplays() { - try { - mDm.scanWifiDisplays(); - } catch (RemoteException ex) { - Log.e(TAG, "Failed to scan for Wifi displays.", ex); + public void startWifiDisplayScan() { + synchronized (mLock) { + if (mWifiDisplayScanNestCount++ == 0) { + registerCallbackIfNeededLocked(); + try { + mDm.startWifiDisplayScan(); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to scan for Wifi displays.", ex); + } + } + } + } + + public void stopWifiDisplayScan() { + synchronized (mLock) { + if (--mWifiDisplayScanNestCount == 0) { + try { + mDm.stopWifiDisplayScan(); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to scan for Wifi displays.", ex); + } + } else if (mWifiDisplayScanNestCount < 0) { + Log.wtf(TAG, "Wifi display scan nest count became negative: " + + mWifiDisplayScanNestCount); + mWifiDisplayScanNestCount = 0; + } } } @@ -354,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; @@ -381,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 6b2c887..23c58c8 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -29,11 +29,14 @@ interface IDisplayManager { void registerCallback(in IDisplayManagerCallback callback); - // No permissions required. - void scanWifiDisplays(); + // Requires CONFIGURE_WIFI_DISPLAY permission. + // The process must have previously registered a callback. + void startWifiDisplayScan(); - // Requires CONFIGURE_WIFI_DISPLAY permission to connect to an unknown device. - // No permissions required to connect to a known device. + // Requires CONFIGURE_WIFI_DISPLAY permission. + void stopWifiDisplayScan(); + + // Requires CONFIGURE_WIFI_DISPLAY permission. void connectWifiDisplay(String address); // No permissions required. @@ -45,6 +48,12 @@ interface IDisplayManager { // Requires CONFIGURE_WIFI_DISPLAY permission. void forgetWifiDisplay(String address); + // Requires CONFIGURE_WIFI_DISPLAY permission. + void pauseWifiDisplay(); + + // Requires CONFIGURE_WIFI_DISPLAY permission. + void resumeWifiDisplay(); + // No permissions required. WifiDisplayStatus getWifiDisplayStatus(); @@ -54,11 +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 releaseVirtualDisplay(in IBinder token); - - // Requires CONFIGURE_WIFI_DISPLAY permission. - void pauseWifiDisplay(); + void setVirtualDisplaySurface(in IBinder token, in Surface surface); - // Requires CONFIGURE_WIFI_DISPLAY permission. - void resumeWifiDisplay(); + // 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..8578a32 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiCec.java @@ -0,0 +1,257 @@ +/* + * 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. + */ +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..cd86cd8 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiCecClient.java @@ -0,0 +1,143 @@ +/* + * 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. + */ +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..10b058c --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiCecManager.java @@ -0,0 +1,76 @@ +/* + * 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. + */ +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..be94d97 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiCecMessage.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.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. + */ +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/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index c78a973..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 @@ -395,6 +401,8 @@ public class ConnectivityManager { private final IConnectivityManager mService; + private final String mPackageName; + /** * Tests if a given integer represents a valid network type. * @param networkType the type to be tested @@ -444,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); } @@ -802,7 +812,7 @@ public class ConnectivityManager { public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) { byte[] address = hostAddress.getAddress(); try { - return mService.requestRouteToHostAddress(networkType, address); + return mService.requestRouteToHostAddress(networkType, address, mPackageName); } catch (RemoteException e) { return false; } @@ -898,8 +908,9 @@ public class ConnectivityManager { /** * {@hide} */ - public ConnectivityManager(IConnectivityManager service) { + public ConnectivityManager(IConnectivityManager service, String packageName) { mService = checkNotNull(service, "missing IConnectivityManager"); + mPackageName = checkNotNull(packageName, "missing package name"); } /** {@hide} */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index c1da2e3..4bca7fe 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -71,9 +71,9 @@ interface IConnectivityManager int stopUsingNetworkFeature(int networkType, in String feature); - boolean requestRouteToHost(int networkType, int hostAddress); + boolean requestRouteToHost(int networkType, int hostAddress, String packageName); - boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress); + boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress, String packageName); boolean getMobileDataEnabled(); void setMobileDataEnabled(boolean enabled); diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index c106514..21352bf 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -566,6 +566,17 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { return false; } + + public void setInternalDataEnable(boolean enabled) { + if (DBG) log("setInternalDataEnable: E enabled=" + enabled); + final AsyncChannel channel = mDataConnectionTrackerAc; + if (channel != null) { + channel.sendMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE, + enabled ? DctConstants.ENABLED : DctConstants.DISABLED); + } + if (VDBG) log("setInternalDataEnable: X enabled=" + enabled); + } + @Override public void setUserDataEnable(boolean enabled) { if (DBG) log("setUserDataEnable: E enabled=" + enabled); diff --git a/core/java/android/net/ProxyDataTracker.java b/core/java/android/net/ProxyDataTracker.java new file mode 100644 index 0000000..461e8b8 --- /dev/null +++ b/core/java/android/net/ProxyDataTracker.java @@ -0,0 +1,204 @@ +/* + * 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 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)); + } 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/VpnService.java b/core/java/android/net/VpnService.java index d7dc7f5..7385dff 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -151,9 +151,10 @@ public class VpnService extends Service { } /** - * Protect a socket from VPN connections. The socket will be bound to the - * current default network interface, so its traffic will not be forwarded - * through VPN. This method is useful if some connections need to be kept + * Protect a socket from VPN connections. After protecting, data sent + * through this socket will go directly to the underlying network, + * so its traffic will not be forwarded through the VPN. + * This method is useful if some connections need to be kept * outside of VPN. For example, a VPN tunnel should protect itself if its * destination is covered by VPN routes. Otherwise its outgoing packets * will be sent back to the VPN interface and cause an infinite loop. This diff --git a/core/java/android/net/nsd/NsdServiceInfo.java b/core/java/android/net/nsd/NsdServiceInfo.java index 205a21d..6fdb0d0 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,134 @@ 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. + */ + 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 */ + 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}. + */ + 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 +235,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 +265,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/AsyncTask.java b/core/java/android/os/AsyncTask.java index d4a3006..26e09b6 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -610,7 +610,7 @@ public abstract class AsyncTask<Params, Progress, Result> { * still running. Each call to this method will trigger the execution of * {@link #onProgressUpdate} on the UI thread. * - * {@link #onProgressUpdate} will note be called if the task has been + * {@link #onProgressUpdate} will not be called if the task has been * canceled. * * @param values The progress values to update the UI with. diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index c3313c5..fae2d52 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -49,7 +49,7 @@ public class Build { /** The manufacturer of the product/hardware. */ public static final String MANUFACTURER = getString("ro.product.manufacturer"); - /** The brand (e.g., carrier) the software is customized for, if any. */ + /** The consumer-visible brand with which the product/hardware will be associated, if any. */ public static final String BRAND = getString("ro.product.brand"); /** The end-user-visible name for the end product. */ @@ -474,6 +474,11 @@ public class Build { * </ul> */ public static final int KITKAT = 19; + + /** + * Android 4.5: KitKat for watches, snacks on the run. + */ + public static final int KITKAT_WATCH = CUR_DEVELOPMENT; // STOPSHIP: update API level } /** The type of build, like "user" or "eng". */ 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/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 21b8ae5..f65b6ba 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -420,7 +420,7 @@ interface INetworkManagementService /** * Clear a user range from being associated with an interface. */ - void clearDnsInterfaceForUidRange(int uid_start, int uid_end); + void clearDnsInterfaceForUidRange(String iface, int uid_start, int uid_end); /** * Clear the mappings from pid to Dns interface and from uid range to Dns interface. diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 56176a4..92af1a5 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -37,8 +37,8 @@ interface IPowerManager void wakeUp(long time); void goToSleep(long time, int reason); 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..646bfef 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 @@ -418,6 +430,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."); @@ -569,21 +582,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 96f42cc..995e396 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -982,19 +982,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/os/Vibrator.java b/core/java/android/os/Vibrator.java index 6650fca..5d55143 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -21,11 +21,11 @@ import android.content.Context; /** * Class that operates the vibrator on the device. * <p> - * If your process exits, any vibration you started with will stop. + * If your process exits, any vibration you started will stop. * </p> * * To obtain an instance of the system vibrator, call - * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as argument. + * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as the argument. */ public abstract class Vibrator { /** diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index 7ddfa87..2ab5a91 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -33,6 +33,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.util.TypedValue; import android.util.Xml; import android.view.LayoutInflater; @@ -124,6 +125,8 @@ public abstract class PreferenceActivity extends ListActivity implements PreferenceManager.OnPreferenceTreeClickListener, PreferenceFragment.OnPreferenceStartFragmentCallback { + private static final String TAG = "PreferenceActivity"; + // Constants for state save/restore private static final String HEADERS_TAG = ":android:headers"; private static final String CUR_HEADER_TAG = ":android:cur_header"; @@ -521,9 +524,7 @@ public abstract class PreferenceActivity extends ListActivity implements int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0); int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0); - // Restore from headers only if they are supported which - // is in multi-pane mode. - if (savedInstanceState != null && !mSinglePane) { + if (savedInstanceState != null) { // We are restarting from a previous saved state; used that to // initialize, instead of starting fresh. ArrayList<Header> headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG); diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java index b615600..806a89d 100644 --- a/core/java/android/print/PrinterCapabilitiesInfo.java +++ b/core/java/android/print/PrinterCapabilitiesInfo.java @@ -475,6 +475,12 @@ public final class PrinterCapabilitiesInfo implements Parcelable { * @param colorModes The color mode bit mask. * @param defaultColorMode The default color mode. * @return This builder. + * <p> + * <strong>Note:</strong> On platform version 19 (Kitkat) specifying + * only PrintAttributes#COLOR_MODE_MONOCHROME leads to a print spooler + * crash. Hence, you should declare either both color modes or + * PrintAttributes#COLOR_MODE_COLOR. + * </p> * * @throws IllegalArgumentException If color modes contains an invalid * mode bit or if the default color mode is invalid. diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b16df28..0863368 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -18,6 +18,7 @@ package android.provider; import android.accounts.Account; import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.ContentProviderClient; import android.content.ContentProviderOperation; import android.content.ContentResolver; @@ -40,6 +41,7 @@ import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Pair; import android.view.View; +import android.widget.Toast; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -7981,7 +7983,7 @@ public final class ContactsContract { // Trigger with obtained rectangle Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode, excludeMimes); - context.startActivity(intent); + startActivityWithErrorToast(context, intent); } /** @@ -8014,7 +8016,16 @@ public final class ContactsContract { String[] excludeMimes) { Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode, excludeMimes); - context.startActivity(intent); + startActivityWithErrorToast(context, intent); + } + + private static void startActivityWithErrorToast(Context context, Intent intent) { + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + Toast.makeText(context, com.android.internal.R.string.quick_contacts_not_available, + Toast.LENGTH_SHORT).show(); + } } } diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 86d3cf8..f0520b5 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -257,8 +257,6 @@ public final class DocumentsContract { * {@link #MIME_TYPE_DIR}. * * @see #COLUMN_FLAGS - * @see DocumentsContract#createDocument(ContentResolver, Uri, String, - * String) * @see DocumentsProvider#createDocument(String, String, String) */ public static final int FLAG_DIR_SUPPORTS_CREATE = 1 << 3; 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/provider/Settings.java b/core/java/android/provider/Settings.java index 04f3f0a..3810eb3 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5140,6 +5140,12 @@ public final class Settings { public static final String SMS_SHORT_CODE_RULE = "sms_short_code_rule"; /** + * Used to select TCP's default initial receiver window size in segments - defaults to a build config value + * @hide + */ + public static final String TCP_DEFAULT_INIT_RWND = "tcp_default_init_rwnd"; + + /** * Used to disable Tethering on a device - defaults to true * @hide */ 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..2303d65 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 */ @@ -289,6 +300,10 @@ public class DreamService extends Service implements Window.Callback { public void onDetachedFromWindow() { } + @Override + public void onWindowDismissed() { + } + /** {@inheritDoc} */ @Override public void onPanelClosed(int featureId, Menu menu) { @@ -325,7 +340,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 +459,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 +484,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 +506,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 +524,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 +670,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 +695,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 +729,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 +771,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 +807,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 +818,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 +845,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 +867,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..8eaee29 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -83,6 +83,17 @@ 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. + */ + public void onListenerConnected(String[] notificationKeys) { + // optional + } + private final INotificationManager getNotificationInterface() { if (mNoMan == null) { mNoMan = INotificationManager.Stub.asInterface( @@ -112,6 +123,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 +143,23 @@ 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)} + */ + 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 +167,25 @@ 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. + */ + 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 +200,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 +225,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/StaticLayout.java b/core/java/android/text/StaticLayout.java index e7d6fda..1f32d4d 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -654,7 +654,7 @@ public class StaticLayout extends Layout { int extra; - if (needMultiply) { + if (needMultiply && end != bufEnd) { double ex = (below - above) * (spacingmult - 1) + spacingadd; if (ex >= 0) { extra = (int)(ex + EXTRA_ROUNDING); diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index 5ef86b1..f34e746 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -512,7 +512,7 @@ public class Time { * <pre> * Time time = new Time(); * time.set(4, 10, 2007); // set the date to Nov 4, 2007, 12am - * time.normalize(); // this sets isDst = 1 + * time.normalize(false); // this sets isDst = 1 * time.monthDay += 1; // changes the date to Nov 5, 2007, 12am * millis = time.toMillis(false); // millis is Nov 4, 2007, 11pm * millis = time.toMillis(true); // millis is Nov 5, 2007, 12am diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java index d6e116f..dab853a 100644 --- a/core/java/android/util/LongSparseArray.java +++ b/core/java/android/util/LongSparseArray.java @@ -44,7 +44,7 @@ import com.android.internal.util.ArrayUtils; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * <code>keyAt(int)</code> with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of <code>valueAt(int)<code>.</p> + * order in the case of <code>valueAt(int)</code>.</p> */ public class LongSparseArray<E> implements Cloneable { private static final Object DELETED = new Object(); diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index 87d868b..6654899 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -39,7 +39,7 @@ import java.util.Arrays; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * <code>keyAt(int)</code> with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of <code>valueAt(int)<code>.</p> + * order in the case of <code>valueAt(int)</code>.</p> * * @hide */ diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index 6e168a8..46d9d45 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -44,7 +44,7 @@ import com.android.internal.util.ArrayUtils; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * <code>keyAt(int)</code> with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of <code>valueAt(int)<code>.</p> + * order in the case of <code>valueAt(int)</code>.</p> */ public class SparseArray<E> implements Cloneable { private static final Object DELETED = new Object(); diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index 68487e3..905dcb0 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -38,7 +38,7 @@ import com.android.internal.util.ArrayUtils; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * <code>keyAt(int)</code> with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of <code>valueAt(int)<code>.</p> + * order in the case of <code>valueAt(int)</code>.</p> */ public class SparseBooleanArray implements Cloneable { /** diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java index 0835cb0..4f5ca07 100644 --- a/core/java/android/util/SparseIntArray.java +++ b/core/java/android/util/SparseIntArray.java @@ -37,7 +37,7 @@ import com.android.internal.util.ArrayUtils; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * <code>keyAt(int)</code> with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of <code>valueAt(int)<code>.</p> + * order in the case of <code>valueAt(int)</code>.</p> */ public class SparseIntArray implements Cloneable { private int[] mKeys; diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java index 62c1c0d..39fc8a3 100644 --- a/core/java/android/util/SparseLongArray.java +++ b/core/java/android/util/SparseLongArray.java @@ -37,7 +37,7 @@ import com.android.internal.util.ArrayUtils; * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using * <code>keyAt(int)</code> with ascending values of the index will return the * keys in ascending order, or the values corresponding to the keys in ascending - * order in the case of <code>valueAt(int)<code>.</p> + * order in the case of <code>valueAt(int)</code>.</p> */ public class SparseLongArray implements Cloneable { private int[] mKeys; 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/InputDevice.java b/core/java/android/view/InputDevice.java index e829116..ae5f37e 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,19 @@ 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. + */ + public boolean supportsSource(int source) { + return (mSources & source) == source; + } + /** * Gets the keyboard type. * @return The keyboard type. @@ -569,7 +597,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..7b389c0 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,32 @@ public class KeyEvent extends InputEvent implements Parcelable { } } - /** Whether key will, by default, trigger a click on the focused view. - * @hide + /** + * 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}. */ - 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}. + */ + 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 bdf4141..eb7f45e 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.cloneWithSystemWindowInsetsConsumed(); + } + } else { + // We were called from within a direct call to fitSystemWindows. + if (fitSystemWindowsInt(insets.getSystemWindowInsets())) { + return insets.cloneWithSystemWindowInsetsConsumed(); + } + } + 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 */ @@ -6543,7 +6677,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_filterTouchesWhenObscured */ public void setFilterTouchesWhenObscured(boolean enabled) { - setFlags(enabled ? 0 : FILTER_TOUCHES_WHEN_OBSCURED, + setFlags(enabled ? FILTER_TOUCHES_WHEN_OBSCURED : 0, FILTER_TOUCHES_WHEN_OBSCURED); } @@ -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; } @@ -16499,7 +16633,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 +16728,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 +16974,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 +18511,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 +18802,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 +18872,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 +18938,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 c3f064f..e67659c 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -212,6 +212,14 @@ public class ViewConfiguration { */ private static final int OVERFLING_DISTANCE = 6; + /** + * Configuration values for overriding {@link #hasPermanentMenuKey()} behavior. + * These constants must match the definition in res/values/config.xml. + */ + private static final int HAS_PERMANENT_MENU_KEY_AUTODETECT = 0; + private static final int HAS_PERMANENT_MENU_KEY_TRUE = 1; + private static final int HAS_PERMANENT_MENU_KEY_FALSE = 2; + private final int mEdgeSlop; private final int mFadingEdgeLength; private final int mMinimumFlingVelocity; @@ -296,12 +304,31 @@ public class ViewConfiguration { mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f); if (!sHasPermanentMenuKeySet) { - IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); - try { - sHasPermanentMenuKey = !wm.hasNavigationBar(); - sHasPermanentMenuKeySet = true; - } catch (RemoteException ex) { - sHasPermanentMenuKey = false; + final int configVal = res.getInteger( + com.android.internal.R.integer.config_overrideHasPermanentMenuKey); + + switch (configVal) { + default: + case HAS_PERMANENT_MENU_KEY_AUTODETECT: { + IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + sHasPermanentMenuKey = !wm.hasNavigationBar(); + sHasPermanentMenuKeySet = true; + } catch (RemoteException ex) { + sHasPermanentMenuKey = false; + } + } + break; + + case HAS_PERMANENT_MENU_KEY_TRUE: + sHasPermanentMenuKey = true; + sHasPermanentMenuKeySet = true; + break; + + case HAS_PERMANENT_MENU_KEY_FALSE: + sHasPermanentMenuKey = false; + sHasPermanentMenuKeySet = true; + break; } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 9414237..f346ee8 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++) { @@ -5430,21 +5429,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 3fd2aa9..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]; @@ -232,8 +235,6 @@ public final class ViewRootImpl implements ViewParent, InputStage mFirstInputStage; InputStage mFirstPostImeInputStage; - boolean mFlipControllerFallbackKeys; - boolean mWindowAttributesChanged = false; int mWindowAttributesChangesFlag = 0; @@ -370,11 +371,7 @@ public final class ViewRootImpl implements ViewParent, mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi; mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context); mChoreographer = Choreographer.getInstance(); - mFlipControllerFallbackKeys = - context.getResources().getBoolean(R.bool.flip_controller_fallback_keys); - - PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - mAttachInfo.mScreenOn = powerManager.isScreenOn(); + mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); loadSystemProperties(); } @@ -429,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); @@ -798,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() { @@ -1125,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; @@ -1216,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 { @@ -1342,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 @@ -1523,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); @@ -2240,7 +2276,7 @@ public final class ViewRootImpl implements ViewParent, } private void performDraw() { - if (!mAttachInfo.mScreenOn && !mReportNextDraw) { + if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { return; } @@ -2876,6 +2912,8 @@ public final class ViewRootImpl implements ViewParent, mInputChannel = null; } + mDisplayManager.unregisterDisplayListener(mDisplayListener); + unscheduleTraversals(); } @@ -2912,11 +2950,8 @@ public final class ViewRootImpl implements ViewParent, mView.dispatchConfigurationChanged(config); } } - - mFlipControllerFallbackKeys = - mContext.getResources().getBoolean(R.bool.flip_controller_fallback_keys); } - + /** * Return true if child is an ancestor of parent, (or equal to the parent). */ @@ -2958,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; @@ -3005,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: @@ -3222,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; @@ -3699,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 } @@ -3985,7 +4013,6 @@ public final class ViewRootImpl implements ViewParent, private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler(); private final SyntheticTouchNavigationHandler mTouchNavigation = new SyntheticTouchNavigationHandler(); - private final SyntheticKeyHandler mKeys = new SyntheticKeyHandler(); public SyntheticInputStage() { super(null); @@ -4008,12 +4035,7 @@ public final class ViewRootImpl implements ViewParent, mTouchNavigation.process(event); return FINISH_HANDLED; } - } else if (q.mEvent instanceof KeyEvent) { - if (mKeys.process((KeyEvent) q.mEvent)) { - return FINISH_HANDLED; - } } - return FORWARD; } @@ -4332,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; @@ -4364,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); } @@ -4843,63 +4877,6 @@ public final class ViewRootImpl implements ViewParent, }; } - final class SyntheticKeyHandler { - - public boolean process(KeyEvent event) { - // In some locales (like Japan) controllers use B for confirm and A for back, rather - // than vice versa, so we need to special case this here since the input system itself - // is not locale-aware. - int keyCode; - switch(event.getKeyCode()) { - case KeyEvent.KEYCODE_BUTTON_A: - case KeyEvent.KEYCODE_BUTTON_C: - case KeyEvent.KEYCODE_BUTTON_X: - case KeyEvent.KEYCODE_BUTTON_Z: - keyCode = mFlipControllerFallbackKeys ? - KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_DPAD_CENTER; - break; - case KeyEvent.KEYCODE_BUTTON_B: - case KeyEvent.KEYCODE_BUTTON_Y: - keyCode = mFlipControllerFallbackKeys ? - KeyEvent.KEYCODE_DPAD_CENTER : KeyEvent.KEYCODE_BACK; - break; - case KeyEvent.KEYCODE_BUTTON_THUMBL: - case KeyEvent.KEYCODE_BUTTON_THUMBR: - case KeyEvent.KEYCODE_BUTTON_START: - case KeyEvent.KEYCODE_BUTTON_1: - case KeyEvent.KEYCODE_BUTTON_2: - case KeyEvent.KEYCODE_BUTTON_3: - case KeyEvent.KEYCODE_BUTTON_4: - case KeyEvent.KEYCODE_BUTTON_5: - case KeyEvent.KEYCODE_BUTTON_6: - case KeyEvent.KEYCODE_BUTTON_7: - case KeyEvent.KEYCODE_BUTTON_8: - case KeyEvent.KEYCODE_BUTTON_9: - case KeyEvent.KEYCODE_BUTTON_10: - case KeyEvent.KEYCODE_BUTTON_11: - case KeyEvent.KEYCODE_BUTTON_12: - case KeyEvent.KEYCODE_BUTTON_13: - case KeyEvent.KEYCODE_BUTTON_14: - case KeyEvent.KEYCODE_BUTTON_15: - case KeyEvent.KEYCODE_BUTTON_16: - keyCode = KeyEvent.KEYCODE_DPAD_CENTER; - break; - case KeyEvent.KEYCODE_BUTTON_SELECT: - case KeyEvent.KEYCODE_BUTTON_MODE: - keyCode = KeyEvent.KEYCODE_MENU; - default: - return false; - } - - enqueueInputEvent(new KeyEvent(event.getDownTime(), event.getEventTime(), - event.getAction(), keyCode, event.getRepeatCount(), event.getMetaState(), - event.getDeviceId(), event.getScanCode(), - event.getFlags() | KeyEvent.FLAG_FALLBACK, event.getSource())); - return true; - } - - } - /** * Returns true if the key is used for keyboard navigation. * @param keyEvent The key event. @@ -5863,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); @@ -6207,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/VolumePanel.java b/core/java/android/view/VolumePanel.java index f0e6677..52f9c0b 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -311,7 +311,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie lp.type = LayoutParams.TYPE_VOLUME_OVERLAY; lp.width = LayoutParams.WRAP_CONTENT; lp.height = LayoutParams.WRAP_CONTENT; - lp.privateFlags |= LayoutParams.PRIVATE_FLAG_FORCE_SHOW_NAV_BAR; window.setAttributes(lp); window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index b3a0699..c450f3c 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; @@ -385,6 +390,12 @@ public abstract class Window { * @param mode The mode that was just finished. */ public void onActionModeFinished(ActionMode mode); + + /** + * 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) { diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java new file mode 100644 index 0000000..2160efe --- /dev/null +++ b/core/java/android/view/WindowInsets.java @@ -0,0 +1,304 @@ +/* + * 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 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(); + } + mTempRect.set(mSystemWindowInsets); + 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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + 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; + } + + public WindowInsets cloneWithSystemWindowInsetsConsumed() { + final WindowInsets result = new WindowInsets(this); + result.mSystemWindowInsets = new Rect(0, 0, 0, 0); + return result; + } + + public WindowInsets cloneWithSystemWindowInsetsConsumed(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; + } + + public WindowInsets cloneWithSystemWindowInsets(int left, int top, int right, int bottom) { + final WindowInsets result = new WindowInsets(this); + result.mSystemWindowInsets = new Rect(left, top, right, bottom); + return result; + } + + public WindowInsets cloneWithWindowDecorInsetsConsumed() { + final WindowInsets result = new WindowInsets(this); + result.mWindowDecorInsets.set(0, 0, 0, 0); + return result; + } + + public WindowInsets cloneWithWindowDecorInsetsConsumed(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; + } + + public WindowInsets cloneWithWindowDecorInsets(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 0ce4da5..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 @@ -1063,13 +1066,6 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_SHOW_FOR_ALL_USERS = 0x00000010; /** - * Special flag for the volume overlay: force the window manager out of "hide nav bar" - * mode while the window is on screen. - * - * {@hide} */ - public static final int PRIVATE_FLAG_FORCE_SHOW_NAV_BAR = 0x00000020; - - /** * Never animate position changes of the window. * * {@hide} */ diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java new file mode 100644 index 0000000..a1bd4bd --- /dev/null +++ b/core/java/android/view/WindowManagerInternal.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * 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(); +}
\ No newline at end of file diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index c5a1b86..893db89 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(). @@ -749,12 +734,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 +748,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. 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/WebView.java b/core/java/android/webkit/WebView.java index 5bc39f1..d53bb74 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -686,6 +686,15 @@ public class WebView extends AbsoluteLayout } /** + * Used only by internal tests to free up memory. + * + * @hide + */ + public static void freeMemoryForTests() { + getFactory().getStatics().freeMemoryForTests(); + } + + /** * Informs WebView of the network state. This is used to set * the JavaScript property window.navigator.isOnline and * generates the online/offline event as specified in HTML5, sec. 5.7.7 diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java index 9d9d882..e391aaf 100644 --- a/core/java/android/webkit/WebViewFactoryProvider.java +++ b/core/java/android/webkit/WebViewFactoryProvider.java @@ -50,6 +50,11 @@ public interface WebViewFactoryProvider { String getDefaultUserAgent(Context context); /** + * Used for tests only. + */ + void freeMemoryForTests(); + + /** * Implements the API method: * {@link android.webkit.WebView#setWebContentsDebuggingEnabled(boolean) } */ diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 092f474..bbaa33d 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -723,7 +723,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * * @param view The view whose scroll state is being reported * - * @param scrollState The current scroll state. One of {@link #SCROLL_STATE_IDLE}, + * @param scrollState The current scroll state. One of * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}. */ public void onScrollStateChanged(AbsListView view, int scrollState); @@ -2076,22 +2076,23 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); mInLayout = true; + final int childCount = getChildCount(); if (changed) { - int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { getChildAt(i).forceLayout(); } mRecycler.markChildrenDirty(); } - if (mFastScroller != null && (mItemCount != mOldItemCount || mDataChanged)) { - mFastScroller.onItemCountChanged(mItemCount); - } - layoutChildren(); mInLayout = false; mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR; + + // TODO: Move somewhere sane. This doesn't belong in onLayout(). + if (mFastScroller != null) { + mFastScroller.onItemCountChanged(getChildCount(), mItemCount); + } } /** @@ -2228,26 +2229,34 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView"); isScrap[0] = false; - View scrapView; - scrapView = mRecycler.getTransientStateView(position); - if (scrapView == null) { - scrapView = mRecycler.getScrapView(position); - } + // Check whether we have a transient state view. Attempt to re-bind the + // data and discard the view if we fail. + final View transientView = mRecycler.getTransientStateView(position); + if (transientView != null) { + final LayoutParams params = (LayoutParams) transientView.getLayoutParams(); - View child; - if (scrapView != null) { - child = mAdapter.getView(position, scrapView, this); + // If the view type hasn't changed, attempt to re-bind the data. + if (params.viewType == mAdapter.getItemViewType(position)) { + final View updatedView = mAdapter.getView(position, transientView, this); - if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { - child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + // If we failed to re-bind the data, scrap the obtained view. + if (updatedView != transientView) { + mRecycler.addScrapView(updatedView, position); + } } + // Scrap view implies temporary detachment. + isScrap[0] = true; + return transientView; + } + + final View scrapView = mRecycler.getScrapView(position); + final View child = mAdapter.getView(position, scrapView, this); + if (scrapView != null) { if (child != scrapView) { + // Failed to re-bind the data, return scrap to the heap. mRecycler.addScrapView(scrapView, position); - if (mCacheColorHint != 0) { - child.setDrawingCacheBackgroundColor(mCacheColorHint); - } } else { isScrap[0] = true; @@ -2259,16 +2268,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te child.dispatchFinishTemporaryDetach(); } - } else { - child = mAdapter.getView(position, null, this); + } - if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { - child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); - } + if (mCacheColorHint != 0) { + child.setDrawingCacheBackgroundColor(mCacheColorHint); + } - if (mCacheColorHint != 0) { - child.setDrawingCacheBackgroundColor(mCacheColorHint); - } + if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); } if (mAdapterHasStableIds) { @@ -3032,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; } @@ -6562,12 +6569,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } - if (mTransientStateViews != null) { - mTransientStateViews.clear(); - } - if (mTransientStateViewsById != null) { - mTransientStateViewsById.clear(); - } + + clearTransientStateViews(); } /** @@ -6634,14 +6637,26 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** - * Dump any currently saved views with transient state. + * Dumps and fully detaches any currently saved views with transient + * state. */ void clearTransientStateViews() { - if (mTransientStateViews != null) { - mTransientStateViews.clear(); + final SparseArray<View> viewsByPos = mTransientStateViews; + if (viewsByPos != null) { + final int N = viewsByPos.size(); + for (int i = 0; i < N; i++) { + removeDetachedView(viewsByPos.valueAt(i), false); + } + viewsByPos.clear(); } - if (mTransientStateViewsById != null) { - mTransientStateViewsById.clear(); + + final LongSparseArray<View> viewsById = mTransientStateViewsById; + if (viewsById != null) { + final int N = viewsById.size(); + for (int i = 0; i < N; i++) { + removeDetachedView(viewsById.valueAt(i), false); + } + viewsById.clear(); } } @@ -6766,44 +6781,48 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (victim != null) { final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) victim.getLayoutParams(); - int whichScrap = lp.viewType; + final int whichScrap = lp.viewType; activeViews[i] = null; - final boolean scrapHasTransientState = victim.hasTransientState(); - if (!shouldRecycleViewType(whichScrap) || scrapHasTransientState) { - // Do not move views that should be ignored - if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER && - scrapHasTransientState) { + if (victim.hasTransientState()) { + // Store views with transient state for later use. + victim.dispatchStartTemporaryDetach(); + + if (mAdapter != null && mAdapterHasStableIds) { + if (mTransientStateViewsById == null) { + mTransientStateViewsById = new LongSparseArray<View>(); + } + long id = mAdapter.getItemId(mFirstActivePosition + i); + mTransientStateViewsById.put(id, victim); + } else if (!mDataChanged) { + if (mTransientStateViews == null) { + mTransientStateViews = new SparseArray<View>(); + } + mTransientStateViews.put(mFirstActivePosition + i, victim); + } else if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { + // The data has changed, we can't keep this view. removeDetachedView(victim, false); } - if (scrapHasTransientState) { - if (mAdapter != null && mAdapterHasStableIds) { - if (mTransientStateViewsById == null) { - mTransientStateViewsById = new LongSparseArray<View>(); - } - long id = mAdapter.getItemId(mFirstActivePosition + i); - mTransientStateViewsById.put(id, victim); - } else { - if (mTransientStateViews == null) { - mTransientStateViews = new SparseArray<View>(); - } - mTransientStateViews.put(mFirstActivePosition + i, victim); - } + } else if (!shouldRecycleViewType(whichScrap)) { + // Discard non-recyclable views except headers/footers. + if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { + removeDetachedView(victim, false); + } + } else { + // Store everything else on the appropriate scrap heap. + if (multipleScraps) { + scrapViews = mScrapViews[whichScrap]; } - continue; - } - if (multipleScraps) { - scrapViews = mScrapViews[whichScrap]; - } - victim.dispatchStartTemporaryDetach(); - lp.scrappedFromPosition = mFirstActivePosition + i; - scrapViews.add(victim); + victim.dispatchStartTemporaryDetach(); + lp.scrappedFromPosition = mFirstActivePosition + i; + scrapViews.add(victim); - victim.setAccessibilityDelegate(null); - if (hasListener) { - mRecyclerListener.onMovedToScrapHeap(victim); + victim.setAccessibilityDelegate(null); + if (hasListener) { + mRecyclerListener.onMovedToScrapHeap(victim); + } } } } @@ -6812,8 +6831,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** - * Makes sure that the size of mScrapViews does not exceed the size of mActiveViews. - * (This can happen if an adapter does not recycle its views). + * Makes sure that the size of mScrapViews does not exceed the size of + * mActiveViews, which can happen if an adapter does not recycle its + * views. Removes cached transient state views that no longer have + * transient state. */ private void pruneScrapViews() { final int maxViews = mActiveViews.length; @@ -6829,20 +6850,25 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } - if (mTransientStateViews != null) { - for (int i = 0; i < mTransientStateViews.size(); i++) { - final View v = mTransientStateViews.valueAt(i); + final SparseArray<View> transViewsByPos = mTransientStateViews; + if (transViewsByPos != null) { + for (int i = 0; i < transViewsByPos.size(); i++) { + final View v = transViewsByPos.valueAt(i); if (!v.hasTransientState()) { - mTransientStateViews.removeAt(i); + removeDetachedView(v, false); + transViewsByPos.removeAt(i); i--; } } } - if (mTransientStateViewsById != null) { - for (int i = 0; i < mTransientStateViewsById.size(); i++) { - final View v = mTransientStateViewsById.valueAt(i); + + final LongSparseArray<View> transViewsById = mTransientStateViewsById; + if (transViewsById != null) { + for (int i = 0; i < transViewsById.size(); i++) { + final View v = transViewsById.valueAt(i); if (!v.hasTransientState()) { - mTransientStateViewsById.removeAt(i); + removeDetachedView(v, false); + transViewsById.removeAt(i); i--; } } diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java index 0957ab4..e90b460 100644 --- a/core/java/android/widget/CalendarView.java +++ b/core/java/android/widget/CalendarView.java @@ -1210,35 +1210,38 @@ public class CalendarView extends FrameLayout { child = (WeekView) view.getChildAt(offset); } - // Find out which month we're moving into - int month; - if (mIsScrollingUp) { - month = child.getMonthOfFirstWeekDay(); - } else { - month = child.getMonthOfLastWeekDay(); - } - - // And how it relates to our current highlighted month - int monthDiff; - if (mCurrentMonthDisplayed == 11 && month == 0) { - monthDiff = 1; - } else if (mCurrentMonthDisplayed == 0 && month == 11) { - monthDiff = -1; - } else { - monthDiff = month - mCurrentMonthDisplayed; - } - - // Only switch months if we're scrolling away from the currently - // selected month - if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) { - Calendar firstDay = child.getFirstDay(); + if (child != null) { + // Find out which month we're moving into + int month; if (mIsScrollingUp) { - firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK); + month = child.getMonthOfFirstWeekDay(); } else { - firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK); + month = child.getMonthOfLastWeekDay(); + } + + // And how it relates to our current highlighted month + int monthDiff; + if (mCurrentMonthDisplayed == 11 && month == 0) { + monthDiff = 1; + } else if (mCurrentMonthDisplayed == 0 && month == 11) { + monthDiff = -1; + } else { + monthDiff = month - mCurrentMonthDisplayed; + } + + // Only switch months if we're scrolling away from the currently + // selected month + if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) { + Calendar firstDay = child.getFirstDay(); + if (mIsScrollingUp) { + firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK); + } else { + firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK); + } + setMonthDisplayed(firstDay); } - setMonthDisplayed(firstDay); } + mPreviousScrollPosition = currScroll; mPreviousScrollState = mCurrentScrollState; } diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 01ac8fd..4379bf6 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -224,6 +224,8 @@ class FastScroller { mHasPendingDrag = false; } }; + private int mOldItemCount; + private int mOldChildCount; /** * Used to delay hiding fast scroll decorations. @@ -248,6 +250,8 @@ class FastScroller { public FastScroller(AbsListView listView) { mList = listView; mOverlay = listView.getOverlay(); + mOldItemCount = listView.getCount(); + mOldChildCount = listView.getChildCount(); final Context context = listView.getContext(); mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); @@ -258,6 +262,7 @@ class FastScroller { final ImageView trackImage = new ImageView(context); mTrackImage = trackImage; + updateLongList(mOldChildCount, mOldItemCount); int width = 0; // Add track to overlay if it has an image. @@ -445,20 +450,23 @@ class FastScroller { updateLayout(); } - public void onItemCountChanged(int totalItemCount) { - final int visibleItemCount = mList.getChildCount(); - final boolean hasMoreItems = totalItemCount - visibleItemCount > 0; - if (hasMoreItems && mState != STATE_DRAGGING) { - final int firstVisibleItem = mList.getFirstVisiblePosition(); - setThumbPos(getPosFromItemCount(firstVisibleItem, visibleItemCount, totalItemCount)); - } + public void onItemCountChanged(int childCount, int itemCount) { + if (mOldItemCount != itemCount || mOldChildCount != childCount) { + mOldItemCount = itemCount; + mOldChildCount = childCount; - updateLongList(visibleItemCount, totalItemCount); + final boolean hasMoreItems = itemCount - childCount > 0; + if (hasMoreItems && mState != STATE_DRAGGING) { + final int firstVisibleItem = mList.getFirstVisiblePosition(); + setThumbPos(getPosFromItemCount(firstVisibleItem, childCount, itemCount)); + } + + updateLongList(childCount, itemCount); + } } - private void updateLongList(int visibleItemCount, int totalItemCount) { - final boolean longList = visibleItemCount > 0 - && totalItemCount / visibleItemCount >= MIN_PAGES; + private void updateLongList(int childCount, int itemCount) { + final boolean longList = childCount > 0 && itemCount / childCount >= MIN_PAGES; if (mLongList != longList) { mLongList = longList; 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/ImageView.java b/core/java/android/widget/ImageView.java index 7daf798..f05179b 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -563,7 +563,7 @@ public class ImageView extends View { } /** Return the view's optional matrix. This is applied to the - view's drawable when it is drawn. If there is not matrix, + view's drawable when it is drawn. If there is no matrix, this method will return an identity matrix. Do not change this matrix in place but make a copy. If you want a different matrix applied to the drawable, 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/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 5663959..be20d2d 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1330,7 +1330,7 @@ public class PopupWindow { /** * Updates the state of the popup window, if it is currently being displayed, - * from the currently set state. This include: + * from the currently set state. This includes: * {@link #setClippingEnabled(boolean)}, {@link #setFocusable(boolean)}, * {@link #setIgnoreCheekPress()}, {@link #setInputMethodMode(int)}, * {@link #setTouchable(boolean)}, and {@link #setAnimationStyle(int)}. diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 5392a96..6a369a6 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -308,6 +308,11 @@ public class ProgressBar extends View { mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl); a.recycle(); + + // If not explicitly specified this view is important for accessibility. + if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + } } /** diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index 62afd2e..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(); @@ -266,7 +268,7 @@ public class ShareActionProvider extends ActionProvider { * Intent shareIntent = new Intent(Intent.ACTION_SEND); * shareIntent.setType("image/*"); * Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg")); - * shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());</pre> + * shareIntent.putExtra(Intent.EXTRA_STREAM, uri));</pre> * * @param shareIntent The share intent. * diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3f35875..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; } } @@ -8667,6 +8667,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener super.onRtlPropertiesChanged(layoutDirection); mTextDir = getTextDirectionHeuristic(); + + if (mLayout != null) { + checkForRelayout(); + } } TextDirectionHeuristic getTextDirectionHeuristic() { 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/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index ab871fb..942995b 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -372,23 +372,25 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); mAppearingPackages = pkgList; - mChangeType = PACKAGE_TEMPORARY_CHANGE; + mChangeType = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) + ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE; mSomePackagesChanged = true; if (pkgList != null) { onPackagesAvailable(pkgList); for (int i=0; i<pkgList.length; i++) { - onPackageAppeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE); + onPackageAppeared(pkgList[i], mChangeType); } } } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); mDisappearingPackages = pkgList; - mChangeType = PACKAGE_TEMPORARY_CHANGE; + mChangeType = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) + ? PACKAGE_UPDATING : PACKAGE_TEMPORARY_CHANGE; mSomePackagesChanged = true; if (pkgList != null) { onPackagesUnavailable(pkgList); for (int i=0; i<pkgList.length; i++) { - onPackageDisappeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE); + onPackageDisappeared(pkgList[i], mChangeType); } } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 2e5fcec..8728610 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; @@ -113,6 +114,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; @@ -4487,9 +4492,9 @@ 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++) { 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/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 3f5ad86..3785b74 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -192,10 +192,16 @@ public class ZygoteInit { static void closeServerSocket() { try { if (sServerSocket != null) { + FileDescriptor fd = sServerSocket.getFileDescriptor(); sServerSocket.close(); + if (fd != null) { + Libcore.os.close(fd); + } } } catch (IOException ex) { Log.e(TAG, "Zygote: error closing sockets", ex); + } catch (libcore.io.ErrnoException ex) { + Log.e(TAG, "Zygote: error closing descriptor", ex); } sServerSocket = null; 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/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 12ced68..3724a08 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -73,5 +73,5 @@ interface IInputMethodManager { boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme); boolean shouldOfferSwitchingToNextInputMethod(in IBinder token); boolean setInputMethodEnabled(String id, boolean enabled); - oneway void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes); + void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes); } 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/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 4654178..fe1cf72 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -164,8 +164,24 @@ public class ActionMenuPresenter extends BaseMenuPresenter } actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE); + final ActionMenuView menuParent = (ActionMenuView) parent; + final ViewGroup.LayoutParams lp = actionView.getLayoutParams(); + if (!menuParent.checkLayoutParams(lp)) { + actionView.setLayoutParams(menuParent.generateLayoutParams(lp)); + } + return actionView; + } + + @Override + public void bindItemView(final MenuItemImpl item, MenuView.ItemView itemView) { + itemView.initialize(item, 0); + + final ActionMenuView menuView = (ActionMenuView) mMenuView; + final ActionMenuItemView actionItemView = (ActionMenuItemView) itemView; + actionItemView.setItemInvoker(menuView); + if (item.hasSubMenu()) { - actionView.setOnTouchListener(new ForwardingListener(actionView) { + actionItemView.setOnTouchListener(new ForwardingListener(actionItemView) { @Override public ListPopupWindow getPopup() { return mActionButtonPopup != null ? mActionButtonPopup.getPopup() : null; @@ -182,24 +198,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter } }); } else { - actionView.setOnTouchListener(null); + actionItemView.setOnTouchListener(null); } - - final ActionMenuView menuParent = (ActionMenuView) parent; - final ViewGroup.LayoutParams lp = actionView.getLayoutParams(); - if (!menuParent.checkLayoutParams(lp)) { - actionView.setLayoutParams(menuParent.generateLayoutParams(lp)); - } - return actionView; - } - - @Override - public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) { - itemView.initialize(item, 0); - - final ActionMenuView menuView = (ActionMenuView) mMenuView; - ActionMenuItemView actionItemView = (ActionMenuItemView) itemView; - actionItemView.setItemInvoker(menuView); } @Override diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java index 4882adc..e1bb3621 100644 --- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java @@ -163,7 +163,7 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - mMenu.performItemAction(mAdapter.getItem(position), 0); + mMenu.performItemAction(mAdapter.getItem(position), this, 0); } @Override diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index aff697a..5464284 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -247,11 +247,17 @@ public class MenuBuilder implements Menu { startDispatchingItemsChanged(); } - private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu) { + private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu, + MenuPresenter preferredPresenter) { if (mPresenters.isEmpty()) return false; boolean result = false; + // Try the preferred presenter first. + if (preferredPresenter != null) { + result = preferredPresenter.onSubMenuSelected(subMenu); + } + for (WeakReference<MenuPresenter> ref : mPresenters) { final MenuPresenter presenter = ref.get(); if (presenter == null) { @@ -386,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 @@ -399,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); } @@ -865,6 +878,10 @@ public class MenuBuilder implements Menu { } public boolean performItemAction(MenuItem item, int flags) { + return performItemAction(item, null, flags); + } + + public boolean performItemAction(MenuItem item, MenuPresenter preferredPresenter, int flags) { MenuItemImpl itemImpl = (MenuItemImpl) item; if (itemImpl == null || !itemImpl.isEnabled()) { @@ -889,7 +906,7 @@ public class MenuBuilder implements Menu { if (providerHasSubMenu) { provider.onPrepareSubMenu(subMenu); } - invoked |= dispatchSubMenuSelected(subMenu); + invoked |= dispatchSubMenuSelected(subMenu, preferredPresenter); if (!invoked) close(true); } else { if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) { 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..467d42e --- /dev/null +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -0,0 +1,308 @@ +/* + * 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 TRANSLATION_MIN_ALPHA = 0.5f; + + 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; + + 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() * 16; + 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); + updateSwiping(ev); + updateDismiss(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; + mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2; + } + } + + private void updateDismiss(MotionEvent ev) { + float deltaX = ev.getRawX() - mDownX; + if (!mDismissed) { + mVelocityTracker.addMovement(ev); + mVelocityTracker.computeCurrentVelocity(1000); + + float velocityX = mVelocityTracker.getXVelocity(); + float absVelocityX = Math.abs(velocityX); + float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); + + if (deltaX > getWidth() / 2) { + mDismissed = true; + } else if (absVelocityX >= mMinFlingVelocity + && absVelocityX <= mMaxFlingVelocity + && absVelocityY < absVelocityX / 2 + && velocityX > 0 + && deltaX > 0) { + 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() / 2) { + 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()); + } +} |
